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:
@@ -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*/);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -477,6 +477,7 @@ public class PackageSetting extends SettingBase implements PackageStateInternal
|
||||
|
||||
public void setSharedUserAppId(int sharedUserAppId) {
|
||||
mSharedUserAppId = sharedUserAppId;
|
||||
onChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
105
services/core/java/com/android/server/pm/SharedUidMigration.java
Normal file
105
services/core/java/com/android/server/pm/SharedUidMigration.java
Normal 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() {}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user