I have 3 projects that are used as libraries within a 4th (main project).
The 3 projects are complied within each other as follows (build.gradle):
Library Project:
Project A
compile project(":projectA") compile project(":projectB")
Project B
compile project(':projectC')
Main Project:
compile(name: 'projectA', ext: 'aar')
compile(name: 'projectB', ext: 'aar')
compile(name: 'projectC', ext: 'aar')
I would like to do something to the "Library Project", so that from within the Main Project, if I click on any class from within the Library project, I should either not be able to see the code, or it should be encrypted.
So for example if there is InterfaceA
in ProjectA, and the main activity of the Main Project implements that interface, if I "Ctrl-Click" into the interface, the result should be similar to what I specified above.
I understand Proguard does something similar, but that is only if you are building a release .apk, I need the same result for compiled libraries.
Many projects use ProGuard to achieve this protection.
- You can use Gradle to build library components for Android
- Libraries, just like apps, can be build in development or release build types
- Proguard can be configured to run on a component (app or library), but only in the release build type. See here: https://sites.google.com/a/android.com/tools/tech-docs/new-build-system/user-guide#TOC-Running-ProGuard
If the component is minified (highly advised), then you need to tell Progaurd what the "root" classes are, otherwise it will minify the library to literally nothing. This can be achieved by adding a rule to the configuration file:
-keep class your.package.name {public *;}
A more extensive example is here: http://proguard.sourceforge.net/manual/examples.html#library
However there are some limitations:
- ProGuard's main use is is removing as much debug information, line numbers and names as possible from the bytecode without changing what the bytecode actually does. It replaces the names of members and arguments, non-public classes with meaningless ones, for example
vehicleLicensePlate
might become_a
. As any code maintainer will relate, bad member and variable names make maintenance really hard. - ProGuard can (slightly) modify bytecode by optimising as much as possible (computing constants defined as expressions, playing around with inlining, etc. The optimisations are listed here: http://proguard.sourceforge.net/FAQ.html#optimization)
- ProGuard does not encrypt the bytecode - the JVM needs to see the actual bytecode otherwise it could not run the program.
So, obfuscation only makes it harder to reverse-engineer and understand a library, it cannot make this task impossible.
One last pointer: ProGuard dumps a file containing a list of what it has changed, in particular the line numbers. When you get stack traces back from your customers (or through online tools like Crashlytics) you can revert the obfuscation so you can debug. In any release-build process, you need to find a way to save this file.
This file is also needed when you make incremental releases of your library so the obfuscation is consistent to the previously released version. If you don't, the customer cannot drop-in replace your library and will have to do a complete rebuild (and link) of their app.
While ProGuard is a free-n-easy option which just works, there are other free and paid-for obfuscators. Some offer a few more features, but they are fundamentally the same, and the compatibility of ProGuard with IDEs, tools and services is excellent.