blob: 88a5fb48ada49d6b8291efa6abd72474f0565955 [file] [log] [blame]
Richard Uhlere95d0552018-12-27 15:03:41 +00001/*
2 * Copyright (C) 2018 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
Richard Uhler2a5facc2019-02-18 11:33:03 +000019import android.annotation.NonNull;
Richard Uhlere95d0552018-12-27 15:03:41 +000020import android.app.AppOpsManager;
21import android.content.BroadcastReceiver;
22import android.content.Context;
Richard Uhlere95d0552018-12-27 15:03:41 +000023import android.content.Intent;
24import android.content.IntentFilter;
25import android.content.IntentSender;
Richard Uhlerab009ea2019-02-25 12:11:05 +000026import android.content.pm.ApplicationInfo;
Richard Uhlere95d0552018-12-27 15:03:41 +000027import android.content.pm.PackageInfo;
28import android.content.pm.PackageInstaller;
29import android.content.pm.PackageManager;
30import android.content.pm.PackageManagerInternal;
31import android.content.pm.PackageParser;
32import android.content.pm.ParceledListSlice;
Richard Uhlera7e9b2d2019-01-22 17:20:58 +000033import android.content.pm.VersionedPackage;
Richard Uhlere95d0552018-12-27 15:03:41 +000034import android.content.rollback.IRollbackManager;
35import android.content.rollback.PackageRollbackInfo;
36import android.content.rollback.RollbackInfo;
Richard Uhler2a48c292019-01-28 17:33:48 +000037import android.content.rollback.RollbackManager;
Richard Uhlere95d0552018-12-27 15:03:41 +000038import android.os.Binder;
Richard Uhlere95d0552018-12-27 15:03:41 +000039import android.os.Environment;
40import android.os.Handler;
41import android.os.HandlerThread;
Richard Uhlere95d0552018-12-27 15:03:41 +000042import android.os.ParcelFileDescriptor;
Narayan Kamath869f7062019-01-10 12:24:15 +000043import android.os.Process;
shafikda5e4ee2019-02-12 16:29:01 +000044import android.os.SystemClock;
shafik0ad18b82019-01-24 16:27:24 +000045import android.provider.DeviceConfig;
Narayan Kamathc034fe92019-01-23 10:48:17 +000046import android.util.IntArray;
Richard Uhlere95d0552018-12-27 15:03:41 +000047import android.util.Log;
Richard Uhlerb9d54472019-01-22 12:50:08 +000048import android.util.SparseBooleanArray;
Nikita Ioffe952aa7b2019-01-28 19:49:56 +000049import android.util.SparseLongArray;
Richard Uhlere95d0552018-12-27 15:03:41 +000050
51import com.android.internal.annotations.GuardedBy;
Richard Uhlerab009ea2019-02-25 12:11:05 +000052import com.android.internal.util.ArrayUtils;
Richard Uhlere95d0552018-12-27 15:03:41 +000053import com.android.server.LocalServices;
Narayan Kamath869f7062019-01-10 12:24:15 +000054import com.android.server.pm.Installer;
Richard Uhlere95d0552018-12-27 15:03:41 +000055
Richard Uhlere95d0552018-12-27 15:03:41 +000056import java.io.File;
57import java.io.IOException;
Richard Uhlerb9d54472019-01-22 12:50:08 +000058import java.security.SecureRandom;
Richard Uhlere95d0552018-12-27 15:03:41 +000059import java.time.Instant;
Richard Uhlere95d0552018-12-27 15:03:41 +000060import java.time.temporal.ChronoUnit;
61import java.util.ArrayList;
Richard Uhler2d7c7f0d2019-01-04 09:18:21 +000062import java.util.HashMap;
Richard Uhlere95d0552018-12-27 15:03:41 +000063import java.util.Iterator;
64import java.util.List;
Richard Uhlerb9d54472019-01-22 12:50:08 +000065import java.util.Map;
66import java.util.Random;
Narayan Kamathfcd4a042019-02-01 14:16:37 +000067import java.util.concurrent.LinkedBlockingQueue;
shafik0ad18b82019-01-24 16:27:24 +000068import java.util.concurrent.TimeUnit;
Richard Uhlere95d0552018-12-27 15:03:41 +000069
70/**
71 * Implementation of service that manages APK level rollbacks.
72 */
73class RollbackManagerServiceImpl extends IRollbackManager.Stub {
74
75 private static final String TAG = "RollbackManager";
76
77 // Rollbacks expire after 48 hours.
78 // TODO: How to test rollback expiration works properly?
shafik0ad18b82019-01-24 16:27:24 +000079 private static final long DEFAULT_ROLLBACK_LIFETIME_DURATION_MILLIS =
80 TimeUnit.HOURS.toMillis(48);
Richard Uhlere95d0552018-12-27 15:03:41 +000081
82 // Lock used to synchronize accesses to in-memory rollback data
83 // structures. By convention, methods with the suffix "Locked" require
84 // mLock is held when they are called.
85 private final Object mLock = new Object();
86
shafik0ad18b82019-01-24 16:27:24 +000087 // No need for guarding with lock because value is only accessed in handler thread
88 // and the value will be written on boot complete. Initialization here happens before
89 // handler threads are running so that's fine.
90 private long mRollbackLifetimeDurationInMillis = DEFAULT_ROLLBACK_LIFETIME_DURATION_MILLIS;
91
Richard Uhlerb9d54472019-01-22 12:50:08 +000092 // Used for generating rollback IDs.
93 private final Random mRandom = new SecureRandom();
94
95 // Set of allocated rollback ids
96 @GuardedBy("mLock")
97 private final SparseBooleanArray mAllocatedRollbackIds = new SparseBooleanArray();
98
Richard Uhler2d7c7f0d2019-01-04 09:18:21 +000099 // Package rollback data for rollback-enabled installs that have not yet
100 // been committed. Maps from sessionId to rollback data.
101 @GuardedBy("mLock")
Richard Uhler32f63a72019-01-09 10:43:52 +0000102 private final Map<Integer, RollbackData> mPendingRollbacks = new HashMap<>();
Richard Uhler2d7c7f0d2019-01-04 09:18:21 +0000103
Richard Uhlerf1910c52019-01-09 14:27:36 +0000104 // Map from child session id's for enabled rollbacks to their
105 // corresponding parent session ids.
106 @GuardedBy("mLock")
107 private final Map<Integer, Integer> mChildSessions = new HashMap<>();
108
Richard Uhlere95d0552018-12-27 15:03:41 +0000109 // Package rollback data available to be used for rolling back a package.
110 // This list is null until the rollback data has been loaded.
111 @GuardedBy("mLock")
Richard Uhler32f63a72019-01-09 10:43:52 +0000112 private List<RollbackData> mAvailableRollbacks;
Richard Uhlere95d0552018-12-27 15:03:41 +0000113
114 // The list of recently executed rollbacks.
115 // This list is null until the rollback data has been loaded.
116 @GuardedBy("mLock")
117 private List<RollbackInfo> mRecentlyExecutedRollbacks;
118
Richard Uhler28e73232019-01-21 16:48:55 +0000119 private final RollbackStore mRollbackStore;
Richard Uhlere95d0552018-12-27 15:03:41 +0000120
121 private final Context mContext;
122 private final HandlerThread mHandlerThread;
Narayan Kamath869f7062019-01-10 12:24:15 +0000123 private final Installer mInstaller;
Zimuzoc4073cc2019-01-18 18:39:18 +0000124 private final RollbackPackageHealthObserver mPackageHealthObserver;
Nikita Ioffe952aa7b2019-01-28 19:49:56 +0000125 private final AppDataRollbackHelper mAppDataRollbackHelper;
Richard Uhlere95d0552018-12-27 15:03:41 +0000126
shafikda5e4ee2019-02-12 16:29:01 +0000127 // This field stores the difference in Millis between the uptime (millis since device
128 // has booted) and current time (device wall clock) - it's used to update rollback data
129 // timestamps when the time is changed, by the user or by change of timezone.
130 // No need for guarding with lock because value is only accessed in handler thread.
131 private long mRelativeBootTime = calculateRelativeBootTime();
132
133
Richard Uhlere95d0552018-12-27 15:03:41 +0000134 RollbackManagerServiceImpl(Context context) {
135 mContext = context;
Narayan Kamath869f7062019-01-10 12:24:15 +0000136 // Note that we're calling onStart here because this object is only constructed on
137 // SystemService#onStart.
138 mInstaller = new Installer(mContext);
139 mInstaller.onStart();
Richard Uhlere95d0552018-12-27 15:03:41 +0000140 mHandlerThread = new HandlerThread("RollbackManagerServiceHandler");
141 mHandlerThread.start();
142
Richard Uhler28e73232019-01-21 16:48:55 +0000143 mRollbackStore = new RollbackStore(new File(Environment.getDataDirectory(), "rollback"));
Richard Uhlere95d0552018-12-27 15:03:41 +0000144
Zimuzoc4073cc2019-01-18 18:39:18 +0000145 mPackageHealthObserver = new RollbackPackageHealthObserver(mContext);
Nikita Ioffe952aa7b2019-01-28 19:49:56 +0000146 mAppDataRollbackHelper = new AppDataRollbackHelper(mInstaller);
Zimuzoc4073cc2019-01-18 18:39:18 +0000147
Richard Uhlere95d0552018-12-27 15:03:41 +0000148 // Kick off loading of the rollback data from strorage in a background
149 // thread.
150 // TODO: Consider loading the rollback data directly here instead, to
151 // avoid the need to call ensureRollbackDataLoaded every time before
152 // accessing the rollback data?
153 // TODO: Test that this kicks off initial scheduling of rollback
154 // expiration.
155 getHandler().post(() -> ensureRollbackDataLoaded());
156
Narayan Kamath869f7062019-01-10 12:24:15 +0000157 PackageInstaller packageInstaller = mContext.getPackageManager().getPackageInstaller();
158 packageInstaller.registerSessionCallback(new SessionCallback(), getHandler());
Richard Uhler2d7c7f0d2019-01-04 09:18:21 +0000159
Richard Uhlere95d0552018-12-27 15:03:41 +0000160 IntentFilter filter = new IntentFilter();
161 filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
162 filter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED);
163 filter.addDataScheme("package");
164 mContext.registerReceiver(new BroadcastReceiver() {
165 @Override
166 public void onReceive(Context context, Intent intent) {
167 String action = intent.getAction();
168 if (Intent.ACTION_PACKAGE_REPLACED.equals(action)) {
169 String packageName = intent.getData().getSchemeSpecificPart();
170 onPackageReplaced(packageName);
171 }
172 if (Intent.ACTION_PACKAGE_FULLY_REMOVED.equals(action)) {
173 String packageName = intent.getData().getSchemeSpecificPart();
174 onPackageFullyRemoved(packageName);
175 }
176 }
177 }, filter, null, getHandler());
178
Narayan Kamathfcd4a042019-02-01 14:16:37 +0000179 // NOTE: A new intent filter is being created here because this broadcast
180 // doesn't use a data scheme ("package") like above.
181 IntentFilter sessionUpdatedFilter = new IntentFilter();
182 sessionUpdatedFilter.addAction(PackageInstaller.ACTION_SESSION_UPDATED);
183 mContext.registerReceiver(new BroadcastReceiver() {
184 @Override
185 public void onReceive(Context context, Intent intent) {
186 onStagedSessionUpdated(intent);
187 }
188 }, sessionUpdatedFilter, null, getHandler());
189
Richard Uhlere95d0552018-12-27 15:03:41 +0000190 IntentFilter enableRollbackFilter = new IntentFilter();
191 enableRollbackFilter.addAction(Intent.ACTION_PACKAGE_ENABLE_ROLLBACK);
192 try {
193 enableRollbackFilter.addDataType("application/vnd.android.package-archive");
194 } catch (IntentFilter.MalformedMimeTypeException e) {
195 Log.e(TAG, "addDataType", e);
196 }
197
198 mContext.registerReceiver(new BroadcastReceiver() {
199 @Override
200 public void onReceive(Context context, Intent intent) {
201 if (Intent.ACTION_PACKAGE_ENABLE_ROLLBACK.equals(intent.getAction())) {
202 int token = intent.getIntExtra(
203 PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_TOKEN, -1);
204 int installFlags = intent.getIntExtra(
205 PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_INSTALL_FLAGS, 0);
Narayan Kamath869f7062019-01-10 12:24:15 +0000206 int[] installedUsers = intent.getIntArrayExtra(
207 PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_INSTALLED_USERS);
Richard Uhlere95d0552018-12-27 15:03:41 +0000208 File newPackageCodePath = new File(intent.getData().getPath());
209
210 getHandler().post(() -> {
Narayan Kamath869f7062019-01-10 12:24:15 +0000211 boolean success = enableRollback(installFlags, newPackageCodePath,
212 installedUsers);
Richard Uhlere95d0552018-12-27 15:03:41 +0000213 int ret = PackageManagerInternal.ENABLE_ROLLBACK_SUCCEEDED;
214 if (!success) {
215 ret = PackageManagerInternal.ENABLE_ROLLBACK_FAILED;
216 }
217
218 PackageManagerInternal pm = LocalServices.getService(
219 PackageManagerInternal.class);
220 pm.setEnableRollbackCode(token, ret);
221 });
222
223 // We're handling the ordered broadcast. Abort the
224 // broadcast because there is no need for it to go to
225 // anyone else.
226 abortBroadcast();
227 }
228 }
229 }, enableRollbackFilter, null, getHandler());
shafikda5e4ee2019-02-12 16:29:01 +0000230
231 registerTimeChangeReceiver();
Richard Uhlere95d0552018-12-27 15:03:41 +0000232 }
233
234 @Override
Richard Uhler150ad982019-01-23 15:16:10 +0000235 public ParceledListSlice getAvailableRollbacks() {
Richard Uhlere95d0552018-12-27 15:03:41 +0000236 mContext.enforceCallingOrSelfPermission(
237 android.Manifest.permission.MANAGE_ROLLBACKS,
Richard Uhler150ad982019-01-23 15:16:10 +0000238 "getAvailableRollbacks");
Richard Uhlere95d0552018-12-27 15:03:41 +0000239
Richard Uhlere95d0552018-12-27 15:03:41 +0000240 synchronized (mLock) {
241 ensureRollbackDataLoadedLocked();
Richard Uhler150ad982019-01-23 15:16:10 +0000242 List<RollbackInfo> rollbacks = new ArrayList<>();
Richard Uhlere95d0552018-12-27 15:03:41 +0000243 for (int i = 0; i < mAvailableRollbacks.size(); ++i) {
Richard Uhler32f63a72019-01-09 10:43:52 +0000244 RollbackData data = mAvailableRollbacks.get(i);
Richard Uhler60ac7062019-02-05 13:25:39 +0000245 if (data.isAvailable) {
246 rollbacks.add(new RollbackInfo(data.rollbackId,
247 data.packages, data.isStaged()));
248 }
Richard Uhlere95d0552018-12-27 15:03:41 +0000249 }
Richard Uhler150ad982019-01-23 15:16:10 +0000250 return new ParceledListSlice<>(rollbacks);
Richard Uhlere95d0552018-12-27 15:03:41 +0000251 }
Richard Uhlere95d0552018-12-27 15:03:41 +0000252 }
253
254 @Override
255 public ParceledListSlice<RollbackInfo> getRecentlyExecutedRollbacks() {
256 mContext.enforceCallingOrSelfPermission(
257 android.Manifest.permission.MANAGE_ROLLBACKS,
258 "getRecentlyExecutedRollbacks");
259
260 synchronized (mLock) {
261 ensureRollbackDataLoadedLocked();
262 List<RollbackInfo> rollbacks = new ArrayList<>(mRecentlyExecutedRollbacks);
263 return new ParceledListSlice<>(rollbacks);
264 }
265 }
266
267 @Override
Richard Uhlerbf5b5c42019-01-28 15:26:37 +0000268 public void commitRollback(int rollbackId, ParceledListSlice causePackages,
269 String callerPackageName, IntentSender statusReceiver) {
Richard Uhlere95d0552018-12-27 15:03:41 +0000270 mContext.enforceCallingOrSelfPermission(
271 android.Manifest.permission.MANAGE_ROLLBACKS,
272 "executeRollback");
273
274 final int callingUid = Binder.getCallingUid();
275 AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class);
276 appOps.checkPackage(callingUid, callerPackageName);
277
278 getHandler().post(() ->
Richard Uhlerbf5b5c42019-01-28 15:26:37 +0000279 commitRollbackInternal(rollbackId, causePackages.getList(),
280 callerPackageName, statusReceiver));
Richard Uhlere95d0552018-12-27 15:03:41 +0000281 }
282
shafikda5e4ee2019-02-12 16:29:01 +0000283 private void registerTimeChangeReceiver() {
284 final BroadcastReceiver timeChangeIntentReceiver = new BroadcastReceiver() {
285 @Override
286 public void onReceive(Context context, Intent intent) {
287 final long oldRelativeBootTime = mRelativeBootTime;
288 mRelativeBootTime = calculateRelativeBootTime();
289 final long timeDifference = mRelativeBootTime - oldRelativeBootTime;
290
291 synchronized (mLock) {
292 ensureRollbackDataLoadedLocked();
293
294 Iterator<RollbackData> iter = mAvailableRollbacks.iterator();
295 while (iter.hasNext()) {
296 RollbackData data = iter.next();
297
298 data.timestamp = data.timestamp.plusMillis(timeDifference);
299 try {
300 mRollbackStore.saveAvailableRollback(data);
301 } catch (IOException ioe) {
302 // TODO: figure out the right way to deal with this, especially if
303 // it fails for some data and succeeds for others.
304 Log.e(TAG, "Unable to save rollback info for : " + data.rollbackId,
305 ioe);
306 }
307 }
308
309 }
310 }
311 };
312 final IntentFilter filter = new IntentFilter();
313 filter.addAction(Intent.ACTION_TIME_CHANGED);
314 mContext.registerReceiver(timeChangeIntentReceiver, filter,
315 null /* broadcastPermission */, getHandler());
316 }
317
318 private static long calculateRelativeBootTime() {
319 return System.currentTimeMillis() - SystemClock.elapsedRealtime();
320 }
321
Richard Uhlere95d0552018-12-27 15:03:41 +0000322 /**
Richard Uhlere87368e2019-01-24 16:34:14 +0000323 * Performs the actual work to commit a rollback.
Richard Uhlere95d0552018-12-27 15:03:41 +0000324 * The work is done on the current thread. This may be a long running
325 * operation.
326 */
Richard Uhlerbf5b5c42019-01-28 15:26:37 +0000327 private void commitRollbackInternal(int rollbackId, List<VersionedPackage> causePackages,
Richard Uhlere95d0552018-12-27 15:03:41 +0000328 String callerPackageName, IntentSender statusReceiver) {
Richard Uhler0a79b322019-01-23 13:51:07 +0000329 Log.i(TAG, "Initiating rollback");
Richard Uhlere95d0552018-12-27 15:03:41 +0000330
Richard Uhlere87368e2019-01-24 16:34:14 +0000331 RollbackData data = getRollbackForId(rollbackId);
Richard Uhler035e9742019-01-09 13:11:07 +0000332 if (data == null) {
Richard Uhler2a48c292019-01-28 17:33:48 +0000333 sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE_ROLLBACK_UNAVAILABLE,
334 "Rollback unavailable");
Narayan Kamathbc36f8d2019-01-23 12:00:08 +0000335 return;
336 }
337
338 if (data.inProgress) {
Richard Uhler2a48c292019-01-28 17:33:48 +0000339 sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE_ROLLBACK_UNAVAILABLE,
340 "Rollback for package is already in progress.");
Richard Uhlerb9d54472019-01-22 12:50:08 +0000341 return;
Richard Uhlere95d0552018-12-27 15:03:41 +0000342 }
343
Richard Uhler035e9742019-01-09 13:11:07 +0000344 // Verify the RollbackData is up to date with what's installed on
345 // device.
Richard Uhlere95d0552018-12-27 15:03:41 +0000346 // TODO: We assume that between now and the time we commit the
347 // downgrade install, the currently installed package version does not
348 // change. This is not safe to assume, particularly in the case of a
349 // rollback racing with a roll-forward fix of a buggy package.
350 // Figure out how to ensure we don't commit the rollback if
351 // roll forward happens at the same time.
Richard Uhler01b06152019-01-09 13:51:54 +0000352 for (PackageRollbackInfo info : data.packages) {
Richard Uhlera7e9b2d2019-01-22 17:20:58 +0000353 VersionedPackage installedVersion = getInstalledPackageVersion(info.getPackageName());
Richard Uhler01b06152019-01-09 13:51:54 +0000354 if (installedVersion == null) {
355 // TODO: Test this case
Richard Uhler2a48c292019-01-28 17:33:48 +0000356 sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE_ROLLBACK_UNAVAILABLE,
357 "Package to roll back is not installed");
Richard Uhler01b06152019-01-09 13:51:54 +0000358 return;
359 }
Richard Uhlere95d0552018-12-27 15:03:41 +0000360
Richard Uhlera7e9b2d2019-01-22 17:20:58 +0000361 if (!packageVersionsEqual(info.getVersionRolledBackFrom(), installedVersion)) {
Richard Uhler01b06152019-01-09 13:51:54 +0000362 // TODO: Test this case
Richard Uhler2a48c292019-01-28 17:33:48 +0000363 sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE_ROLLBACK_UNAVAILABLE,
364 "Package version to roll back not installed.");
Richard Uhler01b06152019-01-09 13:51:54 +0000365 return;
366 }
Richard Uhlere95d0552018-12-27 15:03:41 +0000367 }
368
369 // Get a context for the caller to use to install the downgraded
370 // version of the package.
371 Context context = null;
372 try {
373 context = mContext.createPackageContext(callerPackageName, 0);
374 } catch (PackageManager.NameNotFoundException e) {
Richard Uhler2a48c292019-01-28 17:33:48 +0000375 sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE,
376 "Invalid callerPackageName");
Richard Uhlere95d0552018-12-27 15:03:41 +0000377 return;
378 }
379
380 PackageManager pm = context.getPackageManager();
381 try {
Richard Uhlere95d0552018-12-27 15:03:41 +0000382 PackageInstaller packageInstaller = pm.getPackageInstaller();
Richard Uhler01b06152019-01-09 13:51:54 +0000383 PackageInstaller.SessionParams parentParams = new PackageInstaller.SessionParams(
Richard Uhlere95d0552018-12-27 15:03:41 +0000384 PackageInstaller.SessionParams.MODE_FULL_INSTALL);
Richard Uhler01b06152019-01-09 13:51:54 +0000385 parentParams.setAllowDowngrade(true);
386 parentParams.setMultiPackage();
Richard Uhler72fb9612019-02-04 14:37:36 +0000387 if (data.isStaged()) {
388 parentParams.setStaged();
389 }
390
Richard Uhler01b06152019-01-09 13:51:54 +0000391 int parentSessionId = packageInstaller.createSession(parentParams);
392 PackageInstaller.Session parentSession = packageInstaller.openSession(parentSessionId);
Richard Uhlere95d0552018-12-27 15:03:41 +0000393
Richard Uhler01b06152019-01-09 13:51:54 +0000394 for (PackageRollbackInfo info : data.packages) {
395 PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
396 PackageInstaller.SessionParams.MODE_FULL_INSTALL);
Richard Uhler7a5eeb12019-02-05 12:51:48 +0000397 // TODO: We can't get the installerPackageName for apex
398 // (b/123920130). Is it okay to ignore the installer package
399 // for apex?
400 if (!info.isApex()) {
401 String installerPackageName = pm.getInstallerPackageName(info.getPackageName());
Richard Uhlera7d18bb2019-02-06 11:58:17 +0000402 if (installerPackageName != null) {
403 params.setInstallerPackageName(installerPackageName);
Richard Uhler7a5eeb12019-02-05 12:51:48 +0000404 }
Richard Uhler0a79b322019-01-23 13:51:07 +0000405 }
Richard Uhler01b06152019-01-09 13:51:54 +0000406 params.setAllowDowngrade(true);
Richard Uhler72fb9612019-02-04 14:37:36 +0000407 if (data.isStaged()) {
408 params.setStaged();
409 }
410 if (info.isApex()) {
411 params.setInstallAsApex();
412 }
Richard Uhler01b06152019-01-09 13:51:54 +0000413 int sessionId = packageInstaller.createSession(params);
414 PackageInstaller.Session session = packageInstaller.openSession(sessionId);
415
Richard Uhlerab009ea2019-02-25 12:11:05 +0000416 File[] packageCodePaths = RollbackStore.getPackageCodePaths(
417 data, info.getPackageName());
418 if (packageCodePaths == null) {
Richard Uhler1f571c62019-01-31 15:16:46 +0000419 sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE,
Richard Uhlerab009ea2019-02-25 12:11:05 +0000420 "Backup copy of package inaccessible");
Richard Uhler1f571c62019-01-31 15:16:46 +0000421 return;
422 }
423
Richard Uhlerab009ea2019-02-25 12:11:05 +0000424 for (File packageCodePath : packageCodePaths) {
425 try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(packageCodePath,
426 ParcelFileDescriptor.MODE_READ_ONLY)) {
427 final long token = Binder.clearCallingIdentity();
428 try {
429 session.write(packageCodePath.getName(), 0, packageCodePath.length(),
430 fd);
431 } finally {
432 Binder.restoreCallingIdentity(token);
433 }
Richard Uhler01b06152019-01-09 13:51:54 +0000434 }
Richard Uhlere95d0552018-12-27 15:03:41 +0000435 }
Richard Uhler01b06152019-01-09 13:51:54 +0000436 parentSession.addChildSessionId(sessionId);
Richard Uhlere95d0552018-12-27 15:03:41 +0000437 }
438
Narayan Kamath869f7062019-01-10 12:24:15 +0000439 final LocalIntentReceiver receiver = new LocalIntentReceiver(
440 (Intent result) -> {
Zimuzoc4073cc2019-01-18 18:39:18 +0000441 getHandler().post(() -> {
442 // We've now completed the rollback, so we mark it as no longer in
443 // progress.
444 data.inProgress = false;
Narayan Kamathbc36f8d2019-01-23 12:00:08 +0000445
Zimuzoc4073cc2019-01-18 18:39:18 +0000446 int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
447 PackageInstaller.STATUS_FAILURE);
448 if (status != PackageInstaller.STATUS_SUCCESS) {
Richard Uhler2a48c292019-01-28 17:33:48 +0000449 sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE_INSTALL,
Zimuzoc4073cc2019-01-18 18:39:18 +0000450 "Rollback downgrade install failed: "
451 + result.getStringExtra(
452 PackageInstaller.EXTRA_STATUS_MESSAGE));
453 return;
454 }
Narayan Kamath869f7062019-01-10 12:24:15 +0000455
Richard Uhlerbf5b5c42019-01-28 15:26:37 +0000456 addRecentlyExecutedRollback(new RollbackInfo(
Richard Uhler72fb9612019-02-04 14:37:36 +0000457 data.rollbackId, data.packages, data.isStaged(),
458 causePackages, parentSessionId));
Zimuzoc4073cc2019-01-18 18:39:18 +0000459 sendSuccess(statusReceiver);
Narayan Kamath869f7062019-01-10 12:24:15 +0000460
Richard Uhlerdca7beb2019-01-24 09:56:03 +0000461 Intent broadcast = new Intent(Intent.ACTION_ROLLBACK_COMMITTED);
Narayan Kamath869f7062019-01-10 12:24:15 +0000462
Zimuzoc4073cc2019-01-18 18:39:18 +0000463 // TODO: This call emits the warning "Calling a method in the
464 // system process without a qualified user". Fix that.
465 // TODO: Limit this to receivers holding the
466 // MANAGE_ROLLBACKS permission?
467 mContext.sendBroadcast(broadcast);
468 });
Narayan Kamath869f7062019-01-10 12:24:15 +0000469 }
470 );
471
Narayan Kamathbc36f8d2019-01-23 12:00:08 +0000472 data.inProgress = true;
Richard Uhler01b06152019-01-09 13:51:54 +0000473 parentSession.commit(receiver.getIntentSender());
Richard Uhlere95d0552018-12-27 15:03:41 +0000474 } catch (IOException e) {
Richard Uhler0a79b322019-01-23 13:51:07 +0000475 Log.e(TAG, "Rollback failed", e);
Richard Uhler2a48c292019-01-28 17:33:48 +0000476 sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE,
477 "IOException: " + e.toString());
Richard Uhlere95d0552018-12-27 15:03:41 +0000478 return;
479 }
480 }
481
482 @Override
483 public void reloadPersistedData() {
484 mContext.enforceCallingOrSelfPermission(
485 android.Manifest.permission.MANAGE_ROLLBACKS,
486 "reloadPersistedData");
487
488 synchronized (mLock) {
489 mAvailableRollbacks = null;
490 mRecentlyExecutedRollbacks = null;
491 }
shafik6d61f5e2019-02-26 09:33:26 +0000492 getHandler().post(() -> {
493 updateRollbackLifetimeDurationInMillis();
494 ensureRollbackDataLoaded();
495 });
Richard Uhlere95d0552018-12-27 15:03:41 +0000496 }
497
498 @Override
499 public void expireRollbackForPackage(String packageName) {
500 mContext.enforceCallingOrSelfPermission(
501 android.Manifest.permission.MANAGE_ROLLBACKS,
502 "expireRollbackForPackage");
503
504 // TODO: Should this take a package version number in addition to
505 // package name? For now, just remove all rollbacks matching the
506 // package name. This method is only currently used to facilitate
507 // testing anyway.
508 synchronized (mLock) {
509 ensureRollbackDataLoadedLocked();
Richard Uhler32f63a72019-01-09 10:43:52 +0000510 Iterator<RollbackData> iter = mAvailableRollbacks.iterator();
Richard Uhlere95d0552018-12-27 15:03:41 +0000511 while (iter.hasNext()) {
Richard Uhler32f63a72019-01-09 10:43:52 +0000512 RollbackData data = iter.next();
Richard Uhler01b06152019-01-09 13:51:54 +0000513 for (PackageRollbackInfo info : data.packages) {
Richard Uhlera7e9b2d2019-01-22 17:20:58 +0000514 if (info.getPackageName().equals(packageName)) {
Richard Uhler01b06152019-01-09 13:51:54 +0000515 iter.remove();
Nikita Ioffe952aa7b2019-01-28 19:49:56 +0000516 deleteRollback(data);
Richard Uhler01b06152019-01-09 13:51:54 +0000517 break;
518 }
Richard Uhlere95d0552018-12-27 15:03:41 +0000519 }
520 }
521 }
522 }
523
Narayan Kamathc034fe92019-01-23 10:48:17 +0000524 void onUnlockUser(int userId) {
525 getHandler().post(() -> {
Nikita Ioffe5dcd17972019-02-04 11:08:13 +0000526 final List<RollbackData> availableRollbacks;
527 final List<RollbackInfo> recentlyExecutedRollbacks;
Narayan Kamathc034fe92019-01-23 10:48:17 +0000528 synchronized (mLock) {
529 ensureRollbackDataLoadedLocked();
Nikita Ioffe5dcd17972019-02-04 11:08:13 +0000530 availableRollbacks = new ArrayList<>(mAvailableRollbacks);
531 recentlyExecutedRollbacks = new ArrayList<>(mRecentlyExecutedRollbacks);
Narayan Kamathc034fe92019-01-23 10:48:17 +0000532 }
533
Nikita Ioffe5dcd17972019-02-04 11:08:13 +0000534 final List<RollbackData> changed =
535 mAppDataRollbackHelper.commitPendingBackupAndRestoreForUser(userId,
536 availableRollbacks, recentlyExecutedRollbacks);
Narayan Kamathc034fe92019-01-23 10:48:17 +0000537
538 for (RollbackData rd : changed) {
539 try {
540 mRollbackStore.saveAvailableRollback(rd);
541 } catch (IOException ioe) {
542 Log.e(TAG, "Unable to save rollback info for : " + rd.rollbackId, ioe);
543 }
544 }
545
546 synchronized (mLock) {
547 mRollbackStore.saveRecentlyExecutedRollbacks(mRecentlyExecutedRollbacks);
548 }
549 });
550 }
551
shafik0ad18b82019-01-24 16:27:24 +0000552 private void updateRollbackLifetimeDurationInMillis() {
553 String strRollbackLifetimeInMillis = DeviceConfig.getProperty(
554 DeviceConfig.Rollback.BOOT_NAMESPACE,
555 DeviceConfig.Rollback.ROLLBACK_LIFETIME_IN_MILLIS);
556
557 try {
558 mRollbackLifetimeDurationInMillis = (strRollbackLifetimeInMillis == null)
559 ? DEFAULT_ROLLBACK_LIFETIME_DURATION_MILLIS
560 : Long.parseLong(strRollbackLifetimeInMillis);
561 } catch (NumberFormatException e) {
562 mRollbackLifetimeDurationInMillis = DEFAULT_ROLLBACK_LIFETIME_DURATION_MILLIS;
563 }
564 }
565
Richard Uhlerb2c5cac2019-02-05 16:14:35 +0000566 void onBootCompleted() {
shafik0ad18b82019-01-24 16:27:24 +0000567 getHandler().post(() -> updateRollbackLifetimeDurationInMillis());
568 // Also posts to handler thread
569 scheduleExpiration(0);
570
Richard Uhlerb2c5cac2019-02-05 16:14:35 +0000571 getHandler().post(() -> {
572 // Check to see if any staged sessions with rollback enabled have
573 // been applied.
574 List<RollbackData> staged = new ArrayList<>();
575 synchronized (mLock) {
576 ensureRollbackDataLoadedLocked();
577 for (RollbackData data : mAvailableRollbacks) {
578 if (data.stagedSessionId != -1) {
579 staged.add(data);
580 }
581 }
582 }
583
584 for (RollbackData data : staged) {
585 PackageInstaller installer = mContext.getPackageManager().getPackageInstaller();
586 PackageInstaller.SessionInfo session = installer.getSessionInfo(
587 data.stagedSessionId);
588 if (session != null) {
Dario Freni60a96c12019-02-24 21:01:29 +0000589 if (session.isStagedSessionApplied()) {
Richard Uhlerb2c5cac2019-02-05 16:14:35 +0000590 synchronized (mLock) {
591 data.isAvailable = true;
592 }
593 try {
594 mRollbackStore.saveAvailableRollback(data);
595 } catch (IOException ioe) {
596 Log.e(TAG, "Unable to save rollback info for : "
597 + data.rollbackId, ioe);
598 }
Dario Freni60a96c12019-02-24 21:01:29 +0000599 } else if (session.isStagedSessionFailed()) {
Richard Uhlerb2c5cac2019-02-05 16:14:35 +0000600 // TODO: Do we need to remove this from
601 // mAvailableRollbacks, or is it okay to leave as
602 // unavailable until the next reboot when it will go
603 // away on its own?
Nikita Ioffe952aa7b2019-01-28 19:49:56 +0000604 deleteRollback(data);
Richard Uhlerb2c5cac2019-02-05 16:14:35 +0000605 }
606 }
607 }
608 });
609 }
610
Richard Uhlere95d0552018-12-27 15:03:41 +0000611 /**
612 * Load rollback data from storage if it has not already been loaded.
613 * After calling this funciton, mAvailableRollbacks and
614 * mRecentlyExecutedRollbacks will be non-null.
615 */
616 private void ensureRollbackDataLoaded() {
617 synchronized (mLock) {
618 ensureRollbackDataLoadedLocked();
619 }
620 }
621
622 /**
623 * Load rollback data from storage if it has not already been loaded.
624 * After calling this function, mAvailableRollbacks and
625 * mRecentlyExecutedRollbacks will be non-null.
626 */
627 @GuardedBy("mLock")
628 private void ensureRollbackDataLoadedLocked() {
629 if (mAvailableRollbacks == null) {
Richard Uhlerb7e2d922019-01-21 14:34:50 +0000630 loadAllRollbackDataLocked();
Richard Uhlere95d0552018-12-27 15:03:41 +0000631 }
632 }
633
634 /**
Richard Uhlerb7e2d922019-01-21 14:34:50 +0000635 * Load all rollback data from storage.
Richard Uhlere95d0552018-12-27 15:03:41 +0000636 * Note: We do potentially heavy IO here while holding mLock, because we
637 * have to have the rollback data loaded before we can do anything else
638 * meaningful.
639 */
640 @GuardedBy("mLock")
Richard Uhlerb7e2d922019-01-21 14:34:50 +0000641 private void loadAllRollbackDataLocked() {
Richard Uhler28e73232019-01-21 16:48:55 +0000642 mAvailableRollbacks = mRollbackStore.loadAvailableRollbacks();
Richard Uhlerb9d54472019-01-22 12:50:08 +0000643 for (RollbackData data : mAvailableRollbacks) {
644 mAllocatedRollbackIds.put(data.rollbackId, true);
645 }
646
Richard Uhler28e73232019-01-21 16:48:55 +0000647 mRecentlyExecutedRollbacks = mRollbackStore.loadRecentlyExecutedRollbacks();
Richard Uhlerb9d54472019-01-22 12:50:08 +0000648 for (RollbackInfo info : mRecentlyExecutedRollbacks) {
649 mAllocatedRollbackIds.put(info.getRollbackId(), true);
650 }
Richard Uhlere95d0552018-12-27 15:03:41 +0000651 }
652
653 /**
654 * Called when a package has been replaced with a different version.
655 * Removes all backups for the package not matching the currently
656 * installed package version.
657 */
658 private void onPackageReplaced(String packageName) {
659 // TODO: Could this end up incorrectly deleting a rollback for a
660 // package that is about to be installed?
Richard Uhlera7e9b2d2019-01-22 17:20:58 +0000661 VersionedPackage installedVersion = getInstalledPackageVersion(packageName);
Richard Uhlere95d0552018-12-27 15:03:41 +0000662
663 synchronized (mLock) {
664 ensureRollbackDataLoadedLocked();
Richard Uhler32f63a72019-01-09 10:43:52 +0000665 Iterator<RollbackData> iter = mAvailableRollbacks.iterator();
Richard Uhlere95d0552018-12-27 15:03:41 +0000666 while (iter.hasNext()) {
Richard Uhler32f63a72019-01-09 10:43:52 +0000667 RollbackData data = iter.next();
Richard Uhler01b06152019-01-09 13:51:54 +0000668 for (PackageRollbackInfo info : data.packages) {
Richard Uhlera7e9b2d2019-01-22 17:20:58 +0000669 if (info.getPackageName().equals(packageName)
670 && !packageVersionsEqual(
671 info.getVersionRolledBackFrom(),
672 installedVersion)) {
Richard Uhler01b06152019-01-09 13:51:54 +0000673 iter.remove();
Nikita Ioffe952aa7b2019-01-28 19:49:56 +0000674 deleteRollback(data);
Richard Uhler01b06152019-01-09 13:51:54 +0000675 break;
676 }
Richard Uhlere95d0552018-12-27 15:03:41 +0000677 }
678 }
679 }
680 }
681
682 /**
683 * Called when a package has been completely removed from the device.
684 * Removes all backups and rollback history for the given package.
685 */
686 private void onPackageFullyRemoved(String packageName) {
687 expireRollbackForPackage(packageName);
688
689 synchronized (mLock) {
690 ensureRollbackDataLoadedLocked();
691 Iterator<RollbackInfo> iter = mRecentlyExecutedRollbacks.iterator();
692 boolean changed = false;
693 while (iter.hasNext()) {
694 RollbackInfo rollback = iter.next();
Richard Uhler0a79b322019-01-23 13:51:07 +0000695 for (PackageRollbackInfo info : rollback.getPackages()) {
696 if (packageName.equals(info.getPackageName())) {
697 iter.remove();
698 changed = true;
699 break;
700 }
Richard Uhlere95d0552018-12-27 15:03:41 +0000701 }
702 }
703
704 if (changed) {
Richard Uhler28e73232019-01-21 16:48:55 +0000705 mRollbackStore.saveRecentlyExecutedRollbacks(mRecentlyExecutedRollbacks);
Richard Uhlere95d0552018-12-27 15:03:41 +0000706 }
707 }
708 }
709
710 /**
Richard Uhlere95d0552018-12-27 15:03:41 +0000711 * Records that the given package has been recently rolled back.
712 */
713 private void addRecentlyExecutedRollback(RollbackInfo rollback) {
714 // TODO: if the list of rollbacks gets too big, trim it to only those
715 // that are necessary to keep track of.
716 synchronized (mLock) {
717 ensureRollbackDataLoadedLocked();
Narayan Kamathc034fe92019-01-23 10:48:17 +0000718
719 // This should never happen because we can't have any pending backups left after
720 // a rollback has been executed. See AppDataRollbackHelper#restoreAppData where we
721 // clear all pending backups at the point of restore because they're guaranteed to be
722 // no-ops.
723 //
724 // We may, however, have one or more pending restores left to handle.
725 for (PackageRollbackInfo target : rollback.getPackages()) {
726 if (target.getPendingBackups().size() > 0) {
727 Log.e(TAG, "No backups allowed to be pending for: " + target);
728 target.getPendingBackups().clear();
729 }
730 }
731
Richard Uhlere95d0552018-12-27 15:03:41 +0000732 mRecentlyExecutedRollbacks.add(rollback);
Richard Uhler28e73232019-01-21 16:48:55 +0000733 mRollbackStore.saveRecentlyExecutedRollbacks(mRecentlyExecutedRollbacks);
Richard Uhlere95d0552018-12-27 15:03:41 +0000734 }
735 }
736
737 /**
738 * Notifies an IntentSender of failure.
739 *
740 * @param statusReceiver where to send the failure
Richard Uhler2a48c292019-01-28 17:33:48 +0000741 * @param status the RollbackManager.STATUS_* code with the failure.
Richard Uhlere95d0552018-12-27 15:03:41 +0000742 * @param message the failure message.
743 */
Richard Uhler2a48c292019-01-28 17:33:48 +0000744 private void sendFailure(IntentSender statusReceiver, int status, String message) {
Richard Uhlere95d0552018-12-27 15:03:41 +0000745 Log.e(TAG, message);
746 try {
Richard Uhlere95d0552018-12-27 15:03:41 +0000747 final Intent fillIn = new Intent();
Richard Uhler2a48c292019-01-28 17:33:48 +0000748 fillIn.putExtra(RollbackManager.EXTRA_STATUS, status);
749 fillIn.putExtra(RollbackManager.EXTRA_STATUS_MESSAGE, message);
Richard Uhlere95d0552018-12-27 15:03:41 +0000750 statusReceiver.sendIntent(mContext, 0, fillIn, null, null);
751 } catch (IntentSender.SendIntentException e) {
752 // Nowhere to send the result back to, so don't bother.
753 }
754 }
755
756 /**
757 * Notifies an IntentSender of success.
758 */
759 private void sendSuccess(IntentSender statusReceiver) {
760 try {
761 final Intent fillIn = new Intent();
Richard Uhler2a48c292019-01-28 17:33:48 +0000762 fillIn.putExtra(RollbackManager.EXTRA_STATUS, RollbackManager.STATUS_SUCCESS);
Richard Uhlere95d0552018-12-27 15:03:41 +0000763 statusReceiver.sendIntent(mContext, 0, fillIn, null, null);
764 } catch (IntentSender.SendIntentException e) {
765 // Nowhere to send the result back to, so don't bother.
766 }
767 }
768
769 // Check to see if anything needs expiration, and if so, expire it.
770 // Schedules future expiration as appropriate.
771 // TODO: Handle cases where the user changes time on the device.
772 private void runExpiration() {
773 Instant now = Instant.now();
774 Instant oldest = null;
775 synchronized (mLock) {
776 ensureRollbackDataLoadedLocked();
777
Richard Uhler32f63a72019-01-09 10:43:52 +0000778 Iterator<RollbackData> iter = mAvailableRollbacks.iterator();
Richard Uhlere95d0552018-12-27 15:03:41 +0000779 while (iter.hasNext()) {
Richard Uhler32f63a72019-01-09 10:43:52 +0000780 RollbackData data = iter.next();
Richard Uhler60ac7062019-02-05 13:25:39 +0000781 if (!data.isAvailable) {
782 continue;
783 }
shafik0ad18b82019-01-24 16:27:24 +0000784 if (!now.isBefore(data.timestamp.plusMillis(mRollbackLifetimeDurationInMillis))) {
Richard Uhlere95d0552018-12-27 15:03:41 +0000785 iter.remove();
Nikita Ioffe952aa7b2019-01-28 19:49:56 +0000786 deleteRollback(data);
Richard Uhlere95d0552018-12-27 15:03:41 +0000787 } else if (oldest == null || oldest.isAfter(data.timestamp)) {
788 oldest = data.timestamp;
789 }
790 }
791 }
792
793 if (oldest != null) {
shafik0ad18b82019-01-24 16:27:24 +0000794 scheduleExpiration(now.until(oldest.plusMillis(mRollbackLifetimeDurationInMillis),
Richard Uhlere95d0552018-12-27 15:03:41 +0000795 ChronoUnit.MILLIS));
796 }
797 }
798
799 /**
800 * Schedules an expiration check to be run after the given duration in
801 * milliseconds has gone by.
802 */
803 private void scheduleExpiration(long duration) {
804 getHandler().postDelayed(() -> runExpiration(), duration);
805 }
806
807 private Handler getHandler() {
808 return mHandlerThread.getThreadHandler();
809 }
810
Richard Uhler2d7c7f0d2019-01-04 09:18:21 +0000811 // Returns true if <code>session</code> has installFlags and code path
812 // matching the installFlags and new package code path given to
813 // enableRollback.
814 private boolean sessionMatchesForEnableRollback(PackageInstaller.SessionInfo session,
815 int installFlags, File newPackageCodePath) {
816 if (session == null || session.resolvedBaseCodePath == null) {
817 return false;
818 }
819
820 File packageCodePath = new File(session.resolvedBaseCodePath).getParentFile();
821 if (newPackageCodePath.equals(packageCodePath) && installFlags == session.installFlags) {
822 return true;
823 }
824
825 return false;
826 }
827
Richard Uhlere95d0552018-12-27 15:03:41 +0000828 /**
829 * Called via broadcast by the package manager when a package is being
830 * staged for install with rollback enabled. Called before the package has
831 * been installed.
832 *
Richard Uhlere95d0552018-12-27 15:03:41 +0000833 * @param installFlags information about what is being installed.
834 * @param newPackageCodePath path to the package about to be installed.
Narayan Kamath869f7062019-01-10 12:24:15 +0000835 * @param installedUsers the set of users for which a given package is installed.
Richard Uhlere95d0552018-12-27 15:03:41 +0000836 * @return true if enabling the rollback succeeds, false otherwise.
837 */
Narayan Kamath869f7062019-01-10 12:24:15 +0000838 private boolean enableRollback(int installFlags, File newPackageCodePath,
839 int[] installedUsers) {
Richard Uhlere95d0552018-12-27 15:03:41 +0000840
Richard Uhlerf0bdca52019-01-31 16:43:59 +0000841 // Find the session id associated with this install.
842 // TODO: It would be nice if package manager or package installer told
843 // us the session directly, rather than have to search for it
844 // ourselves.
845 PackageInstaller.SessionInfo session = null;
Richard Uhlere95d0552018-12-27 15:03:41 +0000846
Richard Uhlerac0d0f92019-02-05 15:53:47 +0000847 int parentSessionId = -1;
Richard Uhler2d7c7f0d2019-01-04 09:18:21 +0000848 PackageInstaller installer = mContext.getPackageManager().getPackageInstaller();
849 for (PackageInstaller.SessionInfo info : installer.getAllSessions()) {
Richard Uhlerf1910c52019-01-09 14:27:36 +0000850 if (info.isMultiPackage()) {
851 for (int childId : info.getChildSessionIds()) {
852 PackageInstaller.SessionInfo child = installer.getSessionInfo(childId);
853 if (sessionMatchesForEnableRollback(child, installFlags, newPackageCodePath)) {
854 // TODO: Check we only have one matching session?
Richard Uhlerac0d0f92019-02-05 15:53:47 +0000855 parentSessionId = info.getSessionId();
Richard Uhlerf0bdca52019-01-31 16:43:59 +0000856 session = child;
857 break;
Richard Uhlerf1910c52019-01-09 14:27:36 +0000858 }
859 }
Richard Uhlerf0bdca52019-01-31 16:43:59 +0000860 } else if (sessionMatchesForEnableRollback(info, installFlags, newPackageCodePath)) {
861 // TODO: Check we only have one matching session?
Richard Uhlerac0d0f92019-02-05 15:53:47 +0000862 parentSessionId = info.getSessionId();
Richard Uhlerf0bdca52019-01-31 16:43:59 +0000863 session = info;
864 break;
Richard Uhler2d7c7f0d2019-01-04 09:18:21 +0000865 }
866 }
867
Richard Uhlerf0bdca52019-01-31 16:43:59 +0000868 if (session == null) {
Richard Uhler2d7c7f0d2019-01-04 09:18:21 +0000869 Log.e(TAG, "Unable to find session id for enabled rollback.");
870 return false;
871 }
872
Richard Uhlerac0d0f92019-02-05 15:53:47 +0000873 // Check to see if this is the apk session for a staged session with
874 // rollback enabled.
875 // TODO: This check could be made more efficient.
876 RollbackData rd = null;
877 synchronized (mLock) {
878 ensureRollbackDataLoadedLocked();
879 for (int i = 0; i < mAvailableRollbacks.size(); ++i) {
880 RollbackData data = mAvailableRollbacks.get(i);
881 if (data.apkSessionId == parentSessionId) {
882 rd = data;
883 break;
884 }
885 }
886 }
887
888 if (rd != null) {
889 // This is the apk session for a staged session. We have already
890 // backed up the apks, we just need to do user data backup.
891 PackageParser.PackageLite newPackage = null;
892 try {
893 newPackage = PackageParser.parsePackageLite(
894 new File(session.resolvedBaseCodePath), 0);
895 } catch (PackageParser.PackageParserException e) {
896 Log.e(TAG, "Unable to parse new package", e);
897 return false;
898 }
899 String packageName = newPackage.packageName;
900 for (PackageRollbackInfo info : rd.packages) {
901 if (info.getPackageName().equals(packageName)) {
Richard Uhler2a5facc2019-02-18 11:33:03 +0000902 info.getInstalledUsers().addAll(IntArray.wrap(installedUsers));
Nikita Ioffe5dcd17972019-02-04 11:08:13 +0000903 mAppDataRollbackHelper.snapshotAppData(rd.rollbackId, info);
Richard Uhlerac0d0f92019-02-05 15:53:47 +0000904 try {
905 mRollbackStore.saveAvailableRollback(rd);
906 } catch (IOException ioe) {
907 // TODO: Hopefully this is okay because we will try
908 // again to save the rollback when the staged session
909 // is applied. Just so long as the device doesn't
910 // reboot before then.
911 Log.e(TAG, "Unable to save rollback info for : " + rd.rollbackId, ioe);
912 }
913 return true;
914 }
915 }
916 Log.e(TAG, "Unable to find package in apk session");
917 return false;
918 }
919
Narayan Kamathfcd4a042019-02-01 14:16:37 +0000920 return enableRollbackForSession(session, installedUsers, true);
Richard Uhlerf0bdca52019-01-31 16:43:59 +0000921 }
922
923 /**
924 * Do code and userdata backups to enable rollback of the given session.
925 * In case of multiPackage sessions, <code>session</code> should be one of
926 * the child sessions, not the parent session.
927 */
928 private boolean enableRollbackForSession(PackageInstaller.SessionInfo session,
Richard Uhler2a5facc2019-02-18 11:33:03 +0000929 @NonNull int[] installedUsers, boolean snapshotUserData) {
Richard Uhlerf0bdca52019-01-31 16:43:59 +0000930 // TODO: Don't attempt to enable rollback for split installs.
931 final int installFlags = session.installFlags;
932 if ((installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) == 0) {
933 Log.e(TAG, "Rollback is not enabled.");
934 return false;
935 }
936 if ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) {
937 Log.e(TAG, "Rollbacks not supported for instant app install");
938 return false;
939 }
940
941 // Get information about the package to be installed.
942 PackageParser.PackageLite newPackage = null;
943 try {
944 newPackage = PackageParser.parsePackageLite(new File(session.resolvedBaseCodePath), 0);
945 } catch (PackageParser.PackageParserException e) {
946 Log.e(TAG, "Unable to parse new package", e);
947 return false;
948 }
949
950 String packageName = newPackage.packageName;
Narayan Kamathfcd4a042019-02-01 14:16:37 +0000951 Log.i(TAG, "Enabling rollback for install of " + packageName
952 + ", session:" + session.sessionId);
Richard Uhlerf0bdca52019-01-31 16:43:59 +0000953
Richard Uhlera7e9b2d2019-01-22 17:20:58 +0000954 VersionedPackage newVersion = new VersionedPackage(packageName, newPackage.versionCode);
Narayan Kamathfcd4a042019-02-01 14:16:37 +0000955 final boolean isApex = ((installFlags & PackageManager.INSTALL_APEX) != 0);
Richard Uhlere95d0552018-12-27 15:03:41 +0000956
957 // Get information about the currently installed package.
Richard Uhler1f571c62019-01-31 15:16:46 +0000958 PackageManager pm = mContext.getPackageManager();
959 PackageInfo pkgInfo = null;
960 try {
Narayan Kamathfcd4a042019-02-01 14:16:37 +0000961 pkgInfo = pm.getPackageInfo(packageName, isApex ? PackageManager.MATCH_APEX : 0);
Richard Uhler1f571c62019-01-31 15:16:46 +0000962 } catch (PackageManager.NameNotFoundException e) {
Richard Uhlere95d0552018-12-27 15:03:41 +0000963 // TODO: Support rolling back fresh package installs rather than
964 // fail here. Test this case.
965 Log.e(TAG, packageName + " is not installed");
966 return false;
967 }
Richard Uhlere95d0552018-12-27 15:03:41 +0000968
Richard Uhler1f571c62019-01-31 15:16:46 +0000969 VersionedPackage installedVersion = new VersionedPackage(packageName,
970 pkgInfo.getLongVersionCode());
Narayan Kamath869f7062019-01-10 12:24:15 +0000971
Narayan Kamathc034fe92019-01-23 10:48:17 +0000972 PackageRollbackInfo info = new PackageRollbackInfo(newVersion, installedVersion,
Nikita Ioffe5dcd17972019-02-04 11:08:13 +0000973 new IntArray() /* pendingBackups */, new ArrayList<>() /* pendingRestores */,
974 isApex, IntArray.wrap(installedUsers),
975 new SparseLongArray() /* ceSnapshotInodes */);
Richard Uhlerf1910c52019-01-09 14:27:36 +0000976 RollbackData data;
Richard Uhlere95d0552018-12-27 15:03:41 +0000977 try {
Richard Uhlerf0bdca52019-01-31 16:43:59 +0000978 int childSessionId = session.getSessionId();
979 int parentSessionId = session.getParentSessionId();
980 if (parentSessionId == PackageInstaller.SessionInfo.INVALID_ID) {
981 parentSessionId = childSessionId;
982 }
Narayan Kamathfcd4a042019-02-01 14:16:37 +0000983
Richard Uhlerf1910c52019-01-09 14:27:36 +0000984 synchronized (mLock) {
Richard Uhlerf0bdca52019-01-31 16:43:59 +0000985 // TODO: no need to add to mChildSessions if childSessionId is
986 // the same as parentSessionId.
Richard Uhlerf1910c52019-01-09 14:27:36 +0000987 mChildSessions.put(childSessionId, parentSessionId);
988 data = mPendingRollbacks.get(parentSessionId);
989 if (data == null) {
Richard Uhlerb9d54472019-01-22 12:50:08 +0000990 int rollbackId = allocateRollbackIdLocked();
Narayan Kamathfcd4a042019-02-01 14:16:37 +0000991 if (session.isStaged()) {
992 data = mRollbackStore.createPendingStagedRollback(rollbackId,
Nikita Ioffe5dcd17972019-02-04 11:08:13 +0000993 parentSessionId);
Narayan Kamathfcd4a042019-02-01 14:16:37 +0000994 } else {
995 data = mRollbackStore.createAvailableRollback(rollbackId);
996 }
Richard Uhlerf1910c52019-01-09 14:27:36 +0000997 mPendingRollbacks.put(parentSessionId, data);
998 }
999 data.packages.add(info);
1000 }
Richard Uhlere95d0552018-12-27 15:03:41 +00001001 } catch (IOException e) {
1002 Log.e(TAG, "Unable to create rollback for " + packageName, e);
1003 return false;
1004 }
1005
Nikita Ioffe5dcd17972019-02-04 11:08:13 +00001006 if (snapshotUserData && !isApex) {
1007 mAppDataRollbackHelper.snapshotAppData(data.rollbackId, info);
1008 }
1009
Richard Uhler1f571c62019-01-31 15:16:46 +00001010 try {
Richard Uhlerab009ea2019-02-25 12:11:05 +00001011 ApplicationInfo appInfo = pkgInfo.applicationInfo;
1012 RollbackStore.backupPackageCodePath(data, packageName, appInfo.sourceDir);
1013 if (!ArrayUtils.isEmpty(appInfo.splitSourceDirs)) {
1014 for (String sourceDir : appInfo.splitSourceDirs) {
1015 RollbackStore.backupPackageCodePath(data, packageName, sourceDir);
1016 }
1017 }
Richard Uhler1f571c62019-01-31 15:16:46 +00001018 } catch (IOException e) {
1019 Log.e(TAG, "Unable to copy package for rollback for " + packageName, e);
Richard Uhlere95d0552018-12-27 15:03:41 +00001020 return false;
1021 }
Richard Uhlere95d0552018-12-27 15:03:41 +00001022 return true;
1023 }
1024
Narayan Kamath869f7062019-01-10 12:24:15 +00001025 @Override
Narayan Kamath67af3272019-01-28 19:37:37 +00001026 public void restoreUserData(String packageName, int[] userIds, int appId, long ceDataInode,
Narayan Kamath869f7062019-01-10 12:24:15 +00001027 String seInfo, int token) {
1028 if (Binder.getCallingUid() != Process.SYSTEM_UID) {
1029 throw new SecurityException("restoureUserData may only be called by the system.");
1030 }
1031
1032 getHandler().post(() -> {
Richard Uhler38fab3f2019-02-22 16:53:10 +00001033 restoreUserDataInternal(packageName, userIds, appId, ceDataInode, seInfo, token);
Narayan Kamathc034fe92019-01-23 10:48:17 +00001034 final PackageManagerInternal pmi = LocalServices.getService(
1035 PackageManagerInternal.class);
Narayan Kamath869f7062019-01-10 12:24:15 +00001036 pmi.finishPackageInstall(token, false);
1037 });
1038 }
1039
Richard Uhler38fab3f2019-02-22 16:53:10 +00001040 private void restoreUserDataInternal(String packageName, int[] userIds, int appId,
1041 long ceDataInode, String seInfo, int token) {
1042 final RollbackData rollbackData = getRollbackForPackage(packageName);
1043 if (rollbackData == null) {
1044 return;
1045 }
1046
1047 if (!rollbackData.inProgress) {
1048 Log.e(TAG, "Request to restore userData for: " + packageName
1049 + ", but no rollback in progress.");
1050 return;
1051 }
1052
1053 for (int userId : userIds) {
1054 final PackageRollbackInfo info = getPackageRollbackInfo(rollbackData, packageName);
1055 final boolean changedRollbackData = mAppDataRollbackHelper.restoreAppData(
1056 rollbackData.rollbackId, info, userId, appId, seInfo);
1057
1058 // We've updated metadata about this rollback, so save it to flash.
1059 if (changedRollbackData) {
1060 try {
1061 mRollbackStore.saveAvailableRollback(rollbackData);
1062 } catch (IOException ioe) {
1063 // TODO(narayan): What is the right thing to do here ? This isn't a fatal
1064 // error, since it will only result in us trying to restore data again,
1065 // which will be a no-op if there's no data available.
1066 Log.e(TAG, "Unable to save available rollback: " + packageName, ioe);
1067 }
1068 }
1069 }
1070 }
1071
Narayan Kamathfcd4a042019-02-01 14:16:37 +00001072 @Override
1073 public boolean notifyStagedSession(int sessionId) {
1074 final LinkedBlockingQueue<Boolean> result = new LinkedBlockingQueue<>();
1075
1076 // NOTE: We post this runnable on the RollbackManager's binder thread because we'd prefer
1077 // to preserve the invariant that all operations that modify state happen there.
1078 getHandler().post(() -> {
1079 PackageInstaller installer = mContext.getPackageManager().getPackageInstaller();
1080
1081 final PackageInstaller.SessionInfo session = installer.getSessionInfo(sessionId);
1082 if (session == null) {
1083 Log.e(TAG, "No matching install session for: " + sessionId);
1084 result.offer(false);
1085 return;
1086 }
1087
1088 if (!session.isMultiPackage()) {
Richard Uhler2a5facc2019-02-18 11:33:03 +00001089 if (!enableRollbackForSession(session, new int[0], false)) {
Narayan Kamathfcd4a042019-02-01 14:16:37 +00001090 Log.e(TAG, "Unable to enable rollback for session: " + sessionId);
1091 result.offer(false);
1092 return;
1093 }
1094 } else {
1095 for (int childSessionId : session.getChildSessionIds()) {
1096 final PackageInstaller.SessionInfo childSession =
1097 installer.getSessionInfo(childSessionId);
1098 if (childSession == null) {
1099 Log.e(TAG, "No matching child install session for: " + childSessionId);
1100 result.offer(false);
1101 return;
1102 }
Richard Uhler2a5facc2019-02-18 11:33:03 +00001103 if (!enableRollbackForSession(childSession, new int[0], false)) {
Narayan Kamathfcd4a042019-02-01 14:16:37 +00001104 Log.e(TAG, "Unable to enable rollback for session: " + sessionId);
1105 result.offer(false);
1106 return;
1107 }
1108 }
1109 }
1110
1111 result.offer(true);
1112 });
1113
1114 try {
1115 return result.take();
1116 } catch (InterruptedException ie) {
1117 Log.e(TAG, "Interrupted while waiting for notifyStagedSession response");
1118 return false;
1119 }
1120 }
1121
Richard Uhler6fa7d132019-02-05 13:55:11 +00001122 @Override
1123 public void notifyStagedApkSession(int originalSessionId, int apkSessionId) {
Richard Uhlerba13ab22019-02-05 15:27:12 +00001124 getHandler().post(() -> {
1125 RollbackData rd = null;
1126 synchronized (mLock) {
1127 ensureRollbackDataLoadedLocked();
1128 for (int i = 0; i < mAvailableRollbacks.size(); ++i) {
1129 RollbackData data = mAvailableRollbacks.get(i);
1130 if (data.stagedSessionId == originalSessionId) {
1131 data.apkSessionId = apkSessionId;
1132 rd = data;
1133 break;
1134 }
1135 }
1136 }
1137
1138 if (rd != null) {
1139 try {
1140 mRollbackStore.saveAvailableRollback(rd);
1141 } catch (IOException ioe) {
1142 Log.e(TAG, "Unable to save rollback info for : " + rd.rollbackId, ioe);
1143 }
1144 }
1145 });
Richard Uhler6fa7d132019-02-05 13:55:11 +00001146 }
1147
Richard Uhlere95d0552018-12-27 15:03:41 +00001148 /**
Richard Uhlere95d0552018-12-27 15:03:41 +00001149 * Gets the version of the package currently installed.
1150 * Returns null if the package is not currently installed.
1151 */
Richard Uhlera7e9b2d2019-01-22 17:20:58 +00001152 private VersionedPackage getInstalledPackageVersion(String packageName) {
Richard Uhlere95d0552018-12-27 15:03:41 +00001153 PackageManager pm = mContext.getPackageManager();
1154 PackageInfo pkgInfo = null;
1155 try {
Richard Uhlerb502afb2019-02-05 12:16:34 +00001156 pkgInfo = pm.getPackageInfo(packageName, PackageManager.MATCH_APEX);
Richard Uhlere95d0552018-12-27 15:03:41 +00001157 } catch (PackageManager.NameNotFoundException e) {
1158 return null;
1159 }
1160
Richard Uhlera7e9b2d2019-01-22 17:20:58 +00001161 return new VersionedPackage(packageName, pkgInfo.getLongVersionCode());
1162 }
1163
1164 private boolean packageVersionsEqual(VersionedPackage a, VersionedPackage b) {
1165 return a.getPackageName().equals(b.getPackageName())
1166 && a.getLongVersionCode() == b.getLongVersionCode();
Richard Uhlere95d0552018-12-27 15:03:41 +00001167 }
Richard Uhler2d7c7f0d2019-01-04 09:18:21 +00001168
1169 private class SessionCallback extends PackageInstaller.SessionCallback {
1170
1171 @Override
1172 public void onCreated(int sessionId) { }
1173
1174 @Override
1175 public void onBadgingChanged(int sessionId) { }
1176
1177 @Override
1178 public void onActiveChanged(int sessionId, boolean active) { }
1179
1180 @Override
1181 public void onProgressChanged(int sessionId, float progress) { }
1182
1183 @Override
1184 public void onFinished(int sessionId, boolean success) {
Narayan Kamathfcd4a042019-02-01 14:16:37 +00001185 // If sessionId refers to a staged session, we can't deal with it here since the
1186 // session might take an unbounded amount of time to become "ready" after the package
1187 // installer session is committed. In those cases, we respond to it in response to
1188 // a session ready broadcast.
1189 PackageInstaller packageInstaller = mContext.getPackageManager().getPackageInstaller();
1190 PackageInstaller.SessionInfo si = packageInstaller.getSessionInfo(sessionId);
1191 if (si != null && si.isStaged()) {
1192 return;
Richard Uhler2d7c7f0d2019-01-04 09:18:21 +00001193 }
1194
Narayan Kamathfcd4a042019-02-01 14:16:37 +00001195 completeEnableRollback(sessionId, success);
1196 }
1197 }
Richard Uhler2d7c7f0d2019-01-04 09:18:21 +00001198
Narayan Kamathfcd4a042019-02-01 14:16:37 +00001199 private void completeEnableRollback(int sessionId, boolean success) {
1200 RollbackData data = null;
1201 synchronized (mLock) {
1202 Integer parentSessionId = mChildSessions.remove(sessionId);
1203 if (parentSessionId != null) {
1204 sessionId = parentSessionId;
1205 }
1206
1207 data = mPendingRollbacks.remove(sessionId);
1208 }
1209
1210 if (data != null) {
1211 if (success) {
1212 try {
1213 data.timestamp = Instant.now();
1214
1215 mRollbackStore.saveAvailableRollback(data);
1216 synchronized (mLock) {
1217 // Note: There is a small window of time between when
1218 // the session has been committed by the package
1219 // manager and when we make the rollback available
1220 // here. Presumably the window is small enough that
1221 // nobody will want to roll back the newly installed
1222 // package before we make the rollback available.
1223 // TODO: We'll lose the rollback data if the
1224 // device reboots between when the session is
1225 // committed and this point. Revisit this after
1226 // adding support for rollback of staged installs.
1227 ensureRollbackDataLoadedLocked();
1228 mAvailableRollbacks.add(data);
Richard Uhler2d7c7f0d2019-01-04 09:18:21 +00001229 }
Narayan Kamathfcd4a042019-02-01 14:16:37 +00001230 // TODO(zezeozue): Provide API to explicitly start observing instead
1231 // of doing this for all rollbacks. If we do this for all rollbacks,
1232 // should document in PackageInstaller.SessionParams#setEnableRollback
1233 // After enabling and commiting any rollback, observe packages and
1234 // prepare to rollback if packages crashes too frequently.
1235 List<String> packages = new ArrayList<>();
1236 for (int i = 0; i < data.packages.size(); i++) {
1237 packages.add(data.packages.get(i).getPackageName());
1238 }
1239 mPackageHealthObserver.startObservingHealth(packages,
shafik0ad18b82019-01-24 16:27:24 +00001240 mRollbackLifetimeDurationInMillis);
1241 scheduleExpiration(mRollbackLifetimeDurationInMillis);
Narayan Kamathfcd4a042019-02-01 14:16:37 +00001242 } catch (IOException e) {
1243 Log.e(TAG, "Unable to enable rollback", e);
Nikita Ioffe952aa7b2019-01-28 19:49:56 +00001244 deleteRollback(data);
Richard Uhler2d7c7f0d2019-01-04 09:18:21 +00001245 }
Narayan Kamathfcd4a042019-02-01 14:16:37 +00001246 } else {
1247 // The install session was aborted, clean up the pending
1248 // install.
Nikita Ioffe952aa7b2019-01-28 19:49:56 +00001249 deleteRollback(data);
Richard Uhler2d7c7f0d2019-01-04 09:18:21 +00001250 }
1251 }
1252 }
Richard Uhler035e9742019-01-09 13:11:07 +00001253
Narayan Kamathfcd4a042019-02-01 14:16:37 +00001254 private void onStagedSessionUpdated(Intent intent) {
1255 PackageInstaller.SessionInfo pi = intent.getParcelableExtra(PackageInstaller.EXTRA_SESSION);
1256 if (pi == null) {
1257 Log.e(TAG, "Missing intent extra: " + PackageInstaller.EXTRA_SESSION);
1258 return;
1259 }
1260
1261 if (pi.isStaged()) {
Dario Freni60a96c12019-02-24 21:01:29 +00001262 if (!pi.isStagedSessionFailed()) {
Narayan Kamathfcd4a042019-02-01 14:16:37 +00001263 // TODO: The session really isn't "enabled" at this point, since more work might
1264 // be required post reboot.
1265 // TODO: We need to make this case consistent with the call from onFinished.
1266 // Ideally, we'd call completeEnableRollback excatly once per multi-package session
1267 // with the parentSessionId only.
Dario Freni60a96c12019-02-24 21:01:29 +00001268 completeEnableRollback(pi.sessionId, pi.isStagedSessionReady());
Narayan Kamathfcd4a042019-02-01 14:16:37 +00001269 } else {
1270 // TODO: Clean up the saved rollback when the session fails. This may need to be
1271 // unified with the case where things fail post reboot.
1272 }
1273 } else {
1274 Log.e(TAG, "Received onStagedSessionUpdated for: " + pi.sessionId
1275 + ", which isn't staged");
1276 }
1277 }
1278
Richard Uhler035e9742019-01-09 13:11:07 +00001279 /*
1280 * Returns the RollbackData, if any, for an available rollback that would
1281 * roll back the given package. Note: This assumes we have at most one
1282 * available rollback for a given package at any one time.
1283 */
1284 private RollbackData getRollbackForPackage(String packageName) {
1285 synchronized (mLock) {
1286 // TODO: Have ensureRollbackDataLoadedLocked return the list of
1287 // available rollbacks, to hopefully avoid forgetting to call it?
1288 ensureRollbackDataLoadedLocked();
1289 for (int i = 0; i < mAvailableRollbacks.size(); ++i) {
1290 RollbackData data = mAvailableRollbacks.get(i);
Richard Uhler60ac7062019-02-05 13:25:39 +00001291 if (data.isAvailable && getPackageRollbackInfo(data, packageName) != null) {
Narayan Kamathc034fe92019-01-23 10:48:17 +00001292 return data;
Richard Uhler035e9742019-01-09 13:11:07 +00001293 }
1294 }
1295 }
1296 return null;
1297 }
Richard Uhlerb9d54472019-01-22 12:50:08 +00001298
Richard Uhler0a79b322019-01-23 13:51:07 +00001299 /*
1300 * Returns the RollbackData, if any, for an available rollback with the
1301 * given rollbackId.
1302 */
1303 private RollbackData getRollbackForId(int rollbackId) {
1304 synchronized (mLock) {
1305 // TODO: Have ensureRollbackDataLoadedLocked return the list of
1306 // available rollbacks, to hopefully avoid forgetting to call it?
1307 ensureRollbackDataLoadedLocked();
1308 for (int i = 0; i < mAvailableRollbacks.size(); ++i) {
1309 RollbackData data = mAvailableRollbacks.get(i);
Richard Uhler60ac7062019-02-05 13:25:39 +00001310 if (data.isAvailable && data.rollbackId == rollbackId) {
Richard Uhler0a79b322019-01-23 13:51:07 +00001311 return data;
1312 }
1313 }
1314 }
Narayan Kamathc034fe92019-01-23 10:48:17 +00001315
1316 return null;
1317 }
1318
1319 /**
1320 * Returns the {@code PackageRollbackInfo} associated with {@code packageName} from
1321 * a specified {@code RollbackData}.
1322 */
Nikita Ioffe5dcd17972019-02-04 11:08:13 +00001323 private static PackageRollbackInfo getPackageRollbackInfo(RollbackData data,
Narayan Kamathc034fe92019-01-23 10:48:17 +00001324 String packageName) {
1325 for (PackageRollbackInfo info : data.packages) {
1326 if (info.getPackageName().equals(packageName)) {
1327 return info;
1328 }
1329 }
1330
Richard Uhler0a79b322019-01-23 13:51:07 +00001331 return null;
1332 }
1333
Richard Uhlerb9d54472019-01-22 12:50:08 +00001334 @GuardedBy("mLock")
1335 private int allocateRollbackIdLocked() throws IOException {
1336 int n = 0;
1337 int rollbackId;
1338 do {
1339 rollbackId = mRandom.nextInt(Integer.MAX_VALUE - 1) + 1;
1340 if (!mAllocatedRollbackIds.get(rollbackId, false)) {
1341 mAllocatedRollbackIds.put(rollbackId, true);
1342 return rollbackId;
1343 }
1344 } while (n++ < 32);
1345
1346 throw new IOException("Failed to allocate rollback ID");
1347 }
Nikita Ioffe952aa7b2019-01-28 19:49:56 +00001348
1349 private void deleteRollback(RollbackData rollbackData) {
1350 for (PackageRollbackInfo info : rollbackData.packages) {
1351 IntArray installedUsers = info.getInstalledUsers();
Nikita Ioffe952aa7b2019-01-28 19:49:56 +00001352 for (int i = 0; i < installedUsers.size(); i++) {
1353 int userId = installedUsers.get(i);
Nikita Ioffe5dcd17972019-02-04 11:08:13 +00001354 mAppDataRollbackHelper.destroyAppDataSnapshot(rollbackData.rollbackId, info,
1355 userId);
Nikita Ioffe952aa7b2019-01-28 19:49:56 +00001356 }
1357 }
1358 mRollbackStore.deleteAvailableRollback(rollbackData);
1359 }
Richard Uhlere95d0552018-12-27 15:03:41 +00001360}