summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/core/java/com/android/server/pm/PackageArchiver.java122
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerService.java30
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java4
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java8
4 files changed, 129 insertions, 35 deletions
diff --git a/services/core/java/com/android/server/pm/PackageArchiver.java b/services/core/java/com/android/server/pm/PackageArchiver.java
index fb311dadd36c..9f072f932a95 100644
--- a/services/core/java/com/android/server/pm/PackageArchiver.java
+++ b/services/core/java/com/android/server/pm/PackageArchiver.java
@@ -24,6 +24,8 @@ import static android.app.AppOpsManager.MODE_IGNORED;
import static android.app.ComponentOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED;
import static android.content.pm.ArchivedActivityInfo.bytesFromBitmap;
import static android.content.pm.ArchivedActivityInfo.drawableToBitmap;
+import static android.content.pm.PackageInstaller.EXTRA_UNARCHIVE_STATUS;
+import static android.content.pm.PackageInstaller.UNARCHIVAL_OK;
import static android.content.pm.PackageManager.DELETE_ARCHIVE;
import static android.content.pm.PackageManager.DELETE_KEEP_DATA;
import static android.content.pm.PackageManager.INSTALL_UNARCHIVE_DRAFT;
@@ -38,6 +40,7 @@ import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.app.BroadcastOptions;
+import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.IIntentReceiver;
@@ -118,6 +121,15 @@ public class PackageArchiver {
private static final String ACTION_UNARCHIVE_DIALOG =
"com.android.intent.action.UNARCHIVE_DIALOG";
+ private static final String ACTION_UNARCHIVE_ERROR_DIALOG =
+ "com.android.intent.action.UNARCHIVE_ERROR_DIALOG";
+
+ private static final String EXTRA_REQUIRED_BYTES =
+ "com.android.content.pm.extra.UNARCHIVE_EXTRA_REQUIRED_BYTES";
+ private static final String EXTRA_INSTALLER_PACKAGE_NAME =
+ "com.android.content.pm.extra.UNARCHIVE_INSTALLER_PACKAGE_NAME";
+ private static final String EXTRA_INSTALLER_TITLE =
+ "com.android.content.pm.extra.UNARCHIVE_INSTALLER_TITLE";
private final Context mContext;
private final PackageManagerService mPm;
@@ -305,11 +317,14 @@ public class PackageArchiver {
/** Creates archived state for the package and user. */
private CompletableFuture<ArchiveState> createArchiveState(String packageName, int userId)
throws PackageManager.NameNotFoundException {
- PackageStateInternal ps = getPackageState(packageName, mPm.snapshotComputer(),
+ Computer snapshot = mPm.snapshotComputer();
+ PackageStateInternal ps = getPackageState(packageName, snapshot,
Binder.getCallingUid(), userId);
verifyNotSystemApp(ps.getFlags());
String responsibleInstallerPackage = getResponsibleInstallerPackage(ps);
verifyInstaller(responsibleInstallerPackage, userId);
+ ApplicationInfo installerInfo = snapshot.getApplicationInfo(
+ responsibleInstallerPackage, /* flags= */ 0, userId);
verifyOptOutStatus(packageName,
UserHandle.getUid(userId, UserHandle.getUid(userId, ps.getAppId())));
@@ -320,7 +335,7 @@ public class PackageArchiver {
try {
archiveState.complete(
createArchiveStateInternal(packageName, userId, mainActivities,
- responsibleInstallerPackage));
+ installerInfo.loadLabel(mContext.getPackageManager()).toString()));
} catch (IOException e) {
archiveState.completeExceptionally(e);
}
@@ -328,8 +343,17 @@ public class PackageArchiver {
return archiveState;
}
- static ArchiveState createArchiveState(@NonNull ArchivedPackageParcel archivedPackage,
+ @Nullable
+ ArchiveState createArchiveState(@NonNull ArchivedPackageParcel archivedPackage,
int userId, String installerPackage) {
+ ApplicationInfo installerInfo = mPm.snapshotComputer().getApplicationInfo(
+ installerPackage, /* flags= */ 0, userId);
+ if (installerInfo == null) {
+ // Should never happen because we just fetched the installerInfo.
+ Slog.e(TAG, "Couldnt find installer " + installerPackage);
+ return null;
+ }
+
try {
var packageName = archivedPackage.packageName;
var mainActivities = archivedPackage.archivedActivities;
@@ -346,7 +370,8 @@ public class PackageArchiver {
archiveActivityInfos.add(activityInfo);
}
- return new ArchiveState(archiveActivityInfos, installerPackage);
+ return new ArchiveState(archiveActivityInfos,
+ installerInfo.loadLabel(mContext.getPackageManager()).toString());
} catch (IOException e) {
Slog.e(TAG, "Failed to create archive state", e);
return null;
@@ -354,7 +379,7 @@ public class PackageArchiver {
}
ArchiveState createArchiveStateInternal(String packageName, int userId,
- List<LauncherActivityInfo> mainActivities, String installerPackage)
+ List<LauncherActivityInfo> mainActivities, String installerTitle)
throws IOException {
final int iconSize = mContext.getSystemService(
ActivityManager.class).getLauncherLargeIconSize();
@@ -372,7 +397,7 @@ public class PackageArchiver {
archiveActivityInfos.add(activityInfo);
}
- return new ArchiveState(archiveActivityInfos, installerPackage);
+ return new ArchiveState(archiveActivityInfos, installerTitle);
}
// TODO(b/298452477) Handle monochrome icons.
@@ -412,14 +437,14 @@ public class PackageArchiver {
return iconFile.toPath();
}
- private void verifyInstaller(String installerPackage, int userId)
+ private void verifyInstaller(String installerPackageName, int userId)
throws PackageManager.NameNotFoundException {
- if (TextUtils.isEmpty(installerPackage)) {
+ if (TextUtils.isEmpty(installerPackageName)) {
throw new PackageManager.NameNotFoundException("No installer found");
}
// Allow shell for easier development.
if ((Binder.getCallingUid() != Process.SHELL_UID)
- && !verifySupportsUnarchival(installerPackage, userId)) {
+ && !verifySupportsUnarchival(installerPackageName, userId)) {
throw new PackageManager.NameNotFoundException("Installer does not support unarchival");
}
}
@@ -588,7 +613,7 @@ public class PackageArchiver {
final Intent broadcastIntent = new Intent();
broadcastIntent.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, packageName);
- broadcastIntent.putExtra(PackageInstaller.EXTRA_UNARCHIVE_STATUS,
+ broadcastIntent.putExtra(EXTRA_UNARCHIVE_STATUS,
PackageInstaller.STATUS_PENDING_USER_ACTION);
broadcastIntent.putExtra(Intent.EXTRA_INTENT, dialogIntent);
sendIntent(statusReceiver, packageName, /* message= */ "", broadcastIntent);
@@ -782,6 +807,83 @@ public class PackageArchiver {
: ps.getInstallSource().mUpdateOwnerPackageName;
}
+ void notifyUnarchivalListener(int status, String installerPackageName, String appPackageName,
+ long requiredStorageBytes, @Nullable PendingIntent userActionIntent,
+ IntentSender unarchiveIntentSender, int userId) {
+ final Intent broadcastIntent = new Intent();
+ broadcastIntent.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, appPackageName);
+ broadcastIntent.putExtra(EXTRA_UNARCHIVE_STATUS, status);
+
+ if (status != UNARCHIVAL_OK) {
+ final Intent dialogIntent = createErrorDialogIntent(status, installerPackageName,
+ appPackageName,
+ requiredStorageBytes, userActionIntent, userId);
+ if (dialogIntent == null) {
+ // Error already logged.
+ return;
+ }
+ broadcastIntent.putExtra(Intent.EXTRA_INTENT, dialogIntent);
+ }
+
+ final BroadcastOptions options = BroadcastOptions.makeBasic();
+ options.setPendingIntentBackgroundActivityStartMode(
+ MODE_BACKGROUND_ACTIVITY_START_DENIED);
+ try {
+ unarchiveIntentSender.sendIntent(mContext, 0, broadcastIntent, /* onFinished= */ null,
+ /* handler= */ null, /* requiredPermission= */ null,
+ options.toBundle());
+ } catch (IntentSender.SendIntentException e) {
+ Slog.e(TAG, TextUtils.formatSimple("Failed to send unarchive intent"), e);
+ }
+ }
+
+ @Nullable
+ private Intent createErrorDialogIntent(int status, String installerPackageName,
+ String appPackageName,
+ long requiredStorageBytes, PendingIntent userActionIntent, int userId) {
+ final Intent dialogIntent = new Intent(ACTION_UNARCHIVE_ERROR_DIALOG);
+ dialogIntent.putExtra(EXTRA_UNARCHIVE_STATUS, status);
+ if (requiredStorageBytes > 0) {
+ dialogIntent.putExtra(EXTRA_REQUIRED_BYTES, requiredStorageBytes);
+ }
+ // Note that the userActionIntent is provided by the installer and is used only by the
+ // system package installer as a follow-up action after the user confirms the dialog.
+ if (userActionIntent != null) {
+ dialogIntent.putExtra(Intent.EXTRA_INTENT, userActionIntent);
+ }
+ dialogIntent.putExtra(EXTRA_INSTALLER_PACKAGE_NAME, installerPackageName);
+ // We fetch this label from the archive state because the installer might not be installed
+ // anymore in an edge case.
+ String installerTitle = getInstallerTitle(appPackageName, userId);
+ if (installerTitle == null) {
+ // Error already logged.
+ return null;
+ }
+ dialogIntent.putExtra(EXTRA_INSTALLER_TITLE, installerTitle);
+ return dialogIntent;
+ }
+
+ private String getInstallerTitle(String appPackageName, int userId) {
+ PackageStateInternal packageState;
+ try {
+ packageState = getPackageState(appPackageName,
+ mPm.snapshotComputer(),
+ Process.SYSTEM_UID, userId);
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.e(TAG, TextUtils.formatSimple(
+ "notifyUnarchivalListener: Couldn't fetch package state for %s.",
+ appPackageName), e);
+ return null;
+ }
+ ArchiveState archiveState = packageState.getUserStateOrDefault(userId).getArchiveState();
+ if (archiveState == null) {
+ Slog.e(TAG, TextUtils.formatSimple("notifyUnarchivalListener: App not archived %s.",
+ appPackageName));
+ return null;
+ }
+ return archiveState.getInstallerTitle();
+ }
+
@NonNull
private static PackageStateInternal getPackageState(String packageName,
Computer snapshot, int callingUid, int userId)
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 882e05dd778b..f731f95b404d 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -16,9 +16,10 @@
package com.android.server.pm;
-import static android.app.ComponentOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED;
import static android.app.admin.DevicePolicyResources.Strings.Core.PACKAGE_DELETED_BY_DO;
import static android.content.pm.PackageInstaller.LOCATION_DATA_APP;
+import static android.content.pm.PackageInstaller.UNARCHIVAL_ERROR_INSTALLER_DISABLED;
+import static android.content.pm.PackageInstaller.UNARCHIVAL_ERROR_INSTALLER_UNINSTALLED;
import static android.content.pm.PackageInstaller.UNARCHIVAL_ERROR_INSUFFICIENT_STORAGE;
import static android.content.pm.PackageInstaller.UNARCHIVAL_ERROR_NO_CONNECTIVITY;
import static android.content.pm.PackageInstaller.UNARCHIVAL_ERROR_USER_ACTION_NEEDED;
@@ -1703,7 +1704,6 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
});
}
- // TODO(b/307299702) Implement error dialog and propagate userActionIntent.
@Override
public void reportUnarchivalStatus(
int unarchiveId,
@@ -1746,8 +1746,10 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
// Execute expensive calls outside the sync block.
mPm.mHandler.post(
- () -> notifyUnarchivalListener(status, session.params.appPackageName,
- unarchiveIntentSender));
+ () -> mPackageArchiver.notifyUnarchivalListener(status,
+ session.getInstallerPackageName(),
+ session.params.appPackageName, requiredStorageBytes, userActionIntent,
+ unarchiveIntentSender, userId));
session.params.unarchiveIntentSender = null;
if (status != UNARCHIVAL_OK) {
Binder.withCleanCallingIdentity(session::abandon);
@@ -1776,29 +1778,13 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
UNARCHIVAL_ERROR_USER_ACTION_NEEDED,
UNARCHIVAL_ERROR_INSUFFICIENT_STORAGE,
UNARCHIVAL_ERROR_NO_CONNECTIVITY,
+ UNARCHIVAL_ERROR_INSTALLER_DISABLED,
+ UNARCHIVAL_ERROR_INSTALLER_UNINSTALLED,
UNARCHIVAL_GENERIC_ERROR).contains(status)) {
throw new IllegalStateException("Invalid status code passed " + status);
}
}
- private void notifyUnarchivalListener(int status, String packageName,
- IntentSender unarchiveIntentSender) {
- final Intent fillIn = new Intent();
- fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, packageName);
- fillIn.putExtra(PackageInstaller.EXTRA_UNARCHIVE_STATUS, status);
- // TODO(b/307299702) Attach failure dialog with EXTRA_INTENT and requiredStorageBytes here.
- final BroadcastOptions options = BroadcastOptions.makeBasic();
- options.setPendingIntentBackgroundActivityStartMode(
- MODE_BACKGROUND_ACTIVITY_START_DENIED);
- try {
- unarchiveIntentSender.sendIntent(mContext, 0, fillIn, /* onFinished= */ null,
- /* handler= */ null, /* requiredPermission= */ null,
- options.toBundle());
- } catch (SendIntentException e) {
- Slog.e(TAG, TextUtils.formatSimple("Failed to send unarchive intent"), e);
- }
- }
-
private static int getSessionCount(SparseArray<PackageInstallerSession> sessions,
int installerUid) {
int count = 0;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index adb6906a2915..5daada94d815 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1534,8 +1534,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService
return;
}
for (int userId : userIds) {
- var archiveState = PackageArchiver.createArchiveState(archivePackage, userId,
- responsibleInstallerPackage);
+ var archiveState = mInstallerService.mPackageArchiver.createArchiveState(
+ archivePackage, userId, responsibleInstallerPackage);
if (archiveState == null) {
continue;
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java
index 733a43329478..a3ec936273b4 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java
@@ -45,6 +45,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.LauncherApps;
import android.content.pm.PackageInstaller;
@@ -92,6 +93,7 @@ public class PackageArchiverTest {
private static final String PACKAGE = "com.example";
private static final String CALLER_PACKAGE = "com.caller";
private static final String INSTALLER_PACKAGE = "com.installer";
+ private static final String INSTALLER_LABEL = "Installer";
private static final Path ICON_PATH = Path.of("icon.png");
@Rule
@@ -198,6 +200,10 @@ public class PackageArchiverTest {
when(mContext.getPackageManager()).thenReturn(mPackageManager);
when(mPackageManager.getResourcesForApplication(eq(PACKAGE))).thenReturn(
mock(Resources.class));
+ ApplicationInfo installerAi = mock(ApplicationInfo.class);
+ when(mComputer.getApplicationInfo(eq(INSTALLER_PACKAGE), anyLong(), anyInt())).thenReturn(
+ installerAi);
+ when(installerAi.loadLabel(any())).thenReturn(INSTALLER_LABEL);
when(mInstallerService.createSessionInternal(any(), any(), any(), anyInt(),
anyInt())).thenReturn(1);
when(mInstallerService.getExistingDraftSessionId(anyInt(), any(), anyInt())).thenReturn(
@@ -545,7 +551,7 @@ public class PackageArchiverTest {
ICON_PATH, null);
activityInfos.add(activityInfo);
}
- return new ArchiveState(activityInfos, INSTALLER_PACKAGE);
+ return new ArchiveState(activityInfos, INSTALLER_LABEL);
}
private static List<LauncherActivityInfo> createLauncherActivities() {