Why Proguard keeps Activity class in Android? - 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.

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

Proguard shrinking behavior - 2nd level methods

Sorry if this is too basic - I'm struggling to find Proguard documentation I can understand.
My understanding of how Proguard shrinks Android applications is by looking for unused (uncalled?) methods and eliminating them from the build. So if I have a method buynewCoke() that is never called anywhere else in the code, it will be removed.
However, what if there exists a method, say visitStoreAfterMidnight() that calls buyNewCoke(). And visitStoreAfterMidnight itself is never called. Does Proguard still remove both of these methods? Or does it keep buyNewCoke() because it is refrenced by something?
That is, if A calls B and nothing calls A, how does Proguard behave?
Both visitStoreAfterMidnight() and buyNewCoke() would be removed.
Actually it works similar to Garbage Collection, it starts from things that it needs to keep and check what they used and keep only these.
Edit:
official reference:
http://proguard.sourceforge.net/manual/introduction.html
Entry points
In order to determine which code has to be preserved and which code
can be discarded or obfuscated, you have to specify one or more entry
points to your code. These entry points are typically classes with
main methods, applets, midlets, activities, etc.
In the shrinking step, ProGuard starts from these seeds and
recursively determines which classes and class members are used. All
other classes and class members are discarded.

Obfuscate R.id class with or without proguard

I know in Proguard you are recommended to keep the fields names of the R inner classes like ID. Because ProGuard doesn't handle the layout xml files. You will end up with broken links
But is there away to obfuscate classes like R$id by some other means, even if it involves doing it before passing it to ProGuard, via Ant.
I am asking this because if you have a button with an id btnSaveArticle, for a hacker it becomes too easy to grasp what the code around is doing by looking at the name.
Could it be possible to copy all the source code, including the resource files to another folder and use ant to run regex to change the names of the R.ids as well as changing where they appear in the layout xml files, and then somehow running generate to re-create the R classes?
Or you could create translation class eg TR then map it to the fields in the R.class
eg.
TR.btnSaveArticle = R.id.DHTXM;
Where DHTXM is some meaning less word that can be used in the layout XML. But in the code you always refer to TR.btnSaveArticle, which will be obfuscated by proguard.
Are there ways to achieve this or am I wasting my time?
Just use below ,add it to you Proguard config file
-keepclassmembers class **.R$* {
public static <fields>;}
I am asking this because if you have a button with an id btnSaveArticle, for a hacker it becomes too easy to grasp what the code around is doing by looking at the name.
Using Hierarchy View, it would take them less than 30 seconds to determine the actual ID of the "Save Article" button, no matter what you name it. And I can envision even faster solutions with a bit of custom tooling.
am I wasting my time?
IMHO, yes.
With the default configuration for Android, ProGuard removes R classes entirely, unless your code performs introspection on them. In the latter case, ProGuard also preserves the fields with their original names, in order not to break the introspection.
That being said, the resource names can also be retrieved from the resource XML files, which ProGuard leaves untouched.
It is possible through Ant, as it allows you to set a different gen and res folder.
So what you do is copy from the originals to those folders and then you edit the files using regex to update to the new names.
You will need a translation class (eg D) like this to map it to the fields in the R.class, so in your code you can work with non obfuscated names.
public final class D{
public static final class id{
D.btnSaveArticle = R.id.btnSaveArticle //DHTXM;
Then you also need to create a different src folder and copy from the original folder. There you run a task to edit the D class so it becomes
D.btnSaveArticle = R.id.DHTXM;
I had to create a java program which is run through ant to swap the names to obfuscated names.
If you do something similar for strings, and styles your XML in the apk would end up looking like this:
<TextView
android:id="#+id/GnvCMa"
android:text="#string/OVuCbd"
style="#style/ZOVkuu.MGTRgZ" />
It is a little time consuming to setup, but once implemented it can be used for other projects.

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, ProGuard, and keepclasseswithmembernames

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.

Categories

Resources