Forum Xamarin.Android

EBADF (Bad file descriptor) with Andoid 10 target with PackageInstaller API

smedasnsmedasn Member ✭✭
edited March 26 in Xamarin.Android

Before I updated to visual studio 19 and Android 10 (Q), I successfully installed third party apps with my app with the following code.

PackageInstaller installer = activity.PackageManager.PackageInstaller;
PackageInstaller.SessionParams sessionParams = new PackageInstaller.SessionParams(PackageInstallMode.FullInstall);
int sessionId = installer.CreateSession(sessionParams);
PackageInstaller.Session session = installer.OpenSession(sessionId);

var input = new FileStream(pfad, FileMode.Open, FileAccess.Read);
var packageInSession = session.OpenWrite("package", 0, -1);
input.CopyTo(packageInSession);
packageInSession.Close();
input.Close();
packageInSession.Dispose();
input.Dispose();

//That this is necessary could be a Xamarin bug.
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();

Intent intent = new Intent(activity, activity.Class);
intent.SetAction("com.example.android.apis.content.SESSION_API_PACKAGE_INSTALLED");
PendingIntent pendingIntent = PendingIntent.GetActivity(activity, 0, intent, 0);
IntentSender statusReceiver = pendingIntent.IntentSender;

// Commit the session (this will start the installation workflow).
session.Commit(statusReceiver);

When i Dispose() the streams, i get an IOException: write failed (EBADF) bad file descriptor which would indicate a bad APK.

But this is unlikely because the code in visual studio 2017 works with the Android 9 target.

Hope somebody can help me and thank you in advance!

Best Answer

Answers

  • jezhjezh Member, Xamarin Team Xamurai

    You can try the following code:

       //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;
                        }
                    }
                }
    

    Refer to: https://stackoverflow.com/questions/58240297/xamarin-android-10-install-apk-no-activity-found-to-handle-intent

  • smedasnsmedasn Member ✭✭

    @jezh thank you for your fast response.
    the stackoverflow post you refered to is mine. i also tested exactly this code and also with variations. when using Visual Studio 2017 with Android 9 taget everything works fine.
    The EBADF error has just come along after updating to Visual Studio 2019 with Android 10 (Q) target.
    The exception happens when disposing the packageInSession stream.
    there is also a discussion on stackoverflow: https://stackoverflow.com/questions/59685804/android-packageinstaller-not-installing-apk

  • sev13sev13 Member

    I have exactly the same problem. After updating to Visual Studio 2019 it does not work anymore.
    Is the bug already recorded and does someone have a workaround for it?
    I really need this and would not like to downgrade to VS 2017 and Android 9 again

  • smedasnsmedasn Member ✭✭

    i am also dependent on this feature and would be happy about a quick fix or workaround.

  • Tracy320Tracy320 Member ✭✭✭

    Has anyone updated it?

  • smedasnsmedasn Member ✭✭

    can no one help us with this?

  • AllexDiSillvaAllexDiSillva Member

    He guys, any fix for this? Have the same problem

  • smedasnsmedasn Member ✭✭

    thats so frustrating. it took me days to upgrade my code to Android 10 and now i am stuck with that issue. if this is not resolved soon i have to revert back to bring updates for my app :(

  • sev13sev13 Member

    indeed!
    is there a suggestion from the official side?
    is the problem already solved?
    should we discard Android 10 and continue working with Android 9?

  • komankoman Member ✭✭

    I have also encountered this issue.

                   using (var packageInSession = session.OpenWrite("package", 0, -1))
                    {
                        input.CopyTo(packageInSession);
                        packageInSession.Close();
                    }
    

    these codes causes EBADF error in Debug.
    I did not try in Release.

    Any update?
    Some of internal users may not update the new APK now if this issue also happen in Release mode... :(

  • sev13sev13 Member

    @koman said:
    I have also encountered this issue.

                   using (var packageInSession = session.OpenWrite("package", 0, -1))
                    {
                        input.CopyTo(packageInSession);
                        packageInSession.Close();
                    }
    

    these codes causes EBADF error in Debug.
    I did not try in Release.

    Any update?
    Some of internal users may not update the new APK now if this issue also happen in Release mode... :(

    it also happens on release build.

    looks like the xamarin officials are professionally avoiding this issue...

  • komankoman Member ✭✭
    > @sev13 said:
    > (Quote)
    > it also happens on release build.
    >
    > looks like the xamarin officials are professionally avoiding this issue...

    so for now, do u have any ways to prevent this issue / do some workarounds so that users can still install apk? or wait for officals reply
  • smedasnsmedasn Member ✭✭

    @koman said:

    @sev13 said:
    (Quote)
    it also happens on release build.

    looks like the xamarin officials are professionally avoiding this issue...

    so for now, do u have any ways to prevent this issue / do some workarounds so that users can still install apk? or wait for officals reply

    unfortunately no..

    @jezh can you please forward this issue to a developer?

  • smedasnsmedasn Member ✭✭

    When i do not dispose the packageInSession like koman suggests the code runs without an exception but in my case also nothing happens.

    When checking the android logcat i found an error during my tryouts

    "activityresumetrigger not whitelisted"

  • brzbrz Member

    I have gotten APK installation working in Android Q with Xamarin
    There are a couple of things you need to make sure in order for the apk installation to succeed:

    • Do not use using statements inside the addApkToInstallSession method. The Dispose causes the installation to fail. Use try/catch and close instead:

      private static void addApkToInstallSession(Context context, Android.Net.Uri apkUri, PackageInstaller.Session session)
      {
        var packageInSession = session.OpenWrite("package", 0, -1);
        var input = context.ContentResolver.OpenInputStream(apkUri);
      
        if (input != null)
        {
          input.CopyTo(packageInSession);
        }
        else
        {
          throw new Exception("Inputstream is null");
        }
      
        packageInSession.Close();
        input.Close();
      
        //That this is necessary could be a Xamarin bug.
        GC.Collect();
        GC.WaitForPendingFinalizers();
        GC.Collect();
      }
      
    • The Activity where you override the "OnNewIntent" method must have LaunchMode set to LaunchMode.SingleTop

    • The user must have given the application from which you try to install the APK file the necessary permissions to install APK's. You can check if this is the case by calling PackageManager.CanRequestPackageInstalls(). If this function returns false, you can open the application options window by using this code:

      StartActivity(new Intent(
                      Android.Provider.Settings.ActionApplicationDetailsSettings,
                      Android.Net.Uri.Parse("package:" + Android.App.Application.Context.PackageName)));
      

    so the user can easily set the switch to enable this.

    • If you are debugging on a Xiaomi device, you must disable MIUI Optimizations under developer options. Otherwise the installation will fail with permission denied error.
Sign In or Register to comment.