androidxamarin.androidapkandroid-install-apkandroid-10.0

Xamarin Android 10 Install APK - No Activity found to handle Intent


I want to install a 3rd party app from the filesystem from my xamarin android app. The code I used successfully before Android 10 was pretty straightforward and easy.

            Intent intent = new Intent(Intent.ActionView);
            Uri data = Uri.FromFile(file);

            intent.SetDataAndType(data, "application/vnd.android.package-archive");

            context.StartActivity(intent);

This code does not work on Android 10 because of ACTION_VIEW and ACTION_INSTALL_PACKAGE were deprecated in Android 10. Looks like we now need to use the PackageInstaller API.

I tried to write a method using the PackageInstaller API. Unfortunately it doesn't work.

Code with PackageInstaller API

 public static void InstallPackageAndroidQAndAbove(Context context, string filePath, string packageName)
    {

        var packageInstaller = context.PackageManager.PackageInstaller;
        var sessionParams = new PackageInstaller.SessionParams(PackageInstallMode.FullInstall);
        sessionParams.SetAppPackageName(packageName);
        int sessionId = packageInstaller.CreateSession(sessionParams);
        var session = packageInstaller.OpenSession(sessionId);

        var input = new FileStream(filePath, FileMode.Open, FileAccess.Read);
        var output = session.OpenWrite(packageName, 0, -1);

        input.CopyTo(output);

        output.Close();
        input.Close();
        input.Dispose();

        session.Fsync(output);

        var pendingIntent = PendingIntent.GetBroadcast(context, sessionId, new Intent(Intent.ActionInstallPackage), 0);
        session.Commit(pendingIntent.IntentSender);

    }

An exception "Unrecognized stream" occurs during the call.

I hope someone can help me.

Thank you very much in advance.


Solution

  • Xamarin Android Code for installing an apk with the PackageInstaller API.

    //Change to your package name
        const string PACKAGE_INSTALLED_ACTION =
                    "com.example.android.apis.content.SESSION_API_PACKAGE_INSTALLED";
    
         public static void InstallPackageAndroidQAndAbove(Context context, string filePath)
                {
    
                    var packageInstaller = context.PackageManager.PackageInstaller;
                    var sessionParams = new PackageInstaller.SessionParams(PackageInstallMode.FullInstall);
                    int sessionId = packageInstaller.CreateSession(sessionParams);
                    var session = packageInstaller.OpenSession(sessionId);
    
                    addApkToInstallSession(filePath, session);
    
                    // Create an install status receiver.
                    Intent intent = new Intent(context, context.Class);
                    intent.SetAction(Globals.PACKAGE_INSTALLED_ACTION);
                    PendingIntent pendingIntent = PendingIntent.GetActivity(context, 0, intent, 0);
                    IntentSender statusReceiver = pendingIntent.IntentSender;
    
                    // Commit the session (this will start the installation workflow).
                    session.Commit(statusReceiver);
    
                }
    
         private static void addApkToInstallSession(string filePath, PackageInstaller.Session session)
                {
                    using (var input = new FileStream(filePath, FileMode.Open, FileAccess.Read))
                    {
                        using (var packageInSession = session.OpenWrite("package", 0, -1))
                        {
                            input.CopyTo(packageInSession);
                            packageInSession.Close();
                        }
                        input.Close();
                    }
                    //That this is necessary could be a Xamarin bug.
                    GC.Collect();
                    GC.WaitForPendingFinalizers();
                    GC.Collect();
    
                }
    
    // Note: this Activity must run in singleTop launchMode for it to be able to receive the //intent
         protected override void OnNewIntent(Intent intent)
                {
                    base.OnNewIntent(intent);
    
                    Bundle extras = intent.Extras;
    
                    if (Globals.PACKAGE_INSTALLED_ACTION.Equals(intent.Action))
                    {
                        int status = extras.GetInt(PackageInstaller.ExtraStatus);
                        String message = extras.GetString(PackageInstaller.ExtraStatusMessage);
    
                        switch (status)
                        {
                            case (int)PackageInstallStatus.PendingUserAction:
                                // This test app isn't privileged, so the user has to confirm the install.
                                Intent confirmIntent = (Intent)extras.Get(Intent.ExtraIntent);
                                StartActivity(confirmIntent);
                                break;
                            case (int)PackageInstallStatus.Success:
                                Toast.MakeText(this, "Install succeeded!", ToastLength.Long).Show();
                                break;
                            case (int)PackageInstallStatus.Failure:
                            case (int)PackageInstallStatus.FailureAborted:
                            case (int)PackageInstallStatus.FailureBlocked:
                            case (int)PackageInstallStatus.FailureConflict:
                            case (int)PackageInstallStatus.FailureIncompatible:
                            case (int)PackageInstallStatus.FailureInvalid:
                            case (int)PackageInstallStatus.FailureStorage:
                                Toast.MakeText(this, "Install failed! " + status + ", " + message,
                                        ToastLength.Long).Show();
                                break;
                            default:
                                Toast.MakeText(this, "Unrecognized status received from installer: " + status,
                                       ToastLength.Long).Show();
                                break;
                        }
                    }
    
    
                }