summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Chandan Nath <nathch@google.com> 2019-01-28 15:23:38 +0000
committer Chandan Nath <nathch@google.com> 2019-02-22 19:14:53 +0000
commite8e463bd84c73cd298b0a7c1870b923f81c52da0 (patch)
tree006fc738034dc31df97ae78aafdec2bcd187e2ca
parenta54cb88d01d7131127bdf297be261c36e58978ad (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
-rw-r--r--api/current.txt1
-rw-r--r--api/system-current.txt4
-rw-r--r--core/java/android/content/pm/IPackageInstaller.aidl3
-rw-r--r--core/java/android/content/pm/PackageInstaller.java24
-rw-r--r--core/java/android/content/pm/PackageManager.java9
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerService.java7
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java35
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerShellCommand.java20
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");
}