Android, ProGuard, and keepclasseswithmembernames - android

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.

Related

Kotlin: ProGuard erases properties attributes

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...)

Why Proguard keeps Activity class in Android?

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.

Proguard NoSuchMethodError ITelephony

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!

android-mapviewballoons, onBalloonTap does not work if I build with ProGuard

I'm developing a project using android-mapviewballoons (https://github.com/jgilfelt/android-mapviewballoons). I extended com.readystatesoftware.mapviewballoons.BalloonItemizedOverlay with a customized class called CustomItemizedOverlay and in this class I redefined onBalloonTap(int index).
When I build a signed APK without using proguard everything works fine: the application runs, I can tap on the map, see the Balloon, tap on the balloon and, via onBalloonTap, get further information about the point I've been tapping on.
When I build with proguard active the application runs flawlessly except for the balloon: I can tap on the map, see the balloon but when I click on it nothing happens. It's just as the balloon itself was transparent, if there's something behind it it gets selected, just as the balloon was not there.
I tried removing features from proguard.cfg one by one, but couldn't find what's happening. I tried with -dontoptimize but didn't work. I think that maybe the issue comes from the obfuscation process, but I can't avoid obfuscation because if I put the -dontobfuscate directive building fails with a "Dalvik Error 1" message.
In the "library" section of my proguard file I have:
(...)
-libraryjars /home/marco/workspace/../Scrivania/android-sdk-linux/add-ons/addon-google_apis-google_inc_-7/libs/maps.jar
-libraryjars /home/marco/workspace/android-mapviewballoons/bin/android-mapviewballoons.jar
In the "keep" section I have (among others):
-keep public class * extends com.google.android.maps.MapActivity
-keep class com.posteitaliane.postemobile.CoverFlow
-keepclassmembers public class com.readystatesoftware.mapviewballoons.BalloonItemizedOverlay.** {
public static * ;
}
-keepclassmembers public class com.posteitaliane.postemobile.CustomItemizedOverlay.** {
public static * ;
}
-keepclassmembers public class com.readystatesoftware.mapviewballoons.** {
public static * ;
}
Could somebody please help me? I just don't know how to create a signed build without breaking proguard or losing this mapBalloon feature.
Thank you in advance
Edit 2012/03/19
I solved the issue by importing a later version of mapviewballoons in the project and fixing the code to fit the new features of mapviewballoons itself. See https://github.com/jgilfelt/android-mapviewballoons/issues/16

how to keep all methods in a class with ProGuard

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 {*;} )

Categories

Resources