DexPatcher / dexpatcher-gradle

Modify Android applications at source-level in Android Studio
https://dexpatcher.github.io/
GNU General Public License v3.0
83 stars 17 forks source link

Adding an Android library causes resource merger to fail with a "has already been defined" conflict #13

Open Unbrick opened 7 years ago

Unbrick commented 7 years ago

Situation is as follows:

The source application contains the base of com.google.android.gms with some classes stripped out to reduce size of the app (for example the PlacePicker).

In the patched application i'd need the PlacePicker so i'm trying to add it as a gradle dependencie. While syncing with gradle the resources from the source library and the resources from the patched library are clashing, throwing me the error

"xxx" has already been defined

Is there some kind of workaround or should i just delete the assets from the source (these are 645 assets)?

Lanchon commented 7 years ago

hi,

thanks for using dexpatcher!

i'm not familiar with the libraries you mention but here are some general guidelines.

if you want to replace the code (only the code, not the resources) of a library:

in your case, if the library code was stripped but its associated android resources were not, then you can get the same version of the android library, extract the classes.jar from within the android library (it is a zipfile with an .aar extension), and add that jar.

if some separately packaged dependency of your library is missing completely (code and resources), then you should be able to add the dependency cleanly once you exclude its own transitive dependencies that are already present in the app being patched. for an example of this, see here. (alterantively, you can remove the transitive dependencies from the source app using package annotations, if they are only code and do not have associated android resources.)

if parts of an android library were completely stripped (code and resources) then i would be a little surprised. quoting you:

The source application contains the base of com.google.android.gms with some classes stripped out to reduce size of the app (for example the PlacePicker).

In the patched application i'd need the PlacePicker so i'm trying to add it as a gradle dependencie.

it is not clear by your message if PlacePicker is part of the GMS .aar or a separately packaged .aar. if it is part of GMS, then this whole thing surprises me a bit: the .aar file does not have info about how to split its resources; so how would the app makers know which ones to keep and which ones to strip? *() [see EDIT.]** i would guess, either PlacePicker is a separately packaged .aar (in which case the second solution applies), or its resources were not stripped and only the code was (so the first solution applies then).

but... if regardless of how unlikely, PlacePicker is part of the GMS .aar and yet its resources were stripped, then the problem becomes a standard android issue regarding android libraries. the build system (i believe) supports overriding resources imported from .aar (and, by extension, dexpatcher's .apk.aar) via subtle stuff like import ordering. google manifest merger and resource merger.

i will not delve deeper into this now because i think the other options are more likely to apply to your case. please elaborate more or tell me if these recommendations helped.

thanks!

*EDIT: ()** the only way of doing this that i can think of is using an Android aware obfuscator that will analyze the non-stripped code and determine which resources have to stay. but your report seems to imply that no obfuscator is being used.

Unbrick commented 7 years ago

First of all: A HUGE thanks for your support! I'll report back tomorrow how far i came.

Unbrick commented 7 years ago

Works like a charm. Nearly. I now used the classes.jar as you said, this works well so far.

But the PlacePicker of google is checking for GoogleApiAvailablility class and its instance (via getInstance()). Now, the getInstance method got renamed and now is "lD" which throws me a MethodNotFountException. I'm digging deeper, maybe i can solve it :P

Lanchon commented 7 years ago

what works like a charm? exactly what did you do?

Unbrick commented 7 years ago

Sorry for my short answers :P

Now i copied the classes.jar from the Google Places aar file (library: com.google.android.gms.play-services-places) and included them in my :patched project. Its now available in Android Studio while coding.

But at runtime the library crashed because of the misleading reference to the GMS base library (com.google.android.gms.play-services-base) which is still the one decompiled from the :source and obfuscated.

Lanchon commented 7 years ago

lol i didn't know there was obfuscation involved! the obfuscator removed all dead code so you can't really use the obfuscated libraries yourself, they'll have missing features.

i'm guessing that the android resource names weren't obfuscated too, so the obfuscator must be proguard. in this case, you can try adding classes.jar from all transitive dependencies of your library. if you use the same versions that are already present in the app, then the resources will match and you'd have two copies of the code, one obfuscated and one not, using the same set of resources.

in general, modding obfuscated apps is kind of a drag...

Unbrick commented 7 years ago

I tried adding the transitive dependencies but the obfuscation of the source only affects the method names, not the class names. So Gradle fails while compiling, reporting two identically named classes with different methods.

Lanchon commented 7 years ago

you are entering uncharted territory here now. you can use one of many tools that will rework java bytecode to rename for example package names. you can apply this to the jars of ALL your added dependencies. a tool that can rename java classes is dex2jar, but there must be many. there must be simpler tools that to package renaming for fat jar generation. google that front.