Open moneytoo opened 9 years ago
I haven't worked with multidex apps yet, but I see no reason why it shouldn't work if the correct classloader is used at the correct time. Check this out: https://developer.android.com/tools/building/multidex.html#mdex-pre-l https://developer.android.com/tools/support-library/features.html#multidex
I assume it will work out of the box with ART. For Dalvik, there is no multidex support in pure Android, apps need to use the support library. As far as I understand the source code of that library, it extracts the additional dex files and adds them to the existing classloader via reflection. The app needs to trigger this in Application.attachBaseContext(), or use the MultiDexApplication class which already does this.
handleLoadPackage() is called as early as possible to allow modules to modify as much as possible. At that point, the app doesn't know anything about the additional dex files yet, so obviously you can't access those classes either. It's similar to apps loading additional dex files themselves (I think Facebook does), either by creating new classloaders or extending existing ones.
It's not an option to execute that callback later, as it would break existing modules. The workaround you have shown basically is the fix already. For the current implementation of the support library, it would be the cleanest way to hook MultiDex.install() and place further hooks after it has been executed. We could discuss whether the Xposed framework could offer a helper for this, but chances are that there have been and will be different implementations that don't work like this. Some apps might not follow that advice, but call the method later. Also, Proguard might obfuscate the names.
It really depends on the target app how early you need to place the hook. For most, I guess it's ok to hook something during the early stages of the Application lifecycle.
At least for those apps following the recommendation, I think the best (early and generic) place to hook is Application.attach(): https://github.com/android/platform_frameworks_base/blob/master/core/java/android/app/Application.java#L180
This calls attachBaseContext(), which would be overridden in case of Multidex, so hooking Application.attachBaseContext() would not be enough. Placing hooks after the attach() method has finished should work fine. But again, the only thing the framework could offer would be a helper which places this particular hook. Doesn't seem like a big saving.
I think helpers are definitely good thing. It's more developer-friendly. Not every developer may know what exactly needs to be hooked (in this case - Application.attach() (as you said)), but simple helper method may be helpful.
Well, but from a helper method, you'd expect that it works in all or at least most cases. I already listed some limitations, such as apps not following the recommendations or using their own ways to load additional classes. It's easy to add new APIs, but hard to get rid of them, even if they were bad. I suggest that people test it first with something like this in handleLoadPackage() for their target app (untested):
findAndHookMethod(Application.class, "attach", Context.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
// place your hooks here, it should work with lpparam.classLoader
}
});
If that works fine as expected in (almost) all cases, we can think about a helper method. But even then, the code wouldn't be shorter, and developers would still need to know when to use it.
Maybe better solution is put this somewhere in Xposed Wiki and make good description of it.
Yes. I've wanted to have a "best practices" page (or multiple pages) for a long time, about good style, efficency, tricks, .... But you know how it is with the documentation. ;)
I hate writing docs or "how to do" manuals :D Maybe it's time to create something like that. Module developers could write their tricks etc. Basically, I shared this idea on XDA: http://forum.xda-developers.com/xposed/xposed-wiki-t2986692. But yet no answer.
But I think there should be TODO list for Xposed. What needs to be done for next release, etc..
Thanks, rovo. I appreciate the work on this! In my original gist, hooking onCreate was purely to avoid ProGuard. attach definitely looks better though. As you mentioned it's probably not a good idea to have helpers that only work sometimes, and more importantly, do not reliably report failure when it does not work.
Putting significant effort into documentation might not be a good use of time unless Xposed for ART has a backwards compatible API. And also defining the "target audience" is a big part of writing documentation like this. Probably not worth writing the docs as if they're for Android/Java beginners.
I head on XDA that there might be problems with XSharedPreferences on ART. I hope @rovo89 knows how to solve it :)
@pylerSM Not to get off topic but the only problems I've experienced with XSharedPreferences were permission related and not necessarily bugs with the XSP class itself. "Unfortunately" Android prefers and defaults to MODE_PRIVATE
for PreferenceManager instances and consequently PreferenceActivity instances. There's no way to set the PreferenceManager's SharedPreferences mode from a PrefAct since its PrefManager field is private. I avoided XSP altogether and used a ContentProvider. It may be overkill but works pretty nicely. I do need to refactor that module though.
Ironically you could probably use Xposed hooks to overcome the above limitations...
getPreferenceManager().setSharedPreferencesMode(MODE_WORLD_READABLE); addPreferencesFromResource(R.xml.preferences);
This is not working for you? I use this code in my modules and their settings work well.
Putting significant effort into documentation might not be a good use of time unless Xposed for ART has a backwards compatible API. And also defining the "target audience" is a big part of writing documentation like this. Probably not worth writing the docs as if they're for Android/Java beginners.
Similar? The API would be exactly the same. ;) There might be some things to consider like for every new, big release, however that's not caused by ART but by other platform changes...
I head on XDA that there might be problems with XSharedPreferences on ART.
People seem to think Lollipop == ART, but ART is only one of the challenges. SELinux indeed makes things more complicated, you can't open every file from everywhere even if it's world-readable.
Anyway, the answer to whether Xposed supports multidex is in my comment above, and I recommend to try and use the example code I provided there.
getPreferenceManager().setSharedPreferencesMode(MODE_WORLD_READABLE); addPreferencesFromResource(R.xml.preferences);
This is not working for you? I use this code in my modules and their settings work well.
Oops. I missed that method. Might still be problematic if the preference XML file was created/added/restored without having the modified permissions set first. Hopefully calling makeWorldReadable
early and often would avoid that. I was honestly surprised to see the MODE_WORLD_X constants still available in API 21.
Similar? The API would be exactly the same. ;) There might be some things to consider like for every new, big release, however that's not caused by ART but by other platform changes...
Great to hear, thanks.
Check this https://github.com/pylerSM/XInstaller/blob/master/src/com/pyler/xinstaller/Utils.java#L155
In my module I implemented option to backup and restore settings. So I open prefs in word-readable mode and permissions stay ok.
Oh, I was referring to backups/restores done outside of the app like TitaniumBackup or whatever Google's multi-device app syncing feature is called these days.
Using prefs.makeWorldReadable() method in every hook would probably solve this issue.
The cleanest way of app backuping for me is adb backup. Google's backup feature in Lollipop is nice, but it requires implementation in every app. I would expect that system would handle it itself since developer could set manifest flag to allow app backup.
Using prefs.makeWorldReadable() method in every hook would probably solve this issue.
Are you aware that this can only be effective when the process is running as root, i.e. in initZygote()?
Uhm, I didn't know that it will not work in same cases. Maybe I missed it in docs.
makeWorldReadable
just calls setReadable(true, false)
on the File
backing the preferences with the same authority as the caller. So it'll only work from within your app's context or during initZygote
Correct. It was only introduced because some recovery "permission fix" scripts remove the world-readable bit. It's not replacement for using MODE_WORLD_READABLE when opening settings.
I have already added some more documentation locally, I'll just if I also added a clear warning about this.
Just gave this a test and @rovo89's Application.attach method worked perfectly. Updated my gist to reflect this. Only disadvantage is that attach is a private API and could change at any time (even between API levels). However given the nature of Android's internals this is unlikely.
@kmark Excuse me, would you tell me that which platform level(API Level) you have tested? I tested in Android 4.4.4 (nexus4) and it just failed~
Based on various comments/threads (see below) I understand there's no support for Multidex in Xposed. Most probably get in touch with it in Google Play Services which is over one dex in size.
Though there's nice workaround for GMS by @kmark: https://gist.github.com/kmark/d6a19bc50c6ba7cc8f1f it would be nice to know if multidex support is something that can be expected from Xposed.
http://forum.xda-developers.com/xposed/loading-class-classes2-dex-t2852950 http://forum.xda-developers.com/xposed/multidex-support-t2963645 https://github.com/rsteckler/unbounce-android#why-was-this-such-a-pain-to-fix