I have an android app I have been developing, and it's now ready to launch. It works great running from eclipse unsigned. When I export a signed copy and run it, an NPE occurs. I've tried creating the signed version with the latest supplied proguard files (proguard-android.txt and proguard-android-optimize.txt). No difference with either. The only mods I've made via my local proguard config file is to leave line numbers so I can get a decent stack trace.
The NPE is what has me scratching my head. I use a lock object for synchronization, and I initialize it in the definition of the object and also during stream initialization:
public class HttpRequest extends NetworkRequest {
private transient Object lock = new Object();
<snip>
private void readObject(java.io.ObjectInputStream in)
throws IOException, ClassNotFoundException {
in.defaultReadObject();
lock = new Object();
}
In my debugging version, lock is never null, but in my signed version, when I go to use the lock object, it's null, consistently. I can only assume proguard is doing something I don't want, but this is the first time I've delved into proguard so I'm a bit out of my element.
Can anyone suggest how I can debug this further?
Proguard does three things: shrink, optimize, obfuscate.
In the shrink step, it cuts out any unused program code. What may be happening to you is that Proguard may be stripping away the readObject method because it appears to be unused. I would expect there to be a keep-rule for that method out of the box, but apparently there isn't, or at least not in the Android SDK.
One way to verify that would be to assemble your app with -dontobfuscate in the proguard config, then extracting the classes.dex file from the APK package and running dexdump classes.dex and looking for the presence or absence of the readObject method.
Ultimately, to ensure the readObject method is preserved, you would add the following rule to your ProGuard config:
-keepclassmembers class * implements java.io.Serializable {
private void readObject(java.io.ObjectInputStream);
}
Updated by mvsjes2 to add the config that actually worked for me:
-keepnames class * implements java.io.Serializable
-keepclassmembers class * implements java.io.Serializable {
static final long serialVersionUID;
private static final java.io.ObjectStreamField[] serialPersistentFields;
!static !transient <fields>;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
}
Related
I am trying to use Proguard to keep my private fields, but it won't work.
I stole most of this from Proguard keep classmembers because that question is similar to what I'm asking, and also followed this link How to tell ProGuard to keep private fields without specifying each field
But it still doesn't work.
I want to make a library for another company and still keep my access level modifiers fields and methods.
Proguard:
-keepclassmembers class com.example.mylibrary.Bedika {
private <fields>;
}
-keep class com.example.mylibrary.Bedika {
*;
}
My AAR library
public class Bedika {
private String stam;
public Bedika(String stam) {
this.stam = stam;
}
public void print() {
System.out.println(stam);
}
}
output after Proguard:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.example.mylibrary;
public class Bedika {
public String stam;
public Bedika(String var1) {
this.stam = var1;
}
public void print() {
System.out.println(this.stam);
}
}
It seems like R8 is causing this issue and not Proguard.
Go into gradle.properties, and set android.enableR8=false. Next time you build, it will use Proguard.
R8 is Google's answer to Proguard and in the recent versions of The Android Gradle Plugin (3.4.0+) it defaults to R8's code shrinker/obfuscator. There are some pros and cons to using Google's version instead of Guardsquare's technology.
For more information, look at the documentation
You can usually add the #Keep annotation above the class or object (in Java or Kotlin). This is pretty self-explanatory, and will work.
I use
-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(...);
public static java.lang.String getStackTraceString(java.lang.Throwable);
}
However when I decompile, I see the one and only custom string I used in a Log.d line in decompiled jar file. How can that be possible? Doesn't Proguard remove Log.d lines completely?
This is relevant if proguard optimization is enabled i.e. your proguard.config is this
proguard.config=${sdk.dir}/tools/proguard/proguard-android-optimize.txt:proguard-project.txt
Just for clarification, it's my understanding optimisation 'has' to been enabled for the log calls to be stripped.
You could try using a Log wrapper class (like this one) to wrap android.util.Log. I used this approach due to a note from Eric (a.k.a Mr Proguard) in this answer about how Proguard inspects the code. So this ends up with a simpler call to Log.d(tag, string) rather than Log.d(tag, string + value+ String)
Or use DexGuard IIRC is has some explicit function for log removal. --
[update: sorry my bad it's not a explicit function DexGuard uses the same config as you noted. I guess I was thinking the fact the decompiled code is more mangled with DexGuard.]
I know that removing logger calls with Proguard works for Android applications.
How can one accomplish this in standard Java application?
import java.util.logging.Logger;
public class Clazz {
private static final Logger LOGGER = Logger.getLogger(Clazz.class.getName());
public void foo() {
LOGGER.info("bar");
}
}
in my Proguard configuration I have the following:
-assumenosideeffects class java.util.logging.Logger { *; }
-whyareyoukeeping class java.util.logging.Logger
which gives the following output when running:
[proguard] java.util.logging.Logger
[proguard] is a library class.
I understand that it's a library but I want to strip all calls to it anyway. Is this possible with Proguard? If not, why? How come this works so conveniently for Android, does the logger field or lack of it have something to do with this?
You should be able to remove logging calls like this, assuming you haven't disabled optimization -- it's the optimization step that removes unnecessary and unwanted calls. ProGuard can't remove the Logger class itself, since it is a run-time library class, as you've seen.
You mustn't use a wildcard for matching the methods though, since this includes essential methods like wait() and finalize() (affecting all classes). You'll have to enumerate the methods that you want to remove. For instance:
-assumenosideeffects class java.util.logging.Logger {
void info(java.lang.String);
}
Is there an explicit way to stop ProGuard from changing a class from implementing an interface?
I have a class that implements java.io.Serializable, let's call it com.my.package.name.Foo. I've found that after running through ProGuard, it no longer implements Serializable. I get null after I cast from Serializable to Foo and false if I check an instance with instanceof Serializable. I've made sure to set ProGuard to ignore this class:
-keep class com.my.package.name.Foo
I've also tried:
-keep class com.my.package.name.Foo { *; }
and I've also tried the whole package by doing this:
-keep class com.my.package.name.** { *; }
or:
-keep class com.my.package.** { *; }
and also just to keep all Serializable classes:
-keep class * implements java.io.Serializable { *; }
but to no avail. I have another class in a sibling package (roughly: com.my.package.name2.Bar) that also implements Serializable and is used similarly but has no issues.
I'm not sure it's relevant, but I'm packing this in a jar for use with Android. The code that uses these classes include putting them in Bundles which is why I need Serializable. I considered that perhaps somehow ProGuard thinks that Foo is never used as a Serializable but that seems unlikely given that I pass it as a parameter to Bundle.putSerializable(String, Serializable) and also I do an implicit cast: Serializable serializable = foo;. In fact, when I debug, I can see the Foo get put into the Bundle and I can examine the Bundle and see the instance of Foo there, but when retrieving it the cast fails.
I had the same issue fixed using below config.
-keepnames class * implements java.io.Serializable
-keepclassmembers class * implements java.io.Serializable {
static final long serialVersionUID;
private static final java.io.ObjectStreamField[] serialPersistentFields;
!static !transient <fields>;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
}
Official Documentation
http://proguard.sourceforge.net/manual/examples.html#serializable
ProGuard doesn't ever strip interfaces that are defined in libraries (like Serializable) from classes in code that is processed (like Foo). Library code may be casting to those interfaces, so they can't be removed.
I get null after I cast from Serializable to Foo
This means that the instance must be null to start with. Your analysis would be correct if you'd get a ClassCastException. You can check that Foo still implements Serializable with javap. The problem probably lies elsewhere. For tips on serialization, you can look at the ProGuard manual > Examples > Processing serializable classes.
Update:
In this case, it turns out to be a configuration problem. ProGuard can only process class files if it knows everything about their hierarchy (just like a compiler). You really have to specify the runtime classes:
-libraryjars <java.home>/lib/rt.jar
or for Android:
-libraryjars /usr/local/android-sdk/platforms/android-17/android.jar
The Android Ant/Eclipse builds automatically specify all necessary -injars/-outjars/-libraryjars options for you, but in a custom build process, you have to specify them yourself. Cfr. ProGuard manual > Examples > A complete Android application.
Note that the option -dontwarn makes the warnings go away, but not the problems. Only use it if really necessary.
i am optimizing a jar with proguard, but it crashes after optimization.
here is my proguard task:
<proguard>
-injars ${dist}/${jarname}
-outjars ${dist}-proguard/${jarname}
-target 5
-libraryjars '${java.home}/lib/rt.jar'
-dontobfuscate
-optimizationpasses 4
-overloadaggressively
-repackageclasses ''
-allowaccessmodification
-keep public class * {
public static void main(java.lang.String[]);
}
</proguard>
as soon as i put in the -dontoptimize option, it works.
according to the stack of the exception it crashes when accessing a static public member of a class with a nullpointer. here is the code:
public static Texture ring, dust, spikering, thinring, crystal, clouds;
public static void init() {
Field [] fields = TexturePool.class.getDeclaredFields();
for (Field field : fields) {
if(field.getType() == Texture.class) {
field.set( null, /*imagine new object here*/ );
}
}
}
thanks!
ok, i just found out myself. i think the optimization completely optimized that classmembers away, since they are not directly accessed in this class. if i specify the option:
-keepclassmembers public class com.package.** {
public static * ;
}
it works even with optimization.
According to Best Java Obfuscation Application For Size Reduction:
"I was always able to fix the problem by not using the Proguard argument "-overloadaggressively"."
Perhaps you should try the same?
EDIT: The problem could easily be that an assignment is optimized away. The initializations happening in the source code, where a field is defined, is actually done by the compiler in a static code blokc. Appears that the optimizations tinker with that. What happens with fewer optimization passes?
I had the same issue with ProGuard optimizing away class fields that were modified using reflection API only. However, the suggested answer didn't work for me as there were too many classes scattered throughout the code base to specify class filter.
Instead, disabling field optimization did the trick for me:
-optimizations !field/*