Merge "Migrate data when apps leave sharedUserId"

This commit is contained in:
John Wu
2021-11-02 21:20:42 +00:00
committed by Android (Google) Code Review
4 changed files with 101 additions and 107 deletions

View File

@@ -26,8 +26,10 @@ import android.annotation.Nullable;
import android.content.pm.PackageManager;
import android.content.pm.SELinuxUtil;
import android.content.pm.UserInfo;
import android.os.CreateAppDataArgs;
import android.os.Environment;
import android.os.FileUtils;
import android.os.Process;
import android.os.Trace;
import android.os.UserHandle;
import android.os.storage.StorageManager;
@@ -86,12 +88,20 @@ final class AppDataHelper {
* <p>
* Verifies that directories exist and that ownership and labeling is
* correct for all installed apps. If there is an ownership mismatch, it
* will try recovering system apps by wiping data; third-party app data is
* left intact.
* will wipe and recreate the data.
* <p>
* <em>Note: To avoid a deadlock, do not call this method with {@code mLock} lock held</em>
*/
public void prepareAppDataAfterInstallLIF(AndroidPackage pkg) {
prepareAppDataPostCommitLIF(pkg, 0 /* previousAppId */);
}
/**
* For more details about data verification and previousAppId, check
* {@link #prepareAppData(Installer.Batch, AndroidPackage, int, int, int)}
* @see #prepareAppDataAfterInstallLIF(AndroidPackage)
*/
public void prepareAppDataPostCommitLIF(AndroidPackage pkg, int previousAppId) {
final PackageSetting ps;
synchronized (mPm.mLock) {
ps = mPm.mSettings.getPackageLPr(pkg.getPackageName());
@@ -113,13 +123,9 @@ final class AppDataHelper {
continue;
}
// TODO@ashfall check ScanResult.mNeedsNewAppId, and if true instead
// of creating app data, migrate / change ownership of existing
// data.
if (ps.getInstalled(user.id)) {
// TODO: when user data is locked, mark that we're still dirty
prepareAppData(batch, pkg, user.id, flags).thenRun(() -> {
prepareAppData(batch, pkg, previousAppId, user.id, flags).thenRun(() -> {
// Note: this code block is executed with the Installer lock
// already held, since it's invoked as a side-effect of
// executeBatchLI()
@@ -147,22 +153,26 @@ final class AppDataHelper {
* Prepare app data for the given app.
* <p>
* Verifies that directories exist and that ownership and labeling is
* correct for all installed apps. If there is an ownership mismatch, this
* will try recovering system apps by wiping data; third-party app data is
* left intact.
* correct for all installed apps. If there is an ownership mismatch:
* <ul>
* <li>If previousAppId < 0, app data will be migrated to the new app ID
* <li>If previousAppId == 0, no migration will happen and data will be wiped and recreated
* <li>If previousAppId > 0, it will migrate all data owned by previousAppId
* to the new app ID
* </ul>
*/
private @NonNull CompletableFuture<?> prepareAppData(@NonNull Installer.Batch batch,
@Nullable AndroidPackage pkg, int userId, int flags) {
@Nullable AndroidPackage pkg, int previousAppId, int userId, int flags) {
if (pkg == null) {
Slog.wtf(TAG, "Package was null!", new Throwable());
return CompletableFuture.completedFuture(null);
}
return prepareAppDataLeaf(batch, pkg, userId, flags);
return prepareAppDataLeaf(batch, pkg, previousAppId, userId, flags);
}
private void prepareAppDataAndMigrate(@NonNull Installer.Batch batch,
@NonNull AndroidPackage pkg, int userId, int flags, boolean maybeMigrateAppData) {
prepareAppData(batch, pkg, userId, flags).thenRun(() -> {
prepareAppData(batch, pkg, Process.INVALID_UID, userId, flags).thenRun(() -> {
// Note: this code block is executed with the Installer lock
// already held, since it's invoked as a side-effect of
// executeBatchLI()
@@ -170,14 +180,14 @@ final class AppDataHelper {
// We may have just shuffled around app data directories, so
// prepare them one more time
final Installer.Batch batchInner = new Installer.Batch();
prepareAppData(batchInner, pkg, userId, flags);
prepareAppData(batchInner, pkg, Process.INVALID_UID, userId, flags);
executeBatchLI(batchInner);
}
});
}
private @NonNull CompletableFuture<?> prepareAppDataLeaf(@NonNull Installer.Batch batch,
@NonNull AndroidPackage pkg, int userId, int flags) {
@NonNull AndroidPackage pkg, int previousAppId, int userId, int flags) {
if (DEBUG_APP_DATA) {
Slog.v(TAG, "prepareAppData for " + pkg.getPackageName() + " u" + userId + " 0x"
+ Integer.toHexString(flags));
@@ -200,65 +210,64 @@ final class AppDataHelper {
final String seInfo = pkgSeInfo + seInfoUser;
final int targetSdkVersion = pkg.getTargetSdkVersion();
final CreateAppDataArgs args = Installer.buildCreateAppDataArgs(volumeUuid, packageName,
userId, flags, appId, seInfo, targetSdkVersion);
args.previousAppId = previousAppId;
return batch.createAppData(volumeUuid, packageName, userId, flags, appId, seInfo,
targetSdkVersion).whenComplete((ceDataInode, e) -> {
// Note: this code block is executed with the Installer lock
// already held, since it's invoked as a side-effect of
// executeBatchLI()
if (e != null) {
logCriticalInfo(Log.WARN, "Failed to create app data for " + packageName
+ ", but trying to recover: " + e);
destroyAppDataLeafLIF(pkg, userId, flags);
try {
ceDataInode = mInstaller.createAppData(volumeUuid, packageName, userId,
flags, appId, seInfo, pkg.getTargetSdkVersion());
logCriticalInfo(Log.DEBUG, "Recovery succeeded!");
} catch (Installer.InstallerException e2) {
logCriticalInfo(Log.DEBUG, "Recovery failed!");
}
}
return batch.createAppData(args).whenComplete((ceDataInode, e) -> {
// Note: this code block is executed with the Installer lock
// already held, since it's invoked as a side-effect of
// executeBatchLI()
if (e != null) {
logCriticalInfo(Log.WARN, "Failed to create app data for " + packageName
+ ", but trying to recover: " + e);
destroyAppDataLeafLIF(pkg, userId, flags);
try {
ceDataInode = mInstaller.createAppData(args).ceDataInode;
logCriticalInfo(Log.DEBUG, "Recovery succeeded!");
} catch (Installer.InstallerException e2) {
logCriticalInfo(Log.DEBUG, "Recovery failed!");
}
}
// Prepare the application profiles only for upgrades and
// first boot (so that we don't repeat the same operation at
// each boot).
//
// We only have to cover the upgrade and first boot here
// because for app installs we prepare the profiles before
// invoking dexopt (in installPackageLI).
//
// We also have to cover non system users because we do not
// call the usual install package methods for them.
//
// NOTE: in order to speed up first boot time we only create
// the current profile and do not update the content of the
// reference profile. A system image should already be
// configured with the right profile keys and the profiles
// for the speed-profile prebuilds should already be copied.
// That's done in #performDexOptUpgrade.
//
// TODO(calin, mathieuc): We should use .dm files for
// prebuilds profiles instead of manually copying them in
// #performDexOptUpgrade. When we do that we should have a
// more granular check here and only update the existing
// profiles.
if (mPm.isDeviceUpgrading() || mPm.isFirstBoot()
|| (userId != UserHandle.USER_SYSTEM)) {
mArtManagerService.prepareAppProfiles(pkg, userId,
/* updateReferenceProfileContent= */ false);
}
// Prepare the application profiles only for upgrades and
// first boot (so that we don't repeat the same operation at
// each boot).
//
// We only have to cover the upgrade and first boot here
// because for app installs we prepare the profiles before
// invoking dexopt (in installPackageLI).
//
// We also have to cover non system users because we do not
// call the usual install package methods for them.
//
// NOTE: in order to speed up first boot time we only create
// the current profile and do not update the content of the
// reference profile. A system image should already be
// configured with the right profile keys and the profiles
// for the speed-profile prebuilds should already be copied.
// That's done in #performDexOptUpgrade.
//
// TODO(calin, mathieuc): We should use .dm files for
// prebuilds profiles instead of manually copying them in
// #performDexOptUpgrade. When we do that we should have a
// more granular check here and only update the existing
// profiles.
if (mPm.isDeviceUpgrading() || mPm.isFirstBoot()
|| (userId != UserHandle.USER_SYSTEM)) {
mArtManagerService.prepareAppProfiles(pkg, userId,
/* updateReferenceProfileContent= */ false);
}
if ((flags & StorageManager.FLAG_STORAGE_CE) != 0 && ceDataInode != -1) {
// TODO: mark this structure as dirty so we persist it!
synchronized (mPm.mLock) {
if (ps != null) {
ps.setCeDataInode(ceDataInode, userId);
}
}
}
if ((flags & StorageManager.FLAG_STORAGE_CE) != 0 && ceDataInode != -1) {
// TODO: mark this structure as dirty so we persist it!
synchronized (mPm.mLock) {
ps.setCeDataInode(ceDataInode, userId);
}
}
prepareAppDataContentsLeafLIF(pkg, ps, userId, flags);
});
prepareAppDataContentsLeafLIF(pkg, ps, userId, flags);
});
}
public void prepareAppDataContentsLIF(AndroidPackage pkg, @Nullable PackageSetting pkgSetting,

View File

@@ -2433,7 +2433,12 @@ final class InstallPackageHelper {
}
incrementalStorages.add(storage);
}
appDataHelper.prepareAppDataAfterInstallLIF(pkg);
int previousAppId = 0;
if (reconciledPkg.mScanResult.needsNewAppId()) {
// Only set previousAppId if the app is migrating out of shared UID
previousAppId = reconciledPkg.mScanResult.mPreviousAppId;
}
appDataHelper.prepareAppDataPostCommitLIF(pkg, previousAppId);
if (reconciledPkg.mPrepareResult.mClearCodeCache) {
appDataHelper.clearAppDataLIF(pkg, UserHandle.USER_ALL,
FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL

View File

@@ -26,7 +26,6 @@ import android.os.Build;
import android.os.CreateAppDataArgs;
import android.os.CreateAppDataResult;
import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
import android.os.IInstalld;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -148,12 +147,9 @@ public class Installer extends SystemService {
IBinder binder = ServiceManager.getService("installd");
if (binder != null) {
try {
binder.linkToDeath(new DeathRecipient() {
@Override
public void binderDied() {
Slog.w(TAG, "installd died; reconnecting");
connect();
}
binder.linkToDeath(() -> {
Slog.w(TAG, "installd died; reconnecting");
connect();
}, 0);
} catch (RemoteException e) {
binder = null;
@@ -168,9 +164,7 @@ public class Installer extends SystemService {
}
} else {
Slog.w(TAG, "installd not found; trying again");
BackgroundThread.getHandler().postDelayed(() -> {
connect();
}, DateUtils.SECOND_IN_MILLIS);
BackgroundThread.getHandler().postDelayed(this::connect, DateUtils.SECOND_IN_MILLIS);
}
}
@@ -192,7 +186,9 @@ public class Installer extends SystemService {
}
}
private static CreateAppDataArgs buildCreateAppDataArgs(String uuid, String packageName,
// We explicitly do NOT set previousAppId because the default value should always be 0.
// Manually override previousAppId after building CreateAppDataArgs for specific behaviors.
static CreateAppDataArgs buildCreateAppDataArgs(String uuid, String packageName,
int userId, int flags, int appId, String seInfo, int targetSdkVersion) {
final CreateAppDataArgs args = new CreateAppDataArgs();
args.uuid = uuid;
@@ -213,23 +209,6 @@ public class Installer extends SystemService {
return result;
}
/**
* @deprecated callers are encouraged to migrate to using {@link Batch} to
* more efficiently handle operations in bulk.
*/
@Deprecated
public long createAppData(String uuid, String packageName, int userId, int flags, int appId,
String seInfo, int targetSdkVersion) throws InstallerException {
final CreateAppDataArgs args = buildCreateAppDataArgs(uuid, packageName, userId, flags,
appId, seInfo, targetSdkVersion);
final CreateAppDataResult result = createAppData(args);
if (result.exceptionCode == 0) {
return result.ceDataInode;
} else {
throw new InstallerException(result.exceptionMessage);
}
}
public @NonNull CreateAppDataResult createAppData(@NonNull CreateAppDataArgs args)
throws InstallerException {
if (!checkBeforeRemote()) {
@@ -284,13 +263,11 @@ public class Installer extends SystemService {
* Callers of this method are not required to hold a monitor lock on an
* {@link Installer} object.
*/
public synchronized @NonNull CompletableFuture<Long> createAppData(String uuid,
String packageName, int userId, int flags, int appId, String seInfo,
int targetSdkVersion) {
if (mExecuted) throw new IllegalStateException();
final CreateAppDataArgs args = buildCreateAppDataArgs(uuid, packageName, userId, flags,
appId, seInfo, targetSdkVersion);
@NonNull
public synchronized CompletableFuture<Long> createAppData(CreateAppDataArgs args) {
if (mExecuted) {
throw new IllegalStateException();
}
final CompletableFuture<Long> future = new CompletableFuture<>();
mArgs.add(args);
mFutures.add(future);

View File

@@ -61,6 +61,7 @@ import android.content.pm.pkg.PackageUserStateUtils;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
import android.os.CreateAppDataArgs;
import android.os.Environment;
import android.os.FileUtils;
import android.os.Handler;
@@ -4016,9 +4017,11 @@ public final class Settings implements Watchable, Snappable {
// Accumulate all required args and call the installer after mPackages lock
// has been released
final String seInfo = AndroidPackageUtils.getSeInfo(ps.getPkg(), ps);
batch.createAppData(ps.getVolumeUuid(), ps.getPackageName(), userHandle,
final CreateAppDataArgs args = Installer.buildCreateAppDataArgs(
ps.getVolumeUuid(), ps.getPackageName(), userHandle,
StorageManager.FLAG_STORAGE_CE | StorageManager.FLAG_STORAGE_DE,
ps.getAppId(), seInfo, ps.getPkg().getTargetSdkVersion());
batch.createAppData(args);
} else {
// Make sure the app is excluded from storage mapping for this user
writeKernelMappingLPr(ps);