blob: e9ccea54fe99ae5ab56e983a2afee0cd84f50a12 [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
19import android.content.rollback.PackageRollbackInfo;
20import android.content.rollback.PackageRollbackInfo.RestoreInfo;
21import android.content.rollback.RollbackInfo;
22import android.os.storage.StorageManager;
23import android.util.IntArray;
24import android.util.Log;
Nikita Ioffe952aa7b2019-01-28 19:49:56 +000025import android.util.SparseLongArray;
Narayan Kamathc034fe92019-01-23 10:48:17 +000026
27import com.android.internal.annotations.VisibleForTesting;
28import com.android.server.pm.Installer;
29import com.android.server.pm.Installer.InstallerException;
30
31import java.util.ArrayList;
Nikita Ioffe5dcd17972019-02-04 11:08:13 +000032import java.util.HashMap;
33import java.util.Iterator;
Narayan Kamathc034fe92019-01-23 10:48:17 +000034import java.util.List;
35import java.util.Map;
36
37/**
38 * Encapsulates the logic for initiating userdata snapshots and rollbacks via installd.
39 */
40@VisibleForTesting
41// TODO(narayan): Reason about the failure scenarios that involve one or more IPCs to installd
42// failing. We need to decide what course of action to take if calls to snapshotAppData or
43// restoreAppDataSnapshot fail.
44public class AppDataRollbackHelper {
45 private static final String TAG = "RollbackManager";
46
47 private final Installer mInstaller;
48
49 public AppDataRollbackHelper(Installer installer) {
50 mInstaller = installer;
51 }
52
53 /**
Nikita Ioffe5dcd17972019-02-04 11:08:13 +000054 * Creates an app data snapshot for a specified {@code packageRollbackInfo}. Updates said {@code
55 * packageRollbackInfo} with the inodes of the CE user data snapshot folders.
Narayan Kamathc034fe92019-01-23 10:48:17 +000056 */
Nikita Ioffe5dcd17972019-02-04 11:08:13 +000057 public void snapshotAppData(int snapshotId, PackageRollbackInfo packageRollbackInfo) {
58 final int[] installedUsers = packageRollbackInfo.getInstalledUsers().toArray();
Narayan Kamathc034fe92019-01-23 10:48:17 +000059 for (int user : installedUsers) {
60 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.
64 Log.v(TAG, "User: " + user + " isn't unlocked, skipping CE userdata backup.");
65 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) {
Nikita Ioffe5dcd17972019-02-04 11:08:13 +000078 Log.e(TAG, "Unable to create app data snapshot for: "
79 + 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 */
Nikita Ioffe5dcd17972019-02-04 11:08:13 +000092 public boolean restoreAppData(int rollbackId, PackageRollbackInfo packageRollbackInfo,
93 int userId, int appId, String seInfo) {
Narayan Kamathc034fe92019-01-23 10:48:17 +000094 int storageFlags = Installer.FLAG_STORAGE_DE;
95
Nikita Ioffe5dcd17972019-02-04 11:08:13 +000096 final IntArray pendingBackups = packageRollbackInfo.getPendingBackups();
97 final List<RestoreInfo> pendingRestores = packageRollbackInfo.getPendingRestores();
Narayan Kamathc034fe92019-01-23 10:48:17 +000098 boolean changedRollbackData = false;
99
100 // If we still have a userdata backup pending for this user, it implies that the user
101 // hasn't unlocked their device between the point of backup and the point of restore,
102 // so the data cannot have changed. We simply skip restoring CE data in this case.
103 if (pendingBackups != null && pendingBackups.indexOf(userId) != -1) {
104 pendingBackups.remove(pendingBackups.indexOf(userId));
105 changedRollbackData = true;
106 } else {
107 // There's no pending CE backup for this user, which means that we successfully
108 // managed to backup data for the user, which means we seek to restore it
109 if (isUserCredentialLocked(userId)) {
110 // We've encountered a user that hasn't unlocked on a FBE device, so we can't
111 // copy across app user data until the user unlocks their device.
112 pendingRestores.add(new RestoreInfo(userId, appId, seInfo));
113 changedRollbackData = true;
114 } else {
115 // This user has unlocked, we can proceed to restore both CE and DE data.
116 storageFlags = storageFlags | Installer.FLAG_STORAGE_CE;
117 }
118 }
119
120 try {
Nikita Ioffe5dcd17972019-02-04 11:08:13 +0000121 mInstaller.restoreAppDataSnapshot(packageRollbackInfo.getPackageName(), appId, seInfo,
122 userId, rollbackId, storageFlags);
Narayan Kamathc034fe92019-01-23 10:48:17 +0000123 } catch (InstallerException ie) {
Nikita Ioffe5dcd17972019-02-04 11:08:13 +0000124 Log.e(TAG, "Unable to restore app data snapshot: "
125 + packageRollbackInfo.getPackageName(), ie);
Narayan Kamathc034fe92019-01-23 10:48:17 +0000126 }
127
128 return changedRollbackData;
129 }
130
131 /**
Nikita Ioffe5dcd17972019-02-04 11:08:13 +0000132 * Deletes an app data snapshot with a given {@code rollbackId} for a specified package
133 * {@code packageName} for a given {@code user}.
Nikita Ioffe952aa7b2019-01-28 19:49:56 +0000134 */
Nikita Ioffe5dcd17972019-02-04 11:08:13 +0000135 public void destroyAppDataSnapshot(int rollbackId, PackageRollbackInfo packageRollbackInfo,
136 int user) {
Nikita Ioffe952aa7b2019-01-28 19:49:56 +0000137 int storageFlags = Installer.FLAG_STORAGE_DE;
Nikita Ioffe5dcd17972019-02-04 11:08:13 +0000138 final SparseLongArray ceSnapshotInodes = packageRollbackInfo.getCeSnapshotInodes();
139 long ceSnapshotInode = ceSnapshotInodes.get(user);
Nikita Ioffe952aa7b2019-01-28 19:49:56 +0000140 if (ceSnapshotInode > 0) {
141 storageFlags |= Installer.FLAG_STORAGE_CE;
142 }
143 try {
Nikita Ioffe5dcd17972019-02-04 11:08:13 +0000144 mInstaller.destroyAppDataSnapshot(packageRollbackInfo.getPackageName(), user,
145 ceSnapshotInode, rollbackId, storageFlags);
146 if ((storageFlags & Installer.FLAG_STORAGE_CE) != 0) {
147 ceSnapshotInodes.delete(user);
148 }
Nikita Ioffe952aa7b2019-01-28 19:49:56 +0000149 } catch (InstallerException ie) {
Nikita Ioffe5dcd17972019-02-04 11:08:13 +0000150 Log.e(TAG, "Unable to delete app data snapshot for "
151 + packageRollbackInfo.getPackageName(), ie);
Nikita Ioffe952aa7b2019-01-28 19:49:56 +0000152 }
153 }
154
155 /**
Nikita Ioffe5dcd17972019-02-04 11:08:13 +0000156 * Computes the list of pending backups for {@code userId} given lists of available rollbacks.
157 * Packages pending backup for the given user are added to {@code pendingBackupPackages} along
158 * with their corresponding {@code PackageRollbackInfo}.
Narayan Kamathc034fe92019-01-23 10:48:17 +0000159 *
Nikita Ioffe5dcd17972019-02-04 11:08:13 +0000160 * @return the list of {@code RollbackData} that has pending backups. Note that some of the
161 * backups won't be performed, because they might be counteracted by pending restores.
Narayan Kamathc034fe92019-01-23 10:48:17 +0000162 */
Nikita Ioffe5dcd17972019-02-04 11:08:13 +0000163 private static List<RollbackData> computePendingBackups(int userId,
164 Map<String, PackageRollbackInfo> pendingBackupPackages,
165 List<RollbackData> availableRollbacks) {
Narayan Kamathc034fe92019-01-23 10:48:17 +0000166 List<RollbackData> rd = new ArrayList<>();
Nikita Ioffe5dcd17972019-02-04 11:08:13 +0000167
Narayan Kamathc034fe92019-01-23 10:48:17 +0000168 for (RollbackData data : availableRollbacks) {
169 for (PackageRollbackInfo info : data.packages) {
170 final IntArray pendingBackupUsers = info.getPendingBackups();
171 if (pendingBackupUsers != null) {
172 final int idx = pendingBackupUsers.indexOf(userId);
173 if (idx != -1) {
Nikita Ioffe5dcd17972019-02-04 11:08:13 +0000174 pendingBackupPackages.put(info.getPackageName(), info);
Narayan Kamathc034fe92019-01-23 10:48:17 +0000175 if (rd.indexOf(data) == -1) {
176 rd.add(data);
177 }
178 }
179 }
180 }
181 }
Nikita Ioffe5dcd17972019-02-04 11:08:13 +0000182 return rd;
183 }
Narayan Kamathc034fe92019-01-23 10:48:17 +0000184
Nikita Ioffe5dcd17972019-02-04 11:08:13 +0000185 /**
186 * Computes the list of pending restores for {@code userId} given lists of recent rollbacks.
187 * Packages pending restore are added to {@code pendingRestores} along with their corresponding
188 * {@code PackageRollbackInfo}.
189 *
190 * @return the list of {@code RollbackInfo} that has pending restores. Note that some of the
191 * restores won't be performed, because they might be counteracted by pending backups.
192 */
193 private static List<RollbackInfo> computePendingRestores(int userId,
194 Map<String, PackageRollbackInfo> pendingRestorePackages,
195 List<RollbackInfo> recentRollbacks) {
196 List<RollbackInfo> rd = new ArrayList<>();
197
Narayan Kamathc034fe92019-01-23 10:48:17 +0000198 for (RollbackInfo data : recentRollbacks) {
199 for (PackageRollbackInfo info : data.getPackages()) {
200 final RestoreInfo ri = info.getRestoreInfo(userId);
201 if (ri != null) {
Nikita Ioffe5dcd17972019-02-04 11:08:13 +0000202 pendingRestorePackages.put(info.getPackageName(), info);
203 if (rd.indexOf(data) == -1) {
204 rd.add(data);
Narayan Kamathc034fe92019-01-23 10:48:17 +0000205 }
Narayan Kamathc034fe92019-01-23 10:48:17 +0000206 }
207 }
208 }
209
210 return rd;
211 }
212
213 /**
Nikita Ioffe952aa7b2019-01-28 19:49:56 +0000214 * Commits the list of pending backups and restores for a given {@code userId}. For the pending
215 * backups updates corresponding {@code changedRollbackData} with a mapping from {@code userId}
216 * to a inode of theirs CE user data snapshot.
Nikita Ioffe5dcd17972019-02-04 11:08:13 +0000217 *
218 * @return a list {@code RollbackData} that have been changed and should be stored on disk.
Narayan Kamathc034fe92019-01-23 10:48:17 +0000219 */
Nikita Ioffe5dcd17972019-02-04 11:08:13 +0000220 public List<RollbackData> commitPendingBackupAndRestoreForUser(int userId,
221 List<RollbackData> availableRollbacks, List<RollbackInfo> recentlyExecutedRollbacks) {
222
223 final Map<String, PackageRollbackInfo> pendingBackupPackages = new HashMap<>();
224 final List<RollbackData> pendingBackups = computePendingBackups(userId,
225 pendingBackupPackages, availableRollbacks);
226
227 final Map<String, PackageRollbackInfo> pendingRestorePackages = new HashMap<>();
228 final List<RollbackInfo> pendingRestores = computePendingRestores(userId,
229 pendingRestorePackages, recentlyExecutedRollbacks);
230
231 // First remove unnecessary backups, i.e. when user did not unlock their phone between the
232 // request to backup data and the request to restore it.
233 Iterator<Map.Entry<String, PackageRollbackInfo>> iter =
234 pendingBackupPackages.entrySet().iterator();
235 while (iter.hasNext()) {
236 PackageRollbackInfo backupPackage = iter.next().getValue();
237 PackageRollbackInfo restorePackage =
238 pendingRestorePackages.get(backupPackage.getPackageName());
239 if (restorePackage != null) {
240 backupPackage.removePendingBackup(userId);
241 backupPackage.removePendingRestoreInfo(userId);
242 iter.remove();
243 pendingRestorePackages.remove(backupPackage.getPackageName());
244 }
245 }
246
247 if (!pendingBackupPackages.isEmpty()) {
248 for (RollbackData data : pendingBackups) {
249 for (PackageRollbackInfo info : data.packages) {
250 final IntArray pendingBackupUsers = info.getPendingBackups();
251 final int idx = pendingBackupUsers.indexOf(userId);
252 if (idx != -1) {
253 try {
254 long ceSnapshotInode = mInstaller.snapshotAppData(info.getPackageName(),
255 userId, data.rollbackId, Installer.FLAG_STORAGE_CE);
256 info.putCeSnapshotInode(userId, ceSnapshotInode);
257 pendingBackupUsers.remove(idx);
258 } catch (InstallerException ie) {
259 Log.e(TAG,
260 "Unable to create app data snapshot for: "
261 + info.getPackageName() + ", userId: " + userId, ie);
Nikita Ioffe952aa7b2019-01-28 19:49:56 +0000262 }
263 }
Narayan Kamathc034fe92019-01-23 10:48:17 +0000264 }
265 }
266 }
267
Nikita Ioffe5dcd17972019-02-04 11:08:13 +0000268 if (!pendingRestorePackages.isEmpty()) {
269 for (RollbackInfo data : pendingRestores) {
270 for (PackageRollbackInfo info : data.getPackages()) {
271 final RestoreInfo ri = info.getRestoreInfo(userId);
272 if (ri != null) {
273 try {
274 mInstaller.restoreAppDataSnapshot(info.getPackageName(), ri.appId,
275 ri.seInfo, userId, data.getRollbackId(),
276 Installer.FLAG_STORAGE_CE);
277 info.removeRestoreInfo(ri);
278 } catch (InstallerException ie) {
279 Log.e(TAG, "Unable to restore app data snapshot for: "
280 + info.getPackageName(), ie);
281 }
282 }
Narayan Kamathc034fe92019-01-23 10:48:17 +0000283 }
284 }
285 }
Nikita Ioffe5dcd17972019-02-04 11:08:13 +0000286
287 return pendingBackups;
Narayan Kamathc034fe92019-01-23 10:48:17 +0000288 }
289
290 /**
291 * @return {@code true} iff. {@code userId} is locked on an FBE device.
292 */
293 @VisibleForTesting
294 public boolean isUserCredentialLocked(int userId) {
295 return StorageManager.isFileEncryptedNativeOrEmulated()
296 && !StorageManager.isUserKeyUnlocked(userId);
297 }
Narayan Kamathc034fe92019-01-23 10:48:17 +0000298}