TL;DR: with proguard enabled, when using reflection, my properties look private, non-nullable and without annotations, despite proguard config that should keep all these attributes.
I have some simple data classes with public properties to serve as data models in my Android app. Later, when doing generic [de]serialization of said classes, I filter the property list like this:
val properties = instance::class.memberProperties
.filter { it.visibility == KVisibility.PUBLIC } // && some other conditions, unrelated here
.filterIsInstance<KMutableProperty<*>>()
It works normally on my debug builds (I mean it selects the properties I want it to). But, when doing a release build, where proguard is active, the result is empty. To check why, I logged all the relevant stuff about the properties of one class -- turns out their visibility field reads PRIVATE (and all other attributes remain the same as on a debug build).
I already have a line in proguard config to keep all the models:
-keepclassmembers class * extends com.models.package.name.BaseModel { *; }
I tried this one before, with same result:
-keep class com.models.package.name.** { *; }
Why/how does proguard affect property visibility? Should I modify the config somehow? Or am I missing something else here?
UPDATE: It seems like visibility is not the only thing. prop.returnType.isMarkedNullable also doesn't work, it returns false for properties declared nullable. And annotations also seem to get lost, even though I asked proguard to keep them. Is there any way to work around this? It pretty much renders 2 weeks of my work useless...
Thanks to the suggestion from #yole in question comments, I've been able to make this work. Even though my classes were configured to be kept by ProGuard, it stripped the kotlin.Metadata annotations from them. These annotatons are where Kotlin stores all the attributes I was missing. The solution is to prevent ProGuard from deleting them, adding to configuration:
-keep class kotlin.Metadata { *; }
(on a side note: it's weird that it's not included in the default config, at least if you're using the kotlin.reflect.full package. Or at least it should be mentioned clearly somewhere in the docs...)
Related
Is there a way to detect (during runtime) if your application is ProGuarded? My goal here is to set a boolean value based on whether the application is ProGuarded or not.
I think it'd be easier if you set a boolean on build variants/flavors that you run ProGuard on.
For example: if you run ProGuard on Release and Staging but not on Debug, set a boolean variable to true on release and staging, but false on debug. This may help
This is a bit of a workaround but there are a few avenues by which reflection will solve this.
One way would be for you to first determine a class that is obfuscated/minified in the case of proguard. So say you have a class com.package.Thing that when proguard is enabled gets minified to be called just com.package.a (or whatever, it doesn't matter). Then use reflection to see if the original class exists.
Something like this:
boolean isProguardEnabled() {
try{
return Class.forName("com.package.Thing") == null;
catch(ClassNotFoundException cnfe) {
return true;
}
}
Obviously this only works if you know of a class that will be changed when proguard is enabled.
Perhaps more generically, you could look for a class com.package.a and use that as a signal that proguard is enabled, assuming of course that you don't genuinely have a class by the name of a. By default proguard will just use the alphabet to come up with class names, but you can also specify in your proguard configuration your own list of class names to use (which presents another way to do this same trick, look for one of those class names that you've specified).
I have applied ProGuard to my Android application.
I'm using
android-studio/sdk/tools/proguard/proguard-android.txt
as a configuration file, changing nothing.
In this file I can see the only statement regarding Activity:
We want to keep methods in Activity that could be used in the XML attribute onClick
-keepclassmembers class * extends android.app.Activity {
public void *(android.view.View);
}
It follows from that the Activities class names are obfusctated by ProGuard while some methods are preserved as they are. This is understandable.
However, in my application I then create an Activity Class from a string using
Class.forName("my.app.MyActivity")
then I start this Activity and it starts fine.
But that means Activity-derived classes are not obfuscated??. It should have failed because ProGuard does not have -keep class directive for Activity, it only has -keepclassmembers.
Can someone, please, explain, if I can rely on the behaviour I observe? Or do I misunderstand the -keepclassmembers directive?
Because the activities are listed in the manifest and classes referenced there are automagically kept. This is needed because the Android framework accesses these app entry points via reflection.
From here:
The build process runs the tool aapt to automatically create the configuration file bin/proguard.txt, based on AndroidManifest.xml and other xml files. The build process then passes the configuration file to ProGuard. So ProGuard itself indeed doesn't consider AndroidManifest.xml, but aapt+ProGuard do.
From the ProGuard FAQ:
Does ProGuard handle Class.forName calls?
Yes. ProGuard automatically handles constructs like Class.forName("SomeClass") and SomeClass.class. The referenced classes are preserved in the shrinking phase, and the string arguments are properly replaced in the obfuscation phase.
With variable string arguments, it's generally not possible to determine their possible values. They might be read from a configuration file, for instance. However, ProGuard will note a number of constructs like "(SomeClass)Class.forName(variable).newInstance()". These might be an indication that the class or interface SomeClass and/or its implementations may need to be preserved. The developer can adapt his configuration accordingly.
So, ProGuard's being cleverer than you expected: it will automatically detect and handle simple cases of the use for forName(). Even if the class isn't referenced in the Android manifest file, ProGuard will obfuscate the class name in both the class and in your call to forName().
For Activities, it wouldn't surprise me if you're doubly-covered by both this behaviour and by the Android-specific input to ProGuard that's part of the build process, as #laalto mentions in his answer.
My code works fine but after applying proguard i am getting the following exception
05-04 16:12:00.803: E/AndroidRuntime(22257):
java.lang.NoSuchMethodError:
com.android.internal.telephony.ITelephony.a
I ma having Itelephony.aidl but still getting the error. Can anyone tell me if there is a way to around this ?
On google, i couldn't get more information on examples of how to keep interfaces, interface members and inner classes along with inner class members.
Actually i want to keep everything in my app but just obsfucate and optimize it. Is there a way to achieve it ?
You need to exclude the ITelephony class from proguard, i.e. add this ...
-keep class com.android.internal.telephony.ITelephony { *; }
... entry to your proguard.cfg file. You'll find it in your project root folder.
Proguard is a tool that obfuscates your code, i.e. makes it more compact and less readable for others by applying various optimization (e.g. renaming classes). So in some cases this might have a negative impact on the functionality, e.g. if you consider dynamic reflection calls.
Cheers!
A common pattern in ProGuard configs for Android applications is to preserve custom View classes, since they are probably referenced only from layout XML instead of application code.
Upon project creation, the ADT therefore add these rules to a project's proguard.cfg:
-keepclasseswithmembernames class * {
public <init>(android.content.Context, android.util.AttributeSet);
}
-keepclasseswithmembernames class * {
public <init>(android.content.Context, android.util.AttributeSet, int);
}
I guess the idea here is to say that whenever a class defines a constructor that may be called by a layout inflater, then preserve it. However, according to the ProGuard docs, the keepclasseswithmembernames qualifier is shorthand for keepclasseswithmembers and allowshrinking, which if I understand correctly means: it's allowed to remove these classes, but if they're kept, don't obfuscate its member names (probably to not break bindings between XML attribute names and class setters).
But doesn't that mean that those classes will still be removed during the shrinking phase (allowshrinking = true), unless they are referenced directly in code? Indeed that is what happened with a custom widget we're using in our app, and I could fix the issue by setting the rule to just keepclasseswithmembers since that will simply preserve the matching classes entirely (it's worth noting that this is what the official ProGuard Android example does, too).
Am I misreading the ProGuard docs or is this a bug in the ADT project wizard?
The configuration in the Android SDK (at least up to version 11) is not entirely correct, indeed.
The configuration for Android in the ProGuard documentation correctly specifies "-keepclasseswithmembers", not "-keepclasseswithmembernames".
When I first tried the integrated proguard with Ant, my app kept crashing with runtime errors on the clickhandlers. (I always set these in XML). I assumed I must be doing something wrong, couldn't work out what, so added the line
-dontshrink
at the top of the proguard.cfg.
Maybe this isn't optimal but it stopped the run time errors!
Addendum
In fact I checked this by looking at usage.txt. The clickhandlers were listed in there before I added the dontshrink option, after I added it, usage.txt was empty as would be expected.
I use ProGuard to optimize my Android Application. However for Android instrumentation test I need some (but not all) classes to keep all there members. I tried various approaches, the last being:
-keepclassmembers public class com.mycompany.myclass {
*;
}
But surprisingly I still get
java.lang.NoSuchMethodError: com.mycompany.myclass.<init>
The painful part here is that there are two constructors and one has quite a few parameters.
Does anyone know the correct syntax to keep a class completely unchanged and untouched by ProGuard?
Well, it is confession time. The question is bollocks. The -keepclassmembers is correct. The problem occurred because a team mate broke the code and the constructor was truly not there.
Note that if there is a change that the whole class is optimized away then you should use -keep as kharles suggested but the {*;} is needed to ensure that all methods stay in place.
Note that the {*;} is for testing only. For production one should use a more fine grained approach.
I keep the question for anybody with the same problem.
try
-keep public class com.mycompany.myclass
( use keep, and no {*;} )