How to properly add proguard to application? - android

I have added pro guards to my Android application, but it makes too much on resposne files and provide to crash an application during launching.
This is my build.gradle settings of optimizing and reducing code
minifyEnabled true
shrinkResources true
debuggable false
manifestPlaceholders = [crashlyticsCollectionEnabled:"true"]
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
in android manifest
<application
<meta-data
android:name="firebase_crashlytics_collection_enabled"
android:value="${crashlyticsCollectionEnabled}" />
[...]
in proguard-rules.pro
-keep class com.google.android.gms.** { *; }
-keep class com.google.firebase.** { *; }
-keep class com.package.packageandroid2.endpoints.responses.** { *; }
-keep class * extends com.package.packageandroid2.endpoints.** { *; }
-keep class * extends com.package.packageandroid2.PackageApplication.** { *; }
-keep class * extends com.package.packageandroid2.** { *;}
error
Process: com.package.dev.staging, PID: 9574
java.lang.RuntimeException: Unable to create application com.package.packageandroid2.PackageApplication: zh.d: Could not create instance for [Single:'a9.p']
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:6465)
This is the place where is Single class
package com.package.packageandroid2.endpoints.responses
sealed class ResponseId {
abstract val count: Int
data class Single(val id: Int) : ResponseId() {
override val count: Int = 1
}
data class Multiple(val ids: List<Int>) : ResponseId() {
override val count: Int = ids.count()
}
}
From my perspective I keep classes that could provide to crash. What could be the problem here?

Related

Could not compute caller for function: public constructor Error when enable proguard in Android

The project working well without enabling proguard, the problem comes when enable it, I am using retrofit with Moshi converter and Coroutines to fetch list of data, and Hilt for DI, and I added all rules and kept all models
This is the error:
Could not compute caller for function: public constructor MovieListEntity(movie_data: kotlin.collections.List<com...domain.entities.MovieData>) defined in com...domain.entities.MovieListEntity[c#dad1eb0] (member = null)
And these are the classes mentioned
data class MovieListEntity(
#field:Json(name = "movie_data")
val movie_data: List<MovieData>
)
data class MovieData(
#field:Json(name = "movie_id")
val movie_id: Int,
#field:Json(name = "sub_title")
val sub_title: String,
#field:Json(name = "title")
val title: String
)
Note: I tried also without annotations, and it didn't help
These are the proguard rules:
-keep class com.***.***.domain.entitie.** { *; }
-keep class com.***.***.domain.entities.*
-keep class com.***.***.domain.entities.MovieListEntity
-keep class com.***.***.domain.entities.MovieData
-keep class com.***.***.DataBinderMapperImpl { *; }
-keep class com.***.***.DataBinderMapperImpl { *; }
-keep class com.***.*****{
public ** component1();
<fields>;
}
Plus other rules for retrofit, OkHttp, hilt .. etc.
How Can I solve this error?
Solved by adding these rules
-keepclassmembers class kotlin.Metadata {
public <methods>;
}
-keepclassmembers class * {
#com.squareup.moshi.FromJson <methods>;
#com.squareup.moshi.ToJson <methods>;
}
-keepnames #kotlin.Metadata class com.******.domain.entities.**
-keep class com.******.domain.entities.** { *; }
-keepclassmembers class com.*****.domain.entities.** { *; }
The problem was in Moshi library, check this

Android Kotlin ProGuard Rules Wrong?

Currently I have a library that is to be consumed by multiple other projects, however I have a problem with the code obfuscation when consuming the .aar in particular with some classes:
This one to handle backend responses or asynchronous tasks, etc.
sealed class Result<out T : Any> {
open class Success<out T : Any>(val data: T) : Result<T>()
open class Error(val error: ErrorModel) : Result<Nothing>()
}
And this one to act as a initializer or something like it to the library:
class LibApp private constructor(
val appContext: Context
) {
companion object {
#JvmStatic lateinit var instance: LibApp
private set
fun init(
appContext: Context
) {
if (this::instance.isInitialized.not()) {
instance = LibApp(appContext)
}
}
}
}
The rules I'm using are:
#noinspection ShrinkerUnresolvedReference
-keep class com.cross.project.compilation.testlib.response.** { *; }
-keep class com.cross.project.compilation.testlib.LibApp.** { *; }
Currently I have 3 build types and 2 flavors:
buildTypes {
release {
debuggable false
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
releaseDebug{
debuggable true
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
debug{
debuggable true
minifyEnabled false
}
}
flavorDimensions "libFlavors"
productFlavors {
dev {
}
prod {
}
}
When I check the decompiled sources for release variants in the consumer project I get:
Result gets the < T > stripped from the Success return:
public sealed class Result<out T : kotlin.Any> private constructor() {
public open class Error public constructor(error: com.cross.project.compilation.testlib.response.ErrorModel) : com.cross.project.compilation.testlib.response.Result {
public final val error: com.cross.project.compilation.testlib.response.ErrorModel /* compiled code */
}
public open class Success<out T : kotlin.Any> public constructor(data: T) : com.cross.project.compilation.testlib.response.Result {
public final val data: T /* compiled code */
}
}
This changes the way something is coded within the variants, from something like this in dev:
return when (val response = accountService.getAccountData()) {
is Result.Success -> ConsumerResult.Success(response.data)
is Result.ErrorModel -> ConsumerResult.Failure(response.errorModel)
}
To this:
return when (val response = accountService.getAccountData()) {
is Result.Success<*> -> ConsumerResult.Success((response.data as AccountData))
is Result.ErrorModel -> ConsumerResult.Failure(response.errorModel)
}
The LibApp class gets removed for some reason.
I've tried to modify the rules to avoid these issues, but only succeding in keeping the LibApp class by applying the following:
-keepclasseswithmembers class com.cross.project.compilation.testlib.LibApp {
public *;
}
-keep #interface kotlin.Metadata {
*;
}
-keepattributes RuntimeVisibleAnnotations
And also add the #Keep annotation to every level in the class like:
#Keep class LibApp private constructor(
...
#Keep
companion object {
...
#Keep
fun init(
...
However I've had no luck modifying the rule to keep the generic return of the Result.Success, Any idea of what I'm doing wrong?
As an additional information, I'm using and building with the maven-publish plugin:
Android Gradle Plugin 4.0.1
Gradle Wrapper 6.1.1
This problem seems to have appeared after I upgraded from:
Android Gradle Plugin 3.6.3
Gradle Wrapper 5.6.4
The type Nothing in the Result.Error is erased while obfuscating by R8. Try to add in build.gradle file the following:
buildscript {
repositories {
// other repos are omitted
maven { url 'https://storage.googleapis.com/r8-releases/raw' }
}
dependencies {
classpath 'com.android.tools:r8:2.1.68'
// other dependencies are omitted
In proguard file declare the Result type to keep from obfuscation
-keep class [path_to_class].Result { *; }
-keep class [path_to_class].Result$** { *; }

R8 stripping out Kotlin companion object needed for reflection

I have a class with a companion object which implements a factory interface.
class GoalInspectorData(
...
) {
companion object : DataClassFactory<GoalInspectorData> {
override fun fromV8Object(v8Object: V8Object): GoalInspectorData {
...
}
}
}
I have some code which examines this class at runtime using reflection to see if the class provides a factory method. It does this by checking to see if the class has a companion object (companionObjectInstance) and, if so, if that companion object implements the factory interface.
internal inline fun <reified T> convert(obj: Any): T {
val companionObject = T::class.companionObjectInstance
#Suppress("UNCHECKED_CAST")
return when {
T::class in builtInClasses -> obj as T
companionObject as? DataClassFactory<T> != null -> companionObject.fromV8Object(obj as V8Object)
else -> throw IllegalArgumentException("No converter for type ${T::class}")
}
}
This all works fine in a debug build.
It fails in a release build with R8 enabled (minifyEnabled true in build.gradle). It fails because companionObjectInstance returns null.
I'm using the don't optimize Proguard config:
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
and in my own proguard-rules.pro I've added just about every -keep rule I can imagine in an attempt to retain this companion object, and added #Keep annotations to everything, but nothing works. R8 is determined to strip it out.
For example:
-keep class my.package.** {
*;
}
-keep interface my.package.** {
*;
}
-if class **$Companion extends **
-keep class <2>
-if class **$Companion implements **
-keep class <2>
Are there any other -keep rules or configuration options which would instruct R8 to retain this companion object?
First of all, the keep rule
-keep class my.package.** {
*;
}
should be sufficient to keep all the classes - including companion classes in your program. You should not need the -dontoptimize flag, so using the configuration proguard-android-optimize.txt should be fine.
However, as you use Kotlin reflection you probably also need to keep the annotation class kotlin.Metadata and runtime visible annotations with these rules:
-keep #interface kotlin.Metadata {
*;
}
-keepattributes RuntimeVisibleAnnotations
If that does still not work could you please file an R8 issue? If you can include a simple reproduction that would be great.

Duplicate variable names in MODEL class, error when Obfuscation is done in ANDROID STUDIO

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

Gson do not deserialize fields that never used

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>;
}

Categories

Resources