Loading core/java/android/content/rollback/PackageRollbackInfo.java +68 −1 Original line number Diff line number Diff line Loading @@ -16,10 +16,14 @@ package android.content.rollback; import android.annotation.NonNull; import android.annotation.SystemApi; import android.content.pm.VersionedPackage; import android.os.Parcel; import android.os.Parcelable; import android.util.IntArray; import java.util.ArrayList; /** * Information about a rollback available for a particular package. Loading @@ -32,6 +36,38 @@ public final class PackageRollbackInfo implements Parcelable { private final VersionedPackage mVersionRolledBackFrom; private final VersionedPackage mVersionRolledBackTo; /** * Encapsulates information required to restore a snapshot of an app's userdata. * * @hide */ public static class RestoreInfo { public final int userId; public final int appId; public final String seInfo; public RestoreInfo(int userId, int appId, String seInfo) { this.userId = userId; this.appId = appId; this.seInfo = seInfo; } } /* * The list of users for which we need to backup userdata for this package. Backups of * credential encrypted data are listed as pending if the user hasn't unlocked their device * with credentials yet. */ // NOTE: Not a part of the Parcelable representation of this object. private final IntArray mPendingBackups; /** * The list of users for which we need to restore userdata for this package. This field is * non-null only after a rollback for this package has been committed. */ // NOTE: Not a part of the Parcelable representation of this object. private final ArrayList<RestoreInfo> mPendingRestores; /** * Returns the name of the package to roll back from. */ Loading @@ -53,16 +89,47 @@ public final class PackageRollbackInfo implements Parcelable { return mVersionRolledBackTo; } /** @hide */ public IntArray getPendingBackups() { return mPendingBackups; } /** @hide */ public ArrayList<RestoreInfo> getPendingRestores() { return mPendingRestores; } /** @hide */ public RestoreInfo getRestoreInfo(int userId) { for (RestoreInfo ri : mPendingRestores) { if (ri.userId == userId) { return ri; } } return null; } /** @hide */ public void removeRestoreInfo(RestoreInfo ri) { mPendingRestores.remove(ri); } /** @hide */ public PackageRollbackInfo(VersionedPackage packageRolledBackFrom, VersionedPackage packageRolledBackTo) { VersionedPackage packageRolledBackTo, @NonNull IntArray pendingBackups, @NonNull ArrayList<RestoreInfo> pendingRestores) { this.mVersionRolledBackFrom = packageRolledBackFrom; this.mVersionRolledBackTo = packageRolledBackTo; this.mPendingBackups = pendingBackups; this.mPendingRestores = pendingRestores; } private PackageRollbackInfo(Parcel in) { this.mVersionRolledBackFrom = VersionedPackage.CREATOR.createFromParcel(in); this.mVersionRolledBackTo = VersionedPackage.CREATOR.createFromParcel(in); this.mPendingRestores = null; this.mPendingBackups = null; } @Override Loading services/core/java/com/android/server/rollback/AppDataRollbackHelper.java 0 → 100644 +236 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 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.rollback; import android.content.rollback.PackageRollbackInfo; import android.content.rollback.PackageRollbackInfo.RestoreInfo; import android.content.rollback.RollbackInfo; import android.os.storage.StorageManager; import android.util.IntArray; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.server.pm.Installer; import com.android.server.pm.Installer.InstallerException; import java.util.ArrayList; import java.util.List; import java.util.Map; /** * Encapsulates the logic for initiating userdata snapshots and rollbacks via installd. */ @VisibleForTesting // TODO(narayan): Reason about the failure scenarios that involve one or more IPCs to installd // failing. We need to decide what course of action to take if calls to snapshotAppData or // restoreAppDataSnapshot fail. public class AppDataRollbackHelper { private static final String TAG = "RollbackManager"; private final Installer mInstaller; public AppDataRollbackHelper(Installer installer) { mInstaller = installer; } /** * Creates an app data snapshot for a specified {@code packageName} for {@code installedUsers}, * a specified set of users for whom the package is installed. * * @return a list of users for which the snapshot is pending, usually because data for one or * more users is still credential locked. */ public IntArray snapshotAppData(String packageName, int[] installedUsers) { final IntArray pendingBackups = new IntArray(); for (int user : installedUsers) { final int storageFlags; if (isUserCredentialLocked(user)) { // We've encountered a user that hasn't unlocked on a FBE device, so we can't copy // across app user data until the user unlocks their device. Log.v(TAG, "User: " + user + " isn't unlocked, skipping CE userdata backup."); storageFlags = Installer.FLAG_STORAGE_DE; pendingBackups.add(user); } else { storageFlags = Installer.FLAG_STORAGE_CE | Installer.FLAG_STORAGE_DE; } try { mInstaller.snapshotAppData(packageName, user, storageFlags); } catch (InstallerException ie) { Log.e(TAG, "Unable to create app data snapshot for: " + packageName + ", userId: " + user, ie); } } return pendingBackups; } /** * Restores an app data snapshot for a specified package ({@code packageName}, * {@code rollbackData}) for a specified {@code userId}. * * @return {@code true} iff. a change to the {@code rollbackData} has been made. Changes to * {@code rollbackData} are restricted to the removal or addition of {@code userId} to * the list of pending backups or restores. */ public boolean restoreAppData(String packageName, RollbackData rollbackData, int userId, int appId, long ceDataInode, String seInfo) { if (rollbackData == null) { return false; } if (!rollbackData.inProgress) { Log.e(TAG, "Request to restore userData for: " + packageName + ", but no rollback in progress."); return false; } PackageRollbackInfo packageInfo = RollbackManagerServiceImpl.getPackageRollbackInfo( rollbackData, packageName); int storageFlags = Installer.FLAG_STORAGE_DE; final IntArray pendingBackups = packageInfo.getPendingBackups(); final List<RestoreInfo> pendingRestores = packageInfo.getPendingRestores(); boolean changedRollbackData = false; // If we still have a userdata backup pending for this user, it implies that the user // hasn't unlocked their device between the point of backup and the point of restore, // so the data cannot have changed. We simply skip restoring CE data in this case. if (pendingBackups != null && pendingBackups.indexOf(userId) != -1) { pendingBackups.remove(pendingBackups.indexOf(userId)); changedRollbackData = true; } else { // There's no pending CE backup for this user, which means that we successfully // managed to backup data for the user, which means we seek to restore it if (isUserCredentialLocked(userId)) { // We've encountered a user that hasn't unlocked on a FBE device, so we can't // copy across app user data until the user unlocks their device. pendingRestores.add(new RestoreInfo(userId, appId, seInfo)); changedRollbackData = true; } else { // This user has unlocked, we can proceed to restore both CE and DE data. storageFlags = storageFlags | Installer.FLAG_STORAGE_CE; } } try { mInstaller.restoreAppDataSnapshot(packageName, appId, ceDataInode, seInfo, userId, storageFlags); } catch (InstallerException ie) { Log.e(TAG, "Unable to restore app data snapshot: " + packageName, ie); } return changedRollbackData; } /** * Computes the list of pending backups and restores for {@code userId} given lists of * available and recent rollbacks. Packages pending backup for the given user are added * to {@code pendingBackups} and packages pending restore are added to {@code pendingRestores} * along with their corresponding {@code RestoreInfo}. * * @return the list of {@code RollbackData} that have been modified during this computation. */ public List<RollbackData> computePendingBackupsAndRestores(int userId, ArrayList<String> pendingBackupPackages, Map<String, RestoreInfo> pendingRestores, List<RollbackData> availableRollbacks, List<RollbackInfo> recentRollbacks) { List<RollbackData> rd = new ArrayList<>(); // First check with the list of available rollbacks to see whether there are any // pending backup operations that we've not managed to execute. for (RollbackData data : availableRollbacks) { for (PackageRollbackInfo info : data.packages) { final IntArray pendingBackupUsers = info.getPendingBackups(); if (pendingBackupUsers != null) { final int idx = pendingBackupUsers.indexOf(userId); if (idx != -1) { pendingBackupPackages.add(info.getPackageName()); pendingBackupUsers.remove(idx); if (rd.indexOf(data) == -1) { rd.add(data); } } } } } // Then check with the list of recently executed rollbacks to see whether there are // any rollback operations for (RollbackInfo data : recentRollbacks) { for (PackageRollbackInfo info : data.getPackages()) { final RestoreInfo ri = info.getRestoreInfo(userId); if (ri != null) { if (pendingBackupPackages.contains(info.getPackageName())) { // This implies that the user hasn't unlocked their device between // the request to backup data for this user and the request to restore // it, so we do nothing here. pendingBackupPackages.remove(info.getPackageName()); } else { pendingRestores.put(info.getPackageName(), ri); } info.removeRestoreInfo(ri); } } } return rd; } /** * Commits the list of pending backups and restores for a given {@code userId}. */ public void commitPendingBackupAndRestoreForUser(int userId, ArrayList<String> pendingBackups, Map<String, RestoreInfo> pendingRestores) { if (!pendingBackups.isEmpty()) { for (String packageName : pendingBackups) { try { mInstaller.snapshotAppData(packageName, userId, Installer.FLAG_STORAGE_CE); } catch (InstallerException ie) { Log.e(TAG, "Unable to create app data snapshot for: " + packageName, ie); } } } // TODO(narayan): Should we perform the restore before the backup for packages that have // both backups and restores pending ? We could get into this case if we have a pending // restore from a rollback + a snapshot request from a new restore. if (!pendingRestores.isEmpty()) { for (String packageName : pendingRestores.keySet()) { try { final RestoreInfo ri = pendingRestores.get(packageName); // TODO(narayan): Verify that the user of "0" for ceDataInode is accurate // here. We know that the user has unlocked (and that their CE data is // available) so we shouldn't need to resort to the fallback path. mInstaller.restoreAppDataSnapshot(packageName, ri.appId, 0 /* ceDataInode */, ri.seInfo, userId, Installer.FLAG_STORAGE_CE); } catch (InstallerException ie) { Log.e(TAG, "Unable to restore app data snapshot for: " + packageName, ie); } } } } /** * @return {@code true} iff. {@code userId} is locked on an FBE device. */ @VisibleForTesting public boolean isUserCredentialLocked(int userId) { return StorageManager.isFileEncryptedNativeOrEmulated() && !StorageManager.isUserKeyUnlocked(userId); } } services/core/java/com/android/server/rollback/RollbackManagerService.java +5 −0 Original line number Diff line number Diff line Loading @@ -39,4 +39,9 @@ public final class RollbackManagerService extends SystemService { mService = new RollbackManagerServiceImpl(getContext()); publishBinderService(Context.ROLLBACK_SERVICE, mService); } @Override public void onUnlockUser(int user) { mService.onUnlockUser(user); } } services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java +84 −56 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ import android.content.pm.ParceledListSlice; import android.content.pm.VersionedPackage; import android.content.rollback.IRollbackManager; import android.content.rollback.PackageRollbackInfo; import android.content.rollback.PackageRollbackInfo.RestoreInfo; import android.content.rollback.RollbackInfo; import android.content.rollback.RollbackManager; import android.os.Binder; Loading @@ -39,14 +40,13 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.storage.StorageManager; import android.util.IntArray; import android.util.Log; import android.util.SparseBooleanArray; import com.android.internal.annotations.GuardedBy; import com.android.server.LocalServices; import com.android.server.pm.Installer; import com.android.server.pm.Installer.InstallerException; import com.android.server.pm.PackageManagerServiceUtils; import java.io.File; Loading Loading @@ -111,6 +111,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { private final HandlerThread mHandlerThread; private final Installer mInstaller; private final RollbackPackageHealthObserver mPackageHealthObserver; private final AppDataRollbackHelper mUserdataHelper; RollbackManagerServiceImpl(Context context) { mContext = context; Loading @@ -124,6 +125,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { mRollbackStore = new RollbackStore(new File(Environment.getDataDirectory(), "rollback")); mPackageHealthObserver = new RollbackPackageHealthObserver(mContext); mUserdataHelper = new AppDataRollbackHelper(mInstaller); // Kick off loading of the rollback data from strorage in a background // thread. Loading Loading @@ -424,6 +426,35 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { } } void onUnlockUser(int userId) { getHandler().post(() -> { final ArrayList<String> pendingBackupPackages = new ArrayList<>(); final Map<String, RestoreInfo> pendingRestorePackages = new HashMap<>(); final List<RollbackData> changed; synchronized (mLock) { ensureRollbackDataLoadedLocked(); changed = mUserdataHelper.computePendingBackupsAndRestores(userId, pendingBackupPackages, pendingRestorePackages, mAvailableRollbacks, mRecentlyExecutedRollbacks); } mUserdataHelper.commitPendingBackupAndRestoreForUser(userId, pendingBackupPackages, pendingRestorePackages); for (RollbackData rd : changed) { try { mRollbackStore.saveAvailableRollback(rd); } catch (IOException ioe) { Log.e(TAG, "Unable to save rollback info for : " + rd.rollbackId, ioe); } } synchronized (mLock) { mRollbackStore.saveRecentlyExecutedRollbacks(mRecentlyExecutedRollbacks); } }); } /** * Load rollback data from storage if it has not already been loaded. * After calling this funciton, mAvailableRollbacks and Loading Loading @@ -533,6 +564,20 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { // that are necessary to keep track of. synchronized (mLock) { ensureRollbackDataLoadedLocked(); // This should never happen because we can't have any pending backups left after // a rollback has been executed. See AppDataRollbackHelper#restoreAppData where we // clear all pending backups at the point of restore because they're guaranteed to be // no-ops. // // We may, however, have one or more pending restores left to handle. for (PackageRollbackInfo target : rollback.getPackages()) { if (target.getPendingBackups().size() > 0) { Log.e(TAG, "No backups allowed to be pending for: " + target); target.getPendingBackups().clear(); } } mRecentlyExecutedRollbacks.add(rollback); mRollbackStore.saveRecentlyExecutedRollbacks(mRecentlyExecutedRollbacks); } Loading Loading @@ -701,27 +746,12 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { VersionedPackage installedVersion = new VersionedPackage(packageName, installedPackage.getLongVersionCode()); for (int user : installedUsers) { final int storageFlags; if (StorageManager.isFileEncryptedNativeOrEmulated() && !StorageManager.isUserKeyUnlocked(user)) { // We've encountered a user that hasn't unlocked on a FBE device, so we can't copy // across app user data until the user unlocks their device. Log.e(TAG, "User: " + user + " isn't unlocked, skipping CE userdata backup."); storageFlags = Installer.FLAG_STORAGE_DE; } else { storageFlags = Installer.FLAG_STORAGE_CE | Installer.FLAG_STORAGE_DE; } try { mInstaller.snapshotAppData(packageName, user, storageFlags); } catch (InstallerException ie) { Log.e(TAG, "Unable to create app data snapshot for: " + packageName, ie); } } PackageRollbackInfo info = new PackageRollbackInfo(newVersion, installedVersion); final IntArray pendingBackups = mUserdataHelper.snapshotAppData(packageName, installedUsers); PackageRollbackInfo info = new PackageRollbackInfo(newVersion, installedVersion, pendingBackups, new ArrayList<>()); RollbackData data; try { synchronized (mLock) { Loading Loading @@ -760,40 +790,24 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { } getHandler().post(() -> { PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class); final RollbackData rollbackData = getRollbackForPackage(packageName); if (rollbackData == null) { pmi.finishPackageInstall(token, false); return; } if (!rollbackData.inProgress) { Log.e(TAG, "Request to restore userData for: " + packageName + ", but no rollback in progress."); final boolean changedRollbackData = mUserdataHelper.restoreAppData(packageName, rollbackData, userId, appId, ceDataInode, seInfo); final PackageManagerInternal pmi = LocalServices.getService( PackageManagerInternal.class); pmi.finishPackageInstall(token, false); return; } final int storageFlags; if (StorageManager.isFileEncryptedNativeOrEmulated() && !StorageManager.isUserKeyUnlocked(userId)) { // We've encountered a user that hasn't unlocked on a FBE device, so we can't copy // across app user data until the user unlocks their device. Log.e(TAG, "User: " + userId + " isn't unlocked, skipping CE userdata restore."); storageFlags = Installer.FLAG_STORAGE_DE; } else { storageFlags = Installer.FLAG_STORAGE_CE | Installer.FLAG_STORAGE_DE; } // We've updated metadata about this rollback, so save it to flash. if (changedRollbackData) { try { mInstaller.restoreAppDataSnapshot(packageName, appId, ceDataInode, seInfo, userId, storageFlags); } catch (InstallerException ie) { Log.e(TAG, "Unable to restore app data snapshot: " + packageName, ie); mRollbackStore.saveAvailableRollback(rollbackData); } catch (IOException ioe) { // TODO(narayan): What is the right thing to do here ? This isn't a fatal error, // since it will only result in us trying to restore data again, which will be // a no-op if there's no data available. Log.e(TAG, "Unable to save available rollback: " + packageName, ioe); } } pmi.finishPackageInstall(token, false); }); } Loading Loading @@ -900,13 +914,11 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { ensureRollbackDataLoadedLocked(); for (int i = 0; i < mAvailableRollbacks.size(); ++i) { RollbackData data = mAvailableRollbacks.get(i); for (PackageRollbackInfo info : data.packages) { if (info.getPackageName().equals(packageName)) { if (getPackageRollbackInfo(data, packageName) != null) { return data; } } } } return null; } Loading @@ -926,6 +938,22 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { } } } return null; } /** * Returns the {@code PackageRollbackInfo} associated with {@code packageName} from * a specified {@code RollbackData}. */ static PackageRollbackInfo getPackageRollbackInfo(RollbackData data, String packageName) { for (PackageRollbackInfo info : data.packages) { if (info.getPackageName().equals(packageName)) { return info; } } return null; } Loading services/core/java/com/android/server/rollback/RollbackStore.java +75 −1 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
core/java/android/content/rollback/PackageRollbackInfo.java +68 −1 Original line number Diff line number Diff line Loading @@ -16,10 +16,14 @@ package android.content.rollback; import android.annotation.NonNull; import android.annotation.SystemApi; import android.content.pm.VersionedPackage; import android.os.Parcel; import android.os.Parcelable; import android.util.IntArray; import java.util.ArrayList; /** * Information about a rollback available for a particular package. Loading @@ -32,6 +36,38 @@ public final class PackageRollbackInfo implements Parcelable { private final VersionedPackage mVersionRolledBackFrom; private final VersionedPackage mVersionRolledBackTo; /** * Encapsulates information required to restore a snapshot of an app's userdata. * * @hide */ public static class RestoreInfo { public final int userId; public final int appId; public final String seInfo; public RestoreInfo(int userId, int appId, String seInfo) { this.userId = userId; this.appId = appId; this.seInfo = seInfo; } } /* * The list of users for which we need to backup userdata for this package. Backups of * credential encrypted data are listed as pending if the user hasn't unlocked their device * with credentials yet. */ // NOTE: Not a part of the Parcelable representation of this object. private final IntArray mPendingBackups; /** * The list of users for which we need to restore userdata for this package. This field is * non-null only after a rollback for this package has been committed. */ // NOTE: Not a part of the Parcelable representation of this object. private final ArrayList<RestoreInfo> mPendingRestores; /** * Returns the name of the package to roll back from. */ Loading @@ -53,16 +89,47 @@ public final class PackageRollbackInfo implements Parcelable { return mVersionRolledBackTo; } /** @hide */ public IntArray getPendingBackups() { return mPendingBackups; } /** @hide */ public ArrayList<RestoreInfo> getPendingRestores() { return mPendingRestores; } /** @hide */ public RestoreInfo getRestoreInfo(int userId) { for (RestoreInfo ri : mPendingRestores) { if (ri.userId == userId) { return ri; } } return null; } /** @hide */ public void removeRestoreInfo(RestoreInfo ri) { mPendingRestores.remove(ri); } /** @hide */ public PackageRollbackInfo(VersionedPackage packageRolledBackFrom, VersionedPackage packageRolledBackTo) { VersionedPackage packageRolledBackTo, @NonNull IntArray pendingBackups, @NonNull ArrayList<RestoreInfo> pendingRestores) { this.mVersionRolledBackFrom = packageRolledBackFrom; this.mVersionRolledBackTo = packageRolledBackTo; this.mPendingBackups = pendingBackups; this.mPendingRestores = pendingRestores; } private PackageRollbackInfo(Parcel in) { this.mVersionRolledBackFrom = VersionedPackage.CREATOR.createFromParcel(in); this.mVersionRolledBackTo = VersionedPackage.CREATOR.createFromParcel(in); this.mPendingRestores = null; this.mPendingBackups = null; } @Override Loading
services/core/java/com/android/server/rollback/AppDataRollbackHelper.java 0 → 100644 +236 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 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.rollback; import android.content.rollback.PackageRollbackInfo; import android.content.rollback.PackageRollbackInfo.RestoreInfo; import android.content.rollback.RollbackInfo; import android.os.storage.StorageManager; import android.util.IntArray; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.server.pm.Installer; import com.android.server.pm.Installer.InstallerException; import java.util.ArrayList; import java.util.List; import java.util.Map; /** * Encapsulates the logic for initiating userdata snapshots and rollbacks via installd. */ @VisibleForTesting // TODO(narayan): Reason about the failure scenarios that involve one or more IPCs to installd // failing. We need to decide what course of action to take if calls to snapshotAppData or // restoreAppDataSnapshot fail. public class AppDataRollbackHelper { private static final String TAG = "RollbackManager"; private final Installer mInstaller; public AppDataRollbackHelper(Installer installer) { mInstaller = installer; } /** * Creates an app data snapshot for a specified {@code packageName} for {@code installedUsers}, * a specified set of users for whom the package is installed. * * @return a list of users for which the snapshot is pending, usually because data for one or * more users is still credential locked. */ public IntArray snapshotAppData(String packageName, int[] installedUsers) { final IntArray pendingBackups = new IntArray(); for (int user : installedUsers) { final int storageFlags; if (isUserCredentialLocked(user)) { // We've encountered a user that hasn't unlocked on a FBE device, so we can't copy // across app user data until the user unlocks their device. Log.v(TAG, "User: " + user + " isn't unlocked, skipping CE userdata backup."); storageFlags = Installer.FLAG_STORAGE_DE; pendingBackups.add(user); } else { storageFlags = Installer.FLAG_STORAGE_CE | Installer.FLAG_STORAGE_DE; } try { mInstaller.snapshotAppData(packageName, user, storageFlags); } catch (InstallerException ie) { Log.e(TAG, "Unable to create app data snapshot for: " + packageName + ", userId: " + user, ie); } } return pendingBackups; } /** * Restores an app data snapshot for a specified package ({@code packageName}, * {@code rollbackData}) for a specified {@code userId}. * * @return {@code true} iff. a change to the {@code rollbackData} has been made. Changes to * {@code rollbackData} are restricted to the removal or addition of {@code userId} to * the list of pending backups or restores. */ public boolean restoreAppData(String packageName, RollbackData rollbackData, int userId, int appId, long ceDataInode, String seInfo) { if (rollbackData == null) { return false; } if (!rollbackData.inProgress) { Log.e(TAG, "Request to restore userData for: " + packageName + ", but no rollback in progress."); return false; } PackageRollbackInfo packageInfo = RollbackManagerServiceImpl.getPackageRollbackInfo( rollbackData, packageName); int storageFlags = Installer.FLAG_STORAGE_DE; final IntArray pendingBackups = packageInfo.getPendingBackups(); final List<RestoreInfo> pendingRestores = packageInfo.getPendingRestores(); boolean changedRollbackData = false; // If we still have a userdata backup pending for this user, it implies that the user // hasn't unlocked their device between the point of backup and the point of restore, // so the data cannot have changed. We simply skip restoring CE data in this case. if (pendingBackups != null && pendingBackups.indexOf(userId) != -1) { pendingBackups.remove(pendingBackups.indexOf(userId)); changedRollbackData = true; } else { // There's no pending CE backup for this user, which means that we successfully // managed to backup data for the user, which means we seek to restore it if (isUserCredentialLocked(userId)) { // We've encountered a user that hasn't unlocked on a FBE device, so we can't // copy across app user data until the user unlocks their device. pendingRestores.add(new RestoreInfo(userId, appId, seInfo)); changedRollbackData = true; } else { // This user has unlocked, we can proceed to restore both CE and DE data. storageFlags = storageFlags | Installer.FLAG_STORAGE_CE; } } try { mInstaller.restoreAppDataSnapshot(packageName, appId, ceDataInode, seInfo, userId, storageFlags); } catch (InstallerException ie) { Log.e(TAG, "Unable to restore app data snapshot: " + packageName, ie); } return changedRollbackData; } /** * Computes the list of pending backups and restores for {@code userId} given lists of * available and recent rollbacks. Packages pending backup for the given user are added * to {@code pendingBackups} and packages pending restore are added to {@code pendingRestores} * along with their corresponding {@code RestoreInfo}. * * @return the list of {@code RollbackData} that have been modified during this computation. */ public List<RollbackData> computePendingBackupsAndRestores(int userId, ArrayList<String> pendingBackupPackages, Map<String, RestoreInfo> pendingRestores, List<RollbackData> availableRollbacks, List<RollbackInfo> recentRollbacks) { List<RollbackData> rd = new ArrayList<>(); // First check with the list of available rollbacks to see whether there are any // pending backup operations that we've not managed to execute. for (RollbackData data : availableRollbacks) { for (PackageRollbackInfo info : data.packages) { final IntArray pendingBackupUsers = info.getPendingBackups(); if (pendingBackupUsers != null) { final int idx = pendingBackupUsers.indexOf(userId); if (idx != -1) { pendingBackupPackages.add(info.getPackageName()); pendingBackupUsers.remove(idx); if (rd.indexOf(data) == -1) { rd.add(data); } } } } } // Then check with the list of recently executed rollbacks to see whether there are // any rollback operations for (RollbackInfo data : recentRollbacks) { for (PackageRollbackInfo info : data.getPackages()) { final RestoreInfo ri = info.getRestoreInfo(userId); if (ri != null) { if (pendingBackupPackages.contains(info.getPackageName())) { // This implies that the user hasn't unlocked their device between // the request to backup data for this user and the request to restore // it, so we do nothing here. pendingBackupPackages.remove(info.getPackageName()); } else { pendingRestores.put(info.getPackageName(), ri); } info.removeRestoreInfo(ri); } } } return rd; } /** * Commits the list of pending backups and restores for a given {@code userId}. */ public void commitPendingBackupAndRestoreForUser(int userId, ArrayList<String> pendingBackups, Map<String, RestoreInfo> pendingRestores) { if (!pendingBackups.isEmpty()) { for (String packageName : pendingBackups) { try { mInstaller.snapshotAppData(packageName, userId, Installer.FLAG_STORAGE_CE); } catch (InstallerException ie) { Log.e(TAG, "Unable to create app data snapshot for: " + packageName, ie); } } } // TODO(narayan): Should we perform the restore before the backup for packages that have // both backups and restores pending ? We could get into this case if we have a pending // restore from a rollback + a snapshot request from a new restore. if (!pendingRestores.isEmpty()) { for (String packageName : pendingRestores.keySet()) { try { final RestoreInfo ri = pendingRestores.get(packageName); // TODO(narayan): Verify that the user of "0" for ceDataInode is accurate // here. We know that the user has unlocked (and that their CE data is // available) so we shouldn't need to resort to the fallback path. mInstaller.restoreAppDataSnapshot(packageName, ri.appId, 0 /* ceDataInode */, ri.seInfo, userId, Installer.FLAG_STORAGE_CE); } catch (InstallerException ie) { Log.e(TAG, "Unable to restore app data snapshot for: " + packageName, ie); } } } } /** * @return {@code true} iff. {@code userId} is locked on an FBE device. */ @VisibleForTesting public boolean isUserCredentialLocked(int userId) { return StorageManager.isFileEncryptedNativeOrEmulated() && !StorageManager.isUserKeyUnlocked(userId); } }
services/core/java/com/android/server/rollback/RollbackManagerService.java +5 −0 Original line number Diff line number Diff line Loading @@ -39,4 +39,9 @@ public final class RollbackManagerService extends SystemService { mService = new RollbackManagerServiceImpl(getContext()); publishBinderService(Context.ROLLBACK_SERVICE, mService); } @Override public void onUnlockUser(int user) { mService.onUnlockUser(user); } }
services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java +84 −56 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ import android.content.pm.ParceledListSlice; import android.content.pm.VersionedPackage; import android.content.rollback.IRollbackManager; import android.content.rollback.PackageRollbackInfo; import android.content.rollback.PackageRollbackInfo.RestoreInfo; import android.content.rollback.RollbackInfo; import android.content.rollback.RollbackManager; import android.os.Binder; Loading @@ -39,14 +40,13 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.storage.StorageManager; import android.util.IntArray; import android.util.Log; import android.util.SparseBooleanArray; import com.android.internal.annotations.GuardedBy; import com.android.server.LocalServices; import com.android.server.pm.Installer; import com.android.server.pm.Installer.InstallerException; import com.android.server.pm.PackageManagerServiceUtils; import java.io.File; Loading Loading @@ -111,6 +111,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { private final HandlerThread mHandlerThread; private final Installer mInstaller; private final RollbackPackageHealthObserver mPackageHealthObserver; private final AppDataRollbackHelper mUserdataHelper; RollbackManagerServiceImpl(Context context) { mContext = context; Loading @@ -124,6 +125,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { mRollbackStore = new RollbackStore(new File(Environment.getDataDirectory(), "rollback")); mPackageHealthObserver = new RollbackPackageHealthObserver(mContext); mUserdataHelper = new AppDataRollbackHelper(mInstaller); // Kick off loading of the rollback data from strorage in a background // thread. Loading Loading @@ -424,6 +426,35 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { } } void onUnlockUser(int userId) { getHandler().post(() -> { final ArrayList<String> pendingBackupPackages = new ArrayList<>(); final Map<String, RestoreInfo> pendingRestorePackages = new HashMap<>(); final List<RollbackData> changed; synchronized (mLock) { ensureRollbackDataLoadedLocked(); changed = mUserdataHelper.computePendingBackupsAndRestores(userId, pendingBackupPackages, pendingRestorePackages, mAvailableRollbacks, mRecentlyExecutedRollbacks); } mUserdataHelper.commitPendingBackupAndRestoreForUser(userId, pendingBackupPackages, pendingRestorePackages); for (RollbackData rd : changed) { try { mRollbackStore.saveAvailableRollback(rd); } catch (IOException ioe) { Log.e(TAG, "Unable to save rollback info for : " + rd.rollbackId, ioe); } } synchronized (mLock) { mRollbackStore.saveRecentlyExecutedRollbacks(mRecentlyExecutedRollbacks); } }); } /** * Load rollback data from storage if it has not already been loaded. * After calling this funciton, mAvailableRollbacks and Loading Loading @@ -533,6 +564,20 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { // that are necessary to keep track of. synchronized (mLock) { ensureRollbackDataLoadedLocked(); // This should never happen because we can't have any pending backups left after // a rollback has been executed. See AppDataRollbackHelper#restoreAppData where we // clear all pending backups at the point of restore because they're guaranteed to be // no-ops. // // We may, however, have one or more pending restores left to handle. for (PackageRollbackInfo target : rollback.getPackages()) { if (target.getPendingBackups().size() > 0) { Log.e(TAG, "No backups allowed to be pending for: " + target); target.getPendingBackups().clear(); } } mRecentlyExecutedRollbacks.add(rollback); mRollbackStore.saveRecentlyExecutedRollbacks(mRecentlyExecutedRollbacks); } Loading Loading @@ -701,27 +746,12 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { VersionedPackage installedVersion = new VersionedPackage(packageName, installedPackage.getLongVersionCode()); for (int user : installedUsers) { final int storageFlags; if (StorageManager.isFileEncryptedNativeOrEmulated() && !StorageManager.isUserKeyUnlocked(user)) { // We've encountered a user that hasn't unlocked on a FBE device, so we can't copy // across app user data until the user unlocks their device. Log.e(TAG, "User: " + user + " isn't unlocked, skipping CE userdata backup."); storageFlags = Installer.FLAG_STORAGE_DE; } else { storageFlags = Installer.FLAG_STORAGE_CE | Installer.FLAG_STORAGE_DE; } try { mInstaller.snapshotAppData(packageName, user, storageFlags); } catch (InstallerException ie) { Log.e(TAG, "Unable to create app data snapshot for: " + packageName, ie); } } PackageRollbackInfo info = new PackageRollbackInfo(newVersion, installedVersion); final IntArray pendingBackups = mUserdataHelper.snapshotAppData(packageName, installedUsers); PackageRollbackInfo info = new PackageRollbackInfo(newVersion, installedVersion, pendingBackups, new ArrayList<>()); RollbackData data; try { synchronized (mLock) { Loading Loading @@ -760,40 +790,24 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { } getHandler().post(() -> { PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class); final RollbackData rollbackData = getRollbackForPackage(packageName); if (rollbackData == null) { pmi.finishPackageInstall(token, false); return; } if (!rollbackData.inProgress) { Log.e(TAG, "Request to restore userData for: " + packageName + ", but no rollback in progress."); final boolean changedRollbackData = mUserdataHelper.restoreAppData(packageName, rollbackData, userId, appId, ceDataInode, seInfo); final PackageManagerInternal pmi = LocalServices.getService( PackageManagerInternal.class); pmi.finishPackageInstall(token, false); return; } final int storageFlags; if (StorageManager.isFileEncryptedNativeOrEmulated() && !StorageManager.isUserKeyUnlocked(userId)) { // We've encountered a user that hasn't unlocked on a FBE device, so we can't copy // across app user data until the user unlocks their device. Log.e(TAG, "User: " + userId + " isn't unlocked, skipping CE userdata restore."); storageFlags = Installer.FLAG_STORAGE_DE; } else { storageFlags = Installer.FLAG_STORAGE_CE | Installer.FLAG_STORAGE_DE; } // We've updated metadata about this rollback, so save it to flash. if (changedRollbackData) { try { mInstaller.restoreAppDataSnapshot(packageName, appId, ceDataInode, seInfo, userId, storageFlags); } catch (InstallerException ie) { Log.e(TAG, "Unable to restore app data snapshot: " + packageName, ie); mRollbackStore.saveAvailableRollback(rollbackData); } catch (IOException ioe) { // TODO(narayan): What is the right thing to do here ? This isn't a fatal error, // since it will only result in us trying to restore data again, which will be // a no-op if there's no data available. Log.e(TAG, "Unable to save available rollback: " + packageName, ioe); } } pmi.finishPackageInstall(token, false); }); } Loading Loading @@ -900,13 +914,11 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { ensureRollbackDataLoadedLocked(); for (int i = 0; i < mAvailableRollbacks.size(); ++i) { RollbackData data = mAvailableRollbacks.get(i); for (PackageRollbackInfo info : data.packages) { if (info.getPackageName().equals(packageName)) { if (getPackageRollbackInfo(data, packageName) != null) { return data; } } } } return null; } Loading @@ -926,6 +938,22 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { } } } return null; } /** * Returns the {@code PackageRollbackInfo} associated with {@code packageName} from * a specified {@code RollbackData}. */ static PackageRollbackInfo getPackageRollbackInfo(RollbackData data, String packageName) { for (PackageRollbackInfo info : data.packages) { if (info.getPackageName().equals(packageName)) { return info; } } return null; } Loading
services/core/java/com/android/server/rollback/RollbackStore.java +75 −1 File changed.Preview size limit exceeded, changes collapsed. Show changes