osl
osl

Reputation: 133

Android 10 - Using PackageManager to install new version of apk instead of Intents doesn't really work

The way my apk updates currently is through the new apk installing itself over the old one(while not overwriting the local database or settings).

So since Android 10(API 29) ACTION_VIEW was deprecated so this doesn't really work anymore:

Intent intent = new Intent(Intent.ACTION_VIEW);
//output file is the apk downloaded earlier
intent.setDataAndType(Uri.fromFile(outputFile), "application/vnd.android.package-archive");
startActivity(intent);

Following different answers I've found over the internet I need to start using PackageInstaller instead. Looking at the demo found in the Android docs I get to this:

Intent intent = new Intent(PSMentorActivity.this,InstallApkSessionApi.class);
intent.putExtra("apkFile",outputFile);
this.startActivity(intent);

Where InstallApkSessionApi contains a button that when pressed should start the install window:

PackageInstaller packageInstaller = getPackageManager().getPackageInstaller();
PackageInstaller.SessionParams params = new 
    PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL);
int sessionId = packageInstaller.createSession(params);
session = packageInstaller.openSession(sessionId);
addApkToInstallSession(file, session);
// Create an install status receiver.
Context context = InstallApkSessionApi.this;
Intent intent = new Intent(context, InstallApkSessionApi.class);
intent.setAction(PACKAGE_INSTALLED_ACTION);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
IntentSender statusReceiver = pendingIntent.getIntentSender();
// Commit the session (this will start the installation workflow).
session.commit(statusReceiver);

Simple enough, albeit more complicated compared to the past. The response I get in onNewIntent is always:

 case PackageInstaller.STATUS_FAILURE:
              Toast.makeText(this, "Install failed!10 " + status + ", " + message,
                            Toast.LENGTH_SHORT).show();
              break;

where the status and message take the following values: status=1, message= "INSTALL_FAILED_INTERNAL_ERROR: Permission denied".

So I assumed it was a problem with permissions. I already had permissions in place over reading and writing to storage. Other related permissions are: INSTALL_PACKAGES and REQUEST_INSTALL_PACKAGES which I cannot use due to the first not being intended for third party uses and the latter being signature level.

Is there something I am terribly missing or is there not a way for me to update my Apk without going through google play?

Upvotes: 8

Views: 9809

Answers (2)

osl
osl

Reputation: 133

So after help and some more searching the great internet, I've been able to make both methods work for me. I still don't really understand what causes the problem. Previously I was creating the Uri used in the intent from the file directly with Uri.fromFile(file). This works for anything under Android 10. The new way I handle this is by using a provider and getting the file using that.

  1. Current method, to be used for a while until I can properly implement PackageManager installation(still have some issues to fix).
Uri urlapk = FileProvider.getUriForFile(this,BuildConfig.APPLICATION_ID+".fileProvider",outputFile);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(urlapk, "application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(intent);
  1. Using PackageManager just as specified in my question with the following difference (the inputStream is now being build from an Uri):
private void addApkToInstallSession(Uri uri, PackageInstaller.Session session)
            throws IOException {
        try (OutputStream packageInSession = session.openWrite("package", 0, -1);
             InputStream is = getContentResolver().openInputStream(uri)) {
            byte[] buffer = new byte[16384];

            int n;
            while ((n = is.read(buffer)) >= 0) {
                packageInSession.write(buffer, 0, n);
            }
        }
}

The main problem I have with PackageManager is that it closes my app while it installs providing no default feedback to the user. So I guess I will have to "make" that feedback. I will move on to PackageManager as ACTION_VIEW is deprecated for apks.

Upvotes: 5

Vijay
Vijay

Reputation: 605

I've faced the same issue and tried the same way(PackageInstaller) just like you did. It doesn't work. So, here is a solution for you,

if(android.os.Build.VERSION.SDK_INT >= 29){
       Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
       intent.setData(Uri.fromFile(outputFile));
       intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
       startActivity(intent);   
}else{
      Intent intent = new Intent(Intent.ACTION_VIEW);
      //output file is the apk downloaded earlier
      intent.setDataAndType(Uri.fromFile(outputFile), "application/vnd.android.package- 
      archive");
      startActivity(intent);
}

I have tested in Android 10 (Google Pixel 2, One+7, Samsung s10) it's working fine. If you are still facing any problem please let me know. Note: Your version code must be greater than the old one.

Upvotes: 4

Related Questions