Hiya,
So I have the hook I need, but I need to get a context. The object I'm in doesn't have a Context member, but it does have an ActivityManager object, and ActivityManager has a context.
The problem is that ActivityManager is an internal class, so I can't do this:
Code:
ActivityManager am = (ActivityManager) XposedHelpers.getObjectField(param.thisObject, "mAm");
Context context = (Context) XposedHelpers.getObjectField(am, "mContext");
I tried this, on a hunch:
Code:
Context context = (Context) XposedHelpers.getObjectField(param.thisObject, "mAm$mContext");
but that didn't work either.
Is there a way to get a field from a nested, internal object?
Thanks!
Ryan
Nevermind. Dumb question. Obviously, you do this:
Code:
Object am = (Object)XposedHelpers.getObjectField(param.thisObject, "mAm");
Context context = (Context) XposedHelpers.getObjectField(am, "mContext");
Related
Okay, so I have some resources in my module/app. Say, a Drawable. Unfortunately, when I try to use them (to create a UI, say) from within handleLoadPackage, well, I can't. I can access R.drawable.abc just fine; but for some reason, all I get is a strange white rectangle instead of the actual Drawable.
How do I work around this issue? Thank you!
This is because your hook is not really part of your app - it runs in the hooked app. To access your own resources, you need to create a context for your app first.
You're going to need a general context first. If you can't get any from the class you're hooking, you can try AndroidAppHelper.currentApplication(). Then, use that context to create a context for your own app (see Context.createContext(...)). You can then access your resources using that context.
GermainZ said:
This is because your hook is not really part of your app - it runs in the hooked app. To access your own resources, you need to create a context for your app first.
You're going to need a general context first. If you can't get any from the class you're hooking, you can try AndroidAppHelper.currentApplication(). Then, use that context to create a context for your own app (see Context.createContext(...)). You can then access your resources using that context.
Click to expand...
Click to collapse
I tried this:
Code:
Context c = tv.getContext().createPackageContext("package.name", Context.CONTEXT_IGNORE_SECURITY);
int resId = c.getResources().getIdentifier("ic_launcher", "drawable", "package.name");
dialog.setFeatureDrawableResource(Window.FEATURE_LEFT_ICON, resId);
No dice. I'm sure using c.getResources.getDrawable would work, but my dialog method requires the ID, not the Drawable itself.
perseus0807 said:
I tried this:
Code:
Context c = tv.getContext().createPackageContext("package.name", Context.CONTEXT_IGNORE_SECURITY);
int resId = c.getResources().getIdentifier("ic_launcher", "drawable", "package.name");
dialog.setFeatureDrawableResource(Window.FEATURE_LEFT_ICON, resId);
No dice. I'm sure using c.getResources.getDrawable would work, but my dialog method requires the ID, not the Drawable itself.
Click to expand...
Click to collapse
Yeah, but that resource ID will always be resolved using the target app's resources. If you can't use the the drawable directly, you can try this:
Code:
private static String MODULE_PATH = null;
private int mFakeId = 0;
public void initZygote(StartupParam startupParam) throws Throwable {
MODULE_PATH = startupParam.modulePath;
}
public void handleInitPackageResources(InitPackageResourcesParam resparam) throws Throwable {
if (!resparam.packageName.equals("your.target.app"))
return;
XModuleResources modRes = XModuleResources.createInstance(MODULE_PATH, resparam.res);
mFakeId = resparam.res.addResource(modRes, R.drawable.ic_launcher);
}
Then use mFakeId in your callback, which should run after the resources have been initialized. addResource() generates an ID for you an sets up a resource replacement for that non-existent ID to the item in your own resources.
rovo89 said:
Yeah, but that resource ID will always be resolved using the target app's resources. If you can't use the the drawable directly, you can try this:
Code:
private static String MODULE_PATH = null;
private int mFakeId = 0;
public void initZygote(StartupParam startupParam) throws Throwable {
MODULE_PATH = startupParam.modulePath;
}
public void handleInitPackageResources(InitPackageResourcesParam resparam) throws Throwable {
if (!resparam.packageName.equals("your.target.app"))
return;
XModuleResources modRes = XModuleResources.createInstance(MODULE_PATH, resparam.res);
mFakeId = resparam.res.addResource(modRes, R.drawable.ic_launcher);
}
Then use mFakeId in your callback, which should run after the resources have been initialized. addResource() generates an ID for you an sets up a resource replacement for that non-existent ID to the item in your own resources.
Click to expand...
Click to collapse
This works, thank you so much! Bit stupid of me, I should have inferred this from the tutorial.
perseus0807 said:
I tried this:
Code:
Context c = tv.getContext().createPackageContext("package.name", Context.CONTEXT_IGNORE_SECURITY);
int resId = c.getResources().getIdentifier("ic_launcher", "drawable", "package.name");
dialog.setFeatureDrawableResource(Window.FEATURE_LEFT_ICON, resId);
No dice. I'm sure using c.getResources.getDrawable would work, but my dialog method requires the ID, not the Drawable itself.
Click to expand...
Click to collapse
To add to what rovo said, you could've use getDrawable(…) instead of getIdentifier(…) here, which would've given you Drawable (that you can then use) instead of a resource ID that won't work in the hooked app.
Thank you, it works!!
I'm a newbee to xposed module development. I searched sometimes in this forum and viewed some open-source mudules on github, but I still don't know how to reload prefs in my module.
I try to change the setting repeatedly, but logcat always displays that prefs returned a false. My code is here below:
This is the main class loaded by xposed:
Code:
public class Main implements IXposedHookLoadPackage, IXposedHookZygoteInit, IXposedHookInitPackageResources{
private static XSharedPreferences prefs = new XSharedPreferences(Main.class.getPackage().getName());
....
SOME HACKS
....
private BroadcastReceiver xReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
context = mContext;
if (intent.getAction().equals("xxx.xxx.SETTING_CHANGED")){
prefs.makeWorldReadable(); // Wether this line is added or not, the result is same.
prefs.reload();
Log.d(TAG, String.valueOf(prefs.getBoolean("key", false)));
}
}
};
}
This is the setting screen class:
Code:
public class Setting extends PreferenceActivity implements OnSharedPreferenceChangeListener{
ListPreference lp;
ListPreference _lp;
EditTextPreference etp;
CheckBoxPreference cbp;
@SuppressWarnings("deprecation")
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.setting);
getPreferenceManager().setSharedPreferencesMode(MODE_WORLD_READABLE);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
prefs.registerOnSharedPreferenceChangeListener(this);
}
@SuppressWarnings("deprecation")
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
Intent intent = new Intent();
intent.setAction("xxx.xxx.SETTING_CHANGED");
Setting.this,sendBroadcast(intent);
}
From memory, I believe the XSharedPreferences constructor takes the preferences' file name, which is usually something like "com.mypackage_preferences" and not only "com.mypackage".
Note that you don't need to use makeWorldReadable in your module since you're doing that in your preferences activity.
GermainZ said:
From memory, I believe the XSharedPreferences constructor takes the preferences' file name, which is usually something like "com.mypackage_preferences" and not only "com.mypackage".
Note that you don't need to use makeWorldReadable in your module since you're doing that in your preferences activity.
Click to expand...
Click to collapse
Thanks for your reply. But I can't understand clearly and I'm even more confused.
Code:
XSharedPreferences prefs = new XSharedPreferences(Main.class.getPackage().getName());
This works well. Read preferences correctly.
In the source code of XSharedPreference class. The function "XSharedPreferences(String packageName)" will call "XSharedPreferences(String packageName, String prefFileName)" and complete the default preferences file name. I don't think I need to use full path and file name?
I still don't know how to reload settings correctly. Could you give me a copy of detailed code to work perfectly?
Awating your reply.
neverweep said:
Thanks for your reply. But I can't understand clearly and I'm even more confused.
Code:
XSharedPreferences prefs = new XSharedPreferences(Main.class.getPackage().getName());
This works well. Read preferences correctly.
In the source code of XSharedPreference class. The function "XSharedPreferences(String packageName)" will call "XSharedPreferences(String packageName, String prefFileName)" and complete the default preferences file name. I don't think I need to use full path and file name?
I still don't know how to reload settings correctly. Could you give me a copy of detailed code to work perfectly?
Awating your reply.
Click to expand...
Click to collapse
If I'm not mistaken, this:
Code:
XSharedPreferences prefs = new XSharedPreferences(Main.class.getPackage().getName());
… will load an "empty" preferences file (because it won't exist) so everything will have the default value. Try something like this instead:
Code:
XSharedPreferences prefs = new XSharedPreferences(Main.class.getPackage().getName() + "_preferences");
As for reloading preferences, you seem to be doing it correctly (XSharedPreferences.reload method), it's just that it's not working as intended for the reason explained above.
No, neverweep's code:
Code:
XSharedPreferences prefs = new XSharedPreferences(Main.class.getPackage().getName());
is good. I also use such code in my modules and everything works.
Rovo89 has code to add "_preferences" automatically (link: https://github.com/rovo89/XposedBri...bv/android/xposed/XSharedPreferences.java#L37)
I also had some trouble figuring out how to reload prefs in realtime, but thank's to this thread and to some attempts, I could finally figure it out.
Here's the code if this can help someone (plus the preference management is very simple so it should be a very easy example to follow):
on github (cannot post links yet):
Code:
lrq3000/XposedJitteryGyroFix/blob/14d7e93949bbfd5cfc31ce30482eff9860c35a49/GyroscopeNoiseFilter/src/net/kajos/gyronoisefilter/GyroscopeNoiseFilter.java#L50
hi all.
i created an xposed module but, inside my app, i also have what a standard android app has, like res/strings.xml.
now, even if the entry point is IXposedHookLoadPackage.handleLoadPackage() and then i don't have an android app context, i sometimes need to access my app package name and to my app strings but context.getPackageName() and context.getResources() don't return what i want.
what's the proper way to do it?
thanks a lot.
Do you already have a context? If not, you can use AndroidAppHelper.currentApplication().
To create a context for your application from an existing context, use Context.createPackageContext.
GermainZ said:
Do you already have a context? If not, you can use AndroidAppHelper.currentApplication().
To create a context for your application from an existing context, use Context.createPackageContext.
Click to expand...
Click to collapse
hi and thanks for your reply.
i tried this code but it returns "android" as the package name:
Code:
Context context = AndroidAppHelper.currentApplication();
String packageName = context.getPackageName();
am i missing something?
This is my way of accessing my app context:
Java:
XposedBridge.hookAllConstructors(<the class you want to hook>, new XC_MethodHook() {
[user=439709]@override[/user]
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
log("initializing");
//The context that you mod (usually android)
//It happens that the constructor of the constructor to be hooked has the context as the first argument
//I think AndroidAppHelper.currentApplication() is a more general way to do it
Context modContext = (Context) param.args[0];
//Context of your app
Context appContext = modContext.createPackageContext(
PACKAGE_NAME, Context.CONTEXT_IGNORE_SECURITY);
Resources modRes = modContext.getResources();
Resources appRes = appContext.getResources();
...
}
});
where PACKAGE_NAME is a static field like this
Java:
public static final String PACKAGE_NAME = <Your class>.class.getPackage().getName();
Not sure if there are other alternatives. This is just my way to doing it in my modules
This helped a lot! Thanks!
I am trying to use m_BTArrayAdapter.getCount()
However this function returns 0 regardless of what is in the spinner popup.
I found some doco on this function: http://developer.android.com/reference/android/widget/ArrayAdapter.html#getCount%28%29
"public int getCount ()"
That is - no description. So what does that mean? In android Studio this function is redundant?
There are no other functions that I can see that tell you how many items there are in an ArrayAdapter or in the spinner control itself.
Surely I don't have to resort to a counter in my BT listener function to keep count of how many items are added to the adapter????
Code:
void setupBTListener()
{
m_BTArrayAdapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_dropdown_item);
final Spinner spinnerBluetooth = (Spinner)findViewById(id.spinner_bluetooth);
spinnerBluetooth.setAdapter(m_BTArrayAdapter);
Can anyone explain to me why m_BTArrayAdapter.getCount() returns non zero inside my listener function but zero in the code below?
Code:
m_BTArrayAdapter.clear();
if (m_BTAdapter.isDiscovering())
m_BTAdapter.cancelDiscovery();
m_BTAdapter.startDiscovery();
long longStart = System.currentTimeMillis(),
longTimer;
boolean bDone = false;
while (m_BTAdapter.isDiscovering() && !bDone)
{
longTimer = System.currentTimeMillis();
//bDone = (longTimer - longStart) < 5000;
}
bEnable = m_BTArrayAdapter.getCount() > 0;
spinnerBluetooth.setEnabled(bEnable);
buttonConnect.setEnabled(bEnable);
buttonSearch.setEnabled(true);
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");
}
[COLOR="Red"] static {
System.loadLibrary("native-lib");
}[/COLOR]
}
The issue with native library is quite strange and I cannot help with it as my experience with native libs is zero.
But maybe try a different method of injecting media key events.
Create a method:
Code:
void injectKey(int keyCode) {
try {
final long eventTime = SystemClock.uptimeMillis();
final InputManager inputManager = (InputManager)
mContext.getSystemService(Context.INPUT_SERVICE);
int flags = KeyEvent.FLAG_FROM_SYSTEM;
XposedHelpers.callMethod(inputManager, "injectInputEvent",
new KeyEvent(eventTime - 50, eventTime - 50, KeyEvent.ACTION_DOWN,
keyCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, flags,
InputDevice.SOURCE_KEYBOARD), 0);
XposedHelpers.callMethod(inputManager, "injectInputEvent",
new KeyEvent(eventTime - 50, eventTime - 25, KeyEvent.ACTION_UP,
keyCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, flags,
InputDevice.SOURCE_KEYBOARD), 0);
} catch (Throwable t) {
// something went wrong
XposedBridge.log(t.getMessage());
}
}
Then just do: injectKey(KeyEvent.KEYCODE_MEDIA_PREVIOUS);
And maybe try playing with different KeyEvent flags and attrs.
Thanks so much for this suggestion! Any idea if this injects at a java level or if it depends on there being a keyboard input device available on one of the /dev/input/eventX streams? The android device that I am using has no keyboard available on any of those input streams. Will give it a try nonetheless
C3C076 said:
The issue with native library is quite strange and I cannot help with it as my experience with native libs is zero.
But maybe try a different method of injecting media key events.
Create a method:
Code:
void injectKey(int keyCode) {
try {
final long eventTime = SystemClock.uptimeMillis();
final InputManager inputManager = (InputManager)
mContext.getSystemService(Context.INPUT_SERVICE);
int flags = KeyEvent.FLAG_FROM_SYSTEM;
XposedHelpers.callMethod(inputManager, "injectInputEvent",
new KeyEvent(eventTime - 50, eventTime - 50, KeyEvent.ACTION_DOWN,
keyCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, flags,
InputDevice.SOURCE_KEYBOARD), 0);
XposedHelpers.callMethod(inputManager, "injectInputEvent",
new KeyEvent(eventTime - 50, eventTime - 25, KeyEvent.ACTION_UP,
keyCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, flags,
InputDevice.SOURCE_KEYBOARD), 0);
} catch (Throwable t) {
// something went wrong
XposedBridge.log(t.getMessage());
}
}
Then just do: injectKey(KeyEvent.KEYCODE_MEDIA_PREVIOUS);
And maybe try playing with different KeyEvent flags and attrs.
Click to expand...
Click to collapse
looxonline said:
Thanks so much for this suggestion! Any idea if this injects at a java level or if it depends on there being a keyboard input device available on one of the /dev/input/eventX streams? The android device that I am using has no keyboard available on any of those input streams. Will give it a try nonetheless
Click to expand...
Click to collapse
Simply use whatever InputDevice that you think should work in your case.
The method basically calls this:
https://android.googlesource.com/pl.../android/hardware/input/InputManager.java#869
which is then propagated to Input Manager Service here:
https://android.googlesource.com/pl...oid/server/input/InputManagerService.java#598
which then calls nativeInjectInputEvent