blob: 9a65ae6dba4dc0e6d4b50acb39fb924485210b7f [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 Uhler1fc10c12019-03-18 11:38:46 +000019import android.Manifest;
Richard Uhler2a5facc2019-02-18 11:33:03 +000020import android.annotation.NonNull;
Richard Uhler82913b72019-04-01 13:02:31 +010021import android.annotation.UserIdInt;
Richard Uhlere95d0552018-12-27 15:03:41 +000022import android.app.AppOpsManager;
23import android.content.BroadcastReceiver;
24import android.content.Context;
Richard Uhlere95d0552018-12-27 15:03:41 +000025import android.content.Intent;
26import android.content.IntentFilter;
27import android.content.IntentSender;
Richard Uhlerab009ea2019-02-25 12:11:05 +000028import android.content.pm.ApplicationInfo;
Richard Uhler1fc10c12019-03-18 11:38:46 +000029import android.content.pm.ModuleInfo;
Richard Uhlere95d0552018-12-27 15:03:41 +000030import android.content.pm.PackageInfo;
31import android.content.pm.PackageInstaller;
32import android.content.pm.PackageManager;
33import android.content.pm.PackageManagerInternal;
34import android.content.pm.PackageParser;
35import android.content.pm.ParceledListSlice;
Richard Uhler82913b72019-04-01 13:02:31 +010036import android.content.pm.UserInfo;
Richard Uhlera7e9b2d2019-01-22 17:20:58 +000037import android.content.pm.VersionedPackage;
Richard Uhlere95d0552018-12-27 15:03:41 +000038import android.content.rollback.IRollbackManager;
Richard Uhlere95d0552018-12-27 15:03:41 +000039import android.content.rollback.RollbackInfo;
Richard Uhler2a48c292019-01-28 17:33:48 +000040import android.content.rollback.RollbackManager;
Richard Uhlere95d0552018-12-27 15:03:41 +000041import android.os.Binder;
Richard Uhlere95d0552018-12-27 15:03:41 +000042import android.os.Environment;
43import android.os.Handler;
44import android.os.HandlerThread;
Narayan Kamath869f7062019-01-10 12:24:15 +000045import android.os.Process;
shafikda5e4ee2019-02-12 16:29:01 +000046import android.os.SystemClock;
shafik74fec182019-03-14 16:29:48 +000047import android.os.UserHandle;
Richard Uhler798b8592019-06-07 14:04:43 +010048import android.os.UserManager;
shafik0ad18b82019-01-24 16:27:24 +000049import android.provider.DeviceConfig;
Richard Uhler47569702019-05-02 12:36:39 +010050import android.util.ArraySet;
Narayan Kamathc034fe92019-01-23 10:48:17 +000051import android.util.IntArray;
JW Wangfab29c62019-11-19 16:17:41 +080052import android.util.LongArrayQueue;
shafikbb771fb2019-06-05 14:59:57 +010053import android.util.Slog;
Richard Uhlerb9d54472019-01-22 12:50:08 +000054import android.util.SparseBooleanArray;
Richard Uhlere95d0552018-12-27 15:03:41 +000055
56import com.android.internal.annotations.GuardedBy;
Oli Lan8753d822019-11-01 11:33:53 +000057import com.android.internal.util.DumpUtils;
shafik60046002019-03-12 17:54:10 +000058import com.android.internal.util.IndentingPrintWriter;
Richard Uhlere95d0552018-12-27 15:03:41 +000059import com.android.server.LocalServices;
Gavin Corkeryefb3ff12019-12-02 18:15:24 +000060import com.android.server.PackageWatchdog;
JW Wang9b51b732019-12-11 17:14:15 +080061import com.android.server.SystemConfig;
shafik18b627e2019-05-01 20:41:48 +010062import com.android.server.Watchdog;
Narayan Kamath869f7062019-01-10 12:24:15 +000063import com.android.server.pm.Installer;
Richard Uhlere95d0552018-12-27 15:03:41 +000064
Richard Uhlere95d0552018-12-27 15:03:41 +000065import java.io.File;
shafik60046002019-03-12 17:54:10 +000066import java.io.FileDescriptor;
shafik60046002019-03-12 17:54:10 +000067import java.io.PrintWriter;
Richard Uhlerb9d54472019-01-22 12:50:08 +000068import java.security.SecureRandom;
Richard Uhlere95d0552018-12-27 15:03:41 +000069import java.time.Instant;
Richard Uhlere95d0552018-12-27 15:03:41 +000070import java.time.temporal.ChronoUnit;
71import java.util.ArrayList;
JW Wang12d0c3e2019-12-02 21:01:35 +080072import java.util.Arrays;
Richard Uhler1924d6d2019-04-26 10:20:12 +010073import java.util.HashSet;
Richard Uhlere95d0552018-12-27 15:03:41 +000074import java.util.Iterator;
75import java.util.List;
Richard Uhlerb9d54472019-01-22 12:50:08 +000076import java.util.Random;
Richard Uhlere9aaf632019-03-01 16:03:01 +000077import java.util.Set;
Richard Uhler74ee2992019-07-01 15:21:44 +010078import java.util.concurrent.CountDownLatch;
Narayan Kamathfcd4a042019-02-01 14:16:37 +000079import java.util.concurrent.LinkedBlockingQueue;
shafik0ad18b82019-01-24 16:27:24 +000080import java.util.concurrent.TimeUnit;
Richard Uhlere95d0552018-12-27 15:03:41 +000081
82/**
83 * Implementation of service that manages APK level rollbacks.
84 */
85class RollbackManagerServiceImpl extends IRollbackManager.Stub {
86
87 private static final String TAG = "RollbackManager";
JW Wang12d0c3e2019-12-02 21:01:35 +080088 private static final boolean LOCAL_LOGV = false;
Richard Uhlere95d0552018-12-27 15:03:41 +000089
Matt Pape6686f1f2019-07-09 10:41:20 -070090 // Rollbacks expire after 14 days.
shafik0ad18b82019-01-24 16:27:24 +000091 private static final long DEFAULT_ROLLBACK_LIFETIME_DURATION_MILLIS =
Matt Pape6686f1f2019-07-09 10:41:20 -070092 TimeUnit.DAYS.toMillis(14);
Richard Uhlere95d0552018-12-27 15:03:41 +000093
94 // Lock used to synchronize accesses to in-memory rollback data
95 // structures. By convention, methods with the suffix "Locked" require
96 // mLock is held when they are called.
97 private final Object mLock = new Object();
98
shafik0ad18b82019-01-24 16:27:24 +000099 // No need for guarding with lock because value is only accessed in handler thread
100 // and the value will be written on boot complete. Initialization here happens before
101 // handler threads are running so that's fine.
102 private long mRollbackLifetimeDurationInMillis = DEFAULT_ROLLBACK_LIFETIME_DURATION_MILLIS;
103
shafik18b627e2019-05-01 20:41:48 +0100104 private static final long HANDLER_THREAD_TIMEOUT_DURATION_MILLIS =
105 TimeUnit.MINUTES.toMillis(10);
106
Richard Uhlerb9d54472019-01-22 12:50:08 +0000107 // Used for generating rollback IDs.
108 private final Random mRandom = new SecureRandom();
109
110 // Set of allocated rollback ids
111 @GuardedBy("mLock")
112 private final SparseBooleanArray mAllocatedRollbackIds = new SparseBooleanArray();
113
Richard Uhler8d512ee2019-07-19 14:55:01 +0100114 // Rollbacks we are in the process of enabling.
Richard Uhler2d7c7f0d2019-01-04 09:18:21 +0000115 @GuardedBy("mLock")
Richard Uhler47569702019-05-02 12:36:39 +0100116 private final Set<NewRollback> mNewRollbacks = new ArraySet<>();
Richard Uhlerf1910c52019-01-09 14:27:36 +0000117
Richard Uhler6f8a33b2019-02-26 10:40:36 +0000118 // The list of all rollbacks, including available and committed rollbacks.
Richard Uhlere95d0552018-12-27 15:03:41 +0000119 @GuardedBy("mLock")
Richard Uhler8d512ee2019-07-19 14:55:01 +0100120 private final List<Rollback> mRollbacks;
Richard Uhlere95d0552018-12-27 15:03:41 +0000121
Oli Lan0bf44862019-08-29 14:25:38 +0100122 // Apk sessions from a staged session with no matching rollback.
123 @GuardedBy("mLock")
124 private final IntArray mOrphanedApkSessionIds = new IntArray();
125
Richard Uhler28e73232019-01-21 16:48:55 +0000126 private final RollbackStore mRollbackStore;
Richard Uhlere95d0552018-12-27 15:03:41 +0000127
128 private final Context mContext;
129 private final HandlerThread mHandlerThread;
Narayan Kamath869f7062019-01-10 12:24:15 +0000130 private final Installer mInstaller;
Zimuzoc4073cc2019-01-18 18:39:18 +0000131 private final RollbackPackageHealthObserver mPackageHealthObserver;
Nikita Ioffe952aa7b2019-01-28 19:49:56 +0000132 private final AppDataRollbackHelper mAppDataRollbackHelper;
Richard Uhlere95d0552018-12-27 15:03:41 +0000133
JW Wangfab29c62019-11-19 16:17:41 +0800134 // The # of milli-seconds to sleep for each received ACTION_PACKAGE_ENABLE_ROLLBACK.
135 // Used by #blockRollbackManager to test timeout in enabling rollbacks.
136 // Accessed on the handler thread only.
137 private final LongArrayQueue mSleepDuration = new LongArrayQueue();
138
shafikda5e4ee2019-02-12 16:29:01 +0000139 // This field stores the difference in Millis between the uptime (millis since device
Richard Uhler8d512ee2019-07-19 14:55:01 +0100140 // has booted) and current time (device wall clock) - it's used to update rollback
shafikda5e4ee2019-02-12 16:29:01 +0000141 // timestamps when the time is changed, by the user or by change of timezone.
142 // No need for guarding with lock because value is only accessed in handler thread.
143 private long mRelativeBootTime = calculateRelativeBootTime();
144
Richard Uhlere95d0552018-12-27 15:03:41 +0000145 RollbackManagerServiceImpl(Context context) {
146 mContext = context;
Narayan Kamath869f7062019-01-10 12:24:15 +0000147 // Note that we're calling onStart here because this object is only constructed on
148 // SystemService#onStart.
149 mInstaller = new Installer(mContext);
150 mInstaller.onStart();
shafik18b627e2019-05-01 20:41:48 +0100151
Richard Uhler28e73232019-01-21 16:48:55 +0000152 mRollbackStore = new RollbackStore(new File(Environment.getDataDirectory(), "rollback"));
Richard Uhlere95d0552018-12-27 15:03:41 +0000153
Zimuzoc4073cc2019-01-18 18:39:18 +0000154 mPackageHealthObserver = new RollbackPackageHealthObserver(mContext);
Nikita Ioffe952aa7b2019-01-28 19:49:56 +0000155 mAppDataRollbackHelper = new AppDataRollbackHelper(mInstaller);
Zimuzoc4073cc2019-01-18 18:39:18 +0000156
Richard Uhler74ee2992019-07-01 15:21:44 +0100157 // Load rollback data from device storage.
158 synchronized (mLock) {
Richard Uhler8d512ee2019-07-19 14:55:01 +0100159 mRollbacks = mRollbackStore.loadRollbacks();
160 for (Rollback rollback : mRollbacks) {
161 mAllocatedRollbackIds.put(rollback.info.getRollbackId(), true);
Richard Uhler74ee2992019-07-01 15:21:44 +0100162 }
163 }
164
165 // Kick off and start monitoring the handler thread.
166 mHandlerThread = new HandlerThread("RollbackManagerServiceHandler");
167 mHandlerThread.start();
168 Watchdog.getInstance().addThread(getHandler(), HANDLER_THREAD_TIMEOUT_DURATION_MILLIS);
Richard Uhlere95d0552018-12-27 15:03:41 +0000169
Richard Uhler82913b72019-04-01 13:02:31 +0100170 for (UserInfo userInfo : UserManager.get(mContext).getUsers(true)) {
171 registerUserCallbacks(userInfo.getUserHandle());
172 }
Richard Uhlere95d0552018-12-27 15:03:41 +0000173
174 IntentFilter enableRollbackFilter = new IntentFilter();
175 enableRollbackFilter.addAction(Intent.ACTION_PACKAGE_ENABLE_ROLLBACK);
176 try {
177 enableRollbackFilter.addDataType("application/vnd.android.package-archive");
178 } catch (IntentFilter.MalformedMimeTypeException e) {
shafikbb771fb2019-06-05 14:59:57 +0100179 Slog.e(TAG, "addDataType", e);
Richard Uhlere95d0552018-12-27 15:03:41 +0000180 }
181
182 mContext.registerReceiver(new BroadcastReceiver() {
183 @Override
184 public void onReceive(Context context, Intent intent) {
185 if (Intent.ACTION_PACKAGE_ENABLE_ROLLBACK.equals(intent.getAction())) {
186 int token = intent.getIntExtra(
187 PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_TOKEN, -1);
188 int installFlags = intent.getIntExtra(
189 PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_INSTALL_FLAGS, 0);
Richard Uhler82913b72019-04-01 13:02:31 +0100190 int user = intent.getIntExtra(
191 PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_USER, 0);
192
Richard Uhlere95d0552018-12-27 15:03:41 +0000193 File newPackageCodePath = new File(intent.getData().getPath());
194
JW Wangfab29c62019-11-19 16:17:41 +0800195 queueSleepIfNeeded();
196
Richard Uhlere95d0552018-12-27 15:03:41 +0000197 getHandler().post(() -> {
Oli Lan48f3cf42019-08-09 10:25:49 +0100198 boolean success =
199 enableRollback(installFlags, newPackageCodePath, user, token);
Richard Uhlere95d0552018-12-27 15:03:41 +0000200 int ret = PackageManagerInternal.ENABLE_ROLLBACK_SUCCEEDED;
201 if (!success) {
202 ret = PackageManagerInternal.ENABLE_ROLLBACK_FAILED;
203 }
204
205 PackageManagerInternal pm = LocalServices.getService(
206 PackageManagerInternal.class);
207 pm.setEnableRollbackCode(token, ret);
208 });
209
210 // We're handling the ordered broadcast. Abort the
211 // broadcast because there is no need for it to go to
212 // anyone else.
213 abortBroadcast();
214 }
215 }
216 }, enableRollbackFilter, null, getHandler());
shafikda5e4ee2019-02-12 16:29:01 +0000217
shafik4831ad72019-05-03 17:36:42 +0100218 IntentFilter enableRollbackTimedOutFilter = new IntentFilter();
219 enableRollbackTimedOutFilter.addAction(Intent.ACTION_CANCEL_ENABLE_ROLLBACK);
220
221 mContext.registerReceiver(new BroadcastReceiver() {
222 @Override
223 public void onReceive(Context context, Intent intent) {
224 if (Intent.ACTION_CANCEL_ENABLE_ROLLBACK.equals(intent.getAction())) {
225 int token = intent.getIntExtra(
226 PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_TOKEN, -1);
JW Wang12d0c3e2019-12-02 21:01:35 +0800227 if (LOCAL_LOGV) {
228 Slog.v(TAG, "broadcast=ACTION_CANCEL_ENABLE_ROLLBACK token=" + token);
229 }
shafik4831ad72019-05-03 17:36:42 +0100230 synchronized (mLock) {
231 for (NewRollback rollback : mNewRollbacks) {
232 if (rollback.hasToken(token)) {
Oli Lan4af2f752019-09-27 10:39:32 +0100233 rollback.setCancelled();
shafik4831ad72019-05-03 17:36:42 +0100234 return;
235 }
236 }
237 }
238 }
239 }
240 }, enableRollbackTimedOutFilter, null, getHandler());
241
shafik8aea1ad2019-05-21 12:34:50 +0100242 IntentFilter userAddedIntentFilter = new IntentFilter(Intent.ACTION_USER_ADDED);
243 mContext.registerReceiver(new BroadcastReceiver() {
244 @Override
245 public void onReceive(Context context, Intent intent) {
246 if (Intent.ACTION_USER_ADDED.equals(intent.getAction())) {
247 final int newUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
248 if (newUserId == -1) {
249 return;
250 }
251 registerUserCallbacks(UserHandle.of(newUserId));
252 }
253 }
254 }, userAddedIntentFilter, null, getHandler());
255
shafikda5e4ee2019-02-12 16:29:01 +0000256 registerTimeChangeReceiver();
Richard Uhlere95d0552018-12-27 15:03:41 +0000257 }
258
Richard Uhler82913b72019-04-01 13:02:31 +0100259 private void registerUserCallbacks(UserHandle user) {
260 Context context = getContextAsUser(user);
261 if (context == null) {
shafikbb771fb2019-06-05 14:59:57 +0100262 Slog.e(TAG, "Unable to register user callbacks for user " + user);
Richard Uhler82913b72019-04-01 13:02:31 +0100263 return;
264 }
265
Richard Uhler82913b72019-04-01 13:02:31 +0100266 context.getPackageManager().getPackageInstaller()
267 .registerSessionCallback(new SessionCallback(), getHandler());
268
269 IntentFilter filter = new IntentFilter();
270 filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
271 filter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED);
272 filter.addDataScheme("package");
273 context.registerReceiver(new BroadcastReceiver() {
274 @Override
275 public void onReceive(Context context, Intent intent) {
276 String action = intent.getAction();
277 if (Intent.ACTION_PACKAGE_REPLACED.equals(action)) {
278 String packageName = intent.getData().getSchemeSpecificPart();
JW Wang12d0c3e2019-12-02 21:01:35 +0800279 if (LOCAL_LOGV) {
280 Slog.v(TAG, "broadcast=ACTION_PACKAGE_REPLACED" + " pkg=" + packageName);
281 }
Richard Uhler82913b72019-04-01 13:02:31 +0100282 onPackageReplaced(packageName);
283 }
284 if (Intent.ACTION_PACKAGE_FULLY_REMOVED.equals(action)) {
285 String packageName = intent.getData().getSchemeSpecificPart();
JW Wang12d0c3e2019-12-02 21:01:35 +0800286 if (LOCAL_LOGV) {
287 Slog.v(TAG, "broadcast=ACTION_PACKAGE_FULLY_REMOVED"
288 + " pkg=" + packageName);
289 }
Richard Uhler82913b72019-04-01 13:02:31 +0100290 onPackageFullyRemoved(packageName);
291 }
292 }
293 }, filter, null, getHandler());
294 }
295
Richard Uhlere95d0552018-12-27 15:03:41 +0000296 @Override
Richard Uhler150ad982019-01-23 15:16:10 +0000297 public ParceledListSlice getAvailableRollbacks() {
Richard Uhler1fc10c12019-03-18 11:38:46 +0000298 enforceManageRollbacks("getAvailableRollbacks");
Richard Uhlere95d0552018-12-27 15:03:41 +0000299 synchronized (mLock) {
Richard Uhler150ad982019-01-23 15:16:10 +0000300 List<RollbackInfo> rollbacks = new ArrayList<>();
Richard Uhler6f8a33b2019-02-26 10:40:36 +0000301 for (int i = 0; i < mRollbacks.size(); ++i) {
Richard Uhler8d512ee2019-07-19 14:55:01 +0100302 Rollback rollback = mRollbacks.get(i);
Oli Lan4af2f752019-09-27 10:39:32 +0100303 if (rollback.isAvailable()) {
304 rollbacks.add(rollback.info);
Richard Uhler60ac7062019-02-05 13:25:39 +0000305 }
Richard Uhlere95d0552018-12-27 15:03:41 +0000306 }
Richard Uhler150ad982019-01-23 15:16:10 +0000307 return new ParceledListSlice<>(rollbacks);
Richard Uhlere95d0552018-12-27 15:03:41 +0000308 }
Richard Uhlere95d0552018-12-27 15:03:41 +0000309 }
310
311 @Override
Richard Uhlerf498dab2019-06-28 10:55:45 +0100312 public ParceledListSlice<RollbackInfo> getRecentlyCommittedRollbacks() {
Richard Uhler1fc10c12019-03-18 11:38:46 +0000313 enforceManageRollbacks("getRecentlyCommittedRollbacks");
Richard Uhlere95d0552018-12-27 15:03:41 +0000314
315 synchronized (mLock) {
Richard Uhler6f8a33b2019-02-26 10:40:36 +0000316 List<RollbackInfo> rollbacks = new ArrayList<>();
317 for (int i = 0; i < mRollbacks.size(); ++i) {
Richard Uhler8d512ee2019-07-19 14:55:01 +0100318 Rollback rollback = mRollbacks.get(i);
Oli Lan4af2f752019-09-27 10:39:32 +0100319 if (rollback.isCommitted()) {
320 rollbacks.add(rollback.info);
Richard Uhler6f8a33b2019-02-26 10:40:36 +0000321 }
322 }
Richard Uhlere95d0552018-12-27 15:03:41 +0000323 return new ParceledListSlice<>(rollbacks);
324 }
325 }
326
327 @Override
Richard Uhlerbf5b5c42019-01-28 15:26:37 +0000328 public void commitRollback(int rollbackId, ParceledListSlice causePackages,
329 String callerPackageName, IntentSender statusReceiver) {
Richard Uhlerf498dab2019-06-28 10:55:45 +0100330 enforceManageRollbacks("commitRollback");
Richard Uhlere95d0552018-12-27 15:03:41 +0000331
332 final int callingUid = Binder.getCallingUid();
333 AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class);
334 appOps.checkPackage(callingUid, callerPackageName);
335
336 getHandler().post(() ->
Richard Uhlerbf5b5c42019-01-28 15:26:37 +0000337 commitRollbackInternal(rollbackId, causePackages.getList(),
338 callerPackageName, statusReceiver));
Richard Uhlere95d0552018-12-27 15:03:41 +0000339 }
340
shafikda5e4ee2019-02-12 16:29:01 +0000341 private void registerTimeChangeReceiver() {
342 final BroadcastReceiver timeChangeIntentReceiver = new BroadcastReceiver() {
343 @Override
344 public void onReceive(Context context, Intent intent) {
345 final long oldRelativeBootTime = mRelativeBootTime;
346 mRelativeBootTime = calculateRelativeBootTime();
347 final long timeDifference = mRelativeBootTime - oldRelativeBootTime;
348
349 synchronized (mLock) {
Richard Uhler8d512ee2019-07-19 14:55:01 +0100350 Iterator<Rollback> iter = mRollbacks.iterator();
shafikda5e4ee2019-02-12 16:29:01 +0000351 while (iter.hasNext()) {
Richard Uhler8d512ee2019-07-19 14:55:01 +0100352 Rollback rollback = iter.next();
Oli Lan4af2f752019-09-27 10:39:32 +0100353 rollback.setTimestamp(
354 rollback.getTimestamp().plusMillis(timeDifference));
shafikda5e4ee2019-02-12 16:29:01 +0000355 }
shafikda5e4ee2019-02-12 16:29:01 +0000356 }
357 }
358 };
359 final IntentFilter filter = new IntentFilter();
360 filter.addAction(Intent.ACTION_TIME_CHANGED);
361 mContext.registerReceiver(timeChangeIntentReceiver, filter,
362 null /* broadcastPermission */, getHandler());
363 }
364
365 private static long calculateRelativeBootTime() {
366 return System.currentTimeMillis() - SystemClock.elapsedRealtime();
367 }
368
Richard Uhlere95d0552018-12-27 15:03:41 +0000369 /**
Richard Uhlere87368e2019-01-24 16:34:14 +0000370 * Performs the actual work to commit a rollback.
Richard Uhlere95d0552018-12-27 15:03:41 +0000371 * The work is done on the current thread. This may be a long running
372 * operation.
373 */
Richard Uhlerbf5b5c42019-01-28 15:26:37 +0000374 private void commitRollbackInternal(int rollbackId, List<VersionedPackage> causePackages,
Richard Uhlere95d0552018-12-27 15:03:41 +0000375 String callerPackageName, IntentSender statusReceiver) {
JW Wang12d0c3e2019-12-02 21:01:35 +0800376 Slog.i(TAG, "commitRollback id=" + rollbackId + " caller=" + callerPackageName);
Richard Uhlere95d0552018-12-27 15:03:41 +0000377
Richard Uhler8d512ee2019-07-19 14:55:01 +0100378 Rollback rollback = getRollbackForId(rollbackId);
Oli Lan8c0084d2019-09-11 13:38:43 +0100379 if (rollback == null) {
Oli Lan7363a622019-09-13 10:06:12 +0100380 sendFailure(
381 mContext, statusReceiver, RollbackManager.STATUS_FAILURE_ROLLBACK_UNAVAILABLE,
Richard Uhler2a48c292019-01-28 17:33:48 +0000382 "Rollback unavailable");
Narayan Kamathbc36f8d2019-01-23 12:00:08 +0000383 return;
384 }
Oli Lan4af2f752019-09-27 10:39:32 +0100385 rollback.commit(mContext, causePackages, callerPackageName, statusReceiver);
Richard Uhlere95d0552018-12-27 15:03:41 +0000386 }
387
388 @Override
389 public void reloadPersistedData() {
390 mContext.enforceCallingOrSelfPermission(
Richard Uhler1fc10c12019-03-18 11:38:46 +0000391 Manifest.permission.TEST_MANAGE_ROLLBACKS,
Richard Uhlere95d0552018-12-27 15:03:41 +0000392 "reloadPersistedData");
393
Richard Uhler74ee2992019-07-01 15:21:44 +0100394 CountDownLatch latch = new CountDownLatch(1);
shafik6d61f5e2019-02-26 09:33:26 +0000395 getHandler().post(() -> {
396 updateRollbackLifetimeDurationInMillis();
Richard Uhler74ee2992019-07-01 15:21:44 +0100397 synchronized (mLock) {
398 mRollbacks.clear();
Richard Uhler8d512ee2019-07-19 14:55:01 +0100399 mRollbacks.addAll(mRollbackStore.loadRollbacks());
Richard Uhler74ee2992019-07-01 15:21:44 +0100400 }
401 latch.countDown();
shafik6d61f5e2019-02-26 09:33:26 +0000402 });
Richard Uhler74ee2992019-07-01 15:21:44 +0100403
404 try {
405 latch.await();
406 } catch (InterruptedException ie) {
407 throw new IllegalStateException("RollbackManagerHandlerThread interrupted");
408 }
Richard Uhlere95d0552018-12-27 15:03:41 +0000409 }
410
411 @Override
412 public void expireRollbackForPackage(String packageName) {
413 mContext.enforceCallingOrSelfPermission(
Richard Uhler1fc10c12019-03-18 11:38:46 +0000414 Manifest.permission.TEST_MANAGE_ROLLBACKS,
Richard Uhlere95d0552018-12-27 15:03:41 +0000415 "expireRollbackForPackage");
Richard Uhlere95d0552018-12-27 15:03:41 +0000416 synchronized (mLock) {
Richard Uhler8d512ee2019-07-19 14:55:01 +0100417 Iterator<Rollback> iter = mRollbacks.iterator();
Richard Uhlere95d0552018-12-27 15:03:41 +0000418 while (iter.hasNext()) {
Richard Uhler8d512ee2019-07-19 14:55:01 +0100419 Rollback rollback = iter.next();
Oli Lan4af2f752019-09-27 10:39:32 +0100420 if (rollback.includesPackage(packageName)) {
421 iter.remove();
422 rollback.delete(mAppDataRollbackHelper);
Richard Uhlere95d0552018-12-27 15:03:41 +0000423 }
424 }
Oli Lan06728082019-08-14 11:45:37 +0100425 for (NewRollback newRollback : mNewRollbacks) {
Oli Lan4af2f752019-09-27 10:39:32 +0100426 if (newRollback.rollback.includesPackage(packageName)) {
427 newRollback.setCancelled();
Oli Lan06728082019-08-14 11:45:37 +0100428 }
429 }
Richard Uhlere95d0552018-12-27 15:03:41 +0000430 }
431 }
432
Richard Uhler798b8592019-06-07 14:04:43 +0100433 @Override
434 public void blockRollbackManager(long millis) {
435 mContext.enforceCallingOrSelfPermission(
436 Manifest.permission.TEST_MANAGE_ROLLBACKS,
437 "blockRollbackManager");
438 getHandler().post(() -> {
JW Wangfab29c62019-11-19 16:17:41 +0800439 mSleepDuration.addLast(millis);
440 });
441 }
442
443 private void queueSleepIfNeeded() {
444 if (mSleepDuration.size() == 0) {
445 return;
446 }
447 long millis = mSleepDuration.removeFirst();
448 if (millis <= 0) {
449 return;
450 }
451 getHandler().post(() -> {
Richard Uhler798b8592019-06-07 14:04:43 +0100452 try {
453 Thread.sleep(millis);
454 } catch (InterruptedException e) {
Richard Uhler74ee2992019-07-01 15:21:44 +0100455 throw new IllegalStateException("RollbackManagerHandlerThread interrupted");
Richard Uhler798b8592019-06-07 14:04:43 +0100456 }
457 });
458 }
459
Narayan Kamathc034fe92019-01-23 10:48:17 +0000460 void onUnlockUser(int userId) {
JW Wang12d0c3e2019-12-02 21:01:35 +0800461 if (LOCAL_LOGV) {
462 Slog.v(TAG, "onUnlockUser id=" + userId);
463 }
Gavin Corkeryf1154512019-09-05 11:48:55 +0100464 // In order to ensure that no package begins running while a backup or restore is taking
465 // place, onUnlockUser must remain blocked until all pending backups and restores have
466 // completed.
467 CountDownLatch latch = new CountDownLatch(1);
Narayan Kamathc034fe92019-01-23 10:48:17 +0000468 getHandler().post(() -> {
Richard Uhler8d512ee2019-07-19 14:55:01 +0100469 final List<Rollback> rollbacks;
Narayan Kamathc034fe92019-01-23 10:48:17 +0000470 synchronized (mLock) {
Richard Uhler6f8a33b2019-02-26 10:40:36 +0000471 rollbacks = new ArrayList<>(mRollbacks);
Narayan Kamathc034fe92019-01-23 10:48:17 +0000472 }
473
Oli Lan31f453f2019-09-11 09:58:39 +0100474 for (int i = 0; i < rollbacks.size(); i++) {
475 Rollback rollback = rollbacks.get(i);
Oli Lan4af2f752019-09-27 10:39:32 +0100476 rollback.commitPendingBackupAndRestoreForUser(userId, mAppDataRollbackHelper);
Narayan Kamathc034fe92019-01-23 10:48:17 +0000477 }
Oli Lan31f453f2019-09-11 09:58:39 +0100478
Gavin Corkeryf1154512019-09-05 11:48:55 +0100479 latch.countDown();
Narayan Kamathc034fe92019-01-23 10:48:17 +0000480 });
Gavin Corkeryf1154512019-09-05 11:48:55 +0100481
482 try {
483 latch.await();
484 } catch (InterruptedException ie) {
485 throw new IllegalStateException("RollbackManagerHandlerThread interrupted");
486 }
Narayan Kamathc034fe92019-01-23 10:48:17 +0000487 }
488
shafik0ad18b82019-01-24 16:27:24 +0000489 private void updateRollbackLifetimeDurationInMillis() {
Stanislav Zholnin6b85dac2019-03-07 12:16:04 +0000490 mRollbackLifetimeDurationInMillis = DeviceConfig.getLong(
Matt Pape12187ae2019-03-14 13:37:32 -0700491 DeviceConfig.NAMESPACE_ROLLBACK_BOOT,
492 RollbackManager.PROPERTY_ROLLBACK_LIFETIME_MILLIS,
Stanislav Zholnin6b85dac2019-03-07 12:16:04 +0000493 DEFAULT_ROLLBACK_LIFETIME_DURATION_MILLIS);
shafik15c511f2019-03-08 11:52:08 +0000494 if (mRollbackLifetimeDurationInMillis < 0) {
495 mRollbackLifetimeDurationInMillis = DEFAULT_ROLLBACK_LIFETIME_DURATION_MILLIS;
496 }
shafik0ad18b82019-01-24 16:27:24 +0000497 }
498
Richard Uhlerb2c5cac2019-02-05 16:14:35 +0000499 void onBootCompleted() {
shafik0ad18b82019-01-24 16:27:24 +0000500 getHandler().post(() -> updateRollbackLifetimeDurationInMillis());
501 // Also posts to handler thread
502 scheduleExpiration(0);
503
Richard Uhlerb2c5cac2019-02-05 16:14:35 +0000504 getHandler().post(() -> {
Richard Uhler6f8a33b2019-02-26 10:40:36 +0000505 // Check to see if any rollback-enabled staged sessions or staged
506 // rollback sessions been applied.
Richard Uhler8d512ee2019-07-19 14:55:01 +0100507 List<Rollback> enabling = new ArrayList<>();
508 List<Rollback> restoreInProgress = new ArrayList<>();
Richard Uhler1924d6d2019-04-26 10:20:12 +0100509 Set<String> apexPackageNames = new HashSet<>();
Richard Uhlerb2c5cac2019-02-05 16:14:35 +0000510 synchronized (mLock) {
JW Wanga4ce4332019-10-29 21:26:03 +0800511 Iterator<Rollback> iter = mRollbacks.iterator();
512 while (iter.hasNext()) {
513 Rollback rollback = iter.next();
514 if (!rollback.isStaged()) {
515 // We only care about staged rollbacks here
516 continue;
517 }
518
519 PackageInstaller.SessionInfo session = mContext.getPackageManager()
520 .getPackageInstaller().getSessionInfo(rollback.getStagedSessionId());
521 if (session == null || session.isStagedSessionFailed()) {
522 iter.remove();
523 rollback.delete(mAppDataRollbackHelper);
524 continue;
525 }
526
527 if (session.isStagedSessionApplied()) {
Oli Lan4af2f752019-09-27 10:39:32 +0100528 if (rollback.isEnabling()) {
529 enabling.add(rollback);
530 } else if (rollback.isRestoreUserDataInProgress()) {
531 restoreInProgress.add(rollback);
Richard Uhler1924d6d2019-04-26 10:20:12 +0100532 }
Richard Uhlerb2c5cac2019-02-05 16:14:35 +0000533 }
JW Wanga4ce4332019-10-29 21:26:03 +0800534 apexPackageNames.addAll(rollback.getApexPackageNames());
Richard Uhlerb2c5cac2019-02-05 16:14:35 +0000535 }
536 }
537
Richard Uhler8d512ee2019-07-19 14:55:01 +0100538 for (Rollback rollback : enabling) {
JW Wanga4ce4332019-10-29 21:26:03 +0800539 makeRollbackAvailable(rollback);
Richard Uhlerb2c5cac2019-02-05 16:14:35 +0000540 }
Richard Uhler6f8a33b2019-02-26 10:40:36 +0000541
Richard Uhler8d512ee2019-07-19 14:55:01 +0100542 for (Rollback rollback : restoreInProgress) {
JW Wanga4ce4332019-10-29 21:26:03 +0800543 rollback.setRestoreUserDataInProgress(false);
Richard Uhler6f8a33b2019-02-26 10:40:36 +0000544 }
Zimuzo841c4942019-03-04 12:31:48 +0000545
Richard Uhler1924d6d2019-04-26 10:20:12 +0100546 for (String apexPackageName : apexPackageNames) {
547 // We will not recieve notifications when an apex is updated,
548 // so check now in case any rollbacks ought to be expired. The
549 // onPackagedReplace function is safe to call if the package
550 // hasn't actually been updated.
551 onPackageReplaced(apexPackageName);
552 }
Oli Lan0bf44862019-08-29 14:25:38 +0100553
554 synchronized (mLock) {
555 mOrphanedApkSessionIds.clear();
556 }
557
shafikc5805fb92019-04-29 20:08:07 +0100558 mPackageHealthObserver.onBootCompletedAsync();
Richard Uhlerb2c5cac2019-02-05 16:14:35 +0000559 });
560 }
561
Richard Uhlere95d0552018-12-27 15:03:41 +0000562 /**
Richard Uhlere95d0552018-12-27 15:03:41 +0000563 * Called when a package has been replaced with a different version.
564 * Removes all backups for the package not matching the currently
565 * installed package version.
566 */
567 private void onPackageReplaced(String packageName) {
568 // TODO: Could this end up incorrectly deleting a rollback for a
569 // package that is about to be installed?
Oli Lan3143ee32019-09-18 17:07:21 +0100570 long installedVersion = getInstalledPackageVersion(packageName);
Richard Uhlere95d0552018-12-27 15:03:41 +0000571
572 synchronized (mLock) {
Richard Uhler8d512ee2019-07-19 14:55:01 +0100573 Iterator<Rollback> iter = mRollbacks.iterator();
Richard Uhlere95d0552018-12-27 15:03:41 +0000574 while (iter.hasNext()) {
Richard Uhler8d512ee2019-07-19 14:55:01 +0100575 Rollback rollback = iter.next();
Oli Lan4af2f752019-09-27 10:39:32 +0100576 // TODO: Should we remove rollbacks in the ENABLING state here?
577 if ((rollback.isEnabling() || rollback.isAvailable())
578 && rollback.includesPackageWithDifferentVersion(packageName,
579 installedVersion)) {
580 iter.remove();
581 rollback.delete(mAppDataRollbackHelper);
Richard Uhlere95d0552018-12-27 15:03:41 +0000582 }
583 }
584 }
585 }
586
587 /**
588 * Called when a package has been completely removed from the device.
589 * Removes all backups and rollback history for the given package.
590 */
591 private void onPackageFullyRemoved(String packageName) {
592 expireRollbackForPackage(packageName);
Richard Uhlere95d0552018-12-27 15:03:41 +0000593 }
594
595 /**
596 * Notifies an IntentSender of failure.
597 *
598 * @param statusReceiver where to send the failure
Richard Uhler2a48c292019-01-28 17:33:48 +0000599 * @param status the RollbackManager.STATUS_* code with the failure.
Richard Uhlere95d0552018-12-27 15:03:41 +0000600 * @param message the failure message.
601 */
Oli Lan7363a622019-09-13 10:06:12 +0100602 static void sendFailure(Context context, IntentSender statusReceiver,
603 @RollbackManager.Status int status, String message) {
shafikbb771fb2019-06-05 14:59:57 +0100604 Slog.e(TAG, message);
Richard Uhlere95d0552018-12-27 15:03:41 +0000605 try {
Richard Uhlere95d0552018-12-27 15:03:41 +0000606 final Intent fillIn = new Intent();
Richard Uhler2a48c292019-01-28 17:33:48 +0000607 fillIn.putExtra(RollbackManager.EXTRA_STATUS, status);
608 fillIn.putExtra(RollbackManager.EXTRA_STATUS_MESSAGE, message);
Oli Lan7363a622019-09-13 10:06:12 +0100609 statusReceiver.sendIntent(context, 0, fillIn, null, null);
Richard Uhlere95d0552018-12-27 15:03:41 +0000610 } catch (IntentSender.SendIntentException e) {
611 // Nowhere to send the result back to, so don't bother.
612 }
613 }
614
615 // Check to see if anything needs expiration, and if so, expire it.
616 // Schedules future expiration as appropriate.
Richard Uhlere95d0552018-12-27 15:03:41 +0000617 private void runExpiration() {
618 Instant now = Instant.now();
619 Instant oldest = null;
620 synchronized (mLock) {
Richard Uhler8d512ee2019-07-19 14:55:01 +0100621 Iterator<Rollback> iter = mRollbacks.iterator();
Richard Uhlere95d0552018-12-27 15:03:41 +0000622 while (iter.hasNext()) {
Richard Uhler8d512ee2019-07-19 14:55:01 +0100623 Rollback rollback = iter.next();
Oli Lan4af2f752019-09-27 10:39:32 +0100624 if (!rollback.isAvailable()) {
625 continue;
626 }
627 Instant rollbackTimestamp = rollback.getTimestamp();
628 if (!now.isBefore(
629 rollbackTimestamp
630 .plusMillis(mRollbackLifetimeDurationInMillis))) {
JW Wang12d0c3e2019-12-02 21:01:35 +0800631 if (LOCAL_LOGV) {
632 Slog.v(TAG, "runExpiration id=" + rollback.info.getRollbackId());
633 }
Oli Lan4af2f752019-09-27 10:39:32 +0100634 iter.remove();
635 rollback.delete(mAppDataRollbackHelper);
636 } else if (oldest == null || oldest.isAfter(rollbackTimestamp)) {
637 oldest = rollbackTimestamp;
Richard Uhlere95d0552018-12-27 15:03:41 +0000638 }
639 }
640 }
641
642 if (oldest != null) {
shafik0ad18b82019-01-24 16:27:24 +0000643 scheduleExpiration(now.until(oldest.plusMillis(mRollbackLifetimeDurationInMillis),
Richard Uhlere95d0552018-12-27 15:03:41 +0000644 ChronoUnit.MILLIS));
645 }
646 }
647
648 /**
649 * Schedules an expiration check to be run after the given duration in
650 * milliseconds has gone by.
651 */
652 private void scheduleExpiration(long duration) {
653 getHandler().postDelayed(() -> runExpiration(), duration);
654 }
655
656 private Handler getHandler() {
657 return mHandlerThread.getThreadHandler();
658 }
659
Richard Uhler2d7c7f0d2019-01-04 09:18:21 +0000660 // Returns true if <code>session</code> has installFlags and code path
661 // matching the installFlags and new package code path given to
662 // enableRollback.
663 private boolean sessionMatchesForEnableRollback(PackageInstaller.SessionInfo session,
664 int installFlags, File newPackageCodePath) {
665 if (session == null || session.resolvedBaseCodePath == null) {
666 return false;
667 }
668
669 File packageCodePath = new File(session.resolvedBaseCodePath).getParentFile();
670 if (newPackageCodePath.equals(packageCodePath) && installFlags == session.installFlags) {
671 return true;
672 }
673
674 return false;
675 }
676
Richard Uhler82913b72019-04-01 13:02:31 +0100677 private Context getContextAsUser(UserHandle user) {
678 try {
679 return mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user);
680 } catch (PackageManager.NameNotFoundException e) {
681 return null;
682 }
683 }
684
Richard Uhlere95d0552018-12-27 15:03:41 +0000685 /**
686 * Called via broadcast by the package manager when a package is being
687 * staged for install with rollback enabled. Called before the package has
688 * been installed.
689 *
Richard Uhlere95d0552018-12-27 15:03:41 +0000690 * @param installFlags information about what is being installed.
691 * @param newPackageCodePath path to the package about to be installed.
Richard Uhler82913b72019-04-01 13:02:31 +0100692 * @param user the user that owns the install session to enable rollback on.
shafik4831ad72019-05-03 17:36:42 +0100693 * @param token the distinct rollback token sent by package manager.
Richard Uhlere95d0552018-12-27 15:03:41 +0000694 * @return true if enabling the rollback succeeds, false otherwise.
695 */
Oli Lan48f3cf42019-08-09 10:25:49 +0100696 private boolean enableRollback(
697 int installFlags, File newPackageCodePath, @UserIdInt int user, int token) {
JW Wang12d0c3e2019-12-02 21:01:35 +0800698 if (LOCAL_LOGV) {
699 Slog.v(TAG, "enableRollback user=" + user + " token=" + token
700 + " path=" + newPackageCodePath.getAbsolutePath());
701 }
Richard Uhlere95d0552018-12-27 15:03:41 +0000702
Richard Uhlerf0bdca52019-01-31 16:43:59 +0000703 // Find the session id associated with this install.
704 // TODO: It would be nice if package manager or package installer told
705 // us the session directly, rather than have to search for it
706 // ourselves.
Richard Uhlere95d0552018-12-27 15:03:41 +0000707
Richard Uhler82913b72019-04-01 13:02:31 +0100708 // getAllSessions only returns sessions for the associated user.
709 // Create a context with the right user so we can find the matching
710 // session.
711 final Context context = getContextAsUser(UserHandle.of(user));
712 if (context == null) {
shafikbb771fb2019-06-05 14:59:57 +0100713 Slog.e(TAG, "Unable to create context for install session user.");
Richard Uhler82913b72019-04-01 13:02:31 +0100714 return false;
715 }
716
Richard Uhler47569702019-05-02 12:36:39 +0100717 PackageInstaller.SessionInfo parentSession = null;
718 PackageInstaller.SessionInfo packageSession = null;
Richard Uhler82913b72019-04-01 13:02:31 +0100719 PackageInstaller installer = context.getPackageManager().getPackageInstaller();
Richard Uhler2d7c7f0d2019-01-04 09:18:21 +0000720 for (PackageInstaller.SessionInfo info : installer.getAllSessions()) {
Richard Uhlerf1910c52019-01-09 14:27:36 +0000721 if (info.isMultiPackage()) {
722 for (int childId : info.getChildSessionIds()) {
723 PackageInstaller.SessionInfo child = installer.getSessionInfo(childId);
724 if (sessionMatchesForEnableRollback(child, installFlags, newPackageCodePath)) {
725 // TODO: Check we only have one matching session?
Richard Uhler47569702019-05-02 12:36:39 +0100726 parentSession = info;
727 packageSession = child;
Richard Uhlerf0bdca52019-01-31 16:43:59 +0000728 break;
Richard Uhlerf1910c52019-01-09 14:27:36 +0000729 }
730 }
Richard Uhlerf0bdca52019-01-31 16:43:59 +0000731 } else if (sessionMatchesForEnableRollback(info, installFlags, newPackageCodePath)) {
732 // TODO: Check we only have one matching session?
Richard Uhler47569702019-05-02 12:36:39 +0100733 parentSession = info;
734 packageSession = info;
Richard Uhlerf0bdca52019-01-31 16:43:59 +0000735 break;
Richard Uhler2d7c7f0d2019-01-04 09:18:21 +0000736 }
737 }
738
Richard Uhler47569702019-05-02 12:36:39 +0100739 if (parentSession == null || packageSession == null) {
shafikbb771fb2019-06-05 14:59:57 +0100740 Slog.e(TAG, "Unable to find session for enabled rollback.");
Richard Uhler2d7c7f0d2019-01-04 09:18:21 +0000741 return false;
742 }
743
Richard Uhlerac0d0f92019-02-05 15:53:47 +0000744 // Check to see if this is the apk session for a staged session with
745 // rollback enabled.
Richard Uhlerac0d0f92019-02-05 15:53:47 +0000746 synchronized (mLock) {
Richard Uhler6f8a33b2019-02-26 10:40:36 +0000747 for (int i = 0; i < mRollbacks.size(); ++i) {
Richard Uhler8d512ee2019-07-19 14:55:01 +0100748 Rollback rollback = mRollbacks.get(i);
Oli Lan4af2f752019-09-27 10:39:32 +0100749 if (rollback.getApkSessionId() == parentSession.getSessionId()) {
750 // This is the apk session for a staged session with rollback enabled. We do
751 // not need to create a new rollback for this session.
752 return true;
Richard Uhlerac0d0f92019-02-05 15:53:47 +0000753 }
754 }
Richard Uhlerac0d0f92019-02-05 15:53:47 +0000755 }
756
Oli Lan0bf44862019-08-29 14:25:38 +0100757 // Check to see if this is the apk session for a staged session for which rollback was
758 // cancelled.
759 synchronized (mLock) {
760 if (mOrphanedApkSessionIds.indexOf(parentSession.getSessionId()) != -1) {
761 Slog.w(TAG, "Not enabling rollback for apk as no matching staged session "
762 + "rollback exists");
763 return false;
764 }
765 }
766
Richard Uhler47569702019-05-02 12:36:39 +0100767 NewRollback newRollback;
768 synchronized (mLock) {
769 // See if we already have a NewRollback that contains this package
770 // session. If not, create a NewRollback for the parent session
771 // that we will use for all the packages in the session.
772 newRollback = getNewRollbackForPackageSessionLocked(packageSession.getSessionId());
773 if (newRollback == null) {
774 newRollback = createNewRollbackLocked(parentSession);
775 mNewRollbacks.add(newRollback);
776 }
777 }
shafik4831ad72019-05-03 17:36:42 +0100778 newRollback.addToken(token);
Richard Uhler47569702019-05-02 12:36:39 +0100779
Richard Uhler8d512ee2019-07-19 14:55:01 +0100780 return enableRollbackForPackageSession(newRollback.rollback, packageSession);
Richard Uhlerf0bdca52019-01-31 16:43:59 +0000781 }
782
783 /**
784 * Do code and userdata backups to enable rollback of the given session.
785 * In case of multiPackage sessions, <code>session</code> should be one of
786 * the child sessions, not the parent session.
Richard Uhler47569702019-05-02 12:36:39 +0100787 *
788 * @return true on success, false on failure.
Richard Uhlerf0bdca52019-01-31 16:43:59 +0000789 */
Richard Uhler8d512ee2019-07-19 14:55:01 +0100790 private boolean enableRollbackForPackageSession(Rollback rollback,
Oli Lan48f3cf42019-08-09 10:25:49 +0100791 PackageInstaller.SessionInfo session) {
Richard Uhlerf0bdca52019-01-31 16:43:59 +0000792 // TODO: Don't attempt to enable rollback for split installs.
793 final int installFlags = session.installFlags;
794 if ((installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) == 0) {
shafikbb771fb2019-06-05 14:59:57 +0100795 Slog.e(TAG, "Rollback is not enabled.");
Richard Uhlerf0bdca52019-01-31 16:43:59 +0000796 return false;
797 }
798 if ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) {
shafikbb771fb2019-06-05 14:59:57 +0100799 Slog.e(TAG, "Rollbacks not supported for instant app install");
Richard Uhlerf0bdca52019-01-31 16:43:59 +0000800 return false;
801 }
802
Richard Uhlerfcad49a2019-03-13 10:18:46 +0000803 if (session.resolvedBaseCodePath == null) {
shafikbb771fb2019-06-05 14:59:57 +0100804 Slog.e(TAG, "Session code path has not been resolved.");
Richard Uhlerfcad49a2019-03-13 10:18:46 +0000805 return false;
806 }
807
Richard Uhlerf0bdca52019-01-31 16:43:59 +0000808 // Get information about the package to be installed.
Oli Lan856391f2019-09-25 16:08:01 +0100809 PackageParser.PackageLite newPackage;
Richard Uhlerf0bdca52019-01-31 16:43:59 +0000810 try {
811 newPackage = PackageParser.parsePackageLite(new File(session.resolvedBaseCodePath), 0);
812 } catch (PackageParser.PackageParserException e) {
shafikbb771fb2019-06-05 14:59:57 +0100813 Slog.e(TAG, "Unable to parse new package", e);
Richard Uhlerf0bdca52019-01-31 16:43:59 +0000814 return false;
815 }
816
817 String packageName = newPackage.packageName;
shafikbb771fb2019-06-05 14:59:57 +0100818 Slog.i(TAG, "Enabling rollback for install of " + packageName
Narayan Kamathfcd4a042019-02-01 14:16:37 +0000819 + ", session:" + session.sessionId);
Richard Uhlerf0bdca52019-01-31 16:43:59 +0000820
Richard Uhler1fc10c12019-03-18 11:38:46 +0000821 String installerPackageName = session.getInstallerPackageName();
822 if (!enableRollbackAllowed(installerPackageName, packageName)) {
shafikbb771fb2019-06-05 14:59:57 +0100823 Slog.e(TAG, "Installer " + installerPackageName
Richard Uhler1fc10c12019-03-18 11:38:46 +0000824 + " is not allowed to enable rollback on " + packageName);
825 return false;
826 }
827
Narayan Kamathfcd4a042019-02-01 14:16:37 +0000828 final boolean isApex = ((installFlags & PackageManager.INSTALL_APEX) != 0);
Richard Uhlere95d0552018-12-27 15:03:41 +0000829
830 // Get information about the currently installed package.
Richard Uhler1fc10c12019-03-18 11:38:46 +0000831 final PackageInfo pkgInfo;
Richard Uhler1f571c62019-01-31 15:16:46 +0000832 try {
Richard Uhler82913b72019-04-01 13:02:31 +0100833 pkgInfo = getPackageInfo(packageName);
Richard Uhler1f571c62019-01-31 15:16:46 +0000834 } catch (PackageManager.NameNotFoundException e) {
Richard Uhlere95d0552018-12-27 15:03:41 +0000835 // TODO: Support rolling back fresh package installs rather than
836 // fail here. Test this case.
shafikbb771fb2019-06-05 14:59:57 +0100837 Slog.e(TAG, packageName + " is not installed");
Richard Uhlere95d0552018-12-27 15:03:41 +0000838 return false;
839 }
Richard Uhlere95d0552018-12-27 15:03:41 +0000840
Oli Lan856391f2019-09-25 16:08:01 +0100841 ApplicationInfo appInfo = pkgInfo.applicationInfo;
Oli Lan4af2f752019-09-27 10:39:32 +0100842 return rollback.enableForPackage(packageName, newPackage.versionCode,
843 pkgInfo.getLongVersionCode(), isApex, appInfo.sourceDir,
JW Wang82768bf2019-12-11 10:05:32 +0800844 appInfo.splitSourceDirs, session.rollbackDataPolicy);
Richard Uhlere95d0552018-12-27 15:03:41 +0000845 }
846
Narayan Kamath869f7062019-01-10 12:24:15 +0000847 @Override
Gavin Corkeryab5ee412019-05-29 15:33:35 +0100848 public void snapshotAndRestoreUserData(String packageName, int[] userIds, int appId,
849 long ceDataInode, String seInfo, int token) {
Narayan Kamath869f7062019-01-10 12:24:15 +0000850 if (Binder.getCallingUid() != Process.SYSTEM_UID) {
Gavin Corkeryab5ee412019-05-29 15:33:35 +0100851 throw new SecurityException(
852 "snapshotAndRestoreUserData may only be called by the system.");
Narayan Kamath869f7062019-01-10 12:24:15 +0000853 }
854
855 getHandler().post(() -> {
Oli Lan48f3cf42019-08-09 10:25:49 +0100856 snapshotUserDataInternal(packageName, userIds);
Oli Lan856391f2019-09-25 16:08:01 +0100857 restoreUserDataInternal(packageName, userIds, appId, seInfo);
Narayan Kamathc034fe92019-01-23 10:48:17 +0000858 final PackageManagerInternal pmi = LocalServices.getService(
859 PackageManagerInternal.class);
Narayan Kamath869f7062019-01-10 12:24:15 +0000860 pmi.finishPackageInstall(token, false);
861 });
862 }
863
Oli Lan48f3cf42019-08-09 10:25:49 +0100864 private void snapshotUserDataInternal(String packageName, int[] userIds) {
JW Wang12d0c3e2019-12-02 21:01:35 +0800865 if (LOCAL_LOGV) {
866 Slog.v(TAG, "snapshotUserData pkg=" + packageName
867 + " users=" + Arrays.toString(userIds));
868 }
Gavin Corkeryab5ee412019-05-29 15:33:35 +0100869 synchronized (mLock) {
870 // staged installs
Gavin Corkeryab5ee412019-05-29 15:33:35 +0100871 for (int i = 0; i < mRollbacks.size(); i++) {
Richard Uhler8d512ee2019-07-19 14:55:01 +0100872 Rollback rollback = mRollbacks.get(i);
Oli Lan4af2f752019-09-27 10:39:32 +0100873 rollback.snapshotUserData(packageName, userIds, mAppDataRollbackHelper);
Gavin Corkeryab5ee412019-05-29 15:33:35 +0100874 }
875 // non-staged installs
Gavin Corkeryab5ee412019-05-29 15:33:35 +0100876 for (NewRollback rollback : mNewRollbacks) {
Oli Lan4af2f752019-09-27 10:39:32 +0100877 rollback.rollback.snapshotUserData(
878 packageName, userIds, mAppDataRollbackHelper);
Gavin Corkeryab5ee412019-05-29 15:33:35 +0100879 }
880 }
881 }
882
Oli Lan856391f2019-09-25 16:08:01 +0100883 private void restoreUserDataInternal(
884 String packageName, int[] userIds, int appId, String seInfo) {
JW Wang12d0c3e2019-12-02 21:01:35 +0800885 if (LOCAL_LOGV) {
886 Slog.v(TAG, "restoreUserData pkg=" + packageName
887 + " users=" + Arrays.toString(userIds));
888 }
Richard Uhler6f8a33b2019-02-26 10:40:36 +0000889 synchronized (mLock) {
Richard Uhler6f8a33b2019-02-26 10:40:36 +0000890 for (int i = 0; i < mRollbacks.size(); ++i) {
Oli Lan856391f2019-09-25 16:08:01 +0100891 Rollback rollback = mRollbacks.get(i);
Oli Lan4af2f752019-09-27 10:39:32 +0100892 if (rollback.restoreUserDataForPackageIfInProgress(
893 packageName, userIds, appId, seInfo, mAppDataRollbackHelper)) {
894 return;
Richard Uhler6f8a33b2019-02-26 10:40:36 +0000895 }
896 }
897 }
Richard Uhler38fab3f2019-02-22 16:53:10 +0000898 }
899
Narayan Kamathfcd4a042019-02-01 14:16:37 +0000900 @Override
Oli Lanc72b0bb2019-12-02 14:03:55 +0000901 public int notifyStagedSession(int sessionId) {
shafik3be41a92019-03-25 14:10:32 +0000902 if (Binder.getCallingUid() != Process.SYSTEM_UID) {
903 throw new SecurityException("notifyStagedSession may only be called by the system.");
904 }
Oli Lanc72b0bb2019-12-02 14:03:55 +0000905 final LinkedBlockingQueue<Integer> result = new LinkedBlockingQueue<>();
Narayan Kamathfcd4a042019-02-01 14:16:37 +0000906
907 // NOTE: We post this runnable on the RollbackManager's binder thread because we'd prefer
908 // to preserve the invariant that all operations that modify state happen there.
909 getHandler().post(() -> {
910 PackageInstaller installer = mContext.getPackageManager().getPackageInstaller();
911
912 final PackageInstaller.SessionInfo session = installer.getSessionInfo(sessionId);
913 if (session == null) {
shafikbb771fb2019-06-05 14:59:57 +0100914 Slog.e(TAG, "No matching install session for: " + sessionId);
Oli Lanc72b0bb2019-12-02 14:03:55 +0000915 result.offer(-1);
Narayan Kamathfcd4a042019-02-01 14:16:37 +0000916 return;
917 }
918
Richard Uhler47569702019-05-02 12:36:39 +0100919 NewRollback newRollback;
920 synchronized (mLock) {
921 newRollback = createNewRollbackLocked(session);
922 }
923
Narayan Kamathfcd4a042019-02-01 14:16:37 +0000924 if (!session.isMultiPackage()) {
Richard Uhler8d512ee2019-07-19 14:55:01 +0100925 if (!enableRollbackForPackageSession(newRollback.rollback, session)) {
shafikbb771fb2019-06-05 14:59:57 +0100926 Slog.e(TAG, "Unable to enable rollback for session: " + sessionId);
Oli Lanc72b0bb2019-12-02 14:03:55 +0000927 result.offer(-1);
Narayan Kamathfcd4a042019-02-01 14:16:37 +0000928 return;
929 }
930 } else {
931 for (int childSessionId : session.getChildSessionIds()) {
932 final PackageInstaller.SessionInfo childSession =
933 installer.getSessionInfo(childSessionId);
934 if (childSession == null) {
shafikbb771fb2019-06-05 14:59:57 +0100935 Slog.e(TAG, "No matching child install session for: " + childSessionId);
Oli Lanc72b0bb2019-12-02 14:03:55 +0000936 result.offer(-1);
Narayan Kamathfcd4a042019-02-01 14:16:37 +0000937 return;
938 }
Richard Uhler8d512ee2019-07-19 14:55:01 +0100939 if (!enableRollbackForPackageSession(newRollback.rollback, childSession)) {
shafikbb771fb2019-06-05 14:59:57 +0100940 Slog.e(TAG, "Unable to enable rollback for session: " + sessionId);
Oli Lanc72b0bb2019-12-02 14:03:55 +0000941 result.offer(-1);
Narayan Kamathfcd4a042019-02-01 14:16:37 +0000942 return;
943 }
944 }
945 }
946
Oli Lanc72b0bb2019-12-02 14:03:55 +0000947 Rollback rollback = completeEnableRollback(newRollback, true);
948 if (rollback == null) {
949 result.offer(-1);
950 } else {
951 result.offer(rollback.info.getRollbackId());
952 }
Narayan Kamathfcd4a042019-02-01 14:16:37 +0000953 });
954
955 try {
956 return result.take();
957 } catch (InterruptedException ie) {
shafikbb771fb2019-06-05 14:59:57 +0100958 Slog.e(TAG, "Interrupted while waiting for notifyStagedSession response");
Oli Lanc72b0bb2019-12-02 14:03:55 +0000959 return -1;
Narayan Kamathfcd4a042019-02-01 14:16:37 +0000960 }
961 }
962
Richard Uhler6fa7d132019-02-05 13:55:11 +0000963 @Override
964 public void notifyStagedApkSession(int originalSessionId, int apkSessionId) {
shafik3be41a92019-03-25 14:10:32 +0000965 if (Binder.getCallingUid() != Process.SYSTEM_UID) {
966 throw new SecurityException("notifyStagedApkSession may only be called by the system.");
967 }
Richard Uhlerba13ab22019-02-05 15:27:12 +0000968 getHandler().post(() -> {
Richard Uhler8d512ee2019-07-19 14:55:01 +0100969 Rollback rollback = null;
Richard Uhlerba13ab22019-02-05 15:27:12 +0000970 synchronized (mLock) {
Richard Uhler6f8a33b2019-02-26 10:40:36 +0000971 for (int i = 0; i < mRollbacks.size(); ++i) {
Richard Uhler8d512ee2019-07-19 14:55:01 +0100972 Rollback candidate = mRollbacks.get(i);
Oli Land0c30412019-09-09 18:21:54 +0100973 if (candidate.getStagedSessionId() == originalSessionId) {
Richard Uhler8d512ee2019-07-19 14:55:01 +0100974 rollback = candidate;
Richard Uhlerba13ab22019-02-05 15:27:12 +0000975 break;
976 }
977 }
Oli Lan0bf44862019-08-29 14:25:38 +0100978 if (rollback == null) {
979 // Did not find rollback matching originalSessionId.
980 Slog.e(TAG, "notifyStagedApkSession did not find rollback for session "
981 + originalSessionId
982 + ". Adding orphaned apk session " + apkSessionId);
983 mOrphanedApkSessionIds.add(apkSessionId);
984 }
Richard Uhlerba13ab22019-02-05 15:27:12 +0000985 }
986
Richard Uhler8d512ee2019-07-19 14:55:01 +0100987 if (rollback != null) {
Oli Lan4af2f752019-09-27 10:39:32 +0100988 rollback.setApkSessionId(apkSessionId);
Richard Uhlerba13ab22019-02-05 15:27:12 +0000989 }
990 });
Richard Uhler6fa7d132019-02-05 13:55:11 +0000991 }
992
Richard Uhlere95d0552018-12-27 15:03:41 +0000993 /**
Richard Uhler1fc10c12019-03-18 11:38:46 +0000994 * Returns true if the installer is allowed to enable rollback for the
995 * given named package, false otherwise.
996 */
997 private boolean enableRollbackAllowed(String installerPackageName, String packageName) {
998 if (installerPackageName == null) {
999 return false;
1000 }
1001
1002 PackageManager pm = mContext.getPackageManager();
1003 boolean manageRollbacksGranted = pm.checkPermission(
1004 Manifest.permission.MANAGE_ROLLBACKS,
1005 installerPackageName) == PackageManager.PERMISSION_GRANTED;
1006
1007 boolean testManageRollbacksGranted = pm.checkPermission(
1008 Manifest.permission.TEST_MANAGE_ROLLBACKS,
1009 installerPackageName) == PackageManager.PERMISSION_GRANTED;
1010
1011 // For now only allow rollbacks for modules or for testing.
JW Wang9b51b732019-12-11 17:14:15 +08001012 return (isRollbackWhitelisted(packageName) && manageRollbacksGranted)
Richard Uhler1fc10c12019-03-18 11:38:46 +00001013 || testManageRollbacksGranted;
1014 }
1015
1016 /**
JW Wang9b51b732019-12-11 17:14:15 +08001017 * Returns true is this package is eligible for enabling rollback.
1018 */
1019 private boolean isRollbackWhitelisted(String packageName) {
1020 // TODO: Remove #isModule when the white list is ready.
1021 return SystemConfig.getInstance().getRollbackWhitelistedPackages().contains(packageName)
1022 || isModule(packageName);
1023 }
1024 /**
Richard Uhler1fc10c12019-03-18 11:38:46 +00001025 * Returns true if the package name is the name of a module.
1026 */
1027 private boolean isModule(String packageName) {
1028 PackageManager pm = mContext.getPackageManager();
1029 final ModuleInfo moduleInfo;
1030 try {
1031 moduleInfo = pm.getModuleInfo(packageName, 0);
1032 } catch (PackageManager.NameNotFoundException e) {
1033 return false;
1034 }
1035
1036 return moduleInfo != null;
1037 }
1038
1039 /**
Richard Uhlere95d0552018-12-27 15:03:41 +00001040 * Gets the version of the package currently installed.
Oli Lan3143ee32019-09-18 17:07:21 +01001041 * Returns -1 if the package is not currently installed.
Richard Uhlere95d0552018-12-27 15:03:41 +00001042 */
Oli Lan3143ee32019-09-18 17:07:21 +01001043 private long getInstalledPackageVersion(String packageName) {
Oli Lan856391f2019-09-25 16:08:01 +01001044 PackageInfo pkgInfo;
Richard Uhlere95d0552018-12-27 15:03:41 +00001045 try {
Richard Uhler82913b72019-04-01 13:02:31 +01001046 pkgInfo = getPackageInfo(packageName);
Richard Uhlere95d0552018-12-27 15:03:41 +00001047 } catch (PackageManager.NameNotFoundException e) {
Oli Lan3143ee32019-09-18 17:07:21 +01001048 return -1;
Richard Uhlere95d0552018-12-27 15:03:41 +00001049 }
1050
Oli Lan3143ee32019-09-18 17:07:21 +01001051 return pkgInfo.getLongVersionCode();
Richard Uhlera7e9b2d2019-01-22 17:20:58 +00001052 }
1053
Richard Uhler82913b72019-04-01 13:02:31 +01001054 /**
Oli Lan856391f2019-09-25 16:08:01 +01001055 * Gets PackageInfo for the given package. Matches any user and apex.
1056 *
1057 * @throws PackageManager.NameNotFoundException if no such package is installed.
Richard Uhler82913b72019-04-01 13:02:31 +01001058 */
1059 private PackageInfo getPackageInfo(String packageName)
1060 throws PackageManager.NameNotFoundException {
1061 PackageManager pm = mContext.getPackageManager();
1062 try {
1063 // The MATCH_ANY_USER flag doesn't mix well with the MATCH_APEX
1064 // flag, so make two separate attempts to get the package info.
1065 // We don't need both flags at the same time because we assume
1066 // apex files are always installed for all users.
1067 return pm.getPackageInfo(packageName, PackageManager.MATCH_ANY_USER);
1068 } catch (PackageManager.NameNotFoundException e) {
1069 return pm.getPackageInfo(packageName, PackageManager.MATCH_APEX);
1070 }
1071 }
1072
Richard Uhler2d7c7f0d2019-01-04 09:18:21 +00001073 private class SessionCallback extends PackageInstaller.SessionCallback {
1074
1075 @Override
1076 public void onCreated(int sessionId) { }
1077
1078 @Override
1079 public void onBadgingChanged(int sessionId) { }
1080
1081 @Override
1082 public void onActiveChanged(int sessionId, boolean active) { }
1083
1084 @Override
1085 public void onProgressChanged(int sessionId, float progress) { }
1086
1087 @Override
1088 public void onFinished(int sessionId, boolean success) {
JW Wang12d0c3e2019-12-02 21:01:35 +08001089 if (LOCAL_LOGV) {
1090 Slog.v(TAG, "SessionCallback.onFinished id=" + sessionId + " success=" + success);
1091 }
Richard Uhler47569702019-05-02 12:36:39 +01001092 NewRollback newRollback;
1093 synchronized (mLock) {
1094 newRollback = getNewRollbackForPackageSessionLocked(sessionId);
1095 if (newRollback != null) {
1096 mNewRollbacks.remove(newRollback);
1097 }
1098 }
1099
1100 if (newRollback != null) {
Richard Uhler8d512ee2019-07-19 14:55:01 +01001101 Rollback rollback = completeEnableRollback(newRollback, success);
Oli Lan4af2f752019-09-27 10:39:32 +01001102 if (rollback != null && !rollback.isStaged()) {
1103 makeRollbackAvailable(rollback);
Richard Uhler47569702019-05-02 12:36:39 +01001104 }
Richard Uhler2d7c7f0d2019-01-04 09:18:21 +00001105 }
JW Wangfab29c62019-11-19 16:17:41 +08001106
1107 // Clear the queue so it will never be leaked to next tests.
1108 mSleepDuration.clear();
Narayan Kamathfcd4a042019-02-01 14:16:37 +00001109 }
1110 }
Richard Uhler2d7c7f0d2019-01-04 09:18:21 +00001111
Richard Uhler6f8a33b2019-02-26 10:40:36 +00001112 /**
Oli Lan8c0084d2019-09-11 13:38:43 +01001113 * Add a rollback to the list of rollbacks. This should be called after rollback has been
1114 * enabled for all packages in the rollback. It does not make the rollback available yet.
1115 *
Richard Uhler8d512ee2019-07-19 14:55:01 +01001116 * @return the Rollback instance for a successfully enable-completed rollback,
Richard Uhler47569702019-05-02 12:36:39 +01001117 * or null on error.
Richard Uhler6f8a33b2019-02-26 10:40:36 +00001118 */
Richard Uhler8d512ee2019-07-19 14:55:01 +01001119 private Rollback completeEnableRollback(NewRollback newRollback, boolean success) {
1120 Rollback rollback = newRollback.rollback;
JW Wang12d0c3e2019-12-02 21:01:35 +08001121 if (LOCAL_LOGV) {
1122 Slog.v(TAG, "completeEnableRollback id="
1123 + rollback.info.getRollbackId() + " success=" + success);
1124 }
Oli Lan4af2f752019-09-27 10:39:32 +01001125 if (!success) {
1126 // The install session was aborted, clean up the pending install.
1127 rollback.delete(mAppDataRollbackHelper);
1128 return null;
Oli Lan8c0084d2019-09-11 13:38:43 +01001129 }
Oli Lan4af2f752019-09-27 10:39:32 +01001130
1131 if (newRollback.isCancelled()) {
1132 Slog.e(TAG, "Rollback has been cancelled by PackageManager");
1133 rollback.delete(mAppDataRollbackHelper);
1134 return null;
1135 }
1136
1137 if (rollback.getPackageCount() != newRollback.getPackageSessionIdCount()) {
1138 Slog.e(TAG, "Failed to enable rollback for all packages in session.");
1139 rollback.delete(mAppDataRollbackHelper);
1140 return null;
1141 }
1142
1143 rollback.saveRollback();
Richard Uhler035e9742019-01-09 13:11:07 +00001144 synchronized (mLock) {
Richard Uhler6f8a33b2019-02-26 10:40:36 +00001145 // Note: There is a small window of time between when
1146 // the session has been committed by the package
1147 // manager and when we make the rollback available
1148 // here. Presumably the window is small enough that
1149 // nobody will want to roll back the newly installed
1150 // package before we make the rollback available.
Richard Uhler8d512ee2019-07-19 14:55:01 +01001151 // TODO: We'll lose the rollback if the
Richard Uhler6f8a33b2019-02-26 10:40:36 +00001152 // device reboots between when the session is
1153 // committed and this point. Revisit this after
1154 // adding support for rollback of staged installs.
Richard Uhler8d512ee2019-07-19 14:55:01 +01001155 mRollbacks.add(rollback);
Richard Uhler035e9742019-01-09 13:11:07 +00001156 }
Richard Uhler6f8a33b2019-02-26 10:40:36 +00001157
Richard Uhler8d512ee2019-07-19 14:55:01 +01001158 return rollback;
Richard Uhler6f8a33b2019-02-26 10:40:36 +00001159 }
1160
Oli Lan8c0084d2019-09-11 13:38:43 +01001161 @GuardedBy("rollback.getLock")
Richard Uhler8d512ee2019-07-19 14:55:01 +01001162 private void makeRollbackAvailable(Rollback rollback) {
JW Wang12d0c3e2019-12-02 21:01:35 +08001163 if (LOCAL_LOGV) {
1164 Slog.v(TAG, "makeRollbackAvailable id=" + rollback.info.getRollbackId());
1165 }
Oli Lan7363a622019-09-13 10:06:12 +01001166 rollback.makeAvailable();
Richard Uhler6f8a33b2019-02-26 10:40:36 +00001167
1168 // TODO(zezeozue): Provide API to explicitly start observing instead
1169 // of doing this for all rollbacks. If we do this for all rollbacks,
1170 // should document in PackageInstaller.SessionParams#setEnableRollback
Oli Lan7363a622019-09-13 10:06:12 +01001171 // After enabling and committing any rollback, observe packages and
Richard Uhler6f8a33b2019-02-26 10:40:36 +00001172 // prepare to rollback if packages crashes too frequently.
Oli Lan3143ee32019-09-18 17:07:21 +01001173 mPackageHealthObserver.startObservingHealth(rollback.getPackageNames(),
Richard Uhler6f8a33b2019-02-26 10:40:36 +00001174 mRollbackLifetimeDurationInMillis);
1175 scheduleExpiration(mRollbackLifetimeDurationInMillis);
Richard Uhler035e9742019-01-09 13:11:07 +00001176 }
Richard Uhlerb9d54472019-01-22 12:50:08 +00001177
Richard Uhler0a79b322019-01-23 13:51:07 +00001178 /*
Richard Uhler8d512ee2019-07-19 14:55:01 +01001179 * Returns the rollback with the given rollbackId, if any.
Richard Uhler0a79b322019-01-23 13:51:07 +00001180 */
Richard Uhler8d512ee2019-07-19 14:55:01 +01001181 private Rollback getRollbackForId(int rollbackId) {
Richard Uhler0a79b322019-01-23 13:51:07 +00001182 synchronized (mLock) {
Richard Uhler6f8a33b2019-02-26 10:40:36 +00001183 for (int i = 0; i < mRollbacks.size(); ++i) {
Richard Uhler8d512ee2019-07-19 14:55:01 +01001184 Rollback rollback = mRollbacks.get(i);
1185 if (rollback.info.getRollbackId() == rollbackId) {
1186 return rollback;
Richard Uhler0a79b322019-01-23 13:51:07 +00001187 }
1188 }
1189 }
Narayan Kamathc034fe92019-01-23 10:48:17 +00001190
1191 return null;
1192 }
1193
Richard Uhlerb9d54472019-01-22 12:50:08 +00001194 @GuardedBy("mLock")
Richard Uhler47569702019-05-02 12:36:39 +01001195 private int allocateRollbackIdLocked() {
Richard Uhlerb9d54472019-01-22 12:50:08 +00001196 int n = 0;
1197 int rollbackId;
1198 do {
1199 rollbackId = mRandom.nextInt(Integer.MAX_VALUE - 1) + 1;
1200 if (!mAllocatedRollbackIds.get(rollbackId, false)) {
1201 mAllocatedRollbackIds.put(rollbackId, true);
1202 return rollbackId;
1203 }
1204 } while (n++ < 32);
1205
Richard Uhler47569702019-05-02 12:36:39 +01001206 throw new IllegalStateException("Failed to allocate rollback ID");
Richard Uhlerb9d54472019-01-22 12:50:08 +00001207 }
Nikita Ioffe952aa7b2019-01-28 19:49:56 +00001208
shafik60046002019-03-12 17:54:10 +00001209 @Override
1210 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Oli Lan8753d822019-11-01 11:33:53 +00001211 if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
1212
shafik60046002019-03-12 17:54:10 +00001213 IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
1214 synchronized (mLock) {
Richard Uhler8d512ee2019-07-19 14:55:01 +01001215 for (Rollback rollback : mRollbacks) {
Oli Lan4af2f752019-09-27 10:39:32 +01001216 rollback.dump(ipw);
shafik60046002019-03-12 17:54:10 +00001217 }
Gavin Corkeryefb3ff12019-12-02 18:15:24 +00001218 ipw.println();
1219 PackageWatchdog.getInstance(mContext).dump(ipw);
shafik60046002019-03-12 17:54:10 +00001220 }
1221 }
Richard Uhler1fc10c12019-03-18 11:38:46 +00001222
1223 private void enforceManageRollbacks(@NonNull String message) {
1224 if ((PackageManager.PERMISSION_GRANTED != mContext.checkCallingOrSelfPermission(
1225 Manifest.permission.MANAGE_ROLLBACKS))
1226 && (PackageManager.PERMISSION_GRANTED != mContext.checkCallingOrSelfPermission(
1227 Manifest.permission.TEST_MANAGE_ROLLBACKS))) {
1228 throw new SecurityException(message + " requires "
1229 + Manifest.permission.MANAGE_ROLLBACKS + " or "
1230 + Manifest.permission.TEST_MANAGE_ROLLBACKS);
1231 }
1232 }
Richard Uhler47569702019-05-02 12:36:39 +01001233
1234 private static class NewRollback {
Richard Uhler8d512ee2019-07-19 14:55:01 +01001235 public final Rollback rollback;
Richard Uhler47569702019-05-02 12:36:39 +01001236
1237 /**
Oli Lan4af2f752019-09-27 10:39:32 +01001238 * This array holds all of the rollback tokens associated with package sessions included in
1239 * this rollback.
shafik4831ad72019-05-03 17:36:42 +01001240 */
Oli Lan4af2f752019-09-27 10:39:32 +01001241 @GuardedBy("mNewRollbackLock")
shafik4831ad72019-05-03 17:36:42 +01001242 private final IntArray mTokens = new IntArray();
1243
1244 /**
Oli Lan4af2f752019-09-27 10:39:32 +01001245 * Session ids for all packages in the install. For multi-package sessions, this is the list
1246 * of child session ids. For normal sessions, this list is a single element with the normal
Richard Uhler47569702019-05-02 12:36:39 +01001247 * session id.
1248 */
Oli Lan4af2f752019-09-27 10:39:32 +01001249 private final int[] mPackageSessionIds;
1250
1251 @GuardedBy("mNewRollbackLock")
1252 private boolean mIsCancelled = false;
1253
1254 private final Object mNewRollbackLock = new Object();
1255
1256 NewRollback(Rollback rollback, int[] packageSessionIds) {
1257 this.rollback = rollback;
1258 this.mPackageSessionIds = packageSessionIds;
1259 }
Richard Uhler47569702019-05-02 12:36:39 +01001260
shafik4831ad72019-05-03 17:36:42 +01001261 /**
Oli Lan4af2f752019-09-27 10:39:32 +01001262 * Adds a rollback token to be associated with this NewRollback. This may be used to
1263 * identify which rollback should be cancelled in case {@link PackageManager} sends an
1264 * {@link Intent#ACTION_CANCEL_ENABLE_ROLLBACK} intent.
1265 */
1266 void addToken(int token) {
1267 synchronized (mNewRollbackLock) {
1268 mTokens.add(token);
1269 }
1270 }
1271
1272 /**
1273 * Returns true if this NewRollback is associated with the provided {@code token}.
1274 */
1275 boolean hasToken(int token) {
1276 synchronized (mNewRollbackLock) {
1277 return mTokens.indexOf(token) != -1;
1278 }
1279 }
1280
1281 /**
1282 * Returns true if this NewRollback has been cancelled.
shafik4831ad72019-05-03 17:36:42 +01001283 *
Richard Uhler8d512ee2019-07-19 14:55:01 +01001284 * <p>Rollback could be invalidated and cancelled if RollbackManager receives
shafik4831ad72019-05-03 17:36:42 +01001285 * {@link Intent#ACTION_CANCEL_ENABLE_ROLLBACK} from {@link PackageManager}.
1286 *
1287 * <p>The main underlying assumption here is that if enabling the rollback times out, then
1288 * {@link PackageManager} will NOT send
1289 * {@link PackageInstaller.SessionCallback#onFinished(int, boolean)} before it broadcasts
1290 * {@link Intent#ACTION_CANCEL_ENABLE_ROLLBACK}.
1291 */
Oli Lan4af2f752019-09-27 10:39:32 +01001292 boolean isCancelled() {
1293 synchronized (mNewRollbackLock) {
1294 return mIsCancelled;
1295 }
Richard Uhler47569702019-05-02 12:36:39 +01001296 }
shafik4831ad72019-05-03 17:36:42 +01001297
Oli Lan4af2f752019-09-27 10:39:32 +01001298 /**
1299 * Sets this NewRollback to be marked as cancelled.
1300 */
1301 void setCancelled() {
1302 synchronized (mNewRollbackLock) {
1303 mIsCancelled = true;
1304 }
shafik4831ad72019-05-03 17:36:42 +01001305 }
1306
Oli Lan4af2f752019-09-27 10:39:32 +01001307 /**
1308 * Returns true if this NewRollback contains the provided {@code packageSessionId}.
1309 */
1310 boolean containsSessionId(int packageSessionId) {
1311 for (int id : mPackageSessionIds) {
1312 if (id == packageSessionId) {
1313 return true;
1314 }
1315 }
1316 return false;
1317 }
1318
1319 /**
1320 * Returns the number of package session ids in this NewRollback.
1321 */
1322 int getPackageSessionIdCount() {
1323 return mPackageSessionIds.length;
shafik4831ad72019-05-03 17:36:42 +01001324 }
Richard Uhler47569702019-05-02 12:36:39 +01001325 }
1326
Oli Lan8c0084d2019-09-11 13:38:43 +01001327 @GuardedBy("mLock")
1328 private NewRollback createNewRollbackLocked(PackageInstaller.SessionInfo parentSession) {
Richard Uhler47569702019-05-02 12:36:39 +01001329 int rollbackId = allocateRollbackIdLocked();
Gavin Corkery0987cb02019-10-07 10:46:55 +01001330 final int userId;
1331 if (parentSession.getUser() == UserHandle.ALL) {
1332 userId = UserHandle.USER_SYSTEM;
1333 } else {
1334 userId = parentSession.getUser().getIdentifier();
1335 }
1336 String installerPackageName = parentSession.getInstallerPackageName();
Richard Uhler8d512ee2019-07-19 14:55:01 +01001337 final Rollback rollback;
Richard Uhler47569702019-05-02 12:36:39 +01001338 int parentSessionId = parentSession.getSessionId();
1339
JW Wang12d0c3e2019-12-02 21:01:35 +08001340 if (LOCAL_LOGV) {
1341 Slog.v(TAG, "createNewRollback id=" + rollbackId
1342 + " user=" + userId + " installer=" + installerPackageName);
1343 }
1344
Richard Uhler47569702019-05-02 12:36:39 +01001345 if (parentSession.isStaged()) {
Gavin Corkery0987cb02019-10-07 10:46:55 +01001346 rollback = mRollbackStore.createStagedRollback(rollbackId, parentSessionId, userId,
1347 installerPackageName);
Richard Uhler47569702019-05-02 12:36:39 +01001348 } else {
Gavin Corkery0987cb02019-10-07 10:46:55 +01001349 rollback = mRollbackStore.createNonStagedRollback(rollbackId, userId,
1350 installerPackageName);
Richard Uhler47569702019-05-02 12:36:39 +01001351 }
1352
1353 int[] packageSessionIds;
1354 if (parentSession.isMultiPackage()) {
1355 packageSessionIds = parentSession.getChildSessionIds();
1356 } else {
1357 packageSessionIds = new int[]{parentSessionId};
1358 }
1359
Richard Uhler8d512ee2019-07-19 14:55:01 +01001360 return new NewRollback(rollback, packageSessionIds);
Richard Uhler47569702019-05-02 12:36:39 +01001361 }
1362
1363 /**
1364 * Returns the NewRollback associated with the given package session.
1365 * Returns null if no NewRollback is found for the given package
1366 * session.
1367 */
Oli Lan4af2f752019-09-27 10:39:32 +01001368 @GuardedBy("mLock")
Richard Uhler47569702019-05-02 12:36:39 +01001369 NewRollback getNewRollbackForPackageSessionLocked(int packageSessionId) {
1370 // We expect mNewRollbacks to be a very small list; linear search
1371 // should be plenty fast.
Richard Uhler8d512ee2019-07-19 14:55:01 +01001372 for (NewRollback newRollback: mNewRollbacks) {
Oli Lan4af2f752019-09-27 10:39:32 +01001373 if (newRollback.containsSessionId(packageSessionId)) {
1374 return newRollback;
Richard Uhler47569702019-05-02 12:36:39 +01001375 }
1376 }
1377 return null;
1378 }
Richard Uhlere95d0552018-12-27 15:03:41 +00001379}