My application works well when proguard is disabled, but once enable it breaks the functionality.
I have a class called CachecManager which is not accessible once proguard is enabled.
Here is my class
object CacheManger {
fun setData(data: String) {
val yourFilePath: String = BaseClass.appContext.filesDir.toString() + "/" + USER_DATA
val yourFile = File(yourFilePath)
val fileOutputStream = FileOutputStream(yourFile)
fileOutputStream.write(data.toByteArray())
fileOutputStream.flush()
fileOutputStream.close()
}
}
This is my proguard file
##---------------Begin: proguard configuration for Gson ----------
# Gson uses generic type information stored in a class file when working with fields. Proguard
# removes such information by default, so configure it to keep all of it.
-keepattributes Signature
# For using GSON #Expose annotation
-keepattributes *Annotation*
# Gson specific classes
-dontwarn sun.misc.**
#-keep class com.google.gson.stream.** { *; }
# Application classes that will be serialized/deserialized over Gson
-keep class com.google.gson.examples.android.model.** { <fields>; }
# Prevent proguard from stripping interface information from TypeAdapter, TypeAdapterFactory,
# JsonSerializer, JsonDeserializer instances (so they can be used in #JsonAdapter)
-keep class * extends com.google.gson.TypeAdapter
-keep class * implements com.google.gson.TypeAdapterFactory
-keep class * implements com.google.gson.JsonSerializer
-keep class * implements com.google.gson.JsonDeserializer
# Prevent R8 from leaving Data object members always null
-keepclassmembers,allowobfuscation class * {
#com.google.gson.annotations.SerializedName <fields>;
}
##---------------End: proguard configuration for Gson ----------
#project related
-keep class com.appname.constants
-keep class com.appname.models.** { *; }
-keepnames class androidx.navigation.fragment.NavHostFragment
#-keep class <class>$Companion { *; }
-keep class kotlin.** { *; }
-keep class kotlin.Metadata { *; }
-dontwarn kotlin.**
-keepclassmembers class **$WhenMappings {
<fields>;
}
-keepclassmembers class kotlin.Metadata {
public <methods>;
}
-assumenosideeffects class kotlin.jvm.internal.Intrinsics {
static void checkParameterIsNotNull(java.lang.Object, java.lang.String);
}
This is error which i am getting
java.lang.ClassNotFoundException: Didn't find class "com.appname.cachemanager.CacheManger" on path: DexPathList[[zip file "/data/app/com.appname-2/base.apk"],nativeLibraryDirectories=[/data/app/com.appname-2/lib/arm64, /data/app/com.appname-2/base.apk!/lib/arm64-v8a, /vendor/lib64, /system/lib64]]
Related
I have a library on Kotlin I want to obfuscate almost completely but leave the public classes, properties and methods untouched. Here is an example of one of the public classes I intend to obfuscate:
class SomeClass(val propertyToShow: SomePublicClass, private val propertyToHide: SomeOtherPublicClass) {
fun methodToShow(someArg: SomeArg) {
// Some code
}
private fun methodToHide(someOtherArg: SomeOtherArg) {
// Some more code
}
}
The ProGuard file is based on a typical library ProGuard file and looks like this:
-keepattributes SourceFile,LineNumberTable
-renamesourcefileattribute SourceFile
-dontwarn javax.annotation.**
-keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase
-dontwarn org.codehaus.mojo.animal_sniffer.*
-dontwarn okhttp3.internal.platform.ConscryptPlatform
-keepattributes Signature
-keepattributes *Annotation*
-dontwarn sun.misc.**
-keep class com.google.gson.stream.** { *; }
-keep class * implements com.google.gson.TypeAdapterFactory
-keep class * implements com.google.gson.JsonSerializer
-keep class * implements com.google.gson.JsonDeserializer
-keepclassmembers,allowobfuscation class * {
#com.google.gson.annotations.SerializedName <fields>;
}
-keep public class * {
public protected *;
}
-keepclassmembernames class * {
java.lang.Class class$(java.lang.String);
java.lang.Class class$(java.lang.String, boolean);
}
-keepclasseswithmembernames class * {
native <methods>;
}
-keeppackagenames **
-keepattributes Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,*Annotation*,EnclosingMethod,Synthetic,PermittedSubclasses
-keepparameternames
-renamesourcefileattribute SourceFile
-keepclassmembers class **$WhenMappings {
<fields>;
}
-keep class kotlin.Metadata { *; }
-keep class kotlin.** { *; }
-keep class kotlin.Metadata { *; }
-keep class kotlin.reflect.** { *; }
-dontwarn kotlin.reflect.**
-dontwarn kotlin.**
-keepclassmembers class **$WhenMappings {
<fields>;
}
-keepclassmembers class kotlin.Metadata {
public <methods>;
}
-assumenosideeffects class kotlin.jvm.internal.Intrinsics {
static void checkParameterIsNotNull(java.lang.Object, java.lang.String);
}
-keepclassmembers,allowobfuscation class * {
#javax.inject.* *;
#dagger.* *;
<init>();
}
-keep class javax.inject.** { *; }
-keep class **$$ModuleAdapter
-keep class **$$InjectAdapter
-keep class **$$StaticInjection
-keep class dagger.** { *; }
-keepclassmembers class * extends java.lang.Enum {
public static **[] values();
public static ** valueOf(java.lang.String);
}
-keepclassmembers class * implements java.io.Serializable {
static final long serialVersionUID;
static final java.io.ObjectStreamField[] serialPersistentFields;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
}
Running ./gradlew assembleRelease results in .aar file. The SomeClass.class from it looks like this (if we take a look at it via Android Studio)
public final class SomeClass public constructor(propertyToShow: SomePublicClass, propertyToHide: SomeOtherPublicClass) {
public final val propertyToShow: SomePublicClass /* compiled code */
private final val propertyToHide: SomeOtherPublicClass /* compiled code */
public final fun methodToShow(someArg: SomeArg): kotlin.Unit { /* compiled code */ }
private final fun a(someOtherArg: SomeOtherArg): kotlin.Unit { /* compiled code */ }
}
As we can see, the name of the private method has been obfuscated but its argument is not as well as the private property. It doesn’t matter if we obfuscate it with ProGuard or R8, the result is the same.
Is it possible to obfuscate private properties and private methods' arguments for Kotlin source code? Or is it pointless, since it will not interfere with others doing reverse engineering?
So the answer to this question was more or less explained in this article. Basically the issue was that the code was indeed obfuscated properly but there was still Kotlin Metadata and Android Studio was reconstructing the code based on this Metadata.
Retrofit and moshi-kotlin Api's are not working in android.
I'm using dagger2 ,while genrating signed apk ,retrofit api calls are not working, in debug mode its working fine .
while we enabled minifyenable true and shrinkresource true.
#JsonClass(generateAdapter = true) #SuppressLint("ParcelCreator") #Keep data class DashboardListRes( #Json(name = "data") vardata: List<Data>?=ArrayList(), #Json(name = "lead_data") var leadData: LeadData= LeadData(), #Json(name = "message") var message: String="", #Json(name = "status") var status: String="", #Json(name = "status_code") var statusCode: Int=0, #Json(name = "user") var user: User=User() ):Serializable
1.
2. -keepattributes Signature, InnerClasses, EnclosingMethod
-keepattributes RuntimeVisibleAnnotations,
RuntimeVisibleParameterAnnotations -keepattributes
AnnotationDefault
-keepclassmembers,allowshrinking,allowobfuscation interface * {
#retrofit2.http.* <methods>; } -dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement -dontwarn
javax.annotation.** -dontwarn kotlin.Unit -dontwarn
retrofit2.KotlinExtensions -dontwarn retrofit2.KotlinExtensions$*
-if interface * { #retrofit2.http.* <methods>; } -keep,allowobfuscation interface <1> -keep,allowobfuscation,allowshrinking interface retrofit2.Call -keep,allowobfuscation,allowshrinking class retrofit2.Response -keep,allowobfuscation,allowshrinking class kotlin.coroutines.Continuation -dontwarn javax.annotation.**
-keepclasseswithmembers class * {
#com.squareup.moshi.* <methods>; }
-keep #com.squareup.moshi.JsonQualifier #interface *
-keepclassmembers #com.squareup.moshi.JsonClass class * extends java.lang.Enum {
<fields>;
**[] values(); } -keepclassmembers class com.squareup.moshi.internal.Util {
private static java.lang.String getKotlinMetadataClassName(); } -keepclassmembers class * {
#com.squareup.moshi.FromJson
<methods>; #com.squareup.moshi.ToJson <methods>; }
-keep class kotlin.Metadata
-keep class com.package.models.**{*;}
-keepclassmembers class com.package.dataclasses.** { *; }
-keepattributes Signature -keepattributes Annotation -dontwarn sun.misc.** -keep class com.google.gson.stream.** { *; } -keep class com.google.gson.examples.android.model.** {
<fields>; } -keep class com.credright.nikhil.models.** { *; }
-keep class com.package.ui.** { *; } -keep class * extends com.google.gson.TypeAdapter -keep class * implements
com.google.gson.TypeAdapterFactory -keep class * implements
com.google.gson.JsonSerializer -keep class * implements
com.google.gson.JsonDeserializer
Prevent R8 from leaving Data object members always null
-keepclassmembers,allowobfuscation class * {
#com.google.gson.annotations.SerializedName <fields>; }
-keep,allowobfuscation,allowshrinking class com.google.gson.reflect.TypeToken
-keep,allowobfuscation,allowshrinking class * extends com.google.gson.reflect.TypeToken -dontwarn javax.annotation.**
-keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase -dontwarn
org.codehaus.mojo.animal_sniffer.* -dontwarn
okhttp3.internal.platform.ConscryptPlatform
-keep class org.apache.commons.logging.**
-keepattributes Annotation
-dontwarn org.apache.**
-keepnames #dagger.hilt.android.lifecycle.HiltViewModel class *
extends androidx.lifecycle.ViewModel
If you are converting the data received into a POJO object using some built in function and then populating a data class or something, you need to make sure you exclude the data classes from proguard.
What I generally do is I put all my data classes in one package and then exclude everything in that package like so:
-keepclassmembers class com.credright.nikhil.dataclasses.** { *; }
The line above should be added to the file proguard-rules.pro and dataclasses is the name of the package that has all the data classes in it.
The reason why this is important is that when proguard obfuscates your code, it basically changes all class names and everything into arbitrary things and so after obfuscation data classes cannot be populated.
There have historically been some difficulties when using moshi-kotli. Updating to a newer Kotlin version + updating some proguard rules fixes it.
You can check out this
GitHub comment.
I am learning to Obfuscate my Android Code by making: minifyEnabled = true in my Manifest file.
But GSON has some issues with the Obfuscated code.
I have tried:
Obfusucation should not rename my Model classes & Model class variables.
Added #SerializedName on every variable of Model class.
But the error says, I have duplicate variable named as a, in my Model class.
But i don't have any duplicate variable in my Object classes.
build.gradle
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
Below is my proguard file:
proguard-rules.pro
-ignorewarnings
-keepattributes Signature
# For using GSON #Expose annotation
-keepattributes *Annotation*
-keepattributes SourceFile,LineNumberTable
-keep class com.crashlytics.** { *; }
-dontwarn com.crashlytics.**
# Gson specific classes
-dontwarn sun.misc.**
# Application classes that will be serialized/deserialized over Gson
-keep class com.google.gson.examples.android.model.** { *; }
-keep class com.hgs.ruralhealth_new.model.** { *; }
-keep class com.hgs.ruralhealth_new.model.* { *; }
-keep class com.hgs.ruralhealth_new.model.masterdata.* { *; }
-keep class * implements com.google.gson.TypeAdapterFactory
-keep class * implements com.google.gson.JsonSerializer
-keep class * implements com.google.gson.JsonDeserializer
-keep class com.hgs.ruralhealth_new.model.** { <fields>; }
-keep class com.hgs.ruralhealth_new.model.masterdata.** { <fields>; }
-keep class com.hgs.ruralhealth_new.model.register.** { <fields>; }
-keep class com.hgs.ruralhealth_new.model.** { *; }
-keep class com.hgs.ruralhealth_new.model.masterdata.** { *; }
-keep class com.hgs.ruralhealth_new.model.register.RegistrationInputData
-keepclassmembers class com.hgs.ruralhealth_new.model.** { *; }
-keepclassmembers class com.hgs.ruralhealth_new.model.masterdata.** { *; }
-keepclassmembers class com.hgs.ruralhealth_new.model.register.** { *; }
-keepclassmembers class com.hgs.ruralhealth_new.model.register.RegistrationInputData
-keepclassmembers enum * { *; }
Error is as below:
FATAL EXCEPTION: main
Process: com.hgs.ruralhealth_new, PID: 6577
java.lang.IllegalArgumentException: class d.h$b declares multiple JSON fields named a
at com.a.a.b.a.i.a(ReflectiveTypeAdapterFactory.java:170)
at com.a.a.b.a.i.a(ReflectiveTypeAdapterFactory.java:100)
at com.a.a.f.a(Gson.java:423)
at com.a.a.b.a.m.a(TypeAdapterRuntimeTypeWrapper.java:56)
at com.a.a.b.a.i$1.a(ReflectiveTypeAdapterFactory.java:125)
at com.a.a.b.a.i$a.a(ReflectiveTypeAdapterFactory.java:243)
at com.a.a.b.a.m.a(TypeAdapterRuntimeTypeWrapper.java:69)
at com.a.a.b.a.i$1.a(ReflectiveTypeAdapterFactory.java:125)
at com.a.a.b.a.i$a.a(ReflectiveTypeAdapterFactory.java:243)
at com.a.a.f.a(Gson.java:669)
at com.a.a.f.a(Gson.java:648)
at com.a.a.f.a(Gson.java:603)
at com.a.a.f.a(Gson.java:583)
at com.hgs.ruralhealth_new.activity.LoginActivity$2.a(LoginActivity.java:457)
at d.g$a$1$1.run(ExecutorCallAdapterFactory.java:68)
at android.os.Handler.handleCallback(Handler.java:789)
at android.os.Handler.dispatchMessage(Handler.java:98)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6541)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
When I copy this error in Proguard, below in the output:
FATAL EXCEPTION: main
Process: com.hgs.ruralhealth_new, PID: 6577
java.lang.IllegalArgumentException: class d.h$b declares multiple JSON fields named a
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.java.util.Map getBoundFields(com.google.gson.Gson,com.google.gson.reflect.TypeToken,java.lang.Class)(ReflectiveTypeAdapterFactory.java:170)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.com.google.gson.TypeAdapter create(com.google.gson.Gson,com.google.gson.reflect.TypeToken)(ReflectiveTypeAdapterFactory.java:100)
at com.google.gson.Gson.com.google.gson.TypeAdapter getAdapter(com.google.gson.reflect.TypeToken)(Gson.java:423)
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.void write(com.google.gson.stream.JsonWriter,java.lang.Object)(TypeAdapterRuntimeTypeWrapper.java:56)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.void write(com.google.gson.stream.JsonWriter,java.lang.Object)(ReflectiveTypeAdapterFactory.java:125)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.void write(com.google.gson.stream.JsonWriter,java.lang.Object)(ReflectiveTypeAdapterFactory.java:243)
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.void write(com.google.gson.stream.JsonWriter,java.lang.Object)(TypeAdapterRuntimeTypeWrapper.java:69)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.void write(com.google.gson.stream.JsonWriter,java.lang.Object)(ReflectiveTypeAdapterFactory.java:125)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.void write(com.google.gson.stream.JsonWriter,java.lang.Object)(ReflectiveTypeAdapterFactory.java:243)
at com.google.gson.Gson.void toJson(java.lang.Object,java.lang.reflect.Type,com.google.gson.stream.JsonWriter)(Gson.java:669)
at com.google.gson.Gson.void toJson(java.lang.Object,java.lang.reflect.Type,java.lang.Appendable)(Gson.java:648)
at com.google.gson.Gson.java.lang.String toJson(java.lang.Object,java.lang.reflect.Type)(Gson.java:603)
at com.google.gson.Gson.java.lang.String toJson(java.lang.Object)(Gson.java:583)
at com.hgs.ruralhealth_new.activity.LoginActivity$11.void onResponse(retrofit2.Call,retrofit2.Response)(LoginActivity.java:457)
at retrofit2.ExecutorCallAdapterFactory$ExecutorCallbackCall$1$1.void run()(ExecutorCallAdapterFactory.java:68)
at android.os.Handler.handleCallback(Handler.java:789)
at android.os.Handler.dispatchMessage(Handler.java:98)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6541)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
Now what code is at LoginActivity.java:457
public void onResponse(Call<PhysiotherapistResponse> call, Response<PhysiotherapistResponse> response) {
Log.i("Physio Backup==>", new Gson().toJson(response));//This is line 457
BELOW ARE MY MODEL CLASSES:
PhysiotherapistResponse.java
public class PhysiotherapistResponse implements Parcelable {
#SerializedName("status")
private String status;
#SerializedName("message")
private String message;
#SerializedName("syncFromId")
private String syncFromId;
#SerializedName("syncToId")
private String syncToId;
#SerializedName("data")
private List<PhysiotherapistInputData> data;
//////////................GETTERS SETTERS GOES BELOW...........
PhysiotherapistInputData.java
public class PhysiotherapistInputData implements Parcelable {
#SerializedName("createdDate")
private String createdDate;
#SerializedName("swpNo")
private String swpNo;
#SerializedName("patientName")
private String patientName;
#SerializedName("age")
private String age;
#SerializedName("gender")
private String gender;
#SerializedName("namePhysio")
private String namePhysio;
#SerializedName("painScore")
private String painScore;
#SerializedName("exercieAdvice")
private String exercieAdvice;
#SerializedName("remark")
private String remark;
#SerializedName("isOld")
private String isOld;
#SerializedName("doctorId")
private int doctorId;
//////////................GETTERS SETTERS GOES BELOW...........
add follow proguard rules into your proguard file ,it comes from here
-keepattributes Signature
# For using GSON #Expose annotation
-keepattributes *Annotation*
# Gson specific classes
-dontwarn sun.misc.**
#-keep class com.google.gson.stream.** { *; }
# Application classes that will be serialized/deserialized over Gson
-keep class com.google.gson.examples.android.model.** { *; }
# Prevent proguard from stripping interface information from TypeAdapterFactory,
# JsonSerializer, JsonDeserializer instances (so they can be used in #JsonAdapter)
-keep class * implements com.google.gson.TypeAdapterFactory
-keep class * implements com.google.gson.JsonSerializer
-keep class * implements com.google.gson.JsonDeserializer
Current situation
I have a model, where class UserSettingModel have two field : long UserId and have as field one exemplar of class UserSettings ( with many field ) with name Settings.
WARNING : none of this fields used directly in my code ( in Android Studio color of this fields is gray), but I need to resend it to server.
public class UserSettingsModel
{
#SerializedName("UserId")
public long UserId = -1L;
#SerializedName("Settings")
public UserSettings Settings;
}//end of class/ UserSettingsModel
class UserSettings
{
#SerializedName("showRegion")
public String showRegion = "";
#SerializedName("showAddress")
public String showAddress = "";
}
Problem
If working with apk in DEBUG mode : GSON deserialize all field of class UserSettingModel, including Settings
If working with apk in RELEASE mode : field Settings - not deserialize
My proguard :
-keepattributes Signature
-keepattributes *Annotation*
-keep class sun.misc.Unsafe { *; }
-keep class com.google.gson.stream.** { *; }
-keep class com.google.gson.examples.android.model.** { *; }
-keep class com.google.gson.** { *; }
-keepclassmembers enum * { *; }
-keep class * implements com.google.gson.TypeAdapterFactory
-keep class * implements com.google.gson.JsonSerializer
-keep class * implements com.google.gson.JsonDeserializer
What I need
How to serialize/deserialize with GSON in Release mode ALL FIELD of classes, also with warning "field is never used" ?
This is because of obfuscation, as DQuade states in the comment.
To prevent gradle from removing them, you can either add the specific class in your proguard-rules.pro file:
-keepclassmembers class [yourPackageName].UserSettingsModel
-keepclassmembers class [yourPackageName].UserSettings
or prevent gradle from removing all members annotated with the SerializedName annotation:
-keepclassmembers class * {
#com.google.gson.annotations.SerializedName <fields>;
}
I have the following class
class CodeRequest(#JsonProperty("phone") val phoneNumber: String)
When I send request (using retrofit) with an object of this class as body (while minification is not enabled) everything works and request will be send in this form {"phone": "123"}
But enabling minification with the following proguard-rules.pro will result in a {"phoneNumber": "123"} request body.
# Jackson
-keep class com.fasterxml.jackson.databind.ObjectMapper {
public <methods>;
protected <methods>;
}
-keep class com.fasterxml.jackson.databind.ObjectWriter {
public ** writeValueAsString(**);
}
-keep #com.fasterxml.jackson.annotation.* class * { *; }
-keep #com.fasterxml.jackson.annotation.** class * { *; }
-keep class com.fasterxml.** { *; }
-keepattributes *Annotation*,EnclosingMethod,Signature,Exceptions,InnerClasses
-keep class * {
#com.fasterxml.jackson.annotation.* *;
}
-keep class * { #com.fasterxml.jackson.annotation.JsonProperty *;}
# Application classes that will be serialized/deserialized over Jackson
-keepclassmembers class my.application.data.models.** { *; }
-keepclassmembers class my.application.domain.network.rest.** { *; }
What's missing here?
Thank you.
Found solution a couple of minutes after posting the question.
The problem is not with proguard nor jackson, it's that Kotlin erases required data which are stored in kotlin.Metadata.
Adding the following rule to proguard fixed the issue:
-keep class kotlin.Metadata { *; }