diff options
| author | 2019-01-28 15:23:38 +0000 | |
|---|---|---|
| committer | 2019-02-22 19:14:53 +0000 | |
| commit | e8e463bd84c73cd298b0a7c1870b923f81c52da0 (patch) | |
| tree | 006fc738034dc31df97ae78aafdec2bcd187e2ca | |
| parent | a54cb88d01d7131127bdf297be261c36e58978ad (diff) | |
[Multi-user] add PackageInstaller.installExistingPackage with IntentSender which is fired only
after the asynchronous restore is complete
This is a better alternative to the existing synchronous PackageManager.installExistingPackage
method where the restore operation happens asynchronously but the method itself will return
success before the restore finishes.
Bug: 122881085
Test: 1) cts-tradefed run cts -m CtsBackupHostTestCases
-t android.cts.backup.ProfileKeyValueBackupRestoreHostSideTest
2) atest RunBackupFrameworksServicesRoboTests
3) Install Hangouts on work profile. Then install on primary profile, backup and uninstall.
Now install again and immediately hit Open. Before this change, the app will crash after a few
seconds (when the background restore operation finishes). With this change and a corresponding
Play Store change to use this new method, Open shows up only after restore has finished so the
app doesn't crash.
Change-Id: I5d2e1f3bb5509894bedd6bbcfac32ed6cf946a80
8 files changed, 94 insertions, 9 deletions
diff --git a/api/current.txt b/api/current.txt index 9e28ada17a93..9d1c5378c141 100644 --- a/api/current.txt +++ b/api/current.txt @@ -11375,6 +11375,7 @@ package android.content.pm { method @NonNull public java.util.List<android.content.pm.PackageInstaller.SessionInfo> getMySessions(); method @Nullable public android.content.pm.PackageInstaller.SessionInfo getSessionInfo(int); method @NonNull public java.util.List<android.content.pm.PackageInstaller.SessionInfo> getStagedSessions(); + method @RequiresPermission(allOf={android.Manifest.permission.INSTALL_PACKAGES, "com.android.permission.INSTALL_EXISTING_PACKAGES"}) public void installExistingPackage(@NonNull String, int, @Nullable android.content.IntentSender); method @NonNull public android.content.pm.PackageInstaller.Session openSession(int) throws java.io.IOException; method public void registerSessionCallback(@NonNull android.content.pm.PackageInstaller.SessionCallback); method public void registerSessionCallback(@NonNull android.content.pm.PackageInstaller.SessionCallback, @NonNull android.os.Handler); diff --git a/api/system-current.txt b/api/system-current.txt index c831522f1286..1fff4db52940 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -1590,8 +1590,8 @@ package android.content.pm { method @android.content.pm.PackageManager.PermissionFlags @RequiresPermission(anyOf={android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS}) public abstract int getPermissionFlags(String, String, @NonNull android.os.UserHandle); method @NonNull @RequiresPermission(android.Manifest.permission.SUSPEND_APPS) public String[] getUnsuspendablePackages(@NonNull String[]); method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public abstract void grantRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle); - method public abstract int installExistingPackage(String) throws android.content.pm.PackageManager.NameNotFoundException; - method public abstract int installExistingPackage(String, int) throws android.content.pm.PackageManager.NameNotFoundException; + method @Deprecated public abstract int installExistingPackage(String) throws android.content.pm.PackageManager.NameNotFoundException; + method @Deprecated public abstract int installExistingPackage(String, int) throws android.content.pm.PackageManager.NameNotFoundException; method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceiversAsUser(android.content.Intent, int, android.os.UserHandle); method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryIntentActivitiesAsUser(@NonNull android.content.Intent, int, @NonNull android.os.UserHandle); method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryIntentContentProvidersAsUser(@NonNull android.content.Intent, int, @NonNull android.os.UserHandle); diff --git a/core/java/android/content/pm/IPackageInstaller.aidl b/core/java/android/content/pm/IPackageInstaller.aidl index a251c0036a78..0cf83fd4655b 100644 --- a/core/java/android/content/pm/IPackageInstaller.aidl +++ b/core/java/android/content/pm/IPackageInstaller.aidl @@ -50,5 +50,8 @@ interface IPackageInstaller { void uninstall(in VersionedPackage versionedPackage, String callerPackageName, int flags, in IntentSender statusReceiver, int userId); + void installExistingPackage(String packageName, int installFlags, int installReason, + in IntentSender statusReceiver, int userId); + void setPermissionsResult(int sessionId, boolean accepted); } diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index 80954731bffb..50205192dede 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -590,6 +590,30 @@ public class PackageInstaller { } } + /** + * Install the given package, which already exists on the device, for the user for which this + * installer was created. + * + * @param packageName The package to install. + * @param installReason Reason for install. + * @param statusReceiver Where to deliver the result. + */ + @RequiresPermission(allOf = { + Manifest.permission.INSTALL_PACKAGES, + Manifest.permission.INSTALL_EXISTING_PACKAGES}) + public void installExistingPackage(@NonNull String packageName, + @InstallReason int installReason, + @Nullable IntentSender statusReceiver) { + Preconditions.checkNotNull(packageName, "packageName cannot be null"); + try { + mInstaller.installExistingPackage(packageName, 0, installReason, statusReceiver, + mUserId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** {@hide} */ @SystemApi @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 00419212544a..11e4a45d16e1 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -5114,7 +5114,10 @@ public abstract class PackageManager { * If there is already an application with the given package name installed * on the system for other users, also install it for the calling user. * @hide + * + * @deprecated use {@link PackageInstaller#installExistingPackage()} instead. */ + @Deprecated @SystemApi public abstract int installExistingPackage(String packageName) throws NameNotFoundException; @@ -5122,7 +5125,10 @@ public abstract class PackageManager { * If there is already an application with the given package name installed * on the system for other users, also install it for the calling user. * @hide + * + * @deprecated use {@link PackageInstaller#installExistingPackage()} instead. */ + @Deprecated @SystemApi public abstract int installExistingPackage(String packageName, @InstallReason int installReason) throws NameNotFoundException; @@ -5131,7 +5137,10 @@ public abstract class PackageManager { * If there is already an application with the given package name installed * on the system for other users, also install it for the specified user. * @hide + * + * @deprecated use {@link PackageInstaller#installExistingPackage()} instead. */ + @Deprecated @RequiresPermission(anyOf = { Manifest.permission.INSTALL_EXISTING_PACKAGES, Manifest.permission.INSTALL_PACKAGES, diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index f06da49f5b94..1e77362681b9 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -821,6 +821,13 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements } @Override + public void installExistingPackage(String packageName, int installFlags, int installReason, + IntentSender statusReceiver, int userId) { + mPm.installExistingPackageAsUser(packageName, userId, installFlags, installReason, + statusReceiver); + } + + @Override public void setPermissionsResult(int sessionId, boolean accepted) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, TAG); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 07d460edb2d2..2e5539767301 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -1303,12 +1303,14 @@ public class PackageManagerService extends IPackageManager.Stub // Recordkeeping of restore-after-install operations that are currently in flight // between the Package Manager and the Backup Manager static class PostInstallData { - public InstallArgs args; - public PackageInstalledInfo res; + public final InstallArgs args; + public final PackageInstalledInfo res; + public final Runnable mPostInstallRunnable; - PostInstallData(InstallArgs _a, PackageInstalledInfo _r) { + PostInstallData(InstallArgs _a, PackageInstalledInfo _r, Runnable postInstallRunnable) { args = _a; res = _r; + mPostInstallRunnable = postInstallRunnable; } } @@ -1440,7 +1442,9 @@ public class PackageManagerService extends IPackageManager.Stub final boolean didRestore = (msg.arg2 != 0); mRunningInstalls.delete(msg.arg1); - if (data != null) { + if (data != null && data.mPostInstallRunnable != null) { + data.mPostInstallRunnable.run(); + } else if (data != null) { InstallArgs args = data.args; PackageInstalledInfo parentRes = data.res; @@ -12683,6 +12687,11 @@ public class PackageManagerService extends IPackageManager.Stub @Override public int installExistingPackageAsUser(String packageName, int userId, int installFlags, int installReason) { + return installExistingPackageAsUser(packageName, userId, installFlags, installReason, null); + } + + int installExistingPackageAsUser(String packageName, int userId, int installFlags, + int installReason, IntentSender intentSender) { if (DEBUG_INSTALL) { Log.v(TAG, "installExistingPackageAsUser package=" + packageName + " userId=" + userId + " installFlags=" + installFlags + " installReason=" + installReason); @@ -12762,7 +12771,11 @@ public class PackageManagerService extends IPackageManager.Stub PackageInstalledInfo res = createPackageInstalledInfo(PackageManager.INSTALL_SUCCEEDED); res.pkg = pkgSetting.pkg; - restoreAndPostInstall(userId, res, null); + res.newUsers = new int[]{ userId }; + PostInstallData postInstallData = intentSender == null ? null : + new PostInstallData(null, res, () -> onRestoreComplete(res.returnCode, + mContext, intentSender)); + restoreAndPostInstall(userId, res, postInstallData); } } finally { Binder.restoreCallingIdentity(callingId); @@ -12771,6 +12784,16 @@ public class PackageManagerService extends IPackageManager.Stub return PackageManager.INSTALL_SUCCEEDED; } + static void onRestoreComplete(int returnCode, Context context, IntentSender target) { + Intent fillIn = new Intent(); + fillIn.putExtra(PackageInstaller.EXTRA_STATUS, + PackageManager.installStatusToPublicStatus(returnCode)); + try { + target.sendIntent(context, 0, fillIn, null, null); + } catch (SendIntentException ignored) { + } + } + static void setInstantAppForUser(PackageSetting pkgSetting, int userId, boolean instantApp, boolean fullApp) { // no state specified; do nothing @@ -13727,7 +13750,7 @@ public class PackageManagerService extends IPackageManager.Stub } for (InstallRequest request : installRequests) { restoreAndPostInstall(request.args.user.getIdentifier(), request.installResult, - new PostInstallData(request.args, request.installResult)); + new PostInstallData(request.args, request.installResult, null)); } }); } diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index 2eb762b59be4..52169676dab1 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -1114,6 +1114,7 @@ class PackageManagerShellCommand extends ShellCommand { int userId = UserHandle.USER_SYSTEM; int installFlags = 0; String opt; + boolean waitTillComplete = false; while ((opt = getNextOption()) != null) { switch (opt) { case "--user": @@ -1128,6 +1129,9 @@ class PackageManagerShellCommand extends ShellCommand { installFlags &= ~PackageManager.INSTALL_INSTANT_APP; installFlags |= PackageManager.INSTALL_FULL_APP; break; + case "--wait": + waitTillComplete = true; + break; default: pw.println("Error: Unknown option: " + opt); return 1; @@ -1140,9 +1144,23 @@ class PackageManagerShellCommand extends ShellCommand { return 1; } + int installReason = PackageManager.INSTALL_REASON_UNKNOWN; try { + if (waitTillComplete) { + final LocalIntentReceiver receiver = new LocalIntentReceiver(); + final IPackageInstaller installer = mInterface.getPackageInstaller(); + pw.println("Installing package " + packageName + " for user: " + userId); + installer.installExistingPackage(packageName, installFlags, installReason, + receiver.getIntentSender(), userId); + final Intent result = receiver.getResult(); + final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS, + PackageInstaller.STATUS_FAILURE); + pw.println("Received intent for package install"); + return status == PackageInstaller.STATUS_SUCCESS ? 0 : 1; + } + final int res = mInterface.installExistingPackageAsUser(packageName, userId, - installFlags, PackageManager.INSTALL_REASON_UNKNOWN); + installFlags, installReason); if (res == PackageManager.INSTALL_FAILED_INVALID_URI) { throw new NameNotFoundException("Package " + packageName + " doesn't exist"); } |