Xamarin Android 10 How to Install 3rd party APK

smedasnsmedasn Member ✭✭
edited October 4 in Xamarin.Android

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.

Best Answer

Answers

  • smedasnsmedasn Member ✭✭

    nobody knows?

  • JarvanJarvan Member, Xamarin Team Xamurai
    edited October 7

    ACTION_INSTALL_PACKAGE is deprecated in API level 29, you should use PackageInstaller instead.

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

    Check the Tutorial:
    https://developer.android.com/reference/android/content/Intent#ACTION_INSTALL_PACKAGE

  • smedasnsmedasn Member ✭✭
    edited October 7

    Ok thank you. I found that and tried to adapt my code.
    In my example code I am using the PackageInstaller but it is not working and i am kind of lost.
    Is there any working xamarin code around?

  • smedasnsmedasn Member ✭✭

    Jarvan thank you very much for your help! I was able to solve the issue yesterday evening.
    I have successfully adapted the code from the InstallApkSessionApi.java example.

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

    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();
    
            }
    
     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;
                    }
                }
    
    
            }
    
  • MichaelMeierMichaelMeier USMember ✭✭

    Thanks smedasn, 1 question. Where do you put your apk file ? Before Q I use Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryDownloads)
    but this depreceated and if I understand it right only App-private directorys are useable for non Mediafiles (Foto/Video/Audio) Right now I use android:requestLegacyExternalStorage="true" in the manifest for Q-Devices.

Sign In or Register to comment.