Kotlin automatically changes parameter names in release build - android

Json object's field name automatically changing in release build and causing api to fail.
Data Object :
data class SleepStage(val awake:Double, val light: Double, val rem:Double, val deep:Double)
IN DEBUG MODE
SleepStage": {"awake": 0.58,"light": 4.23,"rem": 1.28,"deep": 0.35 }
IN RELEASE MODE:
SleepStage": {"a": 0.58,"b": 4.23,"c": 1.28,"d": 0.35 }

This is called obfuscation. When you create a release version, Proguard shortens all variable names.
Check out this StackOverflow post:
Proguard - do not obfuscate Kotlin data classes
It should be enough to add a #Keep annotation to your class:
#Keep
data class SleepStage(
...
)

Related

Android Kotlin + Firestore - DocumentSnapshot.toObject works only on emulator but not after installation

I have an Android application written in Kotlin and uses Firestore to read data.
I have this pretty standard code (I believe):
firestore.collection("my_collection_name")
.addSnapshotListener { snapshot, e ->
snapshot.documents?.forEach { document ->
val myModel: MyModel = document.toObject(MyModel::class.java)
This works perfectly fine when I run from Android Studio, both on the emulator and on my physical device.
But, when installing through APK or through Google Play the document.toObject always returns the MyModel object with it's default values. Not null, no error, just an empty object.
I know the data is retrieved well because I can pull it using document.get(). Plus, the number of documents is correct.
Any ideas?
Some variables from gradle:
kotlin_version = "1.5.10"
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.5.0'
implementation 'androidx.appcompat:appcompat:1.3.0'
implementation 'com.google.firebase:firebase-firestore:23.0.0'
when installing through APK or through Google Play the document.toObject always returns the MyModel object with its default values.
Your objects are getting the default values because you are using Proguard for security. This means that once you add your app's APK to Google Play, your code becomes shuffled, so others cannot see it. To solve this, please add the following optimization of code in your build.gradle file:
buildTypes {
release {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
Then add the following rules in your project's Proguard file to prevent that behavior by replacing the package name with your own package name:
-keepattributes Signature //Global rule
-keepclassmembers class com.example.MyModel.** {
*;
}
Please check below for more information:
https://firebase.google.com/docs/database/android/start#proguard
Please add #Keep annotation to MyModel class.
#Keep
class MyModel(..){..}

Kotlin get class name in Annotation processor

I am creating Annotation processor with annotation that takes class names, check if the name is CamelCase and writes all the classes which don't use camelcase.
This is the function which writes all those classes:
private fun checkCamelVariable(classElement: TypeElement) {
classElement.enclosedElements.filter {
!it.simpleName.toString().isDefinedCamelCase()
}.forEach {
printWarning("Detected non-camelcase name: ${it.simpleName}.")
}
}
In my build log i get:
warning: Detected non-camelcase name: TAG.[WARN] Incremental annotation processing requested, but support is disabled because the following processors are not incremental: com.example.processor.GenerateProcessor (NON_INCREMENTAL), com.google.auto.service.processor.AutoServiceProcessor (NON_INCREMENTAL).
Which means my annotation is working but I can not get the class names.
Looked on places like this: How to get rid of Incremental annotation processing requested warning?
but none of suggestions helped me.

Android R8 obfuscation with flexJson duplicate key issue

While obfuscating the android app using R8 and minifyEnabled true in build.gradle it adds duplicate key like below in one of webservice response.
Response: {"key1":"value1", ......., "key1":"value1"} it adds "key1" multiple time and flexJson throws exception and crashes the app
Caused by: flexjson.JSONException: Duplicate key "key1"
at flexjson.JSONTokener.putOnce(JSONTokener.java:498)
at flexjson.JSONTokener.parseObject(JSONTokener.java:471)
at flexjson.JSONTokener.nextValue(JSONTokener.java:357)
at flexjson.JSONTokener.parseObject(JSONTokener.java:471)
at flexjson.JSONTokener.nextValue(JSONTokener.java:357)
at flexjson.JSONDeserializer.deserialize(JSONDeserializer.java:197)
Everything works fine without obfuscation(minifyEnabled false).
Gradle Plugin Version used: 3.4.2, Also flexJson is used by one of the library included in the project.
In general you should ensure that all fields which are used for generating JSON through reflection are covered by a keep rule. Otherwise the name in the JSON can change from build to build. Also R8 use the property that the JVM and Android runtimes allow fields of different types to have the same name, you can end up in the situation described here.
One option could be to annotate all classes which are serialized, and use a keep rule like this:
-keep class #MyAnnotation ** {
<fields>;
}
or if all these classes are in a separate package:
-keep class com.example.mypackage.serialized_classes.** {
<fields>;
}

App server data fetch in debug mode but doesn't work in release mode

I am fetching data from server it is working fine in debug apk but I am trying on generate signed apk, data is not fetching data from server.
Is there any way to get solution?
Proguard may be causing that issue. Please check if it is enabled in your app's gradle file.
These lines enables proguard for release build:
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
If yes then you will need to keep some fields.
See this: https://developer.android.com/studio/build/shrink-code
You need to add #Keep annotation for all your network models (if you or your libs uses reflection). Because proguard obfuscate all classes in signed apk and Gson can`t parse JSON to you model.
EDIT:
for example you have class for parse from json (NetworkResponse.java class):
#Keep
public class NetworkResponse {
// fields of class...
}

How can I obfuscate my sdk coded with kotlin (and get rid of Metadata)

I'm developing a SDK (Android library), and I have to obfuscate a large part of my code so the customer may not try and play with internal code.
My lib is coded in kotlin, and I used proguard to obfuscate the code. Problem is that there are still #kotlin.Metadata (runtime) annotations inside the code after compile and obfuscation. With those annotations, it's really easy to retrieve the java code that originated this "(not-so-)obfuscated" bytecode.
I first thought it was my fault, and my project had too many entropy sources that might have induced this behaviour, so I made a sample project to prove that the problem does not come from my sdk implementation.
I created a new project with AS, then a lib module with 2 files:
facade.kt is my facade class, the one that I do not wish to obfuscate, so the customer may use it:
package com.example.mylibrary
class MyFacade(val internalClass:InternalClass) {
fun doSomething() {
internalClass.doSomething(
firstArgument=1,
secondArgument=2
)
}
}
and in this sample, internal.kt holds the classes that I want to obfuscate:
package com.example.mylibrary
class InternalClass {
fun doSomething(firstArgument: Int, secondArgument: Int) {
System.out.println("Arguments are : $firstArgument, $secondArgument")
}
}
The proguard rules are injected into gradle project with this release closure:
buildTypes {
release {
minifyEnabled true
proguardFiles 'proguard-rules.pro'
}
}
And here is proguard-rules.pro (only one line, nothing more) :
-keep class com.example.mylibrary.MyFacade {*;}
The result: when I ./gradlew clean myLib:assembleRelease, I do obtain an aar in which my facade is kept, and my internal class has been renamed in 'a', with one method 'a', except that the class is still annotated with kotlin #Metadata, which holds every information that helps the decompiler retrieve the original class name, the method, attribute and argument names, etc...
So my code is not so obfuscated at all...
#Metadata(
mv = {1, 1, 7},
bv = {1, 0, 2},
k = 1,
d1 = {"\u0000\u001a\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\b\u0002\n\u0002\u0010\u0002\n\u0000\n\u0002\u0010\b\n\u0002\b\u0002\u0018\u00002\u00020\u0001B\u0005¢\u0006\u0002\u0010\u0002J\u0016\u0010\u0003\u001a\u00020\u00042\u0006\u0010\u0005\u001a\u00020\u00062\u0006\u0010\u0007\u001a\u00020\u0006¨\u0006\b"},
d2 = {"Lcom/example/mylibrary/InternalClass;", "", "()V", "doSomething", "", "firstArgument", "", "secondArgument", "mylibrary_release"}
)
public final class a {
...
}
So my question: is it possible to get rid of those annotations, am I the only one facing this problem, or have I missed something?
Finally, I found a way to delete Kotlin metadata annotations.
In order to hide Kotlin metadata annotations, you need to enable R8 full mode.
Here is the information about my environment.
Environment
OS: macOS 10.15.1
Android Studio: 3.5.1
Gradle: 5.4.1
Android Gradle Tool: 3.5.2
What you have to do is just add properties to gradle.properties like below
gradle.properties
android.enableR8.fullMode=true
And here is my Proguard Rules
proguard-rules.pro
-dontwarn kotlin.**
-assumenosideeffects class kotlin.jvm.internal.Intrinsics {
static void checkParameterIsNotNull(java.lang.Object, java.lang.String);
}
FYI, R8 full mode is still testing now, so sometimes it doesn't work well. However, for me, it works perfectly for now.
There is some plugin dedicated for this kind of this request : https://github.com/oliver-jonas/unmeta
This plugin allows removing all Kotlin #Metadata / #DebugMetadata annotations from generated class files. This is safe to do as long as:
you do not intend to use the resulting binaries as a Kotlin library (#Metadata annotations are used to determine Kotlin function definitions),
you are not using Kotlin Reflection (certain reflection functionality depends on the presence of the #Metadata annotations).
Must be careful because when removing the metadata kotlin may your application or your library will not work.

Categories

Resources