I've met several proguard examples with these lines:
# Keep the BuildConfig
-keep class com.example.BuildConfig { *; }
I've run app with and without this line (of course, with my package) and haven't found any differences.
I've also looked in generated/.../BuildConfig.java and there are no changes too.
What for do I need to keep my BuildConfig in ProGuard?
Thanks!
BuildConfig contains a number of useful values that are set at compile time. Specifically these:
boolean DEBUG – if the build is debuggable.
int VERSION_CODE
String VERSION_NAME
String APPLICATION_ID
String BUILD_TYPE – name of the build type, e.g. "release"
String FLAVOR – name of the flavor, e.g. "paidapp"
You can also set your own config values, e.g. different urls for testing and production, and retrieve them from the BuildConfig file instead of maintaining your own Config.java file. This can be done by adding buildConfigFields to your gradle buildTypes like so:
buildTypes {
debug {
buildConfigField "boolean", "SOME_VAR", "true"
}
release {
buildConfigField "boolean", "SOME_VAR", "false"
}
}
So to answer your question, as far as I know you don't have to keep the file, but it's good practice to do so and to use it for your config needs.
As with any other class, you need to -keep the class if you're accessing it indirectly via reflection so ProGuard won't obfuscate it or optimize it away as unused.
Most often the access patterns with BuildConfig are direct without reflection so in those cases it's fine to have ProGuard process your BuildConfig, too.
Some crash reporter libraries like ACRA does access BuildConfig via reflection, so if you use one and want to have info from it in your crash reports, you should -keep it.
Related
How to obfuscate string values using buildConfigField
Currently we use buildConfigField to declare our variables (keys) by type of build, but when performing the reverse engineering of the APK we can clearly see the values declared with buildConfigField, as shown in the image below.
BuildConfig file after reverse engineering
P.S.: Even using minifyEnabled with proguard, we have the problem mentioned
This same discussion is covered in this post -> Fight against reverse engineering of Android APK, but without a solution
I have an issue with R8. In MyLib I have public abstract MyLibsClass in which I have protected methods. MyChildClass extends from MyLibsClass in MyApp and after R8's magic all protected methods (including protected abstract) in MyLibsClass are changed into public ones, and of course in MyChildClass I'm getting "attempting to assign weaker access privileges ('protected'); was 'public') issue as trying to override protected abstract methods.
Additional info
gradle-6.0.1
MyLib's build.gradle
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'),'proguard-rules.pro'
}
proguard-rules.pro
-keep class com.example.mylib.*{
public protected *; }
-keep class com.example.mylib.*$*{
public protected *; }
Anyone had this kind of issue or know a way to fix this?
So based on discussion here ,
DON'T USE DEFAULT PROGUARD SETTINGS FOR LIBRARIES
as allowAccessModification is enabled in default proguard settings, which is located in Android SDK (\Android\Sdk\tools\proguard\proguard-android-optimize.txt) and my mistake was using this for my libraries.
Citation from proguard manual
you probably shouldn't use this option when processing code that is to
be used as a library, since classes and class members that weren't
designed to be public in the API may become public.
So if anyone has the same issue I will suggest to create your own base config file for proguard and copy past whole default configs without "allowAccessModification" into it.
Also if someone interested more, you can track this issue. Hopefully will get separate config file for libraries in near feature.
I faced the same problem, and thanks to #Hayk Nahapetyan's answer, I could resolve it.
Here is my solution with a little more detail.
In the library module's build.gradle, remove the default file from the buildTypes's release closure:
release {
minifyEnabled true
proguardFiles 'proguard-rules.pro'
}
R8 no longer uses the default file that is provided in the Android SDK. It generates one at build time, and puts it in the module's build directory at build/intermediates/default_proguard_files/global.
Copy the contents of proguard-android-optimize.txt-a.b.c (where a.b.c is the library version, if set) from that location to the top of the module's proguard-rules.pro. Then remove -allowaccessmodification; two times, if it originally appeared in both files.
This was also reported on the R8 bug tracker, and resolved there. See http://issuetracker.google.com/147447502.
I'm developing Android library and I want to hide/obfuscate the source code implementation of the library.
The way the user project app will use the library is:
startActivity( new Intent(context, LibraryActivityName.class) );
So I need to keep just the name of entry point Activity inside the library project, That's all.
When I used the default ProGuard settings:
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
as well as the suggested example for library - Nothing happened, and by clicking on the Activity name inside the user app (when he imports it) - One can see the source code.
Thanks,
As you do not have a typical library, you should not include the typical library example.
First of all, you need to enable Proguard execution, change this line:
minifyEnabled true
Second, you do not want to keep all public classes, but only the activity:
-keep class LibraryActivityName { public protected <methods>; }
The remaining classes can be fully obfuscated if I understand your question correctly, so there should be no need for further configuration, unless you use reflection somewhere.
It would also be good if you repackage the obfuscated classes into an internal package or something using
-repackageclasses my.library.package.internal
which might also required
-allowaccessmodification
btw. ProGuard will not obfuscate the code itself, only the class / method names.
I'd like to stamp some variable generated from gradle (in my case it's User Agent used later with HTTP requests) to later be able to distinguish which developer build the app (for example if some developer made a mistake and his app is DDoSing the server).
So for now I can distinguish release from debug with:
buildTypes {
debug {
buildConfigField "String", "USER_AGENT", "\"Android-debug\""
}
release {
buildConfigField "String", "USER_AGENT", "\"Android-release\""
}
}
But for the debug I'd like to add something to know who built the app instance, it may be git login, machine name, or something else.
A gradle build file is actually Groovy code, and you're free to put whatever you want in it. You just have to make sure that the code runs before it would be used in the DSL that describes the build. So if you want to grab something from the system, just write the Groovy code to do that. Groovy is a lot like Java, and you have the full JDK to work with at runtime, so it should be easy to get started.
If you want to access things about the build machine and environment, you might have to shell out to different commands in order to gather that data. Populate some variables with that data. Then use buildConfigField as you already are to drop those values into BuildConfig.java.
Bear in mind that you might want to provide some value in both debug and release so they both generate the same BuildConfig symbols. Otherwise your app might not compile in one config or the other.
BTW. You can tell the difference between debug and release with properties that are already added to BuildConfig, so you don't need to add anything more to tell the difference. Lines like these will always appear (look in the generated BuildConfig.java to see for yourself):
public static final boolean DEBUG = Boolean.parseBoolean("true");
public static final String BUILD_TYPE = "debug";
I use gradle to build app. And I add a suffix to the packageName of my debug version. Just as following:
buildTypes {
debug {
packageNameSuffix ".debug"
}
}
However, one of the libs I use can't work with this.
I think the lib uses code like this to get the R class:
drawable = Class.forName(this.context.getPackageName() + ".R$drawable");
And it throws java.lang.IllegalArgumentException: ResClass is not initialized.
The correct package for R is com.xxx.R$drawable. Since I add a suffix to the package, when the lib want to get the class using reflection it gets com.xxx.debug.R$drawable.
Is there any way to fix it? BTW I can't modify the code of the lib because it is a jar file.
Not sure if it can help you, I have seen a similar problem in different circumstances.
The R class is just a class, for example, when the R package name is different from the current app package name, import com.xxx.yyy.R helps.
Probably you can create a missing class as an ancestor of the class with the correct package name. This, of course, will break your non-debug build, so you will have to add this class for debug builds and remove it for non-debug ones.