androidin-app-updateandroid-14android-api-34

In-app-update apps crash in android 14 due to "RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED" error in system-broadcasts


I have an android app which uses in-app-updates. After targeting android 14 (API 34) the app will not start due to this error:

FATAL EXCEPTION: main
Process: no.norva24.mslam, PID: 8281
java.lang.RuntimeException: Unable to start activity
ComponentInfo{no.norva24.mslam/no.norva24.mslam.ui.activities.MainActivity}: java.lang.SecurityException: no.norva24.mslam: One
of RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED should be specified when a receiver isn't being registered
exclusively for system broadcasts
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3782)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3922)
    at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:103)
    at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:139)
    at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:96)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2443)
    at android.os.Handler.dispatchMessage(Handler.java:106)
    at android.os.Looper.loopOnce(Looper.java:205)
    at android.os.Looper.loop(Looper.java:294)
    at android.app.ActivityThread.main(ActivityThread.java:8176)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:971)
Caused by: java.lang.SecurityException: no.norva24.mslam: One of RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED
should be specified when a receiver isn't being registered exclusively for system broadcasts
    at android.os.Parcel.createExceptionOrNull(Parcel.java:3057)
    at android.os.Parcel.createException(Parcel.java:3041)
    at android.os.Parcel.readException(Parcel.java:3024)
    at android.os.Parcel.readException(Parcel.java:2966)
    at android.app.IActivityManager$Stub$Proxy.registerReceiverWithFeature(IActivityManager.java:5668)
    at android.app.ContextImpl.registerReceiverInternal(ContextImpl.java:1852)
    at android.app.ContextImpl.registerReceiver(ContextImpl.java:1792)
    at android.app.ContextImpl.registerReceiver(ContextImpl.java:1780)
    at android.content.ContextWrapper.registerReceiver(ContextWrapper.java:755)
    at com.google.android.play.core.listener.zzc.zzb(com.google.android.play:core@@1.10.3:3)
    at com.google.android.play.core.listener.zzc.zzf(com.google.android.play:core@@1.10.3:4)
    at com.google.android.play.core.appupdate.zzf.registerListener(com.google.android.play:core@@1.10.3:1)
    at no.norva24.mslam.utilities.updates.InAppUpdate.<init>(InAppUpdate.kt:62)
    at no.norva24.mslam.ui.activities.MainActivity.onCreate(MainActivity.kt:741)
    at android.app.Activity.performCreate(Activity.java:8595)
    at android.app.Activity.performCreate(Activity.java:8573)
    at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1456)
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3764)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3922) 
    at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:103) 
    at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:139) 
    at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:96) 
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2443) 
    at android.os.Handler.dispatchMessage(Handler.java:106) 
    at android.os.Looper.loopOnce(Looper.java:205) 
    at android.os.Looper.loop(Looper.java:294) 
    at android.app.ActivityThread.main(ActivityThread.java:8176) 

                                                                                                        at java.lang.reflect.Method.invoke(Native Method) 
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552) 
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:971) 
Caused by: android.os.RemoteException: Remote stack trace:
    at
com.android.server.am.ActivityManagerService.registerReceiverWithFeature(ActivityManagerService.java:13895)
    at android.app.IActivityManager$Stub.onTransact(IActivityManager.java:2563)
    at com.android.server.am.ActivityManagerService.onTransact(ActivityManagerService.java:2708)
    at android.os.Binder.execTransactInternal(Binder.java:1339)
    at android.os.Binder.execTransact(Binder.java:1275)

I have not seen any explanation on how to set those settings.

Following code is used for in-app-updates:

package no.norva24.mslam.utilities.updates

import android.app.Activity
import android.graphics.Color
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.snackbar.Snackbar
import com.google.android.play.core.appupdate.AppUpdateInfo
import com.google.android.play.core.appupdate.AppUpdateManager
import com.google.android.play.core.appupdate.AppUpdateManagerFactory
import com.google.android.play.core.install.InstallState
import com.google.android.play.core.install.InstallStateUpdatedListener
import com.google.android.play.core.install.model.AppUpdateType
import com.google.android.play.core.install.model.InstallStatus
import com.google.android.play.core.install.model.UpdateAvailability
import com.google.android.play.core.ktx.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import no.norva24.mslam.repositories.Message
import no.norva24.mslam.ui.activities.MainActivity
import no.norva24.mslam.utilities.definitions.SELF_UPDATE_REQUEST_CODE
import java.lang.Exception

class InAppUpdate(activity: Activity): InstallStateUpdatedListener {

    var TAG = javaClass.simpleName

    private var appUpdateManager: AppUpdateManager
    private var parentActivity: Activity = activity

    private var currentType = AppUpdateType.FLEXIBLE

    init {
        Log.i(TAG, "init: I100: Init InAppUpdate")

        appUpdateManager = AppUpdateManagerFactory.create(parentActivity)
        appUpdateManager.appUpdateInfo.addOnSuccessListener { info->
            Log.i(TAG, "init: I100: update info $info")
            if (info.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE){
                Message.info("App update available !")
                if((info.clientVersionStalenessDays?:0)<5){
                    if(info.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE)){
                        startUpdate(info, AppUpdateType.FLEXIBLE)
                    } else {
                        Message.info("App update may be available on Google Play !")
                    }
                } else {
                    if(info.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)){
                        startUpdate(info, AppUpdateType.IMMEDIATE)
                    } else if(info.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE)){
                        startUpdate(info, AppUpdateType.FLEXIBLE)
                    } else {
                        Message.info("App update may be available on Google Play !")
                    }
                }

            } else {
                Log.i(TAG, "I100: init: no update available")
            }
        }
        appUpdateManager.registerListener(this)
    }

    private fun startUpdate(info: AppUpdateInfo, type: Int){
        try {
            appUpdateManager.startUpdateFlowForResult(info,type,parentActivity, SELF_UPDATE_REQUEST_CODE)
            currentType = type
        }catch (exception:Exception){
            Message.error(exception.message.toString())
        }
    }

    fun onResume(){
        appUpdateManager.appUpdateInfo.addOnSuccessListener { info->
            if(currentType == AppUpdateType.FLEXIBLE){
                if(info.installStatus() == InstallStatus.DOWNLOADED)
                    flexibleUpdateDownloadCompleted()
            } else if (currentType == AppUpdateType.IMMEDIATE){
                if(info.updateAvailability() == UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS) {
                    startUpdate(info, AppUpdateType.IMMEDIATE)
                }
            }
        }
    }

    fun onActivityResult(requestCode: Int, resultCode: Int){
        if(requestCode == SELF_UPDATE_REQUEST_CODE){
            if(resultCode != AppCompatActivity.RESULT_OK){
                Message.error("Update flow failed: $resultCode")
            }
        }
    }

    fun checkForUpdates(){
        CoroutineScope(Dispatchers.IO).launch {
            Log.i(TAG, "checkForUpdates: requesting appUpdateInfo")
            val appUpdateInfo = appUpdateManager.requestAppUpdateInfo()
            Log.i(TAG, "checkForUpdates: appUpdateInfo returned")
            Log.i(TAG, "checkForUpdates: available version code..........: ${appUpdateInfo.availableVersionCode()}" )
            Log.i(TAG, "checkForUpdates: bytes downloaded................: ${appUpdateInfo.bytesDownloaded}" )
            Log.i(TAG, "checkForUpdates: client version staleness days...: ${appUpdateInfo.clientVersionStalenessDays}" )
            Log.i(TAG, "checkForUpdates: install status..................: ${appUpdateInfo.installStatus}" )
            Log.i(TAG, "checkForUpdates: is update type allowed flexible.: ${appUpdateInfo.isFlexibleUpdateAllowed}" )
            Log.i(TAG, "checkForUpdates: is update type allowed immediate: ${appUpdateInfo.isImmediateUpdateAllowed}" )
            Log.i(TAG, "checkForUpdates: package name ...................: ${appUpdateInfo.packageName()}" )
            Log.i(TAG, "checkForUpdates: total bytes to download.........: ${appUpdateInfo.totalBytesToDownload}" )
            Log.i(TAG, "checkForUpdates: update availability ............: ${appUpdateInfo.updateAvailability()}" )
            Log.i(TAG, "checkForUpdates: update priority ................: ${appUpdateInfo.updatePriority}" )

            when(appUpdateInfo.updateAvailability()){
                UpdateAvailability.UPDATE_AVAILABLE-> {
                    if(appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE)){
                        startUpdate(appUpdateInfo, AppUpdateType.FLEXIBLE)
                    } else {
                        Message.warning("App version ${appUpdateInfo.availableVersionCode()} should be ready\nPlease check Google Play")
                    }
                }
                UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS -> {
                    Message.warning("App update is already in progress, ${appUpdateInfo.bytesDownloaded}/${appUpdateInfo.totalBytesToDownload} bytes downloaded")
                }
                UpdateAvailability.UNKNOWN -> {
                    Message.error("Unknown error occurred checking updates")
                }
                UpdateAvailability.UPDATE_NOT_AVAILABLE -> {
                    Message.info("No updates available at the moment")
                }
            }
        }

    }

    private fun flexibleUpdateDownloadCompleted(){
        Snackbar.make(
            (parentActivity as MainActivity).binding.appBarMain.coordinatorLayout,
            "An app update has just been downloaded. Please restart app.",
            Snackbar.LENGTH_INDEFINITE
        ).apply {
            setAction("RESTART") { appUpdateManager.completeUpdate()}
            setActionTextColor(Color.WHITE)
            show()
        }
    }

    fun onDestroy(){
        appUpdateManager.unregisterListener(this)
    }

    override fun onStateUpdate(state: InstallState) {
        if(state.installStatus() == InstallStatus.DOWNLOADED){
            flexibleUpdateDownloadCompleted()
        }
    }

}

Should there be some additonal parameters in the manifest.xml which isn't properly documented yet ?


Solution

  • The current version of Play In-App Updates Library you're using (1.10.3) doesn't support Android 14. You must upgrade it to version 2.1.0 that supports the new Android OS.

    More here: https://developer.android.com/reference/com/google/android/play/core/release-notes-in_app_updates