In purpose of learning how ProGuard works I have created tiny Android example, added some jar to it, wrote couple lines of code, enabled ProGuard and so on.
my ProGuard options are next
-assumenosideeffects class android.util.Log {
public static boolean isLoggable(java.lang.String, int);
public static int v(...);
public static int i(...);
public static int w(...);
public static int d(...);
public static int e(...);
}
-optimizationpasses 5
-optimizations !class/unboxing/enum
So after reverse engeneering of my APK names to all Object's methods, such as toString(), hashCode(), equals() remains the same. Apart from Object's methods, names of Externalizable methods remains too
So have you any idea why it happens, could it be fixed at all?
ProGuard cannot obfuscate methods that do not originate in your classes (where "your" includes JAR/AARs that you compile in).
Anything you override from framework-supplied classes needs to stay intact. Otherwise, the framework cannot call those methods, since ProGuard is not obfuscating the firmware-installed framework classes on ~1.5 billion Android devices.
Related
I am currently removing Android Logging by using the common Proguard/R8 configuration:
-assumenosideeffects class android.util.Log {
public static boolean isLoggable(java.lang.String, int);
public static int v(...);
public static int i(...);
public static int w(...);
public static int d(...);
public static int e(...);
}
But I have found this official documentation which recommends to set the following R8 option with the corresponding log level (in the example 4) too:
-maximumremovedandroidloglevel 4
I checked to R8 source code to understand what this additional option does. I only found this comment:
Replace Android logging statements like Log.w(...) and Log.IsLoggable(..., WARNING) at or below a certain logging level by false.
Is this really required and what are the benefit vs only using the
assumenosideeffects rule?
I was curious about this rule as well, so I have researched a bit.
Is this really required and what are the benefit vs only using the assumenosideeffects rule?
This -maximumremovedandroidloglevel rule is intended to have the same effect as the -assumenosideeffects for Log.* methods, therefore it might be used as a complete substitute.
However, as of the latest version (3.3.75), it performs slightly differently than -assumenosideeffects. If you intend to remove all Log.* method invocations - then you shouldn't care (you can use one, or the other). But if you're removing only a portion of those methods and you rely on Log#isLoggable, then I'd suggest sticking to your current configuration, and avoiding adding the -maximumremovedandroidloglevel rule to your proguard configuration file.
A deeper dig
Let's assume we have the following lines of code in our source code, and then I'll show what it looks like after R8 processing with different configurations.
if (Log.isLoggable("FOO_TAG", Log.VERBOSE)) {
Log.v("FOO_TAG", "verbose message");
}
if (Log.isLoggable("FOO_TAG", Log.WARN)) {
Log.w("FOO_TAG", "warn message");
}
<1> R8 v3.3.75, with the following rules (note that I have commented out the w and e methods):
-assumenosideeffects class android.util.Log {
public static boolean isLoggable(java.lang.String, int);
public static int v(...);
public static int i(...);
# public static int w(...);
public static int d(...);
# public static int e(...);
}
Produces the following output:
if (Log.isLoggable("FOO_TAG", 5)) {
Log.w("FOO_TAG", "warn message");
}
R8 removed the VERBOSE logs as expected. Note that it keeps the Log#isLoggable method invocation where the level (the second parameter) is WARN (5).
<2> R8 v3.3.75, with the following rules (4 means we want to remove all the log methods up to INFO, including):
-maximumremovedandroidloglevel 4
Produces the following output:
Log.w("FOO_TAG", "warn message");
Note that this rule keeps the Log#w method invocation, but removes the Log#isLoggable invocation (this is where the behavior slightly differs).
This means that the latest R8 version in regards to -maximumremovedandroidloglevel doesn't work exactly as advertised (here):
Example: The value of android.util.log.INFO is 4. Therefore,
specifying -maximumremovedandroidloglevel 4 will remove all calls to
Log.v(), Log.d(), and Log.i(), as well as it will replace calls to
Log.isLoggable(X, {2, 3, 4}) by false.
<3> R8 from the tip of the main branch, with the following rules (4 means we want to remove all the log methods up to INFO, including):
-maximumremovedandroidloglevel 4
Produces the following output:
if (Log.isLoggable("FOO_TAG", 5)) {
Log.w("FOO_TAG", "warn message");
}
It seems that main branch includes a fix that reinstates the behavior parity of two approaches (Log#isLoggable with WARN as a parameter was not removed).
The relevant diff between the main and 3.3.75 tag is those two commits: 1, 2.
Why the official documentation includes both -assumenosideeffects and -maximumremovedandroidloglevel?
According to my tests - it seems misleading, as they should have suggested to using one or the other, definitely not both.
I have opened an issue with a request to elaborate on that.
When I run the usb debugging app, it saves the data correctly when I register the user:
But when I generate the signed apk, doing the same process saves it this way in Firebase Database:
What is happening? (i use android studio)
It's because proguard removes (obfuscation) unused code and renames classes and class members' (variables and methods) names to shorter names. There are two ways to keep them as you want
OPTION 1. Add annotation before every field and between parentheses put what name you want to be displayed in Firebase.
Method A) Add annotation to public fields
public class Datum {
#PropertyName("name")
public String name;
}
Methods B) Add annotation to public setter/getters if your fields are private
public class Datum {
private String name;
#PropertyName("name")
public String getName() {
return name;
}
#PropertyName("name")
public void setName(String name) {
this.name = name;
}
}
OPTION 2. Configure proguard file to keep class, field and method names as is.
Method A) Do it as per Firebase docs. Add following lines to your proguard file. Below lines mean names of every class member (field, constructor and method) in models package and in sub-package of models direcotry will be kept as-is.
# Add this global rule
-keepattributes Signature
# This rule will properly ProGuard all the model classes in
# the package com.yourcompany.models. Modify to fit the structure
# of your app.
-keepclassmembers class com.yourcompany.models.** { *;}
Method B) Adding classes one-by-one
If you want to keep name and class members of User class then add this.
-keep class com.josiah.app.data.models.User{ *;}
Methods C) Add all classes in a package
Let's say all of your model classes are inside models package and you want to keep names of all classes and class members intact in that package. Then you have to add following line into your proguard file.
-keep class com.josiah.app.data.models.** { *;}
NOTE:
* means everything inside class (fieds, methods and contructors)
** after package means everything in this package (subpackages and classes)
Working with an Android project in Android Studio 3.2, having enabled Proguard and some specific rules, I'm not able to figure out the following:
a specific package (and its subpackages) in a library module, used by client code, is preserved through the rule:
-keep public class com.mylib.mypackage.** {
public protected *;
}
Now, within this package there are also a number of package-private classes, which should not be picked by that rule. Some of those classes are effectively obfuscated, both in their own names and their member names, which is fine.
Instead there are some classes, implementing public interfaces, whose class names are not obfuscated, while I'd expect they should. For completeness, their member names, when not part of interface, are effectively obfuscated.
Example:
/* package */ class InternalComponent implements ExternalInterface {
// ExternalInterface is kept: Ok
// InternalComponent is kept: don't like, I'd like it renamed
#Override
public void ExternalMethod() {
// this is kept: Ok
}
public void InternalMethod() {
// this is renamed: Ok
}
}
I'd like to highlight that InternalComponent is created within some other (kept) class and only returned to client code through the ExternalInterface.
How can I also obfuscate their class names as well, if possible?
Edit #1
After #emandt answer on Proguard output files, I double checked and com.mylib.mypackage.InternalComponent is listed in seeds.txt, which according to this blog post lists all items matched by keep rules. So, for some reason, the rule above also picks package-private classes, which still seems wrong to me.
Edit #2
In the meantime, I ended up doing exactly the same approach proposed by #shizhen. For completeness, in order to extend the exclusion to any package named internal, I modified my proguard rule as:
-keep public class !com.mylib.mypackage.**.internal.*, com.mylib.mypackage.** {
public protected *;
}
(note the first part before the comma, prefixed by !)
I'll mark #shizhen answer, though I'd like to be curious as to why the original rule is also picking package-private components.
Are you working on an Android Library project? Probably YES.
In order to achieve your purpose, I am afraid that you need to re-organise your packages into something like below:
Public interfaces
com.my.package.apiforusers
Private/Internal implementations
com.my.package.apiforusers.internal
Then for your obfuscation rules, you can have it like below:
-keep public class com.my.package.apiforusers.** { public *; }
So that only the public classes/interfaces are kept and all those ones inside com.my.package.apiforusers.internal will be obfuscated.
Please note the double-asterisk at the end so that public classes/interface are also kept for the sub-packages.
In "/build/outputs/mapping/release/" folder there are few files ("usage.txt", "seeds.txt", etc..) that contain the REASONS of why and which classes/variables/methods/etc.. are not-processed/not-shrinked/ot-obfuscated via ProGuard utilities.
I am trying to prevent proguard from obfuscating interface (or abstract class) methods parameters.
Lets say I have this interface in my lib :
package com.mypackage;
public interface MyLibListener {
void onSomething(boolean success, String message);
}
And this proguard file :
-keepparameternames
-keep interface com.mypackage.MyLibListener {
*;
}
Then I assemble release and I get :
package com.mypackage;
public interface MyLibListener {
void onSomething(boolean var1, String var2);
}
Same thing with abstract classes or using #Keep annotations. Obfuscation option keepparameternames seems to work only for regular classes. Any idea? Thanks!
(related SO : How to not obfuscate interface methods & it's parameters using Progaurd in android? and Proguard keep interface method variable names)
Add following ProGuard options to your configuration.
-keepattributes MethodParameters
If your class file hava method parameters metadata (compiled using Java8 -parameters or etc...)`, ProGuard will keep the metadata.
To keep all interface methods:
-keep interface * {
<methods>;
}
To keep all public and protected methods, which could be used by reflection:
-keepclassmembernames class * {
public protected <methods>;
}
While I don't understand, why one would want to keep abstract classes, which cannot be instanced anyway, therefore it's pointless to keep & know their names. In theory, one could exclude all that is not abstract with rules which start with -keep !abstract, but that's kind of redundant.
Your proguard file may lack some -keepattributes, especially a -keepattributes Signature.
Check this example proguard configuration for a library from the proguard documentation to look for ideas.
As title says: How do I get Proguard to keepclassmembers of entire package? Also to net delete methods with void signatures.
To keep all class members (fields and methods) of all classes in a given package and all of its subpackages:
-keepclassmembers class mypackage.** { *; }
This includes void methods. To only keep all void methods:
-keepclassmembers class mypackage.** { void *(...); }
These are unusual settings though, because keeping all class members or all void methods (without even keeping all classes) seems like a very random requirement.
For most configurations, -keep is more appropriate than -keepclassmembers, relevant classes are typically only public ones (matching public class), relevant classes typically extend a specific class or interface (e.g. matching extends somepackage.SomeClass), and relevant class members are typically a very specific set of public methods (e.g. public setters, matching public void set*(***)).