blob: e6e6e23bf7141c19259546729a9d9237944af75a [file] [log] [blame]
Narayan Kamathc034fe92019-01-23 10:48:17 +00001/*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.rollback;
18
JW Wang82768bf2019-12-11 10:05:32 +080019import android.content.pm.PackageManager;
Narayan Kamathc034fe92019-01-23 10:48:17 +000020import android.content.rollback.PackageRollbackInfo;
21import android.content.rollback.PackageRollbackInfo.RestoreInfo;
Narayan Kamathc034fe92019-01-23 10:48:17 +000022import android.os.storage.StorageManager;
23import android.util.IntArray;
shafikbb771fb2019-06-05 14:59:57 +010024import android.util.Slog;
Nikita Ioffe952aa7b2019-01-28 19:49:56 +000025import android.util.SparseLongArray;
Narayan Kamathc034fe92019-01-23 10:48:17 +000026
Oli Lan8c0084d2019-09-11 13:38:43 +010027import com.android.internal.annotations.GuardedBy;
Narayan Kamathc034fe92019-01-23 10:48:17 +000028import com.android.internal.annotations.VisibleForTesting;
29import com.android.server.pm.Installer;
30import com.android.server.pm.Installer.InstallerException;
31
Narayan Kamathc034fe92019-01-23 10:48:17 +000032import java.util.List;
Narayan Kamathc034fe92019-01-23 10:48:17 +000033
34/**
35 * Encapsulates the logic for initiating userdata snapshots and rollbacks via installd.
36 */
37@VisibleForTesting
38// TODO(narayan): Reason about the failure scenarios that involve one or more IPCs to installd
39// failing. We need to decide what course of action to take if calls to snapshotAppData or
40// restoreAppDataSnapshot fail.
41public class AppDataRollbackHelper {
42 private static final String TAG = "RollbackManager";
43
44 private final Installer mInstaller;
45
46 public AppDataRollbackHelper(Installer installer) {
47 mInstaller = installer;
48 }
49
50 /**
Oli Lan48f3cf42019-08-09 10:25:49 +010051 * Creates an app data snapshot for a specified {@code packageRollbackInfo} and the specified
52 * {@code userIds}. Updates said {@code packageRollbackInfo} with the inodes of the CE user data
53 * snapshot folders.
Narayan Kamathc034fe92019-01-23 10:48:17 +000054 */
Oli Lan4af2f752019-09-27 10:39:32 +010055 @GuardedBy("rollback.mLock")
Oli Lan8c0084d2019-09-11 13:38:43 +010056 // TODO(b/136241838): Move into Rollback and synchronize there.
Oli Lan48f3cf42019-08-09 10:25:49 +010057 public void snapshotAppData(
58 int snapshotId, PackageRollbackInfo packageRollbackInfo, int[] userIds) {
59 for (int user : userIds) {
Narayan Kamathc034fe92019-01-23 10:48:17 +000060 final int storageFlags;
61 if (isUserCredentialLocked(user)) {
62 // We've encountered a user that hasn't unlocked on a FBE device, so we can't copy
63 // across app user data until the user unlocks their device.
shafikbb771fb2019-06-05 14:59:57 +010064 Slog.v(TAG, "User: " + user + " isn't unlocked, skipping CE userdata backup.");
Narayan Kamathc034fe92019-01-23 10:48:17 +000065 storageFlags = Installer.FLAG_STORAGE_DE;
Nikita Ioffe5dcd17972019-02-04 11:08:13 +000066 packageRollbackInfo.addPendingBackup(user);
Narayan Kamathc034fe92019-01-23 10:48:17 +000067 } else {
68 storageFlags = Installer.FLAG_STORAGE_CE | Installer.FLAG_STORAGE_DE;
69 }
70
71 try {
Nikita Ioffe5dcd17972019-02-04 11:08:13 +000072 long ceSnapshotInode = mInstaller.snapshotAppData(
73 packageRollbackInfo.getPackageName(), user, snapshotId, storageFlags);
Nikita Ioffe952aa7b2019-01-28 19:49:56 +000074 if ((storageFlags & Installer.FLAG_STORAGE_CE) != 0) {
Nikita Ioffe5dcd17972019-02-04 11:08:13 +000075 packageRollbackInfo.putCeSnapshotInode(user, ceSnapshotInode);
Nikita Ioffe952aa7b2019-01-28 19:49:56 +000076 }
Narayan Kamathc034fe92019-01-23 10:48:17 +000077 } catch (InstallerException ie) {
shafikbb771fb2019-06-05 14:59:57 +010078 Slog.e(TAG, "Unable to create app data snapshot for: "
Nikita Ioffe5dcd17972019-02-04 11:08:13 +000079 + packageRollbackInfo.getPackageName() + ", userId: " + user, ie);
Narayan Kamathc034fe92019-01-23 10:48:17 +000080 }
81 }
Narayan Kamathc034fe92019-01-23 10:48:17 +000082 }
83
84 /**
Nikita Ioffe5dcd17972019-02-04 11:08:13 +000085 * Restores an app data snapshot for a specified {@code packageRollbackInfo}, for a specified
86 * {@code userId}.
Narayan Kamathc034fe92019-01-23 10:48:17 +000087 *
Nikita Ioffe5dcd17972019-02-04 11:08:13 +000088 * @return {@code true} iff. a change to the {@code packageRollbackInfo} has been made. Changes
89 * to {@code packageRollbackInfo} are restricted to the removal or addition of {@code
90 * userId} to the list of pending backups or restores.
Narayan Kamathc034fe92019-01-23 10:48:17 +000091 */
Oli Lan4af2f752019-09-27 10:39:32 +010092 @GuardedBy("rollback.mLock")
Oli Lan8c0084d2019-09-11 13:38:43 +010093 // TODO(b/136241838): Move into Rollback and synchronize there.
Nikita Ioffe5dcd17972019-02-04 11:08:13 +000094 public boolean restoreAppData(int rollbackId, PackageRollbackInfo packageRollbackInfo,
95 int userId, int appId, String seInfo) {
Narayan Kamathc034fe92019-01-23 10:48:17 +000096 int storageFlags = Installer.FLAG_STORAGE_DE;
97
Nikita Ioffe5dcd17972019-02-04 11:08:13 +000098 final IntArray pendingBackups = packageRollbackInfo.getPendingBackups();
99 final List<RestoreInfo> pendingRestores = packageRollbackInfo.getPendingRestores();
Richard Uhler8d512ee2019-07-19 14:55:01 +0100100 boolean changedRollback = false;
Narayan Kamathc034fe92019-01-23 10:48:17 +0000101
102 // If we still have a userdata backup pending for this user, it implies that the user
103 // hasn't unlocked their device between the point of backup and the point of restore,
104 // so the data cannot have changed. We simply skip restoring CE data in this case.
105 if (pendingBackups != null && pendingBackups.indexOf(userId) != -1) {
106 pendingBackups.remove(pendingBackups.indexOf(userId));
Richard Uhler8d512ee2019-07-19 14:55:01 +0100107 changedRollback = true;
Narayan Kamathc034fe92019-01-23 10:48:17 +0000108 } else {
109 // There's no pending CE backup for this user, which means that we successfully
110 // managed to backup data for the user, which means we seek to restore it
111 if (isUserCredentialLocked(userId)) {
112 // We've encountered a user that hasn't unlocked on a FBE device, so we can't
113 // copy across app user data until the user unlocks their device.
114 pendingRestores.add(new RestoreInfo(userId, appId, seInfo));
Richard Uhler8d512ee2019-07-19 14:55:01 +0100115 changedRollback = true;
Narayan Kamathc034fe92019-01-23 10:48:17 +0000116 } else {
117 // This user has unlocked, we can proceed to restore both CE and DE data.
118 storageFlags = storageFlags | Installer.FLAG_STORAGE_CE;
119 }
120 }
121
122 try {
JW Wang82768bf2019-12-11 10:05:32 +0800123 switch (packageRollbackInfo.getRollbackDataPolicy()) {
124 case PackageManager.RollbackDataPolicy.WIPE:
125 mInstaller.clearAppData(null, packageRollbackInfo.getPackageName(),
126 userId, storageFlags, 0);
127 break;
128 case PackageManager.RollbackDataPolicy.RESTORE:
129 mInstaller.restoreAppDataSnapshot(packageRollbackInfo.getPackageName(), appId,
130 seInfo, userId, rollbackId, storageFlags);
131 break;
132 default:
133 break;
134 }
Narayan Kamathc034fe92019-01-23 10:48:17 +0000135 } catch (InstallerException ie) {
JW Wang82768bf2019-12-11 10:05:32 +0800136 Slog.e(TAG, "Unable to restore/wipe app data: "
137 + packageRollbackInfo.getPackageName() + " policy="
138 + packageRollbackInfo.getRollbackDataPolicy(), ie);
Narayan Kamathc034fe92019-01-23 10:48:17 +0000139 }
140
Richard Uhler8d512ee2019-07-19 14:55:01 +0100141 return changedRollback;
Narayan Kamathc034fe92019-01-23 10:48:17 +0000142 }
143
144 /**
Nikita Ioffe5dcd17972019-02-04 11:08:13 +0000145 * Deletes an app data snapshot with a given {@code rollbackId} for a specified package
146 * {@code packageName} for a given {@code user}.
Nikita Ioffe952aa7b2019-01-28 19:49:56 +0000147 */
Oli Lan4af2f752019-09-27 10:39:32 +0100148 @GuardedBy("rollback.mLock")
Oli Lan8c0084d2019-09-11 13:38:43 +0100149 // TODO(b/136241838): Move into Rollback and synchronize there.
Nikita Ioffe5dcd17972019-02-04 11:08:13 +0000150 public void destroyAppDataSnapshot(int rollbackId, PackageRollbackInfo packageRollbackInfo,
151 int user) {
Nikita Ioffe952aa7b2019-01-28 19:49:56 +0000152 int storageFlags = Installer.FLAG_STORAGE_DE;
Nikita Ioffe5dcd17972019-02-04 11:08:13 +0000153 final SparseLongArray ceSnapshotInodes = packageRollbackInfo.getCeSnapshotInodes();
154 long ceSnapshotInode = ceSnapshotInodes.get(user);
Nikita Ioffe952aa7b2019-01-28 19:49:56 +0000155 if (ceSnapshotInode > 0) {
156 storageFlags |= Installer.FLAG_STORAGE_CE;
157 }
158 try {
Nikita Ioffe5dcd17972019-02-04 11:08:13 +0000159 mInstaller.destroyAppDataSnapshot(packageRollbackInfo.getPackageName(), user,
160 ceSnapshotInode, rollbackId, storageFlags);
161 if ((storageFlags & Installer.FLAG_STORAGE_CE) != 0) {
162 ceSnapshotInodes.delete(user);
163 }
Nikita Ioffe952aa7b2019-01-28 19:49:56 +0000164 } catch (InstallerException ie) {
shafikbb771fb2019-06-05 14:59:57 +0100165 Slog.e(TAG, "Unable to delete app data snapshot for "
Nikita Ioffe5dcd17972019-02-04 11:08:13 +0000166 + packageRollbackInfo.getPackageName(), ie);
Nikita Ioffe952aa7b2019-01-28 19:49:56 +0000167 }
168 }
169
170 /**
Oli Lan31f453f2019-09-11 09:58:39 +0100171 * Commits the pending backups and restores for a given {@code userId} and {@code rollback}. If
172 * the rollback has a pending backup, it is updated with a mapping from {@code userId} to inode
173 * of the CE user data snapshot.
Narayan Kamathc034fe92019-01-23 10:48:17 +0000174 *
Oli Lan31f453f2019-09-11 09:58:39 +0100175 * @return true if any backups or restores were found for the userId
Narayan Kamathc034fe92019-01-23 10:48:17 +0000176 */
Oli Lan4af2f752019-09-27 10:39:32 +0100177 @GuardedBy("rollback.mLock")
Oli Lan31f453f2019-09-11 09:58:39 +0100178 boolean commitPendingBackupAndRestoreForUser(int userId, Rollback rollback) {
179 boolean foundBackupOrRestore = false;
180 for (PackageRollbackInfo info : rollback.info.getPackages()) {
181 boolean hasPendingBackup = false;
182 boolean hasPendingRestore = false;
183 final IntArray pendingBackupUsers = info.getPendingBackups();
184 if (pendingBackupUsers != null) {
185 if (pendingBackupUsers.indexOf(userId) != -1) {
186 hasPendingBackup = true;
187 foundBackupOrRestore = true;
Narayan Kamathc034fe92019-01-23 10:48:17 +0000188 }
189 }
Narayan Kamathc034fe92019-01-23 10:48:17 +0000190
Oli Lan31f453f2019-09-11 09:58:39 +0100191 RestoreInfo ri = info.getRestoreInfo(userId);
192 if (ri != null) {
193 hasPendingRestore = true;
194 foundBackupOrRestore = true;
Narayan Kamathc034fe92019-01-23 10:48:17 +0000195 }
Narayan Kamathc034fe92019-01-23 10:48:17 +0000196
Oli Lan31f453f2019-09-11 09:58:39 +0100197 if (hasPendingBackup && hasPendingRestore) {
198 // Remove unnecessary backup, i.e. when user did not unlock their phone between the
199 // request to backup data and the request to restore it.
200 info.removePendingBackup(userId);
201 info.removePendingRestoreInfo(userId);
202 continue;
Nikita Ioffe5dcd17972019-02-04 11:08:13 +0000203 }
Nikita Ioffe5dcd17972019-02-04 11:08:13 +0000204
Oli Lan31f453f2019-09-11 09:58:39 +0100205 if (hasPendingBackup) {
206 int idx = pendingBackupUsers.indexOf(userId);
207 try {
208 long ceSnapshotInode = mInstaller.snapshotAppData(info.getPackageName(),
209 userId, rollback.info.getRollbackId(),
210 Installer.FLAG_STORAGE_CE);
211 info.putCeSnapshotInode(userId, ceSnapshotInode);
212 pendingBackupUsers.remove(idx);
213 } catch (InstallerException ie) {
214 Slog.e(TAG,
215 "Unable to create app data snapshot for: "
Nikita Ioffe5dcd17972019-02-04 11:08:13 +0000216 + info.getPackageName() + ", userId: " + userId, ie);
Oli Lan31f453f2019-09-11 09:58:39 +0100217 }
218 }
219
220 if (hasPendingRestore) {
221 try {
JW Wang82768bf2019-12-11 10:05:32 +0800222 switch (info.getRollbackDataPolicy()) {
223 case PackageManager.RollbackDataPolicy.WIPE:
224 mInstaller.clearAppData(null, info.getPackageName(), userId,
225 Installer.FLAG_STORAGE_CE, 0);
226 break;
227 case PackageManager.RollbackDataPolicy.RESTORE:
228 mInstaller.restoreAppDataSnapshot(info.getPackageName(), ri.appId,
229 ri.seInfo, userId, rollback.info.getRollbackId(),
230 Installer.FLAG_STORAGE_CE);
231 break;
232 default:
233 break;
234 }
Oli Lan31f453f2019-09-11 09:58:39 +0100235 info.removeRestoreInfo(ri);
236 } catch (InstallerException ie) {
JW Wang82768bf2019-12-11 10:05:32 +0800237 Slog.e(TAG, "Unable to restore/wipe app data for: "
238 + info.getPackageName() + " policy="
239 + info.getRollbackDataPolicy(), ie);
Narayan Kamathc034fe92019-01-23 10:48:17 +0000240 }
241 }
242 }
Oli Lan31f453f2019-09-11 09:58:39 +0100243 return foundBackupOrRestore;
Narayan Kamathc034fe92019-01-23 10:48:17 +0000244 }
245
246 /**
247 * @return {@code true} iff. {@code userId} is locked on an FBE device.
248 */
249 @VisibleForTesting
250 public boolean isUserCredentialLocked(int userId) {
251 return StorageManager.isFileEncryptedNativeOrEmulated()
252 && !StorageManager.isUserKeyUnlocked(userId);
253 }
Narayan Kamathc034fe92019-01-23 10:48:17 +0000254}