Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VerifyError on Android since 1.3.7 #2041

Closed
Pitel opened this issue May 20, 2020 · 19 comments
Closed

VerifyError on Android since 1.3.7 #2041

Pitel opened this issue May 20, 2020 · 19 comments

Comments

@Pitel
Copy link

Pitel commented May 20, 2020

java.lang.VerifyError: Verifier rejected class cz.masterapp.clones.App$onCreate$4: java.lang.Object cz.masterapp.clones.App$onCreate$4.invokeSuspend(java.lang.Object) failed to verify: java.lang.Object cz.masterapp.clones.App$onCreate$4.invokeSuspend(java.lang.Object): [0x129] cannot access instance field java.lang.Object kotlin.jvm.internal.Ref$ObjectRef.element from object of type Reference: java.io.IOException (declaration of 'cz.masterapp.clones.App$onCreate$4' appears in /data/app/cz.masterapp.annie3-BWvfpH7rb3wf9eY9Lx8gIA==/base.apk:classes2.dex)
     FATAL EXCEPTION: main
Process: cz.masterapp.annie3, PID: 12229
java.lang.VerifyError: Verifier rejected class cz.masterapp.clones.App$onCreate$4: java.lang.Object cz.masterapp.clones.App$onCreate$4.invokeSuspend(java.lang.Object) failed to verify: java.lang.Object cz.masterapp.clones.App$onCreate$4.invokeSuspend(java.lang.Object): [0x129] cannot access instance field java.lang.Object kotlin.jvm.internal.Ref$ObjectRef.element from object of type Reference: java.io.IOException (declaration of 'cz.masterapp.clones.App$onCreate$4' appears in /data/app/cz.masterapp.annie3-BWvfpH7rb3wf9eY9Lx8gIA==/base.apk:classes2.dex)
	at cz.masterapp.clones.App.onCreate(App.kt:277)
	at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1118)
	at android.app.ActivityThread.handleBindApplication(ActivityThread.java:5791)
	at android.app.ActivityThread.-wrap1(Unknown Source:0)
	at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1661)
	at android.os.Handler.dispatchMessage(Handler.java:105)
	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)

In App.kt:277 is just GlobalScope.launch {

Reverting to 1.3.6 fixes the crash (but it has the #2004, which has a workaround)

@qwwdfsad
Copy link
Member

qwwdfsad commented May 20, 2020

Could you please attach a simple reproducer?

I've played with simple Android application with 1.3.7 and it seems to work OK. Probably has something to do with untypical bytecode shape

@Pitel
Copy link
Author

Pitel commented May 20, 2020

I can't. When I try it in one of my simplier apps, it works just fine. Trying to figure out why is that.

They both uses same versions of pretty much everything compile-related (Kotlin, Gradle, Android Gradle Plugin, Build Tools, ...)

@qwwdfsad
Copy link
Member

qwwdfsad commented May 20, 2020

Could you please share the piece of code around at cz.masterapp.clones.App.onCreate(App.kt:277) then?

@nabrozidhs
Copy link

@Pitel I had the same issue but it was my fault. I had some buggy code that looked like this

suspend fun action(model: Model) {
  withContext(cpu context) {
    withContext(db context) {
      updateDb()
    }
    try {
      updateRemoteApiCall()
    } catch (t: HttpException) {
      withContext(db context) {
        revertDbOperation()
        throw t
      }
    }
  }
}

the issue in my case was that I was rethrowing the exception within the db context!

Hopefully it'll help you debug your issue.

@Pitel
Copy link
Author

Pitel commented May 21, 2020

I guess I can share the whole file:

App.kt
package cz.masterapp.clones

import android.app.Application
import android.os.Build
import android.os.StrictMode
import android.widget.Toast
import androidx.fragment.app.FragmentManager
import coil.Coil
import coil.ImageLoaderBuilder
import coil.util.CoilUtils
import com.google.firebase.analytics.FirebaseAnalytics
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.google.firebase.iid.FirebaseInstanceId
import com.uxcam.UXCam
import cz.masterapp.annie2.core2.devicediscovery.DeviceDiscoveryManager
import cz.masterapp.annie2.coroutines.CoroutineContextProvider
import cz.masterapp.annie2.data.UserRepository
import cz.masterapp.annie2.log.AntispamDebugTree
import cz.masterapp.annie2.log.CrashlyticsTree
import cz.masterapp.annie2.log.Log
import cz.masterapp.annie2.messaging.di.MqttModule
import cz.masterapp.annie2.rest.annie2.Annie2RestHelper
import cz.masterapp.annie2.rest.annie2.model.Server
import cz.masterapp.annie2.rest.annie2.model.ServerResponse
import cz.masterapp.annie2.rest.annie2.model.TokenResponse
import cz.masterapp.annie2.rest.annie2.model.User
import cz.masterapp.annie2.rest.dependencyinjection.RestModule
import cz.masterapp.annie2.rest.error.Failure
import cz.masterapp.annie2.rest.extensions.onLeft
import cz.masterapp.annie2.stream2.di.Stream2Module
import cz.masterapp.annie2.types.DeviceId
import cz.masterapp.annie2.types.Mapper
import cz.masterapp.annie2.userModule
import cz.masterapp.clones.extensions.await
import cz.masterapp.clones.helpers.CoilTimberLogger
import cz.masterapp.clones.mapper.RestErrorToMessage
import cz.masterapp.clones.modules.dataModule
import cz.masterapp.clones.modules.viewModelsModule
import kotlinx.coroutines.*
import okhttp3.OkHttpClient
import org.koin.android.ext.android.get
import org.koin.android.ext.android.inject
import org.koin.android.ext.koin.androidContext
import org.koin.android.ext.koin.androidLogger
import org.koin.core.context.startKoin
import org.koin.core.logger.Level
import org.koin.core.qualifier.named
import org.koin.dsl.module
import org.webrtc.PeerConnection
import timber.log.Timber
import java.io.IOException
import java.net.UnknownHostException
import kotlin.time.ExperimentalTime
import kotlin.time.TimeSource

@OptIn(ExperimentalTime::class)
class App : Application() {

    companion object {
        private const val CRASHLYTICS_USER_KEY = "user"
        private val TREE = if (BuildConfig.DEBUG) AntispamDebugTree() else null

        fun setCrashlyticsUser(user: User) {
            FirebaseCrashlytics.getInstance().setCustomKey(
                CRASHLYTICS_USER_KEY,
                user.copy(email = "", token = TokenResponse("", "", "", null)).toString()
            )
        }
    }

    init {
        System.setProperty(
            DEBUG_PROPERTY_NAME, if (BuildConfig.DEBUG && Log.COROUTINES) {
                DEBUG_PROPERTY_VALUE_ON
            } else {
                DEBUG_PROPERTY_VALUE_OFF
            }
        )

        FragmentManager.enableDebugLogging(BuildConfig.DEBUG && Log.FRAGMENT)
    }

    override fun onCreate() {
        super.onCreate()

        UXCam.occludeAllTextFields(true)
        UXCam.startWithKey(getString(R.string.uxcam_key))

        if (BuildConfig.DEBUG && Log.STRICT) {
            StrictMode.setThreadPolicy(
                StrictMode.ThreadPolicy.Builder()
                    .detectAll()
                    .penaltyLog()
                    .build()
            )
            StrictMode.setVmPolicy(
                StrictMode.VmPolicy.Builder()
                    .detectAll()
                    .penaltyLog()
                    .build()
            )
        }

        TREE?.run { Timber.plant(this) }

        startKoin {
            androidContext(this@App)
            modules(listOf(
                RestModule.module,
                dataModule,
                MqttModule.module,
                viewModelsModule,
                Stream2Module.module,
                userModule,
                DeviceDiscoveryManager.module,

                module {
                    single {
                        FirebaseAnalytics.getInstance(androidContext())
                    }

                    single<Mapper<Failure, String>>(named(Mapper.REST_ERROR_MAPPER)) {
                        RestErrorToMessage(
                            androidContext()
                        )
                    }

                    single(named("servers")) {
                        GlobalScope.async(start = CoroutineStart.LAZY) {
                            get<Deferred<Annie2RestHelper>>(named<Annie2RestHelper>()).await()
                                .getAllServersConfig().onLeft {
                                    when (val failure = it.a) {
                                        is Failure.NetworkError -> {
                                            val ioe = failure.e
                                            Timber.w(ioe, "DNS")
                                            withContext(CoroutineContextProvider.UI) {
                                                if (ioe.cause?.cause is UnknownHostException) {
                                                    Toast.makeText(
                                                        this@App,
                                                        ioe.cause?.cause?.localizedMessage,
                                                        Toast.LENGTH_LONG
                                                    ).show()
                                                } else {
                                                    Toast.makeText(
                                                        this@App,
                                                        ioe.localizedMessage,
                                                        Toast.LENGTH_LONG
                                                    ).show()
                                                }
                                            }
                                        }
                                    }
                                    return@async null
                                }
                        }
                    }

                    single(MqttModule.MQTT_URL) {
                        GlobalScope.async(start = CoroutineStart.LAZY) {
                            val mqttServer = get<Deferred<ServerResponse?>>(
                                named("servers")
                            ).await()?.mqttServer
                            when {
                                mqttServer == null -> "tcp://null"
                                mqttServer.sslPort != null -> "ssl://${mqttServer.hostname}:${mqttServer.sslPort}"
                                mqttServer.port != null -> "tcp://${mqttServer.hostname}:${mqttServer.port}"
                                else -> "tcp://${mqttServer.hostname}"
                            }
                        }
                    }

                    single(MqttModule.USERNAME) {
                        GlobalScope.async(start = CoroutineStart.LAZY) {
                            get<Deferred<ServerResponse?>>(named("servers")).await()
                                ?.mqttServer?.user
                        }
                    }

                    single(MqttModule.PASSWORD) {
                        GlobalScope.async(start = CoroutineStart.LAZY) {
                            get<Deferred<ServerResponse?>>(named("servers")).await()
                                ?.mqttServer?.password
                        }
                    }

                    single(MqttModule.LOCAL_DEVICE_ID) {
                        GlobalScope.async(start = CoroutineStart.LAZY) {
                            get<UserRepository>().uuid.await()
                        }
                    }

                    single(MqttModule.CLIENT_ID) {
                        GlobalScope.async(start = CoroutineStart.LAZY) {
                            val deviceId = get<Deferred<DeviceId>>(
                                MqttModule.LOCAL_DEVICE_ID
                            ).await()
                            "${BuildConfig.FLAVOR}-android-${BuildConfig.VERSION_NAME}-${deviceId.uuid}_"
                        }
                    }

                    factory(Stream2Module.GROUP_UUID) {
                        GlobalScope.async(start = CoroutineStart.LAZY) {
                            get<UserRepository>().getUser()!!.groupUUID
                        }
                    }

                    single<Deferred<List<PeerConnection.IceServer>>>(Stream2Module.ICE_SERVERS) {
                        val stunTurn = mutableListOf<PeerConnection.IceServer>()

                        /**
                         * Converts Server model class to IceServer
                         */
                        fun Server.toIceServer(protocol: String) = if (port != null) {
                            PeerConnection.IceServer.builder("$protocol:$hostname")
                        } else {
                            PeerConnection.IceServer.builder("$protocol:$hostname:$port")
                        }.apply {
                            if (user != null) {
                                setUsername(user.toString())
                            }

                            if (password != null) {
                                setPassword(password.toString())
                            }
                        }.createIceServer()

                        GlobalScope.async(start = CoroutineStart.LAZY) {
                            get<Deferred<ServerResponse?>>(named("servers")).await()?.run {
                                stunServers?.forEach {
                                    stunTurn.add(it.toIceServer("stun"))
                                }

                                turnServers?.forEach {
                                    stunTurn.add(it.toIceServer("turn"))
                                }
                            }

                            stunTurn
                        }
                    }

                    single { TimeSource.Monotonic }
                }
            ))

            properties(
                mapOf(
                    RestModule.API_KEY to getString(R.string.api_key),
                    RestModule.USER_AGENT to "${BuildConfig.FLAVOR}/${BuildConfig.VERSION_NAME} Android/${Build.VERSION.SDK_INT}",
                    MqttModule.BASE_TOPIC to getString(R.string.topic)
                )
            )

            androidLogger(if (BuildConfig.DEBUG) Level.DEBUG else Level.INFO)
        }

        GlobalScope.launch {
            get<Deferred<OkHttpClient>>(named<OkHttpClient>()).await()
                .let { okHttp ->
                    Coil.setImageLoader(
                        ImageLoaderBuilder(this@App)
                            .okHttpClient(
                                okHttp.newBuilder().cache(
                                    CoilUtils.createDefaultCache(this@App)
                                ).build()
                            )
                            .apply {
                                if (BuildConfig.DEBUG) {
                                    logger(CoilTimberLogger())
                                }
                            }
                            .build()
                    )
                }
        }

/*277*/ GlobalScope.launch {
            get<UserRepository>().let { userRepository ->
                try {
                    with(userRepository.uuid.await()) {
                        FirebaseCrashlytics.getInstance().setUserId(uuid.toString())
                        get<FirebaseAnalytics>().setUserId(uuid.toString())

                    }
                } catch (ioe: IOException) {
                    Timber.w(ioe, "DNS")

                    withContext(CoroutineContextProvider.UI) {
                        if (ioe.cause?.cause is UnknownHostException) {
                            Toast.makeText(
                                this@App,
                                ioe.cause!!.cause!!.localizedMessage,
                                Toast.LENGTH_LONG
                            ).show()
                        } else {
                            Toast.makeText(this@App, ioe.localizedMessage, Toast.LENGTH_LONG).show()
                        }
                    }
                }

                userRepository.getUser()?.let { setCrashlyticsUser(it) }
            }

            Timber.plant(CrashlyticsTree())

            val rests: Deferred<Annie2RestHelper> by inject(named<Annie2RestHelper>())
            try {
                FirebaseInstanceId.getInstance().instanceId.await()?.token?.let {
                    rests.await().updateFcmToken(it)
                }
            } catch (t: Throwable) {
                Timber.w(t)
            }
        }
    }
}

@Tolriq
Copy link

Tolriq commented May 22, 2020

There was a few fixes on R8/D8 side for such kind of issues, what Android Gradle plugin version do you use ?

@qwwdfsad
Copy link
Member

It could happen either because of R8 or because of Kotlin compiler that has a bug around experimental contracts that were added to coroutine builders in 1.3.7.

I am still trying to reproduce it. As a potential workaround, I'd recommend extracting suspicious withContext calls to a separate function and see if that helps

@Tolriq
Copy link

Tolriq commented May 22, 2020

For the record I've just build my next prod build with 1.3.7, Kotlin 1.3.72 and latest AGP 4.0 RC1 / R8 and have not had this issue but faced https://issuetracker.google.com/issues/157223339 that is good to know if someone else report the issue here.

@Pitel
Copy link
Author

Pitel commented May 22, 2020

It's happening with Android Gradle Plugins 3.6.2, 3.6.3 and 4.0.0-rc01, so I think it's not caused by D8/R8.

Also, it's happening with debug builds, and I think they are not using R8 in default configuration.

@Pitel
Copy link
Author

Pitel commented May 22, 2020

@qwwdfsad Moving withContext to separate function helps! 👍

@qwwdfsad
Copy link
Member

Unfortunately, this is a bug in Kotlin compiler related to function with contracts that were added in #2030.

We are working on the fix, a workaround is to extract withContext calls from try and catch blocks (I'll try to post more specific workaround later) to separate functions.
Sorry for the inconvenience!

@abelkov
Copy link
Member

abelkov commented Jun 4, 2020

I confirm that a similar problem https://youtrack.jetbrains.com/issue/KT-39298 is not reproduced with Kotlin 1.4-M2. The bug with contracts mentioned above was fixed in 1.4-M2.

@elanpang
Copy link

It happend to me when I used coroutines- withContext in 1.3.7 to 1.3.8 on Android/Flutter, revert coroutines lib to 1.3.6 , the app does not crash anymore.

@rodion-m
Copy link

Changing kotlin_version to 1.4.0-rc solved the problem.

@qwwdfsad
Copy link
Member

Fixed in 1.3.9 with Kotlin 1.4

@personshelldon
Copy link

personshelldon commented Aug 20, 2020

The similar error here with Coroutines 1.3.9 and Kotlin 1.4 (Android Gradle Plugin 4.0.1).
Code:

abstract class CompletableUseCase {

    abstract suspend fun work(scope: CoroutineScope, onComplete: suspend () -> Unit)

    operator fun invoke(
        coroutineScope: CoroutineScope,
        runContext: CoroutineDispatcher,
        returnContext: CoroutineDispatcher,
        onError: (Throwable) -> Unit,
        onComplete: () -> Unit
    ): Job = coroutineScope.launch(runContext) {
        try {
            work(this) {
                withContext(returnContext) {
                    onComplete()
                }
            }
        } catch (expected: Throwable) {
            withContext(returnContext) {
                onError(expected)
            }
        }
    }
}

Error on line coroutineScope.launch(runContext):

Caused by: java.lang.VerifyError: Verifier rejected class com.test.app.domain.usecase.common.CompletableUseCase$invoke$1 due to bad method java.lang.Object com.test.app.domain.usecase.common.CompletableUseCase$invoke$1.invokeSuspend(java.lang.Object) (declaration of 'com.test.app.domain.usecase.common.CompletableUseCase$invoke$1' appears in /data/app/com.test.app-1/base.apk:classes2.dex)

If I comment out try catch condition, then it works.

@personshelldon
Copy link

Ok, after some research I found that none of the new features from Kotlin 1.4 work. I think there is a bug in Android Studio, because gradlew :app:dependencies reports that version of kotlinCompilerClasspath is 1.3.72, but, at the same time, in main gradle it set to 1.4.0. Reported this to Android Studio bug tracker.

@ocampoleandro
Copy link

@personshelldon did you fixed the problem? I am having the same issue. Could you share the link to the issue tracker in Android Studio?

@personshelldon
Copy link

@ocampoleandro, yes, I fixed this issue by moving classpath of the kotlin plugin before classpath of the gradle plugin in the main gradle file. There was a conflict of kotlin versions in detekt plugin, in my case (detekt wants 1.3.72). I'm using detekt to check codestyle before build. Check which of Your dependencies wants another version of kotlin.

lukmccall added a commit to expo/expo that referenced this issue May 10, 2021
# Why

Fixes:
```
2021-05-07 17:53:30.805 11096-11096/host.exp.nclexp E/AndroidRuntime: FATAL EXCEPTION: main
    Process: host.exp.nclexp, PID: 11096
    java.lang.VerifyError: Verifier rejected class expo.modules.devlauncher.helpers.DevLauncherCoroutinesExtensionsKt: java.lang.Object expo.modules.devlauncher.helpers.DevLauncherCoroutinesExtensionsKt.runBlockingOnMainThread(kotlin.jvm.functions.Function0) failed to verify: java.lang.Object expo.modules.devlauncher.helpers.DevLauncherCoroutinesExtensionsKt.runBlockingOnMainThread(kotlin.jvm.functions.Function0): [0x1C] cannot access instance field java.lang.Object kotlin.jvm.internal.Ref$ObjectRef.element from object of type Reference: kotlin.jvm.functions.Function0 (declaration of 'expo.modules.devlauncher.helpers.DevLauncherCoroutinesExtensionsKt' appears in /data/app/~~Pctup_2b0rAIMhpgLMjdzQ==/host.exp.nclexp-Jvfz4oK2KizpZpVNYK5cJQ==/base.apk!classes81.dex)
        at expo.modules.devlauncher.helpers.DevLauncherCoroutinesExtensionsKt.runBlockingOnMainThread(Unknown Source:0)
        at expo.modules.devlauncher.DevLauncherController.ensureHostWasCleared(DevLauncherController.kt:139)
        at expo.modules.devlauncher.DevLauncherController.ensureHostWasCleared$default(DevLauncherController.kt:137)
        at expo.modules.devlauncher.DevLauncherController.navigateToLauncher(DevLauncherController.kt:99)
        at expo.modules.devlauncher.DevLauncherController.redirectFromStartActivity(DevLauncherController.kt:180)
        at expo.modules.devlauncher.DevLauncherController.access$redirectFromStartActivity(DevLauncherController.kt:46)
        at expo.modules.devlauncher.DevLauncherController$getCurrentReactActivityDelegate$1.invoke(DevLauncherController.kt:172)
        at expo.modules.devlauncher.DevLauncherController$getCurrentReactActivityDelegate$1.invoke(DevLauncherController.kt:46)
        at expo.modules.devlauncher.react.activitydelegates.DevLauncherReactActivityRedirectDelegate.onCreate(DevLauncherReactActivityRedirectDelegate.kt:13)
        at com.facebook.react.ReactActivity.onCreate(ReactActivity.java:45)
        at host.exp.nclexp.MainActivity.onCreate(MainActivity.java:40)
        at android.app.Activity.performCreate(Activity.java:8000)
        at android.app.Activity.performCreate(Activity.java:7984)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1309)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3422)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3601)
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:223)
        at android.app.ActivityThread.main(ActivityThread.java:7656)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
2021-05-07 17:53:30.883 11096-11096/host.exp.nclexp I/Process: Sending signal. PID: 11096 SIG: 9
```

# How

- Updated coroutines package to the newest version.
- Added a workaround for something that looks like a bug in the compiler (see Kotlin/kotlinx.coroutines#2041).  

# Test Plan

- newly created app with older kotlin version ✅
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants