diff options
| author | 2024-03-21 10:44:39 +0000 | |
|---|---|---|
| committer | 2024-03-21 10:44:39 +0000 | |
| commit | 8800a495ecdd604c34d18c049d7ea01f0a8d0e39 (patch) | |
| tree | 243913c656d4bb29ec07dcfa0da0c7662cf7ae27 | |
| parent | cfd29aa84a8fc91c1f7a0ae246e7bc15ef30ea04 (diff) | |
| parent | 2cee6416657a49a3e110c8db6906899d0107c0d2 (diff) | |
Merge "Allow cross user package suspension" into main
8 files changed, 113 insertions, 24 deletions
diff --git a/core/java/com/android/internal/app/SuspendedAppActivity.java b/core/java/com/android/internal/app/SuspendedAppActivity.java index 467cd49c2279..751368f8e041 100644 --- a/core/java/com/android/internal/app/SuspendedAppActivity.java +++ b/core/java/com/android/internal/app/SuspendedAppActivity.java @@ -16,6 +16,7 @@ package com.android.internal.app; +import static android.app.admin.flags.Flags.crossUserSuspensionEnabled; import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE; import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE; import static android.content.pm.SuspendDialogInfo.BUTTON_ACTION_MORE_DETAILS; @@ -59,6 +60,7 @@ public class SuspendedAppActivity extends AlertActivity public static final String EXTRA_SUSPENDED_PACKAGE = PACKAGE_NAME + ".extra.SUSPENDED_PACKAGE"; public static final String EXTRA_SUSPENDING_PACKAGE = PACKAGE_NAME + ".extra.SUSPENDING_PACKAGE"; + public static final String EXTRA_SUSPENDING_USER = PACKAGE_NAME + ".extra.SUSPENDING_USER"; public static final String EXTRA_DIALOG_INFO = PACKAGE_NAME + ".extra.DIALOG_INFO"; public static final String EXTRA_ACTIVITY_OPTIONS = PACKAGE_NAME + ".extra.ACTIVITY_OPTIONS"; public static final String EXTRA_UNSUSPEND_INTENT = PACKAGE_NAME + ".extra.UNSUSPEND_INTENT"; @@ -67,6 +69,7 @@ public class SuspendedAppActivity extends AlertActivity private IntentSender mOnUnsuspend; private String mSuspendedPackage; private String mSuspendingPackage; + private int mSuspendingUserId; private int mNeutralButtonAction; private int mUserId; private PackageManager mPm; @@ -117,7 +120,7 @@ public class SuspendedAppActivity extends AlertActivity .setPackage(mSuspendingPackage); final String requiredPermission = Manifest.permission.SEND_SHOW_SUSPENDED_APP_DETAILS; final ResolveInfo resolvedInfo = mPm.resolveActivityAsUser(moreDetailsIntent, - MATCH_DIRECT_BOOT_UNAWARE | MATCH_DIRECT_BOOT_AWARE, mUserId); + MATCH_DIRECT_BOOT_UNAWARE | MATCH_DIRECT_BOOT_AWARE, mSuspendingUserId); if (resolvedInfo != null && resolvedInfo.activityInfo != null && requiredPermission.equals(resolvedInfo.activityInfo.permission)) { moreDetailsIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, mSuspendedPackage) @@ -231,12 +234,17 @@ public class SuspendedAppActivity extends AlertActivity } mSuspendedPackage = intent.getStringExtra(EXTRA_SUSPENDED_PACKAGE); mSuspendingPackage = intent.getStringExtra(EXTRA_SUSPENDING_PACKAGE); + if (crossUserSuspensionEnabled()) { + mSuspendingUserId = intent.getIntExtra(EXTRA_SUSPENDING_USER, mUserId); + } else { + mSuspendingUserId = mUserId; + } mSuppliedDialogInfo = intent.getParcelableExtra(EXTRA_DIALOG_INFO, android.content.pm.SuspendDialogInfo.class); mOnUnsuspend = intent.getParcelableExtra(EXTRA_UNSUSPEND_INTENT, android.content.IntentSender.class); if (mSuppliedDialogInfo != null) { try { mSuspendingAppResources = createContextAsUser( - UserHandle.of(mUserId), /* flags */ 0).getPackageManager() + UserHandle.of(mSuspendingUserId), /* flags */ 0).getPackageManager() .getResourcesForApplication(mSuspendingPackage); } catch (PackageManager.NameNotFoundException ne) { Slog.e(TAG, "Could not find resources for " + mSuspendingPackage, ne); @@ -299,7 +307,7 @@ public class SuspendedAppActivity extends AlertActivity case BUTTON_ACTION_MORE_DETAILS: if (mMoreDetailsIntent != null) { startActivityAsUser(mMoreDetailsIntent, mOptions, - UserHandle.of(mUserId)); + UserHandle.of(mSuspendingUserId)); } else { Slog.wtf(TAG, "Neutral button should not have existed!"); } @@ -324,7 +332,7 @@ public class SuspendedAppActivity extends AlertActivity .putExtra(Intent.EXTRA_PACKAGE_NAME, mSuspendedPackage) .setPackage(mSuspendingPackage) .addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); - sendBroadcastAsUser(reportUnsuspend, UserHandle.of(mUserId)); + sendBroadcastAsUser(reportUnsuspend, UserHandle.of(mSuspendingUserId)); if (mOnUnsuspend != null) { Bundle activityOptions = @@ -365,6 +373,9 @@ public class SuspendedAppActivity extends AlertActivity .putExtra(Intent.EXTRA_USER_ID, userId) .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); + if (crossUserSuspensionEnabled()) { + intent.putExtra(EXTRA_SUSPENDING_USER, suspendingPackage.userId); + } return intent; } } diff --git a/core/proto/android/service/package.proto b/core/proto/android/service/package.proto index 068f4dd07ccb..d30f195bf094 100644 --- a/core/proto/android/service/package.proto +++ b/core/proto/android/service/package.proto @@ -142,6 +142,7 @@ message PackageProto { // UTC timestamp of first install for the user optional int32 first_install_time_ms = 11; optional ArchiveState archive_state = 12; + repeated int32 suspending_user = 13; } message InstallSourceProto { diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java index 588c6291f2f1..fd162214031c 100644 --- a/services/core/java/com/android/server/pm/DeletePackageHelper.java +++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java @@ -542,7 +542,8 @@ final class DeletePackageHelper { final Computer snapshot = mPm.snapshotComputer(); for (final int affectedUserId : outInfo.mRemovedUsers) { if (hadSuspendAppsPermission.get(affectedUserId)) { - mPm.unsuspendForSuspendingPackage(snapshot, packageName, affectedUserId); + mPm.unsuspendForSuspendingPackage(snapshot, packageName, + affectedUserId /*suspendingUserId*/, true /*inAllUsers*/); mPm.removeAllDistractingPackageRestrictions(snapshot, affectedUserId); } } diff --git a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java index 8da168375447..7a72e70592d3 100644 --- a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java +++ b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java @@ -16,6 +16,7 @@ package com.android.server.pm; +import static android.app.admin.flags.Flags.crossUserSuspensionEnabled; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; import static android.content.pm.PackageManager.RESTRICTION_NONE; @@ -45,6 +46,7 @@ import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.Process; +import android.os.UserHandle; import android.os.storage.StorageManager; import android.util.ArrayMap; import android.util.ArraySet; @@ -687,14 +689,17 @@ abstract class PackageManagerInternalBase extends PackageManagerInternal { @Override @Deprecated public final void unsuspendAdminSuspendedPackages(int affectedUser) { - final int suspendingUserId = affectedUser; - mService.unsuspendForSuspendingPackage(snapshot(), PLATFORM_PACKAGE_NAME, suspendingUserId); + final int suspendingUserId = + crossUserSuspensionEnabled() ? UserHandle.USER_SYSTEM : affectedUser; + mService.unsuspendForSuspendingPackage( + snapshot(), PLATFORM_PACKAGE_NAME, suspendingUserId, /* inAllUsers= */ false); } @Override @Deprecated public final boolean isAdminSuspendingAnyPackages(int userId) { - final int suspendingUserId = userId; + final int suspendingUserId = + crossUserSuspensionEnabled() ? UserHandle.USER_SYSTEM : userId; return snapshot().isSuspendingAnyPackages(PLATFORM_PACKAGE_NAME, suspendingUserId, userId); } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index d215822d1b1c..9a2b98f316c4 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -18,6 +18,7 @@ package com.android.server.pm; import static android.Manifest.permission.MANAGE_DEVICE_ADMINS; import static android.Manifest.permission.SET_HARMFUL_APP_WARNINGS; import static android.app.AppOpsManager.MODE_IGNORED; +import static android.app.admin.flags.Flags.crossUserSuspensionEnabled; import static android.content.pm.PackageManager.APP_METADATA_SOURCE_APK; import static android.content.pm.PackageManager.APP_METADATA_SOURCE_UNKNOWN; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; @@ -3181,27 +3182,53 @@ public class PackageManagerService implements PackageSender, TestUtilityService callingMethod); } - final int packageUid = snapshot.getPackageUid(suspender.packageName, 0, targetUserId); - final boolean allowedPackageUid = packageUid == callingUid; - // TODO(b/139383163): remove special casing for shell and enforce INTERACT_ACROSS_USERS_FULL - final boolean allowedShell = callingUid == SHELL_UID - && UserHandle.isSameApp(packageUid, callingUid); + if (crossUserSuspensionEnabled()) { + final int suspendingPackageUid = + snapshot.getPackageUid(suspender.packageName, 0, suspender.userId); + if (suspendingPackageUid != callingUid) { + throw new SecurityException("Suspender package %s doesn't match calling uid %d" + .formatted(suspender.packageName, callingUid)); + } + if (targetUserId != suspender.userId) { + mContext.enforceCallingOrSelfPermission( + Manifest.permission.INTERACT_ACROSS_USERS_FULL, callingMethod); + } + } else { + // Here only SHELL can suspend across users + final int packageUid = + snapshot.getPackageUid(suspender.packageName, 0, targetUserId); + final boolean allowedPackageUid = packageUid == callingUid; + final boolean allowedShell = callingUid == SHELL_UID + && UserHandle.isSameApp(packageUid, callingUid); - if (!allowedShell && !allowedPackageUid) { - throw new SecurityException("Suspending package " + suspender.packageName - + " in user " + targetUserId + " does not belong to calling uid " + callingUid); + if (!allowedShell && !allowedPackageUid) { + throw new SecurityException("Suspending package " + suspender.packageName + + " in user " + targetUserId + " does not belong to calling uid " + + callingUid); + } } } + /** + * @param inAllUsers Whether to unsuspend packages suspended by the given package in other + * users. This flag is only used when cross-user suspension is enabled. + */ void unsuspendForSuspendingPackage(@NonNull Computer computer, String suspendingPackage, - @UserIdInt int suspendingUserId) { + @UserIdInt int suspendingUserId, boolean inAllUsers) { // TODO: This can be replaced by a special parameter to iterate all packages, rather than // this weird pre-collect of all packages. final String[] allPackages = computer.getPackageStates().keySet().toArray(new String[0]); final Predicate<UserPackage> suspenderPredicate = UserPackage.of(suspendingUserId, suspendingPackage)::equals; - mSuspendPackageHelper.removeSuspensionsBySuspendingPackage(computer, - allPackages, suspenderPredicate, suspendingUserId); + if (!crossUserSuspensionEnabled() || !inAllUsers) { + mSuspendPackageHelper.removeSuspensionsBySuspendingPackage(computer, + allPackages, suspenderPredicate, suspendingUserId); + } else { + for (int targetUserId: mUserManager.getUserIds()) { + mSuspendPackageHelper.removeSuspensionsBySuspendingPackage( + computer, allPackages, suspenderPredicate, targetUserId); + } + } } void removeAllDistractingPackageRestrictions(@NonNull Computer snapshot, int userId) { @@ -4053,7 +4080,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService // This app should not generally be allowed to get disabled by the UI, but // if it ever does, we don't want to end up with some of the user's apps // permanently suspended. - unsuspendForSuspendingPackage(computer, packageName, userId); + unsuspendForSuspendingPackage(computer, packageName, userId, true /* inAllUsers */); removeAllDistractingPackageRestrictions(computer, userId); } success = true; @@ -4339,6 +4366,19 @@ public class PackageManagerService implements PackageSender, TestUtilityService } mInstantAppRegistry.onUserRemoved(userId); mPackageMonitorCallbackHelper.onUserRemoved(userId); + if (crossUserSuspensionEnabled()) { + cleanUpCrossUserSuspension(userId); + } + } + + private void cleanUpCrossUserSuspension(int removedUser) { + final Computer computer = snapshotComputer(); + var allPackages = computer.getAllAvailablePackageNames(); + for (int targetUserId : mUserManager.getUserIds()) { + if (targetUserId == removedUser) continue; + mSuspendPackageHelper.removeSuspensionsBySuspendingPackage(computer, allPackages, + userPackage -> userPackage.userId == removedUser, targetUserId); + } } /** @@ -4745,7 +4785,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService if (checkPermission(Manifest.permission.SUSPEND_APPS, packageName, userId) == PERMISSION_GRANTED) { final Computer snapshot = snapshotComputer(); - unsuspendForSuspendingPackage(snapshot, packageName, userId); + unsuspendForSuspendingPackage( + snapshot, packageName, userId, true /* inAllUsers */); removeAllDistractingPackageRestrictions(snapshot, userId); synchronized (mLock) { flushPackageRestrictionsAsUserInternalLocked(userId); @@ -6239,7 +6280,9 @@ public class PackageManagerService implements PackageSender, TestUtilityService final boolean quarantined = ((flags & PackageManager.FLAG_SUSPEND_QUARANTINED) != 0) && Flags.quarantinedEnabled(); final Computer snapshot = snapshotComputer(); - final UserPackage suspender = UserPackage.of(targetUserId, suspendingPackage); + final UserPackage suspender = crossUserSuspensionEnabled() + ? UserPackage.of(suspendingUserId, suspendingPackage) + : UserPackage.of(targetUserId, suspendingPackage); enforceCanSetPackagesSuspendedAsUser(snapshot, quarantined, suspender, callingUid, targetUserId, "setPackagesSuspendedAsUser"); return mSuspendPackageHelper.setPackagesSuspended(snapshot, packageNames, suspended, @@ -6707,7 +6750,10 @@ public class PackageManagerService implements PackageSender, TestUtilityService @Override public String[] setPackagesSuspendedByAdmin( @UserIdInt int userId, @NonNull String[] packageNames, boolean suspended) { - final int suspendingUserId = userId; + // Suspension by admin isn't attributed to admin package but to the platform, + // Using USER_SYSTEM for consistency with other internal suspenders, like shell or root. + final int suspendingUserId = + crossUserSuspensionEnabled() ? UserHandle.USER_SYSTEM : userId; final UserPackage suspender = UserPackage.of( suspendingUserId, PackageManagerService.PLATFORM_PACKAGE_NAME); return mSuspendPackageHelper.setPackagesSuspended(snapshotComputer(), packageNames, diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java index 12eb88e518e6..b44042c75e80 100644 --- a/services/core/java/com/android/server/pm/PackageSetting.java +++ b/services/core/java/com/android/server/pm/PackageSetting.java @@ -16,6 +16,7 @@ package com.android.server.pm; +import static android.app.admin.flags.Flags.crossUserSuspensionEnabled; import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED; @@ -1240,6 +1241,10 @@ public class PackageSetting extends SettingBase implements PackageStateInternal for (int j = 0; j < state.getSuspendParams().size(); j++) { proto.write(PackageProto.UserInfoProto.SUSPENDING_PACKAGE, state.getSuspendParams().keyAt(j).packageName); + if (crossUserSuspensionEnabled()) { + proto.write(PackageProto.UserInfoProto.SUSPENDING_USER, + state.getSuspendParams().keyAt(j).userId); + } } } proto.write(PackageProto.UserInfoProto.IS_STOPPED, state.isStopped()); diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index e35a169cdd60..f5ed8d4af45b 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -16,6 +16,7 @@ package com.android.server.pm; +import static android.app.admin.flags.Flags.crossUserSuspensionEnabled; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED; @@ -342,6 +343,7 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile private static final String ATTR_DISTRACTION_FLAGS = "distraction_flags"; private static final String ATTR_SUSPENDED = "suspended"; private static final String ATTR_SUSPENDING_PACKAGE = "suspending-package"; + private static final String ATTR_SUSPENDING_USER = "suspending-user"; private static final String ATTR_OPTIONAL = "optional"; /** @@ -2051,7 +2053,20 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile Slog.wtf(TAG, "No suspendingPackage found inside tag " + TAG_SUSPEND_PARAMS); return null; } - final int suspendingUserId = userId; + int suspendingUserId; + if (crossUserSuspensionEnabled()) { + suspendingUserId = parser.getAttributeInt( + null, ATTR_SUSPENDING_USER, UserHandle.USER_NULL); + if (suspendingUserId == UserHandle.USER_NULL) { + suspendingUserId = switch (suspendingPackage) { + case "root", "com.android.shell", PLATFORM_PACKAGE_NAME + -> UserHandle.USER_SYSTEM; + default -> userId; + }; + } + } else { + suspendingUserId = userId; + } return Map.entry( UserPackage.of(suspendingUserId, suspendingPackage), SuspendParams.restoreFromXml(parser)); @@ -2418,6 +2433,10 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile serializer.startTag(null, TAG_SUSPEND_PARAMS); serializer.attribute(null, ATTR_SUSPENDING_PACKAGE, suspendingPackage.packageName); + if (crossUserSuspensionEnabled()) { + serializer.attributeInt(null, ATTR_SUSPENDING_USER, + suspendingPackage.userId); + } final SuspendParams params = ustate.getSuspendParams().valueAt(i); if (params != null) { diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp index 6d3b8ac45913..4149e44a2ee9 100644 --- a/services/tests/mockingservicestests/Android.bp +++ b/services/tests/mockingservicestests/Android.bp @@ -75,6 +75,7 @@ android_test { "compatibility-device-util-axt", "flag-junit", "am_flags_lib", + "device_policy_aconfig_flags_lib", ], libs: [ |