Merge "Migrate data when apps leave sharedUserId"
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user