Introduce "Best Effort" shared UID migration strategy

Remove/disable all appId migration code in the system.

Introduce 2 levels of shared UID migration:
NEW_INSTALL_ONLY   = 1
BEST_EFFORT        = 2

The strategy to be used at the moment can be toggled by the system
property: "persist.debug.pm.shared_uid_migration_strategy".

The default strategy for now is BEST_EFFORT.

Test: atest CtsSharedUserMigrationTestCases
Bug: 220015249
Change-Id: If0e1148fdfd27a1dc4bf0066b2a661c6389488fe
This commit is contained in:
John Wu
2022-02-28 17:19:55 -08:00
parent 99497f45bb
commit 2f11dc5ef3
10 changed files with 208 additions and 52 deletions

View File

@@ -86,6 +86,7 @@ import static com.android.server.pm.PackageManagerServiceUtils.compareSignatures
import static com.android.server.pm.PackageManagerServiceUtils.compressedFileExists;
import static com.android.server.pm.PackageManagerServiceUtils.deriveAbiOverride;
import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo;
import static com.android.server.pm.SharedUidMigration.BEST_EFFORT;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -287,6 +288,12 @@ final class InstallPackageHelper {
SharedUserSetting sharedUserSetting = mPm.mSettings.getSharedUserSettingLPr(pkgSetting);
if (sharedUserSetting != null) {
sharedUserSetting.addPackage(pkgSetting);
if (parsedPackage.isLeavingSharedUid()
&& SharedUidMigration.applyStrategy(BEST_EFFORT)
&& sharedUserSetting.isSingleUser()) {
// Attempt the transparent shared UID migration
mPm.mSettings.convertSharedUserSettingsLPw(sharedUserSetting);
}
}
if (reconciledPkg.mInstallArgs != null
&& reconciledPkg.mInstallArgs.mForceQueryableOverride) {
@@ -2216,23 +2223,8 @@ final class InstallPackageHelper {
}
incrementalStorages.add(storage);
}
int previousAppId = 0;
if (reconciledPkg.mScanResult.needsNewAppId()) {
// Only set previousAppId if the app is migrating out of shared UID
previousAppId = reconciledPkg.mScanResult.mPreviousAppId;
if (pkg.shouldInheritKeyStoreKeys()) {
// Migrate keystore data
mAppDataHelper.migrateKeyStoreData(
previousAppId, reconciledPkg.mPkgSetting.getAppId());
}
if (reconciledPkg.mInstallResult.mRemovedInfo.mRemovedAppId == previousAppId) {
// If the previous app ID is removed, clear the keys
mAppDataHelper.clearKeystoreData(UserHandle.USER_ALL, previousAppId);
}
}
mAppDataHelper.prepareAppDataPostCommitLIF(pkg, previousAppId);
// Hardcode previousAppId to 0 to disable any data migration (http://b/221088088)
mAppDataHelper.prepareAppDataPostCommitLIF(pkg, 0);
if (reconciledPkg.mPrepareResult.mClearCodeCache) {
mAppDataHelper.clearAppDataLIF(pkg, UserHandle.USER_ALL,
FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL
@@ -3026,8 +3018,7 @@ final class InstallPackageHelper {
installPackageFromSystemLIF(stubPkg.getPath(),
mPm.mUserManager.getUserIds() /*allUserHandles*/,
null /*origUserHandles*/,
true /*writeSettings*/,
Process.INVALID_UID /*previousAppId*/);
true /*writeSettings*/);
} catch (PackageManagerException pme) {
// Serious WTF; we have to be able to install the stub
Slog.wtf(TAG, "Failed to restore system package:" + stubPkg.getPackageName(),
@@ -3154,10 +3145,8 @@ final class InstallPackageHelper {
try {
synchronized (mPm.mInstallLock) {
final int[] origUsers = outInfo == null ? null : outInfo.mOrigUsers;
final int previousAppId = disabledPs.getAppId() != deletedPs.getAppId()
? deletedPs.getAppId() : Process.INVALID_UID;
installPackageFromSystemLIF(disabledPs.getPathString(), allUserHandles,
origUsers, writeSettings, previousAppId);
origUsers, writeSettings);
}
} catch (PackageManagerException e) {
Slog.w(TAG, "Failed to restore system package:" + deletedPs.getPackageName() + ": "
@@ -3200,7 +3189,7 @@ final class InstallPackageHelper {
@GuardedBy("mPm.mInstallLock")
private void installPackageFromSystemLIF(@NonNull String codePathString,
@NonNull int[] allUserHandles, @Nullable int[] origUserHandles,
boolean writeSettings, int previousAppId)
boolean writeSettings)
throws PackageManagerException {
final File codePath = new File(codePathString);
@ParsingPackageUtils.ParseFlags int parseFlags =
@@ -3223,13 +3212,12 @@ final class InstallPackageHelper {
mAppDataHelper.prepareAppDataAfterInstallLIF(pkg);
setPackageInstalledForSystemPackage(pkg, allUserHandles,
origUserHandles, writeSettings, previousAppId);
setPackageInstalledForSystemPackage(pkg, allUserHandles, origUserHandles, writeSettings);
}
private void setPackageInstalledForSystemPackage(@NonNull AndroidPackage pkg,
@NonNull int[] allUserHandles, @Nullable int[] origUserHandles,
boolean writeSettings, int previousAppId) {
boolean writeSettings) {
// writer
synchronized (mPm.mLock) {
PackageSetting ps = mPm.mSettings.getPackageLPr(pkg.getPackageName());
@@ -3263,7 +3251,7 @@ final class InstallPackageHelper {
// The method below will take care of removing obsolete permissions and granting
// install permissions.
mPm.mPermissionManager.onPackageInstalled(pkg, previousAppId,
mPm.mPermissionManager.onPackageInstalled(pkg, Process.INVALID_UID,
PermissionManagerServiceInternal.PackageInstalledParams.DEFAULT,
UserHandle.USER_ALL);
for (final int userId : allUserHandles) {
@@ -3701,7 +3689,14 @@ final class InstallPackageHelper {
}
disabledPkgSetting = mPm.mSettings.getDisabledSystemPkgLPr(
parsedPackage.getPackageName());
if (parsedPackage.getSharedUserId() != null && !parsedPackage.isLeavingSharedUid()) {
boolean ignoreSharedUserId = false;
if (installedPkgSetting == null) {
// We can directly ignore sharedUserSetting for new installs
ignoreSharedUserId = parsedPackage.isLeavingSharedUid();
}
if (!ignoreSharedUserId && parsedPackage.getSharedUserId() != null) {
sharedUserSetting = mPm.mSettings.getSharedUserLPw(
parsedPackage.getSharedUserId(),
0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true /*create*/);

View File

@@ -215,6 +215,8 @@ public class Installer extends SystemService {
if (!checkBeforeRemote()) {
return buildPlaceholderCreateAppDataResult();
}
// Hardcode previousAppId to 0 to disable any data migration (http://b/221088088)
args.previousAppId = 0;
try {
return mInstalld.createAppData(args);
} catch (Exception e) {
@@ -229,6 +231,10 @@ public class Installer extends SystemService {
Arrays.fill(results, buildPlaceholderCreateAppDataResult());
return results;
}
// Hardcode previousAppId to 0 to disable any data migration (http://b/221088088)
for (final CreateAppDataArgs arg : args) {
arg.previousAppId = 0;
}
try {
return mInstalld.createAppDataBatched(args);
} catch (Exception e) {

View File

@@ -477,6 +477,7 @@ public class PackageSetting extends SettingBase implements PackageStateInternal
public void setSharedUserAppId(int sharedUserAppId) {
mSharedUserAppId = sharedUserAppId;
onChanged();
}
@Override

View File

@@ -284,7 +284,11 @@ final class RemovePackageHelper {
List<AndroidPackage> sharedUserPkgs =
sus != null ? sus.getPackages() : Collections.emptyList();
mPermissionManager.onPackageUninstalled(packageName, deletedPs.getAppId(),
deletedPs.getPkg(), sharedUserPkgs, UserHandle.USER_ALL);
deletedPkg, sharedUserPkgs, UserHandle.USER_ALL);
// After permissions are handled, check if the shared user can be migrated
if (sus != null) {
mPm.mSettings.checkAndConvertSharedUserSettingsLPw(sus);
}
}
mPm.clearPackagePreferredActivitiesLPw(
deletedPs.getPackageName(), changedUsers, UserHandle.USER_ALL);

View File

@@ -165,25 +165,15 @@ final class ScanPackageUtils {
}
}
int previousAppId = Process.INVALID_UID;
if (pkgSetting != null && oldSharedUserSetting != sharedUserSetting) {
if (oldSharedUserSetting != null && sharedUserSetting == null) {
previousAppId = pkgSetting.getAppId();
// Log that something is leaving shareduid and keep going
Slog.i(TAG,
"Package " + parsedPackage.getPackageName() + " shared user changed from "
+ oldSharedUserSetting.name + " to " + "<nothing>.");
} else {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Package " + parsedPackage.getPackageName() + " shared user changed from "
+ (oldSharedUserSetting != null
? oldSharedUserSetting.name : "<nothing>")
+ " to "
+ (sharedUserSetting != null ? sharedUserSetting.name : "<nothing>")
+ "; replacing with new");
pkgSetting = null;
}
PackageManagerService.reportSettingsProblem(Log.WARN,
"Package " + parsedPackage.getPackageName() + " shared user changed from "
+ (oldSharedUserSetting != null
? oldSharedUserSetting.name : "<nothing>")
+ " to "
+ (sharedUserSetting != null ? sharedUserSetting.name : "<nothing>")
+ "; replacing with new");
pkgSetting = null;
}
String[] usesSdkLibraries = null;
@@ -474,8 +464,8 @@ final class ScanPackageUtils {
return new ScanResult(request, true, pkgSetting, changedAbiCodePath,
!createNewPackage /* existingSettingCopied */,
previousAppId, sdkLibraryInfo, staticSharedLibraryInfo,
dynamicSharedLibraryInfos);
Process.INVALID_UID /* previousAppId */ , sdkLibraryInfo,
staticSharedLibraryInfo, dynamicSharedLibraryInfos);
}
/**

View File

@@ -70,7 +70,9 @@ final class ScanResult {
mPkgSetting = pkgSetting;
mChangedAbiCodePath = changedAbiCodePath;
mExistingSettingCopied = existingSettingCopied;
mPreviousAppId = previousAppId;
// Hardcode mPreviousAppId to INVALID_UID (http://b/221088088)
// This will disable all migration code paths in PMS and PermMS
mPreviousAppId = Process.INVALID_UID;
mSdkSharedLibraryInfo = sdkSharedLibraryInfo;
mStaticSharedLibraryInfo = staticSharedLibraryInfo;
mDynamicSharedLibraryInfos = dynamicSharedLibraryInfos;

View File

@@ -29,6 +29,7 @@ import static android.os.Process.PACKAGE_INFO_GID;
import static android.os.Process.SYSTEM_UID;
import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
import static com.android.server.pm.SharedUidMigration.BEST_EFFORT;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -1424,6 +1425,35 @@ public final class Settings implements Watchable, Snappable {
}
}
/**
* Transparently convert a SharedUserSetting into PackageSettings without changing appId.
* The sharedUser passed to this method has to be {@link SharedUserSetting#isSingleUser()}.
*/
void convertSharedUserSettingsLPw(SharedUserSetting sharedUser) {
final PackageSetting ps = sharedUser.getPackageSettings().valueAt(0);
replaceAppIdLPw(sharedUser.getAppId(), ps);
// Unlink the SharedUserSetting
ps.setSharedUserAppId(INVALID_UID);
if (!sharedUser.getDisabledPackageSettings().isEmpty()) {
final PackageSetting disabledPs = sharedUser.getDisabledPackageSettings().valueAt(0);
disabledPs.setSharedUserAppId(INVALID_UID);
}
mSharedUsers.remove(sharedUser.getName());
}
/**
* Check and convert eligible SharedUserSettings to PackageSettings.
*/
void checkAndConvertSharedUserSettingsLPw(SharedUserSetting sharedUser) {
if (!sharedUser.isSingleUser()) return;
final AndroidPackage pkg = sharedUser.getPackageSettings().valueAt(0).getPkg();
if (pkg != null && pkg.isLeavingSharedUid()
&& SharedUidMigration.applyStrategy(BEST_EFFORT)) {
convertSharedUserSettingsLPw(sharedUser);
}
}
PreferredIntentResolver editPreferredActivitiesLPw(int userId) {
PreferredIntentResolver pir = mPreferredActivities.get(userId);
if (pir == null) {

View File

@@ -0,0 +1,105 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.pm;
import android.annotation.IntDef;
import android.os.Build;
import android.os.SystemProperties;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* A utility class hosting code configuring shared UID migration behavior.
*/
public final class SharedUidMigration {
/**
* The system property key used to configure the shared UID migration strategy.
*/
public static final String PROPERTY_KEY = "persist.debug.pm.shared_uid_migration_strategy";
/**
* Only leave shared UID for newly installed packages.
*/
public static final int NEW_INSTALL_ONLY = 1;
/**
* Opportunistically migrate any single-package shared UID group.
*/
public static final int BEST_EFFORT = 2;
/**
* Run appId transitions when the device reboots.
*/
public static final int TRANSITION_AT_BOOT = 3;
/**
* Run appId transitions as soon as the upgrade is installed.
*/
public static final int LIVE_TRANSITION = 4;
@IntDef({
NEW_INSTALL_ONLY,
BEST_EFFORT,
TRANSITION_AT_BOOT,
LIVE_TRANSITION,
})
@Retention(RetentionPolicy.SOURCE)
public @interface Strategy {}
private static final int DEFAULT = BEST_EFFORT;
/**
* All shared UID migration is disabled.
* This is not a strategy that can be set with system properties.
* To disable shared UID migration, change {@link #DEFAULT} to this value.
*/
private static final int DISABLED = 0;
/**
* Whether shared UID migration is fully disabled. Disabled means the sharedUserMaxSdkVersion
* attribute will be directly ignored in the parsing phase.
*/
@SuppressWarnings("ConstantConditions")
public static boolean isDisabled() {
return DEFAULT == DISABLED;
}
/**
* If the system is userdebug, returns the strategy to use by getting the system property
* {@link #PROPERTY_KEY}, or else returns the default strategy enabled in the build.
*/
public static int getCurrentStrategy() {
if (!Build.IS_USERDEBUG) {
return DEFAULT;
}
final int s = SystemProperties.getInt(PROPERTY_KEY, DEFAULT);
// No transition strategies can be used (http://b/221088088)
if (s > BEST_EFFORT || s <= DISABLED) {
return DEFAULT;
}
return s;
}
/**
* Should the strategy be used based on the current active strategy.
*/
public static boolean applyStrategy(@Strategy int strategy) {
return !isDisabled() && getCurrentStrategy() >= strategy;
}
private SharedUidMigration() {}
}

View File

@@ -220,6 +220,25 @@ public final class SharedUserSetting extends SettingBase implements SharedUserAp
return (this.getPrivateFlags() & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
}
/**
* A shared user is considered "single user" if there is exactly one single package
* currently using it. In the case when that package is also a system app, the APK on
* the system partition has to also leave shared UID.
*/
public boolean isSingleUser() {
if (mPackages.size() != 1) {
return false;
}
if (mDisabledPackages.size() > 1) {
return false;
}
if (mDisabledPackages.size() == 1) {
final AndroidPackage pkg = mDisabledPackages.valueAt(0).getPkg();
return pkg != null && pkg.isLeavingSharedUid();
}
return true;
}
/**
* Determine the targetSdkVersion for a sharedUser and update pkg.applicationInfo.seInfo
* to ensure that all apps within the sharedUser share an SELinux domain. Use the lowest

View File

@@ -91,6 +91,7 @@ import com.android.internal.R;
import com.android.internal.os.ClassLoaderFactory;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.XmlUtils;
import com.android.server.pm.SharedUidMigration;
import com.android.server.pm.permission.CompatibilityPermissionInfo;
import com.android.server.pm.pkg.component.ComponentMutateUtils;
import com.android.server.pm.pkg.component.ComponentParseUtils;
@@ -1047,8 +1048,11 @@ public class ParsingPackageUtils {
}
}
int maxSdkVersion = anInteger(0, R.styleable.AndroidManifest_sharedUserMaxSdkVersion, sa);
boolean leaving = (maxSdkVersion != 0) && (maxSdkVersion < Build.VERSION.RESOURCES_SDK_INT);
boolean leaving = false;
if (!SharedUidMigration.isDisabled()) {
int max = anInteger(0, R.styleable.AndroidManifest_sharedUserMaxSdkVersion, sa);
leaving = (max != 0) && (max < Build.VERSION.RESOURCES_SDK_INT);
}
return input.success(pkg
.setLeavingSharedUid(leaving)