androidactivitynotfoundexception

Android 10 - No Activity found to handle Intent


My 3rd party app has a way for the end-user to download an updated APK from our server and then the app will invoke the install package manager on that APK after it's done downloading. This same method has worked for all versions of Android OS but now it will crash on Android 10 (api 29). I haven't seen anyone with a similar problem yet, any help would be greatly appreciated!

Here's what I use to call the APK file from within my app:

Intent intent = new Intent(Intent.ACTION_VIEW);
final File apkFile = new File(Files.getApkFileName());
Log.v("dt.update", "Start update from " + apkFile.getAbsolutePath());
intent.setDataAndType(Uri.fromFile(apkFile), application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);

And here is the stacktrace coming back every time, only on Android 10 / API29:

E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.MyAppHere, PID: 11107
android.content.ActivityNotFoundException: No Activity found to handle Intent { act=android.intent.action.VIEW dat=file:///storage/emulated/0/Download/updatedapp.apk typ=application/vnd.android.package-archive flg=0x10000000 }
    at android.app.Instrumentation.checkStartActivityResult(Instrumentation.java:2051)
    at android.app.Instrumentation.execStartActivity(Instrumentation.java:1709)
    at android.app.Activity.startActivityForResult(Activity.java:5192)
    at androidx.fragment.app.FragmentActivity.startActivityForResult(FragmentActivity.java:676)
    at android.app.Activity.startActivityForResult(Activity.java:5150)
    at androidx.fragment.app.FragmentActivity.startActivityForResult(FragmentActivity.java:663)
    at android.app.Activity.startActivity(Activity.java:5521)
    at android.app.Activity.startActivity(Activity.java:5489)
    at android.view.View.performClick(View.java:7140)
    at android.view.View.performClickInternal(View.java:7117)
    at android.view.View.access$3500(View.java:801)
    at android.view.View$PerformClick.run(View.java:27351)
    at android.os.Handler.handleCallback(Handler.java:883)
    at android.os.Handler.dispatchMessage(Handler.java:100)
    at android.os.Looper.loop(Looper.java:214)
    at android.app.ActivityThread.main(ActivityThread.java:7356)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)

Solution

  • ACTION_VIEW (for APKs) and ACTION_INSTALL_PACKAGE were deprecated in Android 10. You need to switch to the PackageInstaller API.

    This sample app demonstrates the basics for getting a simple APK installed. The guts are in the MainMotor:

    /*
      Copyright (c) 2019 CommonsWare, LLC
    
      Licensed under the Apache License, Version 2.0 (the "License"); you may not
      use this file except in compliance with the License. You may obtain   a copy
      of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless required
      by applicable law or agreed to in writing, software distributed under the
      License is distributed on an "AS IS" BASIS,   WITHOUT WARRANTIES OR CONDITIONS
      OF ANY KIND, either express or implied. See the License for the specific
      language governing permissions and limitations under the License.
    
      Covered in detail in the book _Elements of Android Q
    
      https://commonsware.com/AndroidQ
    */
    
    package com.commonsware.q.appinstaller
    
    import android.app.Application
    import android.app.PendingIntent
    import android.content.Intent
    import android.content.pm.PackageInstaller
    import android.net.Uri
    import androidx.documentfile.provider.DocumentFile
    import androidx.lifecycle.AndroidViewModel
    import androidx.lifecycle.viewModelScope
    import kotlinx.coroutines.Dispatchers
    import kotlinx.coroutines.launch
    import kotlinx.coroutines.withContext
    
    private const val NAME = "mostly-unused"
    private const val PI_INSTALL = 3439
    
    class MainMotor(app: Application) : AndroidViewModel(app) {
      private val installer = app.packageManager.packageInstaller
      private val resolver = app.contentResolver
    
      fun install(apkUri: Uri) {
        viewModelScope.launch(Dispatchers.Main) {
          installCoroutine(apkUri)
        }
      }
    
      private suspend fun installCoroutine(apkUri: Uri) =
        withContext(Dispatchers.IO) {
          resolver.openInputStream(apkUri)?.use { apkStream ->
            val length =
              DocumentFile.fromSingleUri(application, apkUri)?.length() ?: -1
            val params =
              PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL)
            val sessionId = installer.createSession(params)
            val session = installer.openSession(sessionId)
    
            session.openWrite(NAME, 0, length).use { sessionStream ->
              apkStream.copyTo(sessionStream)
              session.fsync(sessionStream)
            }
    
            val intent = Intent(application, InstallReceiver::class.java)
            val pi = PendingIntent.getBroadcast(
              application,
              PI_INSTALL,
              intent,
              PendingIntent.FLAG_UPDATE_CURRENT
            )
    
            session.commit(pi.intentSender)
            session.close()
          }
        }
    }
    

    When an activity or fragment calls install(), supplying a Uri to the APK, I use PackageInstaller to install it:

    The API is clunky, but it is designed to handle a wide range of scenarios, including "App Bundle"-style multi-APK installations.