How to resolve KotlinReflectionInternalError when using a custom adapter with Moshi? - android

I use Moshi for JSON conversions.
I have a custom adapter for converting LocalDateTime to JSON:
class LocalDateTimeAdapter {
#ToJson
fun toJson(value: LocalDateTime): String {
return value.toString()
}
#FromJson
fun fromJson(value: String): LocalDateTime {
return LocalDateTime.parse(value)
}
}
I use it like so:
val moshi = Moshi.Builder()
.addLast(KotlinJsonAdapterFactory())
.add(LocalDateTimeAdapter())
.build()
val jsonAdapter = moshi.adapter(MyModel::class.java)
val json = jsonAdapter.toJson(model)
This works fine for the debug build variant. The application crashes for other build variants where debugging is disabled with the following exception.
kotlin.reflect.jvm.internal.KotlinReflectionInternalError: Could not compute caller for function: public constructor
I've put in hours trying to resolve this but to no avail. As far as I understand this is because of proguard. I've tried putting the following in the proguard file:
-keepclasseswithmembers class * {
#com.squareup.moshi.* <methods>;
}
-keep #com.squareup.moshi.JsonQualifier interface *
-dontwarn org.jetbrains.annotations.**
-keep class kotlin.Metadata { *; }
-keepclassmembers class kotlin.Metadata {
public <methods>;
}
-keepclassmembers class ** {
#com.squareup.moshi.FromJson *;
#com.squareup.moshi.ToJson *;
}
-keepclassmembers class com.squareup.moshi.internal.Util {
private static java.lang.String getKotlinMetadataClassName();
}
-keepnames #kotlin.Metadata class com.myapp.domain.model.**
-keep class com.myapp.domain.model.** { *; }
-keepclassmembers class com.myapp.domain.model.** { *; }

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

How to obfuscate Kotlin properties with R8?

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.

I'm using dagger2 ,while genrating signed apk ,retrofit api calls are not working, in debug mode its working fine

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.

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

JsonProperty not working while minification enabled

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

Categories

Resources