E/WM-WorkerFactory: Could not instantiate com.xyz.XYZWorker - android

I am working on native Android app with Kotlin.
I implement a Worker class to run async webservice call.
My worker works fine unless I don't build with dexguard.
With dexguard builds only I get following error
2021-12-07 18:16:46.085 25839-29600/? E/WM-WorkerFactory: Could not instantiate com.xyz.XYZWorker
java.lang.NoSuchMethodException: com.xyz.XYZWorker.<init> [class android.content.Context, class androidx.work.WorkerParameters]
at java.lang.Class.getConstructor0(Class.java:2332)
at java.lang.Class.getDeclaredConstructor(Class.java:2170)
at androidx.work.WorkerFactory.createWorkerWithDefaultFallback(:95)
at androidx.work.impl.WorkerWrapper.runWorker(:244)
at androidx.work.impl.WorkerWrapper.run(:136)
at androidx.work.impl.utils.SerialExecutor$b.run(:91)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:923)
Here is my code, I mask some classnames because it is client project. I inject use cases with Hilt.
#HiltWorker
class XYZWorker #AssistedInject constructor(
#Assisted context: Context,
#Assisted workerParameters: WorkerParameters,
val someUseCase: SomeUseCase,
val someUseCase2: SomeUseCase2,
) : CoroutineWorker(context, workerParameters) {
...
}
Use cases provided by inject
class SomeUseCase #Inject constructor(
private val someRepo: SomeRepo
) : UseCaseParams<List<SomeDomainEntity>, SomeResponseDomainEntity> {
...
}
class MyWorkManagerInitializer : Initializer<WorkManager> {
override fun create(context: Context): WorkManager {
val workerFactory = getWorkerFactory(appContext = context.applicationContext)
val config = Configuration.Builder()
.setWorkerFactory(workerFactory)
.build()
WorkManager.initialize(context, config)
return WorkManager.getInstance(context)
}
override fun dependencies(): MutableList<Class<out Initializer<*>>> = mutableListOf()
private fun getWorkerFactory(appContext: Context): HiltWorkerFactory {
val workManagerEntryPoint = EntryPointAccessors.fromApplication(
appContext,
WorkManagerInitializerEntryPoint::class.java
)
return workManagerEntryPoint.hiltWorkerFactory()
}
#InstallIn(SingletonComponent::class)
#EntryPoint
interface WorkManagerInitializerEntryPoint {
fun hiltWorkerFactory(): HiltWorkerFactory
}
}
<provider
android:name="androidx.work.impl.WorkManagerInitializer"
android:authorities="${applicationId}.workmanager-init"
android:exported="false"
tools:node="remove" />
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge">
<meta-data
android:name="com.xyz.MyWorkManagerInitializer"
android:value="#string/androidx_startup" />
</provider>
-keep public class androidx.startup.** { *; }
-keep public class androidx.work.** { *; }
-keep public class androidx.hilt.work.** { *; }
-keep #interface androidx.hilt.work.** { *; }
-keep #interface dagger.assisted.** { *; }
-keep #interface dagger.assisted.** { *; }
-keep public class * implements androidx.startup.Initializer { *; }
-keep interface com.xyz.WorkManagerInitializerEntryPoint { *; }
-keep public class * implements com.xyz.WorkManagerInitializerEntryPoint { *; }
-keep public class * extends androidx.work.CoroutineWorker { *; }
-keepclassmembers public class * extends androidx.work.CoroutineWorker {
public <init>(...);
#com.google.inject.Inject <init>(...);
#javax.inject.Inject <init>(...);
}
-keep public class * extends androidx.work.Worker { *; }
-keepclassmembers public class * extends androidx.work.Worker {
public <init>(...);
#com.google.inject.Inject <init>(...);
#javax.inject.Inject <init>(...);
}
-keep public class com.xyz.SomeUseCase { *; }
-keep public class com.xyz.SomeUseCase2 { *; }
and here is my libraries
implementation androidx.hilt:hilt-work:1.0.0-alpha03
implementation androidx.work:work-runtime-ktx:2.5.0
kapt androidx.hilt:hilt-compiler:1.0.0-alpha03

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.

Proguard enable build not working with Gson Android Kotlin

Once I enabled proguard i am not unable to convert JSON string in the POJO class with help of gson library, I am getting null value but it's working fine when proguard is disabled
Log.d(TAG, "JSON: ${StringUtils.data}")
try {
val quickReplyButton = Gson().fromJson(StringUtils.data, QuickReplyButton::class.java)
Log.e(TAG, "quickReplyButtondata:" +quickReplyButton)
}
catch (e: Exception){
e.printStackTrace()
}
Pojo class
package com.mypackage.sdk.data.model
data class QuickReplyButton(
val nodes: List<Node>,
val showMessageText: Boolean,
val transitions: List<Transition>,
val widgetAction: String,
val widgetType: String,
val widgetView: String
)
data class Node(
val controls: Controls,
val id: String
)
data class Transition(
val from: String,
val name: String,
val to: To,
val trigger: String
)
data class Controls(
val `0`: X0
)
data class X0(
val event: Event,
val id: String,
val text: List<String>,
val type: String,
val values: List<String>
)
data class Context(
val border: String,
val color: String,
val textColor: String,
val textStyle: String
)
data class Event(
val name: String
)
data class To(
val sendMessage: SendMessage
)
data class SendMessage(
val displayText: String,
val nvaaId: String,
val nvaaType: String,
val selected: String,
val selectedText: String,
val selectedValue: String
)
Progurd rules:
-keep class com.mypackage.sdk.data.model.QuickReplyButton { *; }
-keep class com.mypackage.sdk.data.model.SendMessage { *; }
-keep class com.mypackage.sdk.data.model.To { *; }
-keep class com.mypackage.sdk.data.model.Event { *; }
-keep class com.mypackage.sdk.data.model.Context { *; }
-keep class com.mypackage.sdk.data.model.X0 { *; }
-keep class com.mypackage.sdk.data.model.Controls { *; }
-keep class com.mypackage.sdk.data.model.Transition { *; }
-keep class com.mypackage.sdk.data.model.Node { *; }
-keep class com.mypackage.sdk.** { *; }
-keep class com.mypackage.sdk.ui.** { *; }
-keep class com.mypackage.sdk.Utils.** { *; }
-keep class com.mypackage.sdk.Care.** { *; }
-keep class com.mypackage.sdk.CareApplication.** { *; }
##---------------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.** { *; }
-keep class com.google.gson.**{ *; }
-dontwarn java.lang.reflect.**
-keep class kotlin.** { *; }
-keepattributes InnerClasses
-keepattributes EnclosingMethod
You need to add "SerializedName" annotation to all of properties.
#SerializedName("data")
#Expose
var data: Data? = null

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

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

Kotlin Reflection: primaryConstructor not found with R8 obfuscation

In my current Kotlin Android project I just had to face the following problem:
For this class hierarchy
abstract class MyClass {
...
}
class MySubClassA: MyClass() {
...
}
class MySubClassB: MyClass() {
...
}
class MySubClassC: MyClass() {
...
}
I wrote a creator function like this:
private fun <T : MyClass> createMyClass(myClassType: KClass<T>): T? {
val constructor = myClassType.primaryConstructor
return constructor?.call()?.apply {
...
}
}
This worked nicely for the debug version but for the release version with R8 obfuscation turned on the constructor was alway null. My settings for R8 were this:
-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);
}
-keep class kotlin.reflect.jvm.internal.impl.builtins.BuiltInsLoaderImpl
After some reverse engineering of the Kotlin reflection API I came up with the following workaround:
private fun <T : MyClass> createMyClass(myClassType: KClass<T>): T? {
val constructor = myClassType.constructors.firstOrNull() {
it.valueParameters.isEmpty()
}
return constructor?.call()?.apply {
...
}
}
This seems to indicate, that the constructor information of the class is preserved by R8 but the meta information about a constructor being primary gets lost.
Does anybody know how to solve this in general?
I came across this issue and was able to resolve it using the #Keep annotation.
abstract class MyClass {
}
class MySubClassA #Keep constructor(): MyClass() {
}
class MySubClassB #Keep constructor(): MyClass() {
}
class MySubClassC #Keep constructor(): MyClass() {
}

Categories

Resources