Migrate permissions when leaving sharedUserId
- Retrieve the previous uid permission state and create a copy of it as
the new app's uid state.
- Remove the app from the original shared user group. Other apps in the
shared user group will perceive as if the original app is uninstalled.
- The new permission state is updated in updatePermissions() just like
a normal package upgrade.
Test: atest CtsSharedUserMigrationTestCases
Bug: 179284822
Change-Id: I9d7c3b16959dd4b2c2684ddaf8bd223fb32c3c41
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index c3bf03c..d92b65b4 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -667,6 +667,12 @@
*/
public abstract @Nullable AndroidPackage getPackage(int uid);
+
+ /**
+ * Returns all packages for the given app ID.
+ */
+ public abstract @NonNull List<AndroidPackage> getPackagesForAppId(int appId);
+
/**
* Returns a list without a change observer.
*
diff --git a/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java b/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java
index bf241d4..9412dda 100644
--- a/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java
@@ -50,6 +50,7 @@
import android.content.pm.PackageManager;
import android.content.pm.parsing.ParsingPackageUtils;
import android.os.Environment;
+import android.os.Process;
import android.os.SystemClock;
import android.os.Trace;
import android.os.UserHandle;
@@ -575,6 +576,7 @@
Slog.w(TAG, "updateAllSharedLibrariesLPw failed: ", e);
}
mPm.mPermissionManager.onPackageInstalled(pkg,
+ Process.INVALID_UID /* previousAppId */,
PermissionManagerServiceInternal.PackageInstalledParams.DEFAULT,
UserHandle.USER_ALL);
mPm.writeSettingsLPrTEMP();
@@ -907,6 +909,7 @@
// The method below will take care of removing obsolete permissions and granting
// install permissions.
mPm.mPermissionManager.onPackageInstalled(pkg,
+ Process.INVALID_UID /* previousAppId */,
PermissionManagerServiceInternal.PackageInstalledParams.DEFAULT,
UserHandle.USER_ALL);
for (final int userId : allUserHandles) {
diff --git a/services/core/java/com/android/server/pm/InstallParams.java b/services/core/java/com/android/server/pm/InstallParams.java
index bc7d95e..1df30f5 100644
--- a/services/core/java/com/android/server/pm/InstallParams.java
+++ b/services/core/java/com/android/server/pm/InstallParams.java
@@ -1622,7 +1622,7 @@
AndroidPackage pkg = mPm.commitReconciledScanResultLocked(reconciledPkg,
request.mAllUsers);
- updateSettingsLI(pkg, reconciledPkg.mInstallArgs, request.mAllUsers, res);
+ updateSettingsLI(pkg, reconciledPkg, request.mAllUsers, res);
final PackageSetting ps = mPm.mSettings.getPackageLPr(packageName);
if (ps != null) {
@@ -1642,17 +1642,18 @@
return mPm.mSettings.disableSystemPackageLPw(oldPkg.getPackageName(), true);
}
- private void updateSettingsLI(AndroidPackage newPackage, InstallArgs installArgs,
+ private void updateSettingsLI(AndroidPackage newPackage, ReconciledPackage reconciledPkg,
int[] allUsers, PackageInstalledInfo res) {
- updateSettingsInternalLI(newPackage, installArgs, allUsers, res);
+ updateSettingsInternalLI(newPackage, reconciledPkg, allUsers, res);
}
- private void updateSettingsInternalLI(AndroidPackage pkg, InstallArgs installArgs,
+ private void updateSettingsInternalLI(AndroidPackage pkg, ReconciledPackage reconciledPkg,
int[] allUsers, PackageInstalledInfo res) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "updateSettings");
final String pkgName = pkg.getPackageName();
final int[] installedForUsers = res.mOrigUsers;
+ final InstallArgs installArgs = reconciledPkg.mInstallArgs;
final int installReason = installArgs.mInstallReason;
InstallSource installSource = installArgs.mInstallSource;
final String installerPackageName = installSource.installerPackageName;
@@ -1808,8 +1809,9 @@
}
final int autoRevokePermissionsMode = installArgs.mAutoRevokePermissionsMode;
permissionParamsBuilder.setAutoRevokePermissionsMode(autoRevokePermissionsMode);
- mPm.mPermissionManager.onPackageInstalled(pkg, permissionParamsBuilder.build(),
- userId);
+ final ScanResult scanResult = reconciledPkg.mScanResult;
+ mPm.mPermissionManager.onPackageInstalled(pkg, scanResult.mPreviousAppId,
+ permissionParamsBuilder.build(), userId);
}
res.mName = pkgName;
res.mUid = pkg.getUid();
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index e62102c..853db55 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -13102,6 +13102,7 @@
pkgSetting.pkg.getRequestedPermissions());
}
mPermissionManager.onPackageInstalled(pkgSetting.pkg,
+ Process.INVALID_UID /* previousAppId */,
permissionParamsBuilder.build(), userId);
}
@@ -20022,6 +20023,23 @@
return PackageManagerService.this.getPackage(uid);
}
+ @Override
+ public List<AndroidPackage> getPackagesForAppId(int appId) {
+ final Object obj;
+ synchronized (mLock) {
+ obj = mSettings.getSettingLPr(appId);
+ }
+ if (obj instanceof SharedUserSetting) {
+ final SharedUserSetting sus = (SharedUserSetting) obj;
+ return sus.getPackages();
+ } else if (obj instanceof PackageSetting) {
+ final PackageSetting ps = (PackageSetting) obj;
+ return List.of(ps.getPkg());
+ } else {
+ return Collections.emptyList();
+ }
+ }
+
@Nullable
@Override
public PackageSetting getPackageSetting(String packageName) {
diff --git a/services/core/java/com/android/server/pm/ScanPackageHelper.java b/services/core/java/com/android/server/pm/ScanPackageHelper.java
index 5fab84e..a76e419 100644
--- a/services/core/java/com/android/server/pm/ScanPackageHelper.java
+++ b/services/core/java/com/android/server/pm/ScanPackageHelper.java
@@ -73,6 +73,7 @@
import android.content.pm.parsing.result.ParseResult;
import android.content.pm.parsing.result.ParseTypeImpl;
import android.os.Build;
+import android.os.Process;
import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
@@ -130,10 +131,12 @@
*/
public boolean optimisticallyRegisterAppId(@NonNull ScanResult result)
throws PackageManagerException {
- if (!result.mExistingSettingCopied || result.mNeedsNewAppId) {
- // THROWS: when we can't allocate a user id. add call to check if there's
- // enough space to ensure we won't throw; otherwise, don't modify state
- return mPm.mSettings.registerAppIdLPw(result.mPkgSetting, result.mNeedsNewAppId);
+ if (!result.mExistingSettingCopied || result.needsNewAppId()) {
+ synchronized (mPm.mLock) {
+ // THROWS: when we can't allocate a user id. add call to check if there's
+ // enough space to ensure we won't throw; otherwise, don't modify state
+ return mPm.mSettings.registerAppIdLPw(result.mPkgSetting, result.needsNewAppId());
+ }
}
return false;
}
@@ -337,11 +340,11 @@
}
}
- boolean leavingSharedUser = false;
+ int previousAppId = Process.INVALID_UID;
if (pkgSetting != null && pkgSetting.sharedUser != sharedUserSetting) {
if (pkgSetting.sharedUser != null && sharedUserSetting == null) {
- leavingSharedUser = true;
+ previousAppId = pkgSetting.appId;
// Log that something is leaving shareduid and keep going
Slog.i(TAG,
"Package " + parsedPackage.getPackageName() + " shared user changed from "
@@ -630,7 +633,7 @@
return new ScanResult(request, true, pkgSetting, changedAbiCodePath,
!createNewPackage /* existingSettingCopied */,
- leavingSharedUser /* needsNewAppId */, staticSharedLibraryInfo,
+ previousAppId, staticSharedLibraryInfo,
dynamicSharedLibraryInfos);
}
diff --git a/services/core/java/com/android/server/pm/ScanResult.java b/services/core/java/com/android/server/pm/ScanResult.java
index 34f86ba..eb44a82 100644
--- a/services/core/java/com/android/server/pm/ScanResult.java
+++ b/services/core/java/com/android/server/pm/ScanResult.java
@@ -18,6 +18,7 @@
import android.annotation.Nullable;
import android.content.pm.SharedLibraryInfo;
+import android.os.Process;
import com.android.internal.annotations.VisibleForTesting;
@@ -36,10 +37,11 @@
*/
public final boolean mExistingSettingCopied;
/**
- * Whether or not the original PackageSetting needs to be updated with
- * a new uid. Useful when leaving a sharedUserID.
+ * The previous app ID if the app decided to leave a shared user ID.
+ * The value is set *only* if the app is leaving a shared user ID.
+ * Default value is Process.INVALID_UID.
*/
- public final boolean mNeedsNewAppId;
+ public final int mPreviousAppId;
/**
* The final package settings. This may be the same object passed in
* the {@link ScanRequest}, but, with modified values.
@@ -57,7 +59,7 @@
ScanRequest request, boolean success,
@Nullable PackageSetting pkgSetting,
@Nullable List<String> changedAbiCodePath, boolean existingSettingCopied,
- boolean needsNewAppId,
+ int previousAppId,
SharedLibraryInfo staticSharedLibraryInfo,
List<SharedLibraryInfo> dynamicSharedLibraryInfos) {
mRequest = request;
@@ -65,8 +67,16 @@
mPkgSetting = pkgSetting;
mChangedAbiCodePath = changedAbiCodePath;
mExistingSettingCopied = existingSettingCopied;
- mNeedsNewAppId = needsNewAppId;
+ mPreviousAppId = previousAppId;
mStaticSharedLibraryInfo = staticSharedLibraryInfo;
mDynamicSharedLibraryInfos = dynamicSharedLibraryInfos;
}
+
+ /**
+ * Whether the original PackageSetting needs to be updated with
+ * a new app ID. Useful when leaving a sharedUserId.
+ */
+ public boolean needsNewAppId() {
+ return mPreviousAppId != Process.INVALID_UID;
+ }
}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index e40cb40..e503f21 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -2620,8 +2620,7 @@
// being upgraded to target a newer SDK, in which case dangerous permissions
// are transformed from install time to runtime ones.
- final PackageSetting ps = (PackageSetting) mPackageManagerInt.getPackageSetting(
- pkg.getPackageName());
+ final PackageSetting ps = mPackageManagerInt.getPackageSetting(pkg.getPackageName());
if (ps == null) {
return;
}
@@ -3954,21 +3953,24 @@
}
}
- @UserIdInt
- private int revokeSharedUserPermissionsForDeletedPackageInternal(
- @Nullable AndroidPackage pkg, @NonNull List<AndroidPackage> sharedUserPkgs,
+ private void revokeSharedUserPermissionsForLeavingPackageInternal(
+ @Nullable AndroidPackage pkg, int appId, @NonNull List<AndroidPackage> sharedUserPkgs,
@UserIdInt int userId) {
if (pkg == null) {
Slog.i(TAG, "Trying to update info for null package. Just ignoring");
- return UserHandle.USER_NULL;
+ return;
}
// No shared user packages
if (sharedUserPkgs.isEmpty()) {
- return UserHandle.USER_NULL;
+ return;
}
- int affectedUserId = UserHandle.USER_NULL;
+ PackageSetting disabledPs = mPackageManagerInt.getDisabledSystemPackage(
+ pkg.getPackageName());
+ boolean isShadowingSystemPkg = disabledPs != null && disabledPs.appId == pkg.getUid();
+
+ boolean shouldKillUid = false;
// Update permissions
for (String eachPerm : pkg.getRequestedPermissions()) {
// Check if another package in the shared user needs the permission.
@@ -3985,26 +3987,15 @@
continue;
}
- PackageSetting disabledPs = mPackageManagerInt.getDisabledSystemPackage(
- pkg.getPackageName());
-
- // If the package is shadowing is a disabled system package,
+ // If the package is shadowing a disabled system package,
// do not drop permissions that the shadowed package requests.
- if (disabledPs != null) {
- boolean reqByDisabledSysPkg = false;
- for (String permission : disabledPs.pkg.getRequestedPermissions()) {
- if (permission.equals(eachPerm)) {
- reqByDisabledSysPkg = true;
- break;
- }
- }
- if (reqByDisabledSysPkg) {
- continue;
- }
+ if (isShadowingSystemPkg
+ && disabledPs.getPkg().getRequestedPermissions().contains(eachPerm)) {
+ continue;
}
synchronized (mLock) {
- UidPermissionState uidState = getUidStateLocked(pkg, userId);
+ UidPermissionState uidState = getUidStateLocked(appId, userId);
if (uidState == null) {
Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName()
+ " and user " + userId);
@@ -4019,12 +4010,18 @@
// TODO(zhanghai): Why are we only killing the UID when GIDs changed, instead of any
// permission change?
if (uidState.removePermissionState(bp.getName()) && bp.hasGids()) {
- affectedUserId = userId;
+ shouldKillUid = true;
}
}
}
- return affectedUserId;
+ // If gids changed, kill all affected packages.
+ if (shouldKillUid) {
+ mHandler.post(() -> {
+ // This has to happen with no lock held.
+ killUid(appId, UserHandle.USER_ALL, KILL_APP_REASON_GIDS_CHANGED);
+ });
+ }
}
@GuardedBy("mLock")
@@ -4892,9 +4889,48 @@
return true;
}
- private void onPackageInstalledInternal(@NonNull AndroidPackage pkg,
+ private void onPackageInstalledInternal(@NonNull AndroidPackage pkg, int previousAppId,
@NonNull PermissionManagerServiceInternal.PackageInstalledParams params,
@UserIdInt int[] userIds) {
+ // If previousAppId is not Process.INVALID_UID, the package is performing a migration out
+ // of a shared user group. Operations we need to do before calling updatePermissions():
+ // - Retrieve the original uid permission state and create a copy of it as the new app's
+ // uid state. The new permission state will be properly updated in updatePermissions().
+ // - Remove the app from the original shared user group. Other apps in the shared
+ // user group will perceive as if the original app is uninstalled.
+ if (previousAppId != Process.INVALID_UID) {
+ final PackageSetting ps = mPackageManagerInt.getPackageSetting(pkg.getPackageName());
+ final List<AndroidPackage> origSharedUserPackages =
+ mPackageManagerInt.getPackagesForAppId(previousAppId);
+
+ synchronized (mLock) {
+ // All users are affected
+ for (final int userId : getAllUserIds()) {
+ // Retrieve the original uid state
+ final UserPermissionState userState = mState.getUserState(userId);
+ if (userState == null) {
+ continue;
+ }
+ final UidPermissionState prevUidState = userState.getUidState(previousAppId);
+ if (prevUidState == null) {
+ continue;
+ }
+
+ // Insert new uid state by cloning the original one
+ userState.createUidStateWithExisting(ps.getAppId(), prevUidState);
+
+ // Remove original app ID from original shared user group
+ // Should match the implementation of onPackageUninstalledInternal(...)
+ if (origSharedUserPackages.isEmpty()) {
+ removeUidStateAndResetPackageInstallPermissionsFixed(
+ previousAppId, pkg.getPackageName(), userId);
+ } else {
+ revokeSharedUserPermissionsForLeavingPackageInternal(pkg, previousAppId,
+ origSharedUserPackages, userId);
+ }
+ }
+ }
+ }
updatePermissions(pkg.getPackageName(), pkg);
for (final int userId : userIds) {
addAllowlistedRestrictedPermissionsInternal(pkg,
@@ -4931,6 +4967,9 @@
private void onPackageUninstalledInternal(@NonNull String packageName, int appId,
@Nullable AndroidPackage pkg, @NonNull List<AndroidPackage> sharedUserPkgs,
@UserIdInt int[] userIds) {
+ // TODO: Handle the case when a system app upgrade is uninstalled and need to rejoin
+ // a shared UID permission state.
+
// TODO: Move these checks to check PackageState to be more reliable.
// System packages should always have an available APK.
if (pkg != null && pkg.isSystem()
@@ -4956,16 +4995,8 @@
// or packages running under the shared user of the removed
// package if revoking the permissions requested only by the removed
// package is successful and this causes a change in gids.
- final int userIdToKill = revokeSharedUserPermissionsForDeletedPackageInternal(pkg,
+ revokeSharedUserPermissionsForLeavingPackageInternal(pkg, appId,
sharedUserPkgs, userId);
- final boolean shouldKill = userIdToKill != UserHandle.USER_NULL;
- // If gids changed, kill all affected packages.
- if (shouldKill) {
- mHandler.post(() -> {
- // This has to happen with no lock held.
- killUid(appId, UserHandle.USER_ALL, KILL_APP_REASON_GIDS_CHANGED);
- });
- }
}
}
}
@@ -5236,7 +5267,7 @@
}
@Override
- public void onPackageInstalled(@NonNull AndroidPackage pkg,
+ public void onPackageInstalled(@NonNull AndroidPackage pkg, int previousAppId,
@NonNull PackageInstalledParams params, @UserIdInt int userId) {
Objects.requireNonNull(pkg, "pkg");
Objects.requireNonNull(params, "params");
@@ -5244,7 +5275,7 @@
|| userId == UserHandle.USER_ALL, "userId");
final int[] userIds = userId == UserHandle.USER_ALL ? getAllUserIds()
: new int[] { userId };
- onPackageInstalledInternal(pkg, params, userIds);
+ onPackageInstalledInternal(pkg, previousAppId, params, userIds);
}
@Override
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
index f4fb810..d2c4ec4 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
@@ -252,11 +252,14 @@
* Callback when a package has been installed for a user.
*
* @param pkg the installed package
+ * @param previousAppId the previous app ID if the package is leaving a shared UID,
+ * or Process.INVALID_UID
* @param params the parameters passed in for package installation
* @param userId the user ID this package is installed for
*/
//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
- void onPackageInstalled(@NonNull AndroidPackage pkg, @NonNull PackageInstalledParams params,
+ void onPackageInstalled(@NonNull AndroidPackage pkg, int previousAppId,
+ @NonNull PackageInstalledParams params,
@UserIdInt int userId);
/**
diff --git a/services/core/java/com/android/server/pm/permission/UserPermissionState.java b/services/core/java/com/android/server/pm/permission/UserPermissionState.java
index 2c741cf..f39086b3 100644
--- a/services/core/java/com/android/server/pm/permission/UserPermissionState.java
+++ b/services/core/java/com/android/server/pm/permission/UserPermissionState.java
@@ -69,6 +69,15 @@
return uidState;
}
+ @NonNull
+ UidPermissionState createUidStateWithExisting(
+ @AppIdInt int appId, @NonNull UidPermissionState other) {
+ checkAppId(appId);
+ UidPermissionState uidState = new UidPermissionState(other);
+ mUidStates.put(appId, uidState);
+ return uidState;
+ }
+
public void removeUidState(@AppIdInt int appId) {
checkAppId(appId);
mUidStates.delete(appId);