FORUMS
Remove All Ads from XDA

[HELP] Can we use native libs in a module? Mine dies as soon as a lib is added.

206 posts
Thanks Meter: 52
 
By looxonline, Senior Member on 1st April 2019, 02:45 PM
Post Reply Email Thread
Hi,

First, a disclaimer.

I am a Java and xposed noob. My background is in embedded C development so I can get by with some simple Java code and thanks to the great tutorials online I have been able to put together an xposed module but I'm struggling with a problem that is beyond my abilities now and am reaching out to the community for help.

Next, the background.

I have an Android head unit in my car. There is an app that provides me with CarPlay functionality but none of the controls on the steering wheel work with the app. When I analysed the code I found that they handle all of their button inputs using proprietary methods that do not inject an event into any input streams. I wrote an xposed module to hook the button press methods and then inject a proper input into one of the event streams.

Initially I tried to use the command line 'input' command to do this but since it is a Java app and takes about 1s to load it was too slow. My only other option was to create a virtual device on an input stream that I could then use to inject keypresses through the hooked method. To create a virtual device I needed to write C code that my xposed module would be able to access through the JNI. Long story short, after some pain I was able to get the native library integrated into the project and compiling using the NDK.

Finally, the problem.

When I was using the module without the native library it worked but just with a large delay because of the time it takes to load the 'input' java app. I was able to see logs from the module in the logcat as I hooked the method and as I went through the various actions within the hook.

As soon as I introduce the native library though the entire xposed module just stops running completely. I do not get any logs from the module even though I have installed, activated and rebooted. It shows up in the xposed installer but it just does nothing. The funny thing is that this happens even if I make no reference whatsoever to any native functions within the library. All I need to do to kill the module is to build it with the System.loadlibrary line in the Main.java uncommented. As soon as I comment that piece of code out the module starts to hook the function and output logs again. Below is the code from the Main.Java that I am referring to. I am happy to make any manifest, C and gradle files available too. Looking for any ideas as to why the module dies completely as soon as I include this...

Code:
package projects.labs.spike.zlink_xposed_swc;

import de.robv.android.xposed.XposedBridge;
import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;
import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.IXposedHookZygoteInit;
import de.robv.android.xposed.XSharedPreferences;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.callbacks.XC_LoadPackage;
import de.robv.android.xposed.XposedHelpers;

import android.app.AndroidAppHelper;
import android.content.Intent;
import android.os.Bundle;
import android.content.Context;

/* shellExec and rootExec methods */
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ByteArrayOutputStream;
import android.view.KeyEvent;

import android.media.AudioManager;

public class Main implements IXposedHookLoadPackage {

    public static final String TAG = "ZLINK_XPOSED ";

    public static void log(String message) {
        XposedBridge.log("[" + TAG + "] " + message);
    }

    //public native int CreateVirtualDevice();

    //public native int SendPrev();

    @Override
    public void handleLoadPackage(final XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
        log("handleLoadPackage: Loaded app: " + lpparam.packageName);

        if (lpparam.packageName.equals("com.syu.ms")) {

            findAndHookMethod("module.main.HandlerMain", lpparam.classLoader, "mcuKeyRollLeft", new XC_MethodHook() {
                @Override
                protected void afterHookedMethod(XC_MethodHook.MethodHookParam param) throws Throwable {
                    // previous
                    log("PREVKEYHIT");

                    //rootExec("input keyevent 88");
                    log("EVENTSENT");
                    //Below was trying to use media keys which zlink never responded to...
 /*                   Context context = (Context) AndroidAppHelper.currentApplication();
                    AudioManager mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);

                    KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_PREVIOUS);
                    mAudioManager.dispatchMediaKeyEvent(event);
                    KeyEvent event2 = new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_PREVIOUS);
                    mAudioManager.dispatchMediaKeyEvent(event2);*/



                    //Below is the failed broadcast intent method...
                    /*Context mcontext = (Context) AndroidAppHelper.currentApplication();
                    Intent i = new Intent("com.android.music.musicservicecommand");
                    i.putExtra("command", "pause");
                    mcontext.sendBroadcast(i);*/
                }
            });

        }
    }

    public static String rootExec(String... strings) {
        String res = "";
        DataOutputStream outputStream = null;
        InputStream response = null;
        try {
            Process su = Runtime.getRuntime().exec("su");
            outputStream = new DataOutputStream(su.getOutputStream());
            response = su.getInputStream();

            for (String s : strings) {
                s = s.trim();
                outputStream.writeBytes(s + "\n");
                outputStream.flush();
            }

            outputStream.writeBytes("exit\n");
            outputStream.flush();
            try {
                su.waitFor();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            res = readFully(response);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            Closer.closeSilently(outputStream, response);
        }
        return res;
    }

    public static String readFully(InputStream is) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int length = 0;
        while ((length = is.read(buffer)) != -1) {
            baos.write(buffer, 0, length);
        }
        return baos.toString("UTF-8");
    }

    static {
        System.loadLibrary("native-lib");
    }
}
2nd April 2019, 11:11 PM |#2  
Senior Member
Thanks Meter: 240
 
More
Have you tried capturing an ADB log _during the bootup_?

Xposed bugs in general are unfortunaley hard to identify and harder to fix, since the underlying code isn't well understood and/or maintained by many people.
3rd April 2019, 03:43 PM |#3  
OP Senior Member
Flag Johannesburg
Thanks Meter: 52
 
Donate to Me
More
Quote:
Originally Posted by Namnodorel

Have you tried capturing an ADB log _during the bootup_?

Xposed bugs in general are unfortunaley hard to identify and harder to fix, since the underlying code isn't well understood and/or maintained by many people.

Thanks for the response. I think that I have it figured out. The System.loadlibrary method looks for the native library within a path relative to the process that it is running within. The code within the apk ultimately does not run within that apk process, it runs within the xposed process. You therefore need to give xposed an absolute path to the library using the system.load method instead. Going to do some fiddling tonight and see if it works.
4th April 2019, 08:03 AM |#4  
C3C076's Avatar
Recognized Contributor
Flag Bratislava
Thanks Meter: 24,177
 
Donate to Me
More
Quote:
Originally Posted by looxonline

Thanks for the response. I think that I have it figured out. The System.loadlibrary method looks for the native library within a path relative to the process that it is running within. The code within the apk ultimately does not run within that apk process, it runs within the xposed process. You therefore need to give xposed an absolute path to the library using the system.load method instead. Going to do some fiddling tonight and see if it works.

What about an alternative without using library we discussed earlier? Are you planning to test this as well?
If so, please let me know how it went.
4th April 2019, 08:47 AM |#5  
OP Senior Member
Flag Johannesburg
Thanks Meter: 52
 
Donate to Me
More
Quote:
Originally Posted by C3C076

What about an alternative without using library we discussed earlier? Are you planning to test this as well?
If so, please let me know how it went.

I didn't try it yet for two reasons.

1.) From the research I have done it seems as if my app would need the system INJECT_EVENTS permission in order to send keypress events outside of its own process. I cannot get this permission unless I sign my apk with the system cert that the ROM is compiled with. Maybe the method that you are using in the suggestion does not need this cert? Have you personally used this to inject key events across processes? I did see that you are getting the context of the system input service so maybe that solves this issue if the request appears to come from that PID...???

2.) The unit that I am working with has only two input devices and none of them have the keycodes I require. Does your method use a completely virtual device that is created on the fly? If so then it could work well without the need for me to create an HID input device.

I mostly was just on a role with the method that I was trying and I didn't want to turn back since I was so far down the road. I'm sure you understand how addictive certain challenges become and its quite fun to try to get them working even if they may not be the most optimal way.

In any case I managed to get the native library working last night and can successfully convince Android that I have a real HID keyboard plugged in and then send key events through that keyboard. Still not done though as there are a few hiccups that need solving. May still try your original suggestion. Thanks
5th April 2019, 08:14 AM |#6  
C3C076's Avatar
Recognized Contributor
Flag Bratislava
Thanks Meter: 24,177
 
Donate to Me
More
Quote:
Originally Posted by looxonline

I didn't try it yet for two reasons.

1.) From the research I have done it seems as if my app would need the system INJECT_EVENTS permission in order to send keypress events outside of its own process. I cannot get this permission unless I sign my apk with the system cert that the ROM is compiled with. Maybe the method that you are using in the suggestion does not need this cert? Have you personally used this to inject key events across processes? I did see that you are getting the context of the system input service so maybe that solves this issue if the request appears to come from that PID...???

2.) The unit that I am working with has only two input devices and none of them have the keycodes I require. Does your method use a completely virtual device that is created on the fly? If so then it could work well without the need for me to create an HID input device.

I mostly was just on a role with the method that I was trying and I didn't want to turn back since I was so far down the road. I'm sure you understand how addictive certain challenges become and its quite fun to try to get them working even if they may not be the most optimal way.

In any case I managed to get the native library working last night and can successfully convince Android that I have a real HID keyboard plugged in and then send key events through that keyboard. Still not done though as there are a few hiccups that need solving. May still try your original suggestion. Thanks

I see.

1) Depends on in what process (package) your hooks are running within because permissions of this process apply of course, not the permissions you define in your module's manifest.
I am using key injecting method within "android" process (package) which means it works without me needing to worry about INJECT_EVENTS permission as "android" process already has it.
By the way, missing permissions are not of a big issue when developing with xposed as you can really do some magic with it.
E.g. I was adding some functionality to SystemUI that required some additional permissions that SystemUI typically lacks. So my module takes care of it.
https://github.com/GravityBox/Gravit...anter.java#L75
The Following User Says Thank You to C3C076 For This Useful Post: [ View ] Gift C3C076 Ad-Free
5th April 2019, 08:38 AM |#7  
OP Senior Member
Flag Johannesburg
Thanks Meter: 52
 
Donate to Me
More
Quote:
Originally Posted by C3C076

I see.

1) Depends on in what process (package) your hooks are running within because permissions of this process apply of course, not the permissions you define in your module's manifest.
I am using key injecting method within "android" process (package) which means it works without me needing to worry about INJECT_EVENTS permission as "android" process already has it.
By the way, missing permissions are not of a big issue when developing with xposed as you can really do some magic with it.
E.g. I was adding some functionality to SystemUI that required some additional permissions that SystemUI typically lacks. So my module takes care of it.
https://github.com/GravityBox/Gravit...anter.java#L75


Wow! I had no idea that you can use an xposed helper function to grant permissions to whatever process you are hooked within like that. That is VERY cool. Thanks so much for sharing
Post Reply Subscribe to Thread

Tags
native

Guest Quick Reply (no urls or BBcode)
Message:
Previous Thread Next Thread
Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes