Update ActivityStarter to handle the case for starting unarchival for archived app.
Test: atest PackageInstallerArchiveTest
Bug: 302114464
Bug: 302610214
Change-Id: Ib02439d38282050a394a912435b72ebeb77677a5
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 7e4cf4f..898b693 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -48,6 +48,7 @@
import com.android.permission.persistence.RuntimePermissionsState;
import com.android.server.pm.Installer.LegacyDexoptDisabledException;
import com.android.server.pm.KnownPackages;
+import com.android.server.pm.PackageArchiver;
import com.android.server.pm.PackageList;
import com.android.server.pm.PackageSetting;
import com.android.server.pm.dex.DynamicCodeLogger;
@@ -1442,4 +1443,10 @@
*/
public abstract void sendPackageDataClearedBroadcast(@NonNull String packageName,
int uid, int userId, boolean isRestore, boolean isInstantApp);
+
+ /**
+ * Returns an instance of {@link PackageArchiver} to be used for archiving related operations.
+ */
+ @NonNull
+ public abstract PackageArchiver getPackageArchiver();
}
diff --git a/services/core/java/com/android/server/pm/PackageArchiver.java b/services/core/java/com/android/server/pm/PackageArchiver.java
index 968be5c..c6e8a64 100644
--- a/services/core/java/com/android/server/pm/PackageArchiver.java
+++ b/services/core/java/com/android/server/pm/PackageArchiver.java
@@ -16,6 +16,10 @@
package com.android.server.pm;
+import static android.app.ActivityManager.START_ABORTED;
+import static android.app.ActivityManager.START_CLASS_NOT_FOUND;
+import static android.app.ActivityManager.START_PERMISSION_DENIED;
+import static android.app.ActivityManager.START_SUCCESS;
import static android.app.AppOpsManager.MODE_IGNORED;
import static android.app.ComponentOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED;
import static android.content.pm.ArchivedActivityInfo.bytesFromBitmap;
@@ -34,7 +38,10 @@
import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.app.BroadcastOptions;
+import android.content.ComponentName;
import android.content.Context;
+import android.content.IIntentReceiver;
+import android.content.IIntentSender;
import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.ApplicationInfo;
@@ -58,6 +65,7 @@
import android.os.Binder;
import android.os.Bundle;
import android.os.Environment;
+import android.os.IBinder;
import android.os.ParcelableException;
import android.os.Process;
import android.os.SELinux;
@@ -71,6 +79,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.pm.pkg.ArchiveState;
import com.android.server.pm.pkg.ArchiveState.ArchiveActivityInfo;
+import com.android.server.pm.pkg.PackageState;
import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.pkg.PackageUserState;
import com.android.server.pm.pkg.PackageUserStateInternal;
@@ -185,6 +194,113 @@
});
}
+ /**
+ * Starts unarchival for the package corresponding to the startActivity intent. Note that this
+ * will work only if the caller is the default/Home Launcher or if activity is started via Shell
+ * identity.
+ */
+ @NonNull
+ public int requestUnarchiveOnActivityStart(@Nullable Intent intent,
+ @Nullable String callerPackageName, int userId, int callingUid) {
+ String packageName = getPackageNameFromIntent(intent);
+ if (packageName == null) {
+ Slog.e(TAG, "packageName cannot be null for unarchival!");
+ return START_CLASS_NOT_FOUND;
+ }
+ if (callerPackageName == null) {
+ Slog.e(TAG, "callerPackageName cannot be null for unarchival!");
+ return START_CLASS_NOT_FOUND;
+ }
+ if (!isCallingPackageValid(callerPackageName, callingUid, userId)) {
+ // Return early as the calling UID does not match caller package's UID.
+ return START_CLASS_NOT_FOUND;
+ }
+ String currentLauncherPackageName = getCurrentLauncherPackageName(userId);
+ if ((currentLauncherPackageName == null || !callerPackageName.equals(
+ currentLauncherPackageName)) && callingUid != Process.SHELL_UID) {
+ // TODO(b/311619990): Remove dependency on SHELL_UID for testing
+ Slog.e(TAG, TextUtils.formatSimple(
+ "callerPackageName: %s does not qualify for archival of package: " + "%s!",
+ callerPackageName, packageName));
+ return START_PERMISSION_DENIED;
+ }
+ // TODO(b/302114464): Handle edge cases & also divert to a dialog based on
+ // permissions + compat options
+ Slog.i(TAG, TextUtils.formatSimple("Unarchival is starting for: %s", packageName));
+ try {
+ final IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
+ @Override
+ public void send(int code, Intent intent, String resolvedType,
+ IBinder allowlistToken,
+ IIntentReceiver finishedReceiver, String requiredPermission,
+ Bundle options) {
+ // TODO(b/302114464): Handle intent sender status codes
+ }
+ };
+
+ requestUnarchive(packageName, callerPackageName,
+ new IntentSender((IIntentSender) mLocalSender), UserHandle.of(userId));
+ } catch (Throwable t) {
+ Slog.e(TAG, TextUtils.formatSimple(
+ "Unexpected error occurred while unarchiving package %s: %s.", packageName,
+ t.getLocalizedMessage()));
+ return START_ABORTED;
+ }
+ return START_SUCCESS;
+ }
+
+ /**
+ * Returns true if the componentName targeted by the intent corresponds to that of an archived
+ * app.
+ */
+ public boolean isIntentResolvedToArchivedApp(Intent intent, int userId) {
+ String packageName = getPackageNameFromIntent(intent);
+ if (packageName == null || intent.getComponent() == null) {
+ return false;
+ }
+ PackageState packageState = mPm.snapshotComputer().getPackageStateInternal(packageName);
+ if (packageState == null) {
+ return false;
+ }
+ PackageUserState userState = packageState.getUserStateOrDefault(userId);
+ if (!PackageArchiver.isArchived(userState)) {
+ return false;
+ }
+ List<ArchiveState.ArchiveActivityInfo> archiveActivityInfoList =
+ userState.getArchiveState().getActivityInfos();
+ for (int i = 0; i < archiveActivityInfoList.size(); i++) {
+ if (archiveActivityInfoList.get(i)
+ .getOriginalComponentName().equals(intent.getComponent())) {
+ return true;
+ }
+ }
+ Slog.e(TAG, TextUtils.formatSimple(
+ "Package: %s is archived but component to start main activity"
+ + " cannot be found!", packageName));
+ return false;
+ }
+
+ @Nullable
+ private String getCurrentLauncherPackageName(int userId) {
+ ComponentName defaultLauncherComponent = mPm.snapshotComputer().getDefaultHomeActivity(
+ userId);
+ if (defaultLauncherComponent != null) {
+ return defaultLauncherComponent.getPackageName();
+ }
+ return null;
+ }
+
+ private boolean isCallingPackageValid(String callingPackage, int callingUid, int userId) {
+ int packageUid;
+ packageUid = mPm.snapshotComputer().getPackageUid(callingPackage, 0L, userId);
+ if (packageUid != callingUid) {
+ Slog.w(TAG, TextUtils.formatSimple("Calling package: %s does not belong to uid: %d",
+ callingPackage, callingUid));
+ return false;
+ }
+ return true;
+ }
+
/** Creates archived state for the package and user. */
private CompletableFuture<ArchiveState> createArchiveState(String packageName, int userId)
throws PackageManager.NameNotFoundException {
@@ -403,7 +519,7 @@
Computer snapshot = mPm.snapshotComputer();
int userId = userHandle.getIdentifier();
int binderUid = Binder.getCallingUid();
- if (!PackageManagerServiceUtils.isRootOrShell(binderUid)) {
+ if (!PackageManagerServiceUtils.isSystemOrRootOrShell(binderUid)) {
verifyCaller(snapshot.getPackageUid(callerPackageName, 0, userId), binderUid);
}
snapshot.enforceCrossUserPermission(binderUid, userId, true, true,
@@ -780,6 +896,20 @@
return bytesFromBitmap(BitmapFactory.decodeFile(path.toString()));
}
+ @Nullable
+ private static String getPackageNameFromIntent(@Nullable Intent intent) {
+ if (intent == null) {
+ return null;
+ }
+ if (intent.getPackage() != null) {
+ return intent.getPackage();
+ }
+ if (intent.getComponent() != null) {
+ return intent.getComponent().getPackageName();
+ }
+ return null;
+ }
+
/**
* Creates serializable archived activities from existing ArchiveState.
*/
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index ec3823f..e557f09 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -6976,6 +6976,11 @@
}
@Override
+ public PackageArchiver getPackageArchiver() {
+ return mInstallerService.mPackageArchiver;
+ }
+
+ @Override
public void sendPackageRestartedBroadcast(@NonNull String packageName,
int uid, @Intent.Flags int flags) {
final int userId = UserHandle.getUserId(uid);
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 5e0a449..d6302e0 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -103,6 +103,7 @@
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.AuxiliaryResolveInfo;
+import android.content.pm.Flags;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
@@ -128,6 +129,7 @@
import com.android.internal.protolog.common.ProtoLog;
import com.android.server.am.PendingIntentRecord;
import com.android.server.pm.InstantAppResolver;
+import com.android.server.pm.PackageArchiver;
import com.android.server.power.ShutdownCheckPoints;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.uri.NeededUriGrants;
@@ -958,6 +960,17 @@
}
}
+ if (Flags.archiving()) {
+ PackageArchiver packageArchiver = mService
+ .getPackageManagerInternalLocked()
+ .getPackageArchiver();
+ if (packageArchiver.isIntentResolvedToArchivedApp(intent, mRequest.userId)) {
+ return packageArchiver
+ .requestUnarchiveOnActivityStart(
+ intent, callingPackage, mRequest.userId, realCallingUid);
+ }
+ }
+
final int launchFlags = intent.getFlags();
if ((launchFlags & Intent.FLAG_ACTIVITY_FORWARD_RESULT) != 0 && sourceRecord != null) {
// Transfer the result target from the source activity to the new one being started,