blob: 42f3e2fd31c916c0aee8ecee4c6bbb027615e055 [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;
39import android.content.rollback.PackageRollbackInfo;
40import android.content.rollback.RollbackInfo;
Richard Uhler2a48c292019-01-28 17:33:48 +000041import android.content.rollback.RollbackManager;
Richard Uhlere95d0552018-12-27 15:03:41 +000042import android.os.Binder;
Richard Uhlere95d0552018-12-27 15:03:41 +000043import android.os.Environment;
44import android.os.Handler;
45import android.os.HandlerThread;
Richard Uhlere95d0552018-12-27 15:03:41 +000046import android.os.ParcelFileDescriptor;
Narayan Kamath869f7062019-01-10 12:24:15 +000047import android.os.Process;
shafikda5e4ee2019-02-12 16:29:01 +000048import android.os.SystemClock;
shafik74fec182019-03-14 16:29:48 +000049import android.os.UserHandle;
Richard Uhler0c48af82019-06-07 14:04:43 +010050import android.os.UserManager;
shafik0ad18b82019-01-24 16:27:24 +000051import android.provider.DeviceConfig;
Richard Uhler47569702019-05-02 12:36:39 +010052import android.util.ArraySet;
Narayan Kamathc034fe92019-01-23 10:48:17 +000053import android.util.IntArray;
shafik7ad57752019-06-05 14:59:57 +010054import android.util.Slog;
Richard Uhlerb9d54472019-01-22 12:50:08 +000055import android.util.SparseBooleanArray;
Nikita Ioffe952aa7b2019-01-28 19:49:56 +000056import android.util.SparseLongArray;
Richard Uhlere95d0552018-12-27 15:03:41 +000057
58import com.android.internal.annotations.GuardedBy;
Richard Uhlerab009ea2019-02-25 12:11:05 +000059import com.android.internal.util.ArrayUtils;
shafik60046002019-03-12 17:54:10 +000060import com.android.internal.util.IndentingPrintWriter;
Richard Uhlere95d0552018-12-27 15:03:41 +000061import com.android.server.LocalServices;
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;
Richard Uhlere95d0552018-12-27 15:03:41 +000067import java.io.IOException;
shafik60046002019-03-12 17:54:10 +000068import java.io.PrintWriter;
Richard Uhlerb9d54472019-01-22 12:50:08 +000069import java.security.SecureRandom;
Richard Uhlere95d0552018-12-27 15:03:41 +000070import java.time.Instant;
Richard Uhlere95d0552018-12-27 15:03:41 +000071import java.time.temporal.ChronoUnit;
72import java.util.ArrayList;
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 Uhler32830a32019-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";
88
89 // Rollbacks expire after 48 hours.
shafik0ad18b82019-01-24 16:27:24 +000090 private static final long DEFAULT_ROLLBACK_LIFETIME_DURATION_MILLIS =
91 TimeUnit.HOURS.toMillis(48);
Richard Uhlere95d0552018-12-27 15:03:41 +000092
93 // Lock used to synchronize accesses to in-memory rollback data
94 // structures. By convention, methods with the suffix "Locked" require
95 // mLock is held when they are called.
96 private final Object mLock = new Object();
97
shafik0ad18b82019-01-24 16:27:24 +000098 // No need for guarding with lock because value is only accessed in handler thread
99 // and the value will be written on boot complete. Initialization here happens before
100 // handler threads are running so that's fine.
101 private long mRollbackLifetimeDurationInMillis = DEFAULT_ROLLBACK_LIFETIME_DURATION_MILLIS;
102
shafik18b627e2019-05-01 20:41:48 +0100103 private static final long HANDLER_THREAD_TIMEOUT_DURATION_MILLIS =
104 TimeUnit.MINUTES.toMillis(10);
105
Richard Uhlerb9d54472019-01-22 12:50:08 +0000106 // Used for generating rollback IDs.
107 private final Random mRandom = new SecureRandom();
108
109 // Set of allocated rollback ids
110 @GuardedBy("mLock")
111 private final SparseBooleanArray mAllocatedRollbackIds = new SparseBooleanArray();
112
Richard Uhler91fc4db2019-07-19 14:55:01 +0100113 // Rollbacks we are in the process of enabling.
Richard Uhler2d7c7f0d2019-01-04 09:18:21 +0000114 @GuardedBy("mLock")
Richard Uhler47569702019-05-02 12:36:39 +0100115 private final Set<NewRollback> mNewRollbacks = new ArraySet<>();
Richard Uhlerf1910c52019-01-09 14:27:36 +0000116
Richard Uhler6f8a33b2019-02-26 10:40:36 +0000117 // The list of all rollbacks, including available and committed rollbacks.
Richard Uhlere95d0552018-12-27 15:03:41 +0000118 @GuardedBy("mLock")
Richard Uhler91fc4db2019-07-19 14:55:01 +0100119 private final List<Rollback> mRollbacks;
Richard Uhlere95d0552018-12-27 15:03:41 +0000120
Richard Uhler28e73232019-01-21 16:48:55 +0000121 private final RollbackStore mRollbackStore;
Richard Uhlere95d0552018-12-27 15:03:41 +0000122
123 private final Context mContext;
124 private final HandlerThread mHandlerThread;
Narayan Kamath869f7062019-01-10 12:24:15 +0000125 private final Installer mInstaller;
Zimuzoc4073cc2019-01-18 18:39:18 +0000126 private final RollbackPackageHealthObserver mPackageHealthObserver;
Nikita Ioffe952aa7b2019-01-28 19:49:56 +0000127 private final AppDataRollbackHelper mAppDataRollbackHelper;
Richard Uhlere95d0552018-12-27 15:03:41 +0000128
shafikda5e4ee2019-02-12 16:29:01 +0000129 // This field stores the difference in Millis between the uptime (millis since device
Richard Uhler91fc4db2019-07-19 14:55:01 +0100130 // has booted) and current time (device wall clock) - it's used to update rollback
shafikda5e4ee2019-02-12 16:29:01 +0000131 // timestamps when the time is changed, by the user or by change of timezone.
132 // No need for guarding with lock because value is only accessed in handler thread.
133 private long mRelativeBootTime = calculateRelativeBootTime();
134
Richard Uhlere95d0552018-12-27 15:03:41 +0000135 RollbackManagerServiceImpl(Context context) {
136 mContext = context;
Narayan Kamath869f7062019-01-10 12:24:15 +0000137 // Note that we're calling onStart here because this object is only constructed on
138 // SystemService#onStart.
139 mInstaller = new Installer(mContext);
140 mInstaller.onStart();
shafik18b627e2019-05-01 20:41:48 +0100141
Richard Uhler28e73232019-01-21 16:48:55 +0000142 mRollbackStore = new RollbackStore(new File(Environment.getDataDirectory(), "rollback"));
Richard Uhlere95d0552018-12-27 15:03:41 +0000143
Zimuzoc4073cc2019-01-18 18:39:18 +0000144 mPackageHealthObserver = new RollbackPackageHealthObserver(mContext);
Nikita Ioffe952aa7b2019-01-28 19:49:56 +0000145 mAppDataRollbackHelper = new AppDataRollbackHelper(mInstaller);
Zimuzoc4073cc2019-01-18 18:39:18 +0000146
Richard Uhler32830a32019-07-01 15:21:44 +0100147 // Load rollback data from device storage.
148 synchronized (mLock) {
Richard Uhler91fc4db2019-07-19 14:55:01 +0100149 mRollbacks = mRollbackStore.loadRollbacks();
150 for (Rollback rollback : mRollbacks) {
151 mAllocatedRollbackIds.put(rollback.info.getRollbackId(), true);
Richard Uhler32830a32019-07-01 15:21:44 +0100152 }
153 }
154
155 // Kick off and start monitoring the handler thread.
156 mHandlerThread = new HandlerThread("RollbackManagerServiceHandler");
157 mHandlerThread.start();
158 Watchdog.getInstance().addThread(getHandler(), HANDLER_THREAD_TIMEOUT_DURATION_MILLIS);
Richard Uhlere95d0552018-12-27 15:03:41 +0000159
Richard Uhler82913b72019-04-01 13:02:31 +0100160 for (UserInfo userInfo : UserManager.get(mContext).getUsers(true)) {
161 registerUserCallbacks(userInfo.getUserHandle());
162 }
Richard Uhlere95d0552018-12-27 15:03:41 +0000163
164 IntentFilter enableRollbackFilter = new IntentFilter();
165 enableRollbackFilter.addAction(Intent.ACTION_PACKAGE_ENABLE_ROLLBACK);
166 try {
167 enableRollbackFilter.addDataType("application/vnd.android.package-archive");
168 } catch (IntentFilter.MalformedMimeTypeException e) {
shafik7ad57752019-06-05 14:59:57 +0100169 Slog.e(TAG, "addDataType", e);
Richard Uhlere95d0552018-12-27 15:03:41 +0000170 }
171
172 mContext.registerReceiver(new BroadcastReceiver() {
173 @Override
174 public void onReceive(Context context, Intent intent) {
175 if (Intent.ACTION_PACKAGE_ENABLE_ROLLBACK.equals(intent.getAction())) {
176 int token = intent.getIntExtra(
177 PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_TOKEN, -1);
178 int installFlags = intent.getIntExtra(
179 PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_INSTALL_FLAGS, 0);
Richard Uhler82913b72019-04-01 13:02:31 +0100180 int user = intent.getIntExtra(
181 PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_USER, 0);
182
Richard Uhlere95d0552018-12-27 15:03:41 +0000183 File newPackageCodePath = new File(intent.getData().getPath());
184
185 getHandler().post(() -> {
Oli Lan87a504d2019-08-09 10:25:49 +0100186 boolean success =
187 enableRollback(installFlags, newPackageCodePath, user, token);
Richard Uhlere95d0552018-12-27 15:03:41 +0000188 int ret = PackageManagerInternal.ENABLE_ROLLBACK_SUCCEEDED;
189 if (!success) {
190 ret = PackageManagerInternal.ENABLE_ROLLBACK_FAILED;
191 }
192
193 PackageManagerInternal pm = LocalServices.getService(
194 PackageManagerInternal.class);
195 pm.setEnableRollbackCode(token, ret);
196 });
197
198 // We're handling the ordered broadcast. Abort the
199 // broadcast because there is no need for it to go to
200 // anyone else.
201 abortBroadcast();
202 }
203 }
204 }, enableRollbackFilter, null, getHandler());
shafikda5e4ee2019-02-12 16:29:01 +0000205
shafik4831ad72019-05-03 17:36:42 +0100206 IntentFilter enableRollbackTimedOutFilter = new IntentFilter();
207 enableRollbackTimedOutFilter.addAction(Intent.ACTION_CANCEL_ENABLE_ROLLBACK);
208
209 mContext.registerReceiver(new BroadcastReceiver() {
210 @Override
211 public void onReceive(Context context, Intent intent) {
212 if (Intent.ACTION_CANCEL_ENABLE_ROLLBACK.equals(intent.getAction())) {
213 int token = intent.getIntExtra(
214 PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_TOKEN, -1);
215 synchronized (mLock) {
216 for (NewRollback rollback : mNewRollbacks) {
217 if (rollback.hasToken(token)) {
218 rollback.isCancelled = true;
219 return;
220 }
221 }
222 }
223 }
224 }
225 }, enableRollbackTimedOutFilter, null, getHandler());
226
shafik8aea1ad2019-05-21 12:34:50 +0100227 IntentFilter userAddedIntentFilter = new IntentFilter(Intent.ACTION_USER_ADDED);
228 mContext.registerReceiver(new BroadcastReceiver() {
229 @Override
230 public void onReceive(Context context, Intent intent) {
231 if (Intent.ACTION_USER_ADDED.equals(intent.getAction())) {
232 final int newUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
233 if (newUserId == -1) {
234 return;
235 }
236 registerUserCallbacks(UserHandle.of(newUserId));
237 }
238 }
239 }, userAddedIntentFilter, null, getHandler());
240
shafikda5e4ee2019-02-12 16:29:01 +0000241 registerTimeChangeReceiver();
Richard Uhlere95d0552018-12-27 15:03:41 +0000242 }
243
Richard Uhler82913b72019-04-01 13:02:31 +0100244 private void registerUserCallbacks(UserHandle user) {
245 Context context = getContextAsUser(user);
246 if (context == null) {
shafik7ad57752019-06-05 14:59:57 +0100247 Slog.e(TAG, "Unable to register user callbacks for user " + user);
Richard Uhler82913b72019-04-01 13:02:31 +0100248 return;
249 }
250
Richard Uhler82913b72019-04-01 13:02:31 +0100251 context.getPackageManager().getPackageInstaller()
252 .registerSessionCallback(new SessionCallback(), getHandler());
253
254 IntentFilter filter = new IntentFilter();
255 filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
256 filter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED);
257 filter.addDataScheme("package");
258 context.registerReceiver(new BroadcastReceiver() {
259 @Override
260 public void onReceive(Context context, Intent intent) {
261 String action = intent.getAction();
262 if (Intent.ACTION_PACKAGE_REPLACED.equals(action)) {
263 String packageName = intent.getData().getSchemeSpecificPart();
264 onPackageReplaced(packageName);
265 }
266 if (Intent.ACTION_PACKAGE_FULLY_REMOVED.equals(action)) {
267 String packageName = intent.getData().getSchemeSpecificPart();
268 onPackageFullyRemoved(packageName);
269 }
270 }
271 }, filter, null, getHandler());
272 }
273
Richard Uhlere95d0552018-12-27 15:03:41 +0000274 @Override
Richard Uhler150ad982019-01-23 15:16:10 +0000275 public ParceledListSlice getAvailableRollbacks() {
Richard Uhler1fc10c12019-03-18 11:38:46 +0000276 enforceManageRollbacks("getAvailableRollbacks");
Richard Uhlere95d0552018-12-27 15:03:41 +0000277 synchronized (mLock) {
Richard Uhler150ad982019-01-23 15:16:10 +0000278 List<RollbackInfo> rollbacks = new ArrayList<>();
Richard Uhler6f8a33b2019-02-26 10:40:36 +0000279 for (int i = 0; i < mRollbacks.size(); ++i) {
Richard Uhler91fc4db2019-07-19 14:55:01 +0100280 Rollback rollback = mRollbacks.get(i);
281 if (rollback.state == Rollback.ROLLBACK_STATE_AVAILABLE) {
282 rollbacks.add(rollback.info);
Richard Uhler60ac7062019-02-05 13:25:39 +0000283 }
Richard Uhlere95d0552018-12-27 15:03:41 +0000284 }
Richard Uhler150ad982019-01-23 15:16:10 +0000285 return new ParceledListSlice<>(rollbacks);
Richard Uhlere95d0552018-12-27 15:03:41 +0000286 }
Richard Uhlere95d0552018-12-27 15:03:41 +0000287 }
288
289 @Override
Richard Uhlerfcf64e82019-06-28 10:55:45 +0100290 public ParceledListSlice<RollbackInfo> getRecentlyCommittedRollbacks() {
Richard Uhler1fc10c12019-03-18 11:38:46 +0000291 enforceManageRollbacks("getRecentlyCommittedRollbacks");
Richard Uhlere95d0552018-12-27 15:03:41 +0000292
293 synchronized (mLock) {
Richard Uhler6f8a33b2019-02-26 10:40:36 +0000294 List<RollbackInfo> rollbacks = new ArrayList<>();
295 for (int i = 0; i < mRollbacks.size(); ++i) {
Richard Uhler91fc4db2019-07-19 14:55:01 +0100296 Rollback rollback = mRollbacks.get(i);
297 if (rollback.state == Rollback.ROLLBACK_STATE_COMMITTED) {
298 rollbacks.add(rollback.info);
Richard Uhler6f8a33b2019-02-26 10:40:36 +0000299 }
300 }
Richard Uhlere95d0552018-12-27 15:03:41 +0000301 return new ParceledListSlice<>(rollbacks);
302 }
303 }
304
305 @Override
Richard Uhlerbf5b5c42019-01-28 15:26:37 +0000306 public void commitRollback(int rollbackId, ParceledListSlice causePackages,
307 String callerPackageName, IntentSender statusReceiver) {
Richard Uhlerfcf64e82019-06-28 10:55:45 +0100308 enforceManageRollbacks("commitRollback");
Richard Uhlere95d0552018-12-27 15:03:41 +0000309
310 final int callingUid = Binder.getCallingUid();
311 AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class);
312 appOps.checkPackage(callingUid, callerPackageName);
313
314 getHandler().post(() ->
Richard Uhlerbf5b5c42019-01-28 15:26:37 +0000315 commitRollbackInternal(rollbackId, causePackages.getList(),
316 callerPackageName, statusReceiver));
Richard Uhlere95d0552018-12-27 15:03:41 +0000317 }
318
shafikda5e4ee2019-02-12 16:29:01 +0000319 private void registerTimeChangeReceiver() {
320 final BroadcastReceiver timeChangeIntentReceiver = new BroadcastReceiver() {
321 @Override
322 public void onReceive(Context context, Intent intent) {
323 final long oldRelativeBootTime = mRelativeBootTime;
324 mRelativeBootTime = calculateRelativeBootTime();
325 final long timeDifference = mRelativeBootTime - oldRelativeBootTime;
326
327 synchronized (mLock) {
Richard Uhler91fc4db2019-07-19 14:55:01 +0100328 Iterator<Rollback> iter = mRollbacks.iterator();
shafikda5e4ee2019-02-12 16:29:01 +0000329 while (iter.hasNext()) {
Richard Uhler91fc4db2019-07-19 14:55:01 +0100330 Rollback rollback = iter.next();
331 rollback.timestamp = rollback.timestamp.plusMillis(timeDifference);
332 saveRollback(rollback);
shafikda5e4ee2019-02-12 16:29:01 +0000333 }
shafikda5e4ee2019-02-12 16:29:01 +0000334 }
335 }
336 };
337 final IntentFilter filter = new IntentFilter();
338 filter.addAction(Intent.ACTION_TIME_CHANGED);
339 mContext.registerReceiver(timeChangeIntentReceiver, filter,
340 null /* broadcastPermission */, getHandler());
341 }
342
343 private static long calculateRelativeBootTime() {
344 return System.currentTimeMillis() - SystemClock.elapsedRealtime();
345 }
346
Richard Uhlere95d0552018-12-27 15:03:41 +0000347 /**
Richard Uhlere87368e2019-01-24 16:34:14 +0000348 * Performs the actual work to commit a rollback.
Richard Uhlere95d0552018-12-27 15:03:41 +0000349 * The work is done on the current thread. This may be a long running
350 * operation.
351 */
Richard Uhlerbf5b5c42019-01-28 15:26:37 +0000352 private void commitRollbackInternal(int rollbackId, List<VersionedPackage> causePackages,
Richard Uhlere95d0552018-12-27 15:03:41 +0000353 String callerPackageName, IntentSender statusReceiver) {
shafik7ad57752019-06-05 14:59:57 +0100354 Slog.i(TAG, "Initiating rollback");
Richard Uhlere95d0552018-12-27 15:03:41 +0000355
Richard Uhler91fc4db2019-07-19 14:55:01 +0100356 Rollback rollback = getRollbackForId(rollbackId);
357 if (rollback == null || rollback.state != Rollback.ROLLBACK_STATE_AVAILABLE) {
Richard Uhler2a48c292019-01-28 17:33:48 +0000358 sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE_ROLLBACK_UNAVAILABLE,
359 "Rollback unavailable");
Narayan Kamathbc36f8d2019-01-23 12:00:08 +0000360 return;
361 }
362
Richard Uhlere95d0552018-12-27 15:03:41 +0000363 // Get a context for the caller to use to install the downgraded
364 // version of the package.
Gavin Corkerye92720d2019-08-07 17:38:36 +0100365 final Context context;
Richard Uhlere95d0552018-12-27 15:03:41 +0000366 try {
367 context = mContext.createPackageContext(callerPackageName, 0);
368 } catch (PackageManager.NameNotFoundException e) {
Richard Uhler2a48c292019-01-28 17:33:48 +0000369 sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE,
370 "Invalid callerPackageName");
Richard Uhlere95d0552018-12-27 15:03:41 +0000371 return;
372 }
373
374 PackageManager pm = context.getPackageManager();
375 try {
Richard Uhlere95d0552018-12-27 15:03:41 +0000376 PackageInstaller packageInstaller = pm.getPackageInstaller();
Richard Uhler01b06152019-01-09 13:51:54 +0000377 PackageInstaller.SessionParams parentParams = new PackageInstaller.SessionParams(
Richard Uhlere95d0552018-12-27 15:03:41 +0000378 PackageInstaller.SessionParams.MODE_FULL_INSTALL);
Nikita Ioffeb1d60f12019-03-06 18:56:49 +0000379 parentParams.setRequestDowngrade(true);
Richard Uhler01b06152019-01-09 13:51:54 +0000380 parentParams.setMultiPackage();
Richard Uhler91fc4db2019-07-19 14:55:01 +0100381 if (rollback.isStaged()) {
Richard Uhler72fb9612019-02-04 14:37:36 +0000382 parentParams.setStaged();
383 }
384
Richard Uhler01b06152019-01-09 13:51:54 +0000385 int parentSessionId = packageInstaller.createSession(parentParams);
386 PackageInstaller.Session parentSession = packageInstaller.openSession(parentSessionId);
Richard Uhlere95d0552018-12-27 15:03:41 +0000387
Richard Uhler91fc4db2019-07-19 14:55:01 +0100388 for (PackageRollbackInfo info : rollback.info.getPackages()) {
Richard Uhler01b06152019-01-09 13:51:54 +0000389 PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
390 PackageInstaller.SessionParams.MODE_FULL_INSTALL);
Richard Uhler7a5eeb12019-02-05 12:51:48 +0000391 // TODO: We can't get the installerPackageName for apex
392 // (b/123920130). Is it okay to ignore the installer package
393 // for apex?
394 if (!info.isApex()) {
395 String installerPackageName = pm.getInstallerPackageName(info.getPackageName());
Richard Uhlera7d18bb2019-02-06 11:58:17 +0000396 if (installerPackageName != null) {
397 params.setInstallerPackageName(installerPackageName);
Richard Uhler7a5eeb12019-02-05 12:51:48 +0000398 }
Richard Uhler0a79b322019-01-23 13:51:07 +0000399 }
Nikita Ioffeb1d60f12019-03-06 18:56:49 +0000400 params.setRequestDowngrade(true);
Richard Uhler2124d4b2019-04-25 13:01:39 +0100401 params.setRequiredInstalledVersionCode(
402 info.getVersionRolledBackFrom().getLongVersionCode());
Richard Uhler91fc4db2019-07-19 14:55:01 +0100403 if (rollback.isStaged()) {
Richard Uhler72fb9612019-02-04 14:37:36 +0000404 params.setStaged();
405 }
406 if (info.isApex()) {
407 params.setInstallAsApex();
408 }
Richard Uhler01b06152019-01-09 13:51:54 +0000409 int sessionId = packageInstaller.createSession(params);
410 PackageInstaller.Session session = packageInstaller.openSession(sessionId);
Richard Uhlerab009ea2019-02-25 12:11:05 +0000411 File[] packageCodePaths = RollbackStore.getPackageCodePaths(
Richard Uhler91fc4db2019-07-19 14:55:01 +0100412 rollback, info.getPackageName());
Richard Uhlerab009ea2019-02-25 12:11:05 +0000413 if (packageCodePaths == null) {
Richard Uhler1f571c62019-01-31 15:16:46 +0000414 sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE,
Richard Uhlerab009ea2019-02-25 12:11:05 +0000415 "Backup copy of package inaccessible");
Richard Uhler1f571c62019-01-31 15:16:46 +0000416 return;
417 }
418
Richard Uhlerab009ea2019-02-25 12:11:05 +0000419 for (File packageCodePath : packageCodePaths) {
420 try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(packageCodePath,
421 ParcelFileDescriptor.MODE_READ_ONLY)) {
422 final long token = Binder.clearCallingIdentity();
423 try {
424 session.write(packageCodePath.getName(), 0, packageCodePath.length(),
425 fd);
426 } finally {
427 Binder.restoreCallingIdentity(token);
428 }
Richard Uhler01b06152019-01-09 13:51:54 +0000429 }
Richard Uhlere95d0552018-12-27 15:03:41 +0000430 }
Richard Uhler01b06152019-01-09 13:51:54 +0000431 parentSession.addChildSessionId(sessionId);
Richard Uhlere95d0552018-12-27 15:03:41 +0000432 }
433
Narayan Kamath869f7062019-01-10 12:24:15 +0000434 final LocalIntentReceiver receiver = new LocalIntentReceiver(
435 (Intent result) -> {
Zimuzoc4073cc2019-01-18 18:39:18 +0000436 getHandler().post(() -> {
Narayan Kamathbc36f8d2019-01-23 12:00:08 +0000437
Zimuzoc4073cc2019-01-18 18:39:18 +0000438 int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
439 PackageInstaller.STATUS_FAILURE);
440 if (status != PackageInstaller.STATUS_SUCCESS) {
Richard Uhlere9aaf632019-03-01 16:03:01 +0000441 // Committing the rollback failed, but we
442 // still have all the info we need to try
443 // rolling back again, so restore the rollback
444 // state to how it was before we tried
445 // committing.
446 // TODO: Should we just kill this rollback if
447 // commit failed? Why would we expect commit
448 // not to fail again?
Richard Uhler6f8a33b2019-02-26 10:40:36 +0000449 synchronized (mLock) {
Richard Uhlere9aaf632019-03-01 16:03:01 +0000450 // TODO: Could this cause a rollback to be
451 // resurrected if it should otherwise have
452 // expired by now?
Richard Uhler91fc4db2019-07-19 14:55:01 +0100453 rollback.state = Rollback.ROLLBACK_STATE_AVAILABLE;
454 rollback.restoreUserDataInProgress = false;
Richard Uhler6f8a33b2019-02-26 10:40:36 +0000455 }
Richard Uhler2a48c292019-01-28 17:33:48 +0000456 sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE_INSTALL,
Zimuzoc4073cc2019-01-18 18:39:18 +0000457 "Rollback downgrade install failed: "
458 + result.getStringExtra(
459 PackageInstaller.EXTRA_STATUS_MESSAGE));
460 return;
461 }
Narayan Kamath869f7062019-01-10 12:24:15 +0000462
Richard Uhler6f8a33b2019-02-26 10:40:36 +0000463 synchronized (mLock) {
Richard Uhler91fc4db2019-07-19 14:55:01 +0100464 if (!rollback.isStaged()) {
Richard Uhler6f8a33b2019-02-26 10:40:36 +0000465 // All calls to restoreUserData should have
466 // completed by now for a non-staged install.
Richard Uhler91fc4db2019-07-19 14:55:01 +0100467 rollback.restoreUserDataInProgress = false;
Richard Uhler6f8a33b2019-02-26 10:40:36 +0000468 }
469
Richard Uhler91fc4db2019-07-19 14:55:01 +0100470 rollback.info.setCommittedSessionId(parentSessionId);
471 rollback.info.getCausePackages().addAll(causePackages);
Richard Uhler6f8a33b2019-02-26 10:40:36 +0000472 }
Richard Uhler91fc4db2019-07-19 14:55:01 +0100473 mRollbackStore.deletePackageCodePaths(rollback);
474 saveRollback(rollback);
Richard Uhler6f8a33b2019-02-26 10:40:36 +0000475
Zimuzoc4073cc2019-01-18 18:39:18 +0000476 sendSuccess(statusReceiver);
Narayan Kamath869f7062019-01-10 12:24:15 +0000477
Richard Uhlerdca7beb2019-01-24 09:56:03 +0000478 Intent broadcast = new Intent(Intent.ACTION_ROLLBACK_COMMITTED);
Narayan Kamath869f7062019-01-10 12:24:15 +0000479
Gavin Corkerye92720d2019-08-07 17:38:36 +0100480 for (UserInfo userInfo : UserManager.get(mContext).getUsers(true)) {
481 mContext.sendBroadcastAsUser(broadcast, userInfo.getUserHandle(),
482 Manifest.permission.MANAGE_ROLLBACKS);
483 }
Zimuzoc4073cc2019-01-18 18:39:18 +0000484 });
Narayan Kamath869f7062019-01-10 12:24:15 +0000485 }
486 );
487
Richard Uhler6f8a33b2019-02-26 10:40:36 +0000488 synchronized (mLock) {
Richard Uhler91fc4db2019-07-19 14:55:01 +0100489 rollback.state = Rollback.ROLLBACK_STATE_COMMITTED;
490 rollback.restoreUserDataInProgress = true;
Richard Uhler6f8a33b2019-02-26 10:40:36 +0000491 }
Richard Uhler01b06152019-01-09 13:51:54 +0000492 parentSession.commit(receiver.getIntentSender());
Richard Uhlere95d0552018-12-27 15:03:41 +0000493 } catch (IOException e) {
shafik7ad57752019-06-05 14:59:57 +0100494 Slog.e(TAG, "Rollback failed", e);
Richard Uhler2a48c292019-01-28 17:33:48 +0000495 sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE,
496 "IOException: " + e.toString());
Richard Uhlere95d0552018-12-27 15:03:41 +0000497 return;
498 }
499 }
500
501 @Override
502 public void reloadPersistedData() {
503 mContext.enforceCallingOrSelfPermission(
Richard Uhler1fc10c12019-03-18 11:38:46 +0000504 Manifest.permission.TEST_MANAGE_ROLLBACKS,
Richard Uhlere95d0552018-12-27 15:03:41 +0000505 "reloadPersistedData");
506
Richard Uhler32830a32019-07-01 15:21:44 +0100507 CountDownLatch latch = new CountDownLatch(1);
shafik6d61f5e2019-02-26 09:33:26 +0000508 getHandler().post(() -> {
509 updateRollbackLifetimeDurationInMillis();
Richard Uhler32830a32019-07-01 15:21:44 +0100510 synchronized (mLock) {
511 mRollbacks.clear();
Richard Uhler91fc4db2019-07-19 14:55:01 +0100512 mRollbacks.addAll(mRollbackStore.loadRollbacks());
Richard Uhler32830a32019-07-01 15:21:44 +0100513 }
514 latch.countDown();
shafik6d61f5e2019-02-26 09:33:26 +0000515 });
Richard Uhler32830a32019-07-01 15:21:44 +0100516
517 try {
518 latch.await();
519 } catch (InterruptedException ie) {
520 throw new IllegalStateException("RollbackManagerHandlerThread interrupted");
521 }
Richard Uhlere95d0552018-12-27 15:03:41 +0000522 }
523
524 @Override
525 public void expireRollbackForPackage(String packageName) {
526 mContext.enforceCallingOrSelfPermission(
Richard Uhler1fc10c12019-03-18 11:38:46 +0000527 Manifest.permission.TEST_MANAGE_ROLLBACKS,
Richard Uhlere95d0552018-12-27 15:03:41 +0000528 "expireRollbackForPackage");
Richard Uhlere95d0552018-12-27 15:03:41 +0000529 synchronized (mLock) {
Richard Uhler91fc4db2019-07-19 14:55:01 +0100530 Iterator<Rollback> iter = mRollbacks.iterator();
Richard Uhlere95d0552018-12-27 15:03:41 +0000531 while (iter.hasNext()) {
Richard Uhler91fc4db2019-07-19 14:55:01 +0100532 Rollback rollback = iter.next();
533 for (PackageRollbackInfo info : rollback.info.getPackages()) {
Richard Uhlera7e9b2d2019-01-22 17:20:58 +0000534 if (info.getPackageName().equals(packageName)) {
Richard Uhler01b06152019-01-09 13:51:54 +0000535 iter.remove();
Richard Uhler91fc4db2019-07-19 14:55:01 +0100536 deleteRollback(rollback);
Richard Uhler01b06152019-01-09 13:51:54 +0000537 break;
538 }
Richard Uhlere95d0552018-12-27 15:03:41 +0000539 }
540 }
Oli Landc4a3612019-08-14 11:45:37 +0100541 for (NewRollback newRollback : mNewRollbacks) {
542 for (PackageRollbackInfo info : newRollback.rollback.info.getPackages()) {
543 if (info.getPackageName().equals(packageName)) {
544 newRollback.isCancelled = true;
545 break;
546 }
547 }
548 }
Richard Uhlere95d0552018-12-27 15:03:41 +0000549 }
550 }
551
Richard Uhler0c48af82019-06-07 14:04:43 +0100552 @Override
553 public void blockRollbackManager(long millis) {
554 mContext.enforceCallingOrSelfPermission(
555 Manifest.permission.TEST_MANAGE_ROLLBACKS,
556 "blockRollbackManager");
557 getHandler().post(() -> {
558 try {
559 Thread.sleep(millis);
560 } catch (InterruptedException e) {
Richard Uhler32830a32019-07-01 15:21:44 +0100561 throw new IllegalStateException("RollbackManagerHandlerThread interrupted");
Richard Uhler0c48af82019-06-07 14:04:43 +0100562 }
563 });
564 }
565
Narayan Kamathc034fe92019-01-23 10:48:17 +0000566 void onUnlockUser(int userId) {
567 getHandler().post(() -> {
Richard Uhler91fc4db2019-07-19 14:55:01 +0100568 final List<Rollback> rollbacks;
Narayan Kamathc034fe92019-01-23 10:48:17 +0000569 synchronized (mLock) {
Richard Uhler6f8a33b2019-02-26 10:40:36 +0000570 rollbacks = new ArrayList<>(mRollbacks);
Narayan Kamathc034fe92019-01-23 10:48:17 +0000571 }
572
Richard Uhler91fc4db2019-07-19 14:55:01 +0100573 final Set<Rollback> changed =
Richard Uhler6f8a33b2019-02-26 10:40:36 +0000574 mAppDataRollbackHelper.commitPendingBackupAndRestoreForUser(userId, rollbacks);
Narayan Kamathc034fe92019-01-23 10:48:17 +0000575
Richard Uhler91fc4db2019-07-19 14:55:01 +0100576 for (Rollback rollback : changed) {
577 saveRollback(rollback);
Narayan Kamathc034fe92019-01-23 10:48:17 +0000578 }
579 });
580 }
581
shafik0ad18b82019-01-24 16:27:24 +0000582 private void updateRollbackLifetimeDurationInMillis() {
Stanislav Zholnin6b85dac2019-03-07 12:16:04 +0000583 mRollbackLifetimeDurationInMillis = DeviceConfig.getLong(
Matt Pape12187ae2019-03-14 13:37:32 -0700584 DeviceConfig.NAMESPACE_ROLLBACK_BOOT,
585 RollbackManager.PROPERTY_ROLLBACK_LIFETIME_MILLIS,
Stanislav Zholnin6b85dac2019-03-07 12:16:04 +0000586 DEFAULT_ROLLBACK_LIFETIME_DURATION_MILLIS);
shafik15c511f2019-03-08 11:52:08 +0000587 if (mRollbackLifetimeDurationInMillis < 0) {
588 mRollbackLifetimeDurationInMillis = DEFAULT_ROLLBACK_LIFETIME_DURATION_MILLIS;
589 }
shafik0ad18b82019-01-24 16:27:24 +0000590 }
591
Richard Uhlerb2c5cac2019-02-05 16:14:35 +0000592 void onBootCompleted() {
shafik0ad18b82019-01-24 16:27:24 +0000593 getHandler().post(() -> updateRollbackLifetimeDurationInMillis());
594 // Also posts to handler thread
595 scheduleExpiration(0);
596
Richard Uhlerb2c5cac2019-02-05 16:14:35 +0000597 getHandler().post(() -> {
Richard Uhler6f8a33b2019-02-26 10:40:36 +0000598 // Check to see if any rollback-enabled staged sessions or staged
599 // rollback sessions been applied.
Richard Uhler91fc4db2019-07-19 14:55:01 +0100600 List<Rollback> enabling = new ArrayList<>();
601 List<Rollback> restoreInProgress = new ArrayList<>();
Richard Uhler1924d6d2019-04-26 10:20:12 +0100602 Set<String> apexPackageNames = new HashSet<>();
Richard Uhlerb2c5cac2019-02-05 16:14:35 +0000603 synchronized (mLock) {
Richard Uhler91fc4db2019-07-19 14:55:01 +0100604 for (Rollback rollback : mRollbacks) {
605 if (rollback.isStaged()) {
606 if (rollback.state == Rollback.ROLLBACK_STATE_ENABLING) {
607 enabling.add(rollback);
608 } else if (rollback.restoreUserDataInProgress) {
609 restoreInProgress.add(rollback);
Richard Uhler6f8a33b2019-02-26 10:40:36 +0000610 }
Richard Uhler1924d6d2019-04-26 10:20:12 +0100611
Richard Uhler91fc4db2019-07-19 14:55:01 +0100612 for (PackageRollbackInfo info : rollback.info.getPackages()) {
Richard Uhler1924d6d2019-04-26 10:20:12 +0100613 if (info.isApex()) {
614 apexPackageNames.add(info.getPackageName());
615 }
616 }
Richard Uhlerb2c5cac2019-02-05 16:14:35 +0000617 }
618 }
619 }
620
Richard Uhler91fc4db2019-07-19 14:55:01 +0100621 for (Rollback rollback : enabling) {
Richard Uhlerb2c5cac2019-02-05 16:14:35 +0000622 PackageInstaller installer = mContext.getPackageManager().getPackageInstaller();
623 PackageInstaller.SessionInfo session = installer.getSessionInfo(
Richard Uhler91fc4db2019-07-19 14:55:01 +0100624 rollback.stagedSessionId);
Richard Uhler1d5db772019-07-01 16:27:43 +0100625 if (session == null || session.isStagedSessionFailed()) {
626 // TODO: Do we need to remove this from
627 // mRollbacks, or is it okay to leave as
628 // unavailable until the next reboot when it will go
629 // away on its own?
Richard Uhler91fc4db2019-07-19 14:55:01 +0100630 deleteRollback(rollback);
Richard Uhler1d5db772019-07-01 16:27:43 +0100631 } else if (session.isStagedSessionApplied()) {
Richard Uhler91fc4db2019-07-19 14:55:01 +0100632 makeRollbackAvailable(rollback);
Richard Uhlerb2c5cac2019-02-05 16:14:35 +0000633 }
634 }
Richard Uhler6f8a33b2019-02-26 10:40:36 +0000635
Richard Uhler91fc4db2019-07-19 14:55:01 +0100636 for (Rollback rollback : restoreInProgress) {
Richard Uhler6f8a33b2019-02-26 10:40:36 +0000637 PackageInstaller installer = mContext.getPackageManager().getPackageInstaller();
638 PackageInstaller.SessionInfo session = installer.getSessionInfo(
Richard Uhler91fc4db2019-07-19 14:55:01 +0100639 rollback.stagedSessionId);
Richard Uhlere9aaf632019-03-01 16:03:01 +0000640 // TODO: What if session is null?
Richard Uhler6f8a33b2019-02-26 10:40:36 +0000641 if (session != null) {
642 if (session.isStagedSessionApplied() || session.isStagedSessionFailed()) {
643 synchronized (mLock) {
Richard Uhler91fc4db2019-07-19 14:55:01 +0100644 rollback.restoreUserDataInProgress = false;
Richard Uhler6f8a33b2019-02-26 10:40:36 +0000645 }
Richard Uhler91fc4db2019-07-19 14:55:01 +0100646 saveRollback(rollback);
Richard Uhler6f8a33b2019-02-26 10:40:36 +0000647 }
648 }
649 }
Zimuzo841c4942019-03-04 12:31:48 +0000650
Richard Uhler1924d6d2019-04-26 10:20:12 +0100651 for (String apexPackageName : apexPackageNames) {
652 // We will not recieve notifications when an apex is updated,
653 // so check now in case any rollbacks ought to be expired. The
654 // onPackagedReplace function is safe to call if the package
655 // hasn't actually been updated.
656 onPackageReplaced(apexPackageName);
657 }
shafikc5805fb92019-04-29 20:08:07 +0100658 mPackageHealthObserver.onBootCompletedAsync();
Richard Uhlerb2c5cac2019-02-05 16:14:35 +0000659 });
660 }
661
Richard Uhlere95d0552018-12-27 15:03:41 +0000662 /**
Richard Uhlere95d0552018-12-27 15:03:41 +0000663 * Called when a package has been replaced with a different version.
664 * Removes all backups for the package not matching the currently
665 * installed package version.
666 */
667 private void onPackageReplaced(String packageName) {
668 // TODO: Could this end up incorrectly deleting a rollback for a
669 // package that is about to be installed?
Richard Uhlera7e9b2d2019-01-22 17:20:58 +0000670 VersionedPackage installedVersion = getInstalledPackageVersion(packageName);
Richard Uhlere95d0552018-12-27 15:03:41 +0000671
672 synchronized (mLock) {
Richard Uhler91fc4db2019-07-19 14:55:01 +0100673 Iterator<Rollback> iter = mRollbacks.iterator();
Richard Uhlere95d0552018-12-27 15:03:41 +0000674 while (iter.hasNext()) {
Richard Uhler91fc4db2019-07-19 14:55:01 +0100675 Rollback rollback = iter.next();
Richard Uhlere9aaf632019-03-01 16:03:01 +0000676 // TODO: Should we remove rollbacks in the ENABLING state here?
Richard Uhler91fc4db2019-07-19 14:55:01 +0100677 if (rollback.state == Rollback.ROLLBACK_STATE_AVAILABLE
678 || rollback.state == Rollback.ROLLBACK_STATE_ENABLING) {
679 for (PackageRollbackInfo info : rollback.info.getPackages()) {
Richard Uhler6f8a33b2019-02-26 10:40:36 +0000680 if (info.getPackageName().equals(packageName)
681 && !packageVersionsEqual(
682 info.getVersionRolledBackFrom(),
683 installedVersion)) {
684 iter.remove();
Richard Uhler91fc4db2019-07-19 14:55:01 +0100685 deleteRollback(rollback);
Richard Uhler6f8a33b2019-02-26 10:40:36 +0000686 break;
687 }
Richard Uhler01b06152019-01-09 13:51:54 +0000688 }
Richard Uhlere95d0552018-12-27 15:03:41 +0000689 }
690 }
691 }
692 }
693
694 /**
695 * Called when a package has been completely removed from the device.
696 * Removes all backups and rollback history for the given package.
697 */
698 private void onPackageFullyRemoved(String packageName) {
699 expireRollbackForPackage(packageName);
Richard Uhlere95d0552018-12-27 15:03:41 +0000700 }
701
702 /**
703 * Notifies an IntentSender of failure.
704 *
705 * @param statusReceiver where to send the failure
Richard Uhler2a48c292019-01-28 17:33:48 +0000706 * @param status the RollbackManager.STATUS_* code with the failure.
Richard Uhlere95d0552018-12-27 15:03:41 +0000707 * @param message the failure message.
708 */
Richard Uhlera4b70302019-03-11 14:34:33 +0000709 private void sendFailure(IntentSender statusReceiver, @RollbackManager.Status int status,
710 String message) {
shafik7ad57752019-06-05 14:59:57 +0100711 Slog.e(TAG, message);
Richard Uhlere95d0552018-12-27 15:03:41 +0000712 try {
Richard Uhlere95d0552018-12-27 15:03:41 +0000713 final Intent fillIn = new Intent();
Richard Uhler2a48c292019-01-28 17:33:48 +0000714 fillIn.putExtra(RollbackManager.EXTRA_STATUS, status);
715 fillIn.putExtra(RollbackManager.EXTRA_STATUS_MESSAGE, message);
Richard Uhlere95d0552018-12-27 15:03:41 +0000716 statusReceiver.sendIntent(mContext, 0, fillIn, null, null);
717 } catch (IntentSender.SendIntentException e) {
718 // Nowhere to send the result back to, so don't bother.
719 }
720 }
721
722 /**
723 * Notifies an IntentSender of success.
724 */
725 private void sendSuccess(IntentSender statusReceiver) {
726 try {
727 final Intent fillIn = new Intent();
Richard Uhler2a48c292019-01-28 17:33:48 +0000728 fillIn.putExtra(RollbackManager.EXTRA_STATUS, RollbackManager.STATUS_SUCCESS);
Richard Uhlere95d0552018-12-27 15:03:41 +0000729 statusReceiver.sendIntent(mContext, 0, fillIn, null, null);
730 } catch (IntentSender.SendIntentException e) {
731 // Nowhere to send the result back to, so don't bother.
732 }
733 }
734
735 // Check to see if anything needs expiration, and if so, expire it.
736 // Schedules future expiration as appropriate.
Richard Uhlere95d0552018-12-27 15:03:41 +0000737 private void runExpiration() {
738 Instant now = Instant.now();
739 Instant oldest = null;
740 synchronized (mLock) {
Richard Uhler91fc4db2019-07-19 14:55:01 +0100741 Iterator<Rollback> iter = mRollbacks.iterator();
Richard Uhlere95d0552018-12-27 15:03:41 +0000742 while (iter.hasNext()) {
Richard Uhler91fc4db2019-07-19 14:55:01 +0100743 Rollback rollback = iter.next();
744 if (rollback.state != Rollback.ROLLBACK_STATE_AVAILABLE) {
Richard Uhler60ac7062019-02-05 13:25:39 +0000745 continue;
746 }
Richard Uhler91fc4db2019-07-19 14:55:01 +0100747 if (!now.isBefore(
748 rollback.timestamp.plusMillis(mRollbackLifetimeDurationInMillis))) {
Richard Uhlere95d0552018-12-27 15:03:41 +0000749 iter.remove();
Richard Uhler91fc4db2019-07-19 14:55:01 +0100750 deleteRollback(rollback);
751 } else if (oldest == null || oldest.isAfter(rollback.timestamp)) {
752 oldest = rollback.timestamp;
Richard Uhlere95d0552018-12-27 15:03:41 +0000753 }
754 }
755 }
756
757 if (oldest != null) {
shafik0ad18b82019-01-24 16:27:24 +0000758 scheduleExpiration(now.until(oldest.plusMillis(mRollbackLifetimeDurationInMillis),
Richard Uhlere95d0552018-12-27 15:03:41 +0000759 ChronoUnit.MILLIS));
760 }
761 }
762
763 /**
764 * Schedules an expiration check to be run after the given duration in
765 * milliseconds has gone by.
766 */
767 private void scheduleExpiration(long duration) {
768 getHandler().postDelayed(() -> runExpiration(), duration);
769 }
770
771 private Handler getHandler() {
772 return mHandlerThread.getThreadHandler();
773 }
774
Richard Uhler2d7c7f0d2019-01-04 09:18:21 +0000775 // Returns true if <code>session</code> has installFlags and code path
776 // matching the installFlags and new package code path given to
777 // enableRollback.
778 private boolean sessionMatchesForEnableRollback(PackageInstaller.SessionInfo session,
779 int installFlags, File newPackageCodePath) {
780 if (session == null || session.resolvedBaseCodePath == null) {
781 return false;
782 }
783
784 File packageCodePath = new File(session.resolvedBaseCodePath).getParentFile();
785 if (newPackageCodePath.equals(packageCodePath) && installFlags == session.installFlags) {
786 return true;
787 }
788
789 return false;
790 }
791
Richard Uhler82913b72019-04-01 13:02:31 +0100792 private Context getContextAsUser(UserHandle user) {
793 try {
794 return mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user);
795 } catch (PackageManager.NameNotFoundException e) {
796 return null;
797 }
798 }
799
Richard Uhlere95d0552018-12-27 15:03:41 +0000800 /**
801 * Called via broadcast by the package manager when a package is being
802 * staged for install with rollback enabled. Called before the package has
803 * been installed.
804 *
Richard Uhlere95d0552018-12-27 15:03:41 +0000805 * @param installFlags information about what is being installed.
806 * @param newPackageCodePath path to the package about to be installed.
Richard Uhler82913b72019-04-01 13:02:31 +0100807 * @param user the user that owns the install session to enable rollback on.
shafik4831ad72019-05-03 17:36:42 +0100808 * @param token the distinct rollback token sent by package manager.
Richard Uhlere95d0552018-12-27 15:03:41 +0000809 * @return true if enabling the rollback succeeds, false otherwise.
810 */
Oli Lan87a504d2019-08-09 10:25:49 +0100811 private boolean enableRollback(
812 int installFlags, File newPackageCodePath, @UserIdInt int user, int token) {
Richard Uhlere95d0552018-12-27 15:03:41 +0000813
Richard Uhlerf0bdca52019-01-31 16:43:59 +0000814 // Find the session id associated with this install.
815 // TODO: It would be nice if package manager or package installer told
816 // us the session directly, rather than have to search for it
817 // ourselves.
Richard Uhlere95d0552018-12-27 15:03:41 +0000818
Richard Uhler82913b72019-04-01 13:02:31 +0100819 // getAllSessions only returns sessions for the associated user.
820 // Create a context with the right user so we can find the matching
821 // session.
822 final Context context = getContextAsUser(UserHandle.of(user));
823 if (context == null) {
shafik7ad57752019-06-05 14:59:57 +0100824 Slog.e(TAG, "Unable to create context for install session user.");
Richard Uhler82913b72019-04-01 13:02:31 +0100825 return false;
826 }
827
Richard Uhler47569702019-05-02 12:36:39 +0100828 PackageInstaller.SessionInfo parentSession = null;
829 PackageInstaller.SessionInfo packageSession = null;
Richard Uhler82913b72019-04-01 13:02:31 +0100830 PackageInstaller installer = context.getPackageManager().getPackageInstaller();
Richard Uhler2d7c7f0d2019-01-04 09:18:21 +0000831 for (PackageInstaller.SessionInfo info : installer.getAllSessions()) {
Richard Uhlerf1910c52019-01-09 14:27:36 +0000832 if (info.isMultiPackage()) {
833 for (int childId : info.getChildSessionIds()) {
834 PackageInstaller.SessionInfo child = installer.getSessionInfo(childId);
835 if (sessionMatchesForEnableRollback(child, installFlags, newPackageCodePath)) {
836 // TODO: Check we only have one matching session?
Richard Uhler47569702019-05-02 12:36:39 +0100837 parentSession = info;
838 packageSession = child;
Richard Uhlerf0bdca52019-01-31 16:43:59 +0000839 break;
Richard Uhlerf1910c52019-01-09 14:27:36 +0000840 }
841 }
Richard Uhlerf0bdca52019-01-31 16:43:59 +0000842 } else if (sessionMatchesForEnableRollback(info, installFlags, newPackageCodePath)) {
843 // TODO: Check we only have one matching session?
Richard Uhler47569702019-05-02 12:36:39 +0100844 parentSession = info;
845 packageSession = info;
Richard Uhlerf0bdca52019-01-31 16:43:59 +0000846 break;
Richard Uhler2d7c7f0d2019-01-04 09:18:21 +0000847 }
848 }
849
Richard Uhler47569702019-05-02 12:36:39 +0100850 if (parentSession == null || packageSession == null) {
shafik7ad57752019-06-05 14:59:57 +0100851 Slog.e(TAG, "Unable to find session for enabled rollback.");
Richard Uhler2d7c7f0d2019-01-04 09:18:21 +0000852 return false;
853 }
854
Richard Uhlerac0d0f92019-02-05 15:53:47 +0000855 // Check to see if this is the apk session for a staged session with
856 // rollback enabled.
Richard Uhlerac0d0f92019-02-05 15:53:47 +0000857 synchronized (mLock) {
Richard Uhler6f8a33b2019-02-26 10:40:36 +0000858 for (int i = 0; i < mRollbacks.size(); ++i) {
Richard Uhler91fc4db2019-07-19 14:55:01 +0100859 Rollback rollback = mRollbacks.get(i);
860 if (rollback.apkSessionId == parentSession.getSessionId()) {
Oli Lan87a504d2019-08-09 10:25:49 +0100861 // This is the apk session for a staged session with rollback enabled. We do not
862 // need to create a new rollback for this session.
Richard Uhlerac0d0f92019-02-05 15:53:47 +0000863 return true;
864 }
865 }
Richard Uhlerac0d0f92019-02-05 15:53:47 +0000866 }
867
Richard Uhler47569702019-05-02 12:36:39 +0100868 NewRollback newRollback;
869 synchronized (mLock) {
870 // See if we already have a NewRollback that contains this package
871 // session. If not, create a NewRollback for the parent session
872 // that we will use for all the packages in the session.
873 newRollback = getNewRollbackForPackageSessionLocked(packageSession.getSessionId());
874 if (newRollback == null) {
875 newRollback = createNewRollbackLocked(parentSession);
876 mNewRollbacks.add(newRollback);
877 }
878 }
shafik4831ad72019-05-03 17:36:42 +0100879 newRollback.addToken(token);
Richard Uhler47569702019-05-02 12:36:39 +0100880
Richard Uhler91fc4db2019-07-19 14:55:01 +0100881 return enableRollbackForPackageSession(newRollback.rollback, packageSession);
Richard Uhlerf0bdca52019-01-31 16:43:59 +0000882 }
883
884 /**
885 * Do code and userdata backups to enable rollback of the given session.
886 * In case of multiPackage sessions, <code>session</code> should be one of
887 * the child sessions, not the parent session.
Richard Uhler47569702019-05-02 12:36:39 +0100888 *
889 * @return true on success, false on failure.
Richard Uhlerf0bdca52019-01-31 16:43:59 +0000890 */
Richard Uhler91fc4db2019-07-19 14:55:01 +0100891 private boolean enableRollbackForPackageSession(Rollback rollback,
Oli Lan87a504d2019-08-09 10:25:49 +0100892 PackageInstaller.SessionInfo session) {
Richard Uhlerf0bdca52019-01-31 16:43:59 +0000893 // TODO: Don't attempt to enable rollback for split installs.
894 final int installFlags = session.installFlags;
895 if ((installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) == 0) {
shafik7ad57752019-06-05 14:59:57 +0100896 Slog.e(TAG, "Rollback is not enabled.");
Richard Uhlerf0bdca52019-01-31 16:43:59 +0000897 return false;
898 }
899 if ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) {
shafik7ad57752019-06-05 14:59:57 +0100900 Slog.e(TAG, "Rollbacks not supported for instant app install");
Richard Uhlerf0bdca52019-01-31 16:43:59 +0000901 return false;
902 }
903
Richard Uhlerfcad49a2019-03-13 10:18:46 +0000904 if (session.resolvedBaseCodePath == null) {
shafik7ad57752019-06-05 14:59:57 +0100905 Slog.e(TAG, "Session code path has not been resolved.");
Richard Uhlerfcad49a2019-03-13 10:18:46 +0000906 return false;
907 }
908
Richard Uhlerf0bdca52019-01-31 16:43:59 +0000909 // Get information about the package to be installed.
910 PackageParser.PackageLite newPackage = null;
911 try {
912 newPackage = PackageParser.parsePackageLite(new File(session.resolvedBaseCodePath), 0);
913 } catch (PackageParser.PackageParserException e) {
shafik7ad57752019-06-05 14:59:57 +0100914 Slog.e(TAG, "Unable to parse new package", e);
Richard Uhlerf0bdca52019-01-31 16:43:59 +0000915 return false;
916 }
917
918 String packageName = newPackage.packageName;
shafik7ad57752019-06-05 14:59:57 +0100919 Slog.i(TAG, "Enabling rollback for install of " + packageName
Narayan Kamathfcd4a042019-02-01 14:16:37 +0000920 + ", session:" + session.sessionId);
Richard Uhlerf0bdca52019-01-31 16:43:59 +0000921
Richard Uhler1fc10c12019-03-18 11:38:46 +0000922 String installerPackageName = session.getInstallerPackageName();
923 if (!enableRollbackAllowed(installerPackageName, packageName)) {
shafik7ad57752019-06-05 14:59:57 +0100924 Slog.e(TAG, "Installer " + installerPackageName
Richard Uhler1fc10c12019-03-18 11:38:46 +0000925 + " is not allowed to enable rollback on " + packageName);
926 return false;
927 }
928
Richard Uhlera7e9b2d2019-01-22 17:20:58 +0000929 VersionedPackage newVersion = new VersionedPackage(packageName, newPackage.versionCode);
Narayan Kamathfcd4a042019-02-01 14:16:37 +0000930 final boolean isApex = ((installFlags & PackageManager.INSTALL_APEX) != 0);
Richard Uhlere95d0552018-12-27 15:03:41 +0000931
932 // Get information about the currently installed package.
Richard Uhler1f571c62019-01-31 15:16:46 +0000933 PackageManager pm = mContext.getPackageManager();
Richard Uhler1fc10c12019-03-18 11:38:46 +0000934 final PackageInfo pkgInfo;
Richard Uhler1f571c62019-01-31 15:16:46 +0000935 try {
Richard Uhler82913b72019-04-01 13:02:31 +0100936 pkgInfo = getPackageInfo(packageName);
Richard Uhler1f571c62019-01-31 15:16:46 +0000937 } catch (PackageManager.NameNotFoundException e) {
Richard Uhlere95d0552018-12-27 15:03:41 +0000938 // TODO: Support rolling back fresh package installs rather than
939 // fail here. Test this case.
shafik7ad57752019-06-05 14:59:57 +0100940 Slog.e(TAG, packageName + " is not installed");
Richard Uhlere95d0552018-12-27 15:03:41 +0000941 return false;
942 }
Richard Uhlere95d0552018-12-27 15:03:41 +0000943
Richard Uhler1f571c62019-01-31 15:16:46 +0000944 VersionedPackage installedVersion = new VersionedPackage(packageName,
945 pkgInfo.getLongVersionCode());
Narayan Kamath869f7062019-01-10 12:24:15 +0000946
Richard Uhler47569702019-05-02 12:36:39 +0100947 PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(
948 newVersion, installedVersion,
Nikita Ioffe5dcd17972019-02-04 11:08:13 +0000949 new IntArray() /* pendingBackups */, new ArrayList<>() /* pendingRestores */,
Oli Lan87a504d2019-08-09 10:25:49 +0100950 isApex, new IntArray(), new SparseLongArray() /* ceSnapshotInodes */);
Richard Uhlere95d0552018-12-27 15:03:41 +0000951
Richard Uhler1f571c62019-01-31 15:16:46 +0000952 try {
Richard Uhlerab009ea2019-02-25 12:11:05 +0000953 ApplicationInfo appInfo = pkgInfo.applicationInfo;
Richard Uhler91fc4db2019-07-19 14:55:01 +0100954 RollbackStore.backupPackageCodePath(rollback, packageName, appInfo.sourceDir);
Richard Uhlerab009ea2019-02-25 12:11:05 +0000955 if (!ArrayUtils.isEmpty(appInfo.splitSourceDirs)) {
956 for (String sourceDir : appInfo.splitSourceDirs) {
Richard Uhler91fc4db2019-07-19 14:55:01 +0100957 RollbackStore.backupPackageCodePath(rollback, packageName, sourceDir);
Richard Uhlerab009ea2019-02-25 12:11:05 +0000958 }
959 }
Richard Uhler1f571c62019-01-31 15:16:46 +0000960 } catch (IOException e) {
shafik7ad57752019-06-05 14:59:57 +0100961 Slog.e(TAG, "Unable to copy package for rollback for " + packageName, e);
Richard Uhlere95d0552018-12-27 15:03:41 +0000962 return false;
963 }
Richard Uhler47569702019-05-02 12:36:39 +0100964
965 synchronized (mLock) {
Richard Uhler91fc4db2019-07-19 14:55:01 +0100966 rollback.info.getPackages().add(packageRollbackInfo);
Richard Uhler47569702019-05-02 12:36:39 +0100967 }
Richard Uhlere95d0552018-12-27 15:03:41 +0000968 return true;
969 }
970
Narayan Kamath869f7062019-01-10 12:24:15 +0000971 @Override
Gavin Corkeryab5ee412019-05-29 15:33:35 +0100972 public void snapshotAndRestoreUserData(String packageName, int[] userIds, int appId,
973 long ceDataInode, String seInfo, int token) {
Narayan Kamath869f7062019-01-10 12:24:15 +0000974 if (Binder.getCallingUid() != Process.SYSTEM_UID) {
Gavin Corkeryab5ee412019-05-29 15:33:35 +0100975 throw new SecurityException(
976 "snapshotAndRestoreUserData may only be called by the system.");
Narayan Kamath869f7062019-01-10 12:24:15 +0000977 }
978
979 getHandler().post(() -> {
Oli Lan87a504d2019-08-09 10:25:49 +0100980 snapshotUserDataInternal(packageName, userIds);
Richard Uhler38fab3f2019-02-22 16:53:10 +0000981 restoreUserDataInternal(packageName, userIds, appId, ceDataInode, seInfo, token);
Narayan Kamathc034fe92019-01-23 10:48:17 +0000982 final PackageManagerInternal pmi = LocalServices.getService(
983 PackageManagerInternal.class);
Narayan Kamath869f7062019-01-10 12:24:15 +0000984 pmi.finishPackageInstall(token, false);
985 });
986 }
987
Oli Lan87a504d2019-08-09 10:25:49 +0100988 private void snapshotUserDataInternal(String packageName, int[] userIds) {
Gavin Corkeryab5ee412019-05-29 15:33:35 +0100989 synchronized (mLock) {
990 // staged installs
Gavin Corkeryab5ee412019-05-29 15:33:35 +0100991 for (int i = 0; i < mRollbacks.size(); i++) {
Richard Uhler91fc4db2019-07-19 14:55:01 +0100992 Rollback rollback = mRollbacks.get(i);
993 if (rollback.state != Rollback.ROLLBACK_STATE_ENABLING) {
Gavin Corkeryab5ee412019-05-29 15:33:35 +0100994 continue;
995 }
996
Richard Uhler91fc4db2019-07-19 14:55:01 +0100997 for (PackageRollbackInfo info : rollback.info.getPackages()) {
Gavin Corkeryab5ee412019-05-29 15:33:35 +0100998 if (info.getPackageName().equals(packageName)) {
Oli Lan87a504d2019-08-09 10:25:49 +0100999 mAppDataRollbackHelper.snapshotAppData(
Richard Uhler91fc4db2019-07-19 14:55:01 +01001000 rollback.info.getRollbackId(), info, userIds);
1001 saveRollback(rollback);
Gavin Corkery237dd812019-06-04 22:53:15 +01001002 break;
Gavin Corkeryab5ee412019-05-29 15:33:35 +01001003 }
1004 }
1005 }
1006 // non-staged installs
1007 PackageRollbackInfo info;
1008 for (NewRollback rollback : mNewRollbacks) {
Richard Uhler91fc4db2019-07-19 14:55:01 +01001009 info = getPackageRollbackInfo(rollback.rollback, packageName);
Gavin Corkeryab5ee412019-05-29 15:33:35 +01001010 if (info != null) {
Oli Lan87a504d2019-08-09 10:25:49 +01001011 mAppDataRollbackHelper.snapshotAppData(
Richard Uhler91fc4db2019-07-19 14:55:01 +01001012 rollback.rollback.info.getRollbackId(), info, userIds);
1013 saveRollback(rollback.rollback);
Gavin Corkeryab5ee412019-05-29 15:33:35 +01001014 }
1015 }
1016 }
1017 }
1018
Richard Uhler38fab3f2019-02-22 16:53:10 +00001019 private void restoreUserDataInternal(String packageName, int[] userIds, int appId,
1020 long ceDataInode, String seInfo, int token) {
Richard Uhler6f8a33b2019-02-26 10:40:36 +00001021 PackageRollbackInfo info = null;
Richard Uhler91fc4db2019-07-19 14:55:01 +01001022 Rollback rollback = null;
Richard Uhler6f8a33b2019-02-26 10:40:36 +00001023 synchronized (mLock) {
Richard Uhler6f8a33b2019-02-26 10:40:36 +00001024 for (int i = 0; i < mRollbacks.size(); ++i) {
Richard Uhler91fc4db2019-07-19 14:55:01 +01001025 Rollback candidate = mRollbacks.get(i);
1026 if (candidate.restoreUserDataInProgress) {
1027 info = getPackageRollbackInfo(candidate, packageName);
Richard Uhler6f8a33b2019-02-26 10:40:36 +00001028 if (info != null) {
Richard Uhler91fc4db2019-07-19 14:55:01 +01001029 rollback = candidate;
Richard Uhler6f8a33b2019-02-26 10:40:36 +00001030 break;
1031 }
1032 }
1033 }
1034 }
1035
Richard Uhler91fc4db2019-07-19 14:55:01 +01001036 if (rollback == null) {
Richard Uhler38fab3f2019-02-22 16:53:10 +00001037 return;
1038 }
1039
Richard Uhler38fab3f2019-02-22 16:53:10 +00001040 for (int userId : userIds) {
Richard Uhler91fc4db2019-07-19 14:55:01 +01001041 final boolean changedRollback = mAppDataRollbackHelper.restoreAppData(
1042 rollback.info.getRollbackId(), info, userId, appId, seInfo);
Richard Uhler38fab3f2019-02-22 16:53:10 +00001043
1044 // We've updated metadata about this rollback, so save it to flash.
Richard Uhler91fc4db2019-07-19 14:55:01 +01001045 if (changedRollback) {
1046 saveRollback(rollback);
Richard Uhler38fab3f2019-02-22 16:53:10 +00001047 }
1048 }
1049 }
1050
Narayan Kamathfcd4a042019-02-01 14:16:37 +00001051 @Override
1052 public boolean notifyStagedSession(int sessionId) {
shafik3be41a92019-03-25 14:10:32 +00001053 if (Binder.getCallingUid() != Process.SYSTEM_UID) {
1054 throw new SecurityException("notifyStagedSession may only be called by the system.");
1055 }
Narayan Kamathfcd4a042019-02-01 14:16:37 +00001056 final LinkedBlockingQueue<Boolean> result = new LinkedBlockingQueue<>();
1057
1058 // NOTE: We post this runnable on the RollbackManager's binder thread because we'd prefer
1059 // to preserve the invariant that all operations that modify state happen there.
1060 getHandler().post(() -> {
1061 PackageInstaller installer = mContext.getPackageManager().getPackageInstaller();
1062
1063 final PackageInstaller.SessionInfo session = installer.getSessionInfo(sessionId);
1064 if (session == null) {
shafik7ad57752019-06-05 14:59:57 +01001065 Slog.e(TAG, "No matching install session for: " + sessionId);
Narayan Kamathfcd4a042019-02-01 14:16:37 +00001066 result.offer(false);
1067 return;
1068 }
1069
Richard Uhler47569702019-05-02 12:36:39 +01001070 NewRollback newRollback;
1071 synchronized (mLock) {
1072 newRollback = createNewRollbackLocked(session);
1073 }
1074
Narayan Kamathfcd4a042019-02-01 14:16:37 +00001075 if (!session.isMultiPackage()) {
Richard Uhler91fc4db2019-07-19 14:55:01 +01001076 if (!enableRollbackForPackageSession(newRollback.rollback, session)) {
shafik7ad57752019-06-05 14:59:57 +01001077 Slog.e(TAG, "Unable to enable rollback for session: " + sessionId);
Narayan Kamathfcd4a042019-02-01 14:16:37 +00001078 result.offer(false);
1079 return;
1080 }
1081 } else {
1082 for (int childSessionId : session.getChildSessionIds()) {
1083 final PackageInstaller.SessionInfo childSession =
1084 installer.getSessionInfo(childSessionId);
1085 if (childSession == null) {
shafik7ad57752019-06-05 14:59:57 +01001086 Slog.e(TAG, "No matching child install session for: " + childSessionId);
Narayan Kamathfcd4a042019-02-01 14:16:37 +00001087 result.offer(false);
1088 return;
1089 }
Richard Uhler91fc4db2019-07-19 14:55:01 +01001090 if (!enableRollbackForPackageSession(newRollback.rollback, childSession)) {
shafik7ad57752019-06-05 14:59:57 +01001091 Slog.e(TAG, "Unable to enable rollback for session: " + sessionId);
Narayan Kamathfcd4a042019-02-01 14:16:37 +00001092 result.offer(false);
1093 return;
1094 }
1095 }
1096 }
1097
Richard Uhler47569702019-05-02 12:36:39 +01001098 result.offer(completeEnableRollback(newRollback, true) != null);
Narayan Kamathfcd4a042019-02-01 14:16:37 +00001099 });
1100
1101 try {
1102 return result.take();
1103 } catch (InterruptedException ie) {
shafik7ad57752019-06-05 14:59:57 +01001104 Slog.e(TAG, "Interrupted while waiting for notifyStagedSession response");
Narayan Kamathfcd4a042019-02-01 14:16:37 +00001105 return false;
1106 }
1107 }
1108
Richard Uhler6fa7d132019-02-05 13:55:11 +00001109 @Override
1110 public void notifyStagedApkSession(int originalSessionId, int apkSessionId) {
shafik3be41a92019-03-25 14:10:32 +00001111 if (Binder.getCallingUid() != Process.SYSTEM_UID) {
1112 throw new SecurityException("notifyStagedApkSession may only be called by the system.");
1113 }
Richard Uhlerba13ab22019-02-05 15:27:12 +00001114 getHandler().post(() -> {
Richard Uhler91fc4db2019-07-19 14:55:01 +01001115 Rollback rollback = null;
Richard Uhlerba13ab22019-02-05 15:27:12 +00001116 synchronized (mLock) {
Richard Uhler6f8a33b2019-02-26 10:40:36 +00001117 for (int i = 0; i < mRollbacks.size(); ++i) {
Richard Uhler91fc4db2019-07-19 14:55:01 +01001118 Rollback candidate = mRollbacks.get(i);
1119 if (candidate.stagedSessionId == originalSessionId) {
1120 candidate.apkSessionId = apkSessionId;
1121 rollback = candidate;
Richard Uhlerba13ab22019-02-05 15:27:12 +00001122 break;
1123 }
1124 }
1125 }
1126
Richard Uhler91fc4db2019-07-19 14:55:01 +01001127 if (rollback != null) {
1128 saveRollback(rollback);
Richard Uhlerba13ab22019-02-05 15:27:12 +00001129 }
1130 });
Richard Uhler6fa7d132019-02-05 13:55:11 +00001131 }
1132
Richard Uhlere95d0552018-12-27 15:03:41 +00001133 /**
Richard Uhler1fc10c12019-03-18 11:38:46 +00001134 * Returns true if the installer is allowed to enable rollback for the
1135 * given named package, false otherwise.
1136 */
1137 private boolean enableRollbackAllowed(String installerPackageName, String packageName) {
1138 if (installerPackageName == null) {
1139 return false;
1140 }
1141
1142 PackageManager pm = mContext.getPackageManager();
1143 boolean manageRollbacksGranted = pm.checkPermission(
1144 Manifest.permission.MANAGE_ROLLBACKS,
1145 installerPackageName) == PackageManager.PERMISSION_GRANTED;
1146
1147 boolean testManageRollbacksGranted = pm.checkPermission(
1148 Manifest.permission.TEST_MANAGE_ROLLBACKS,
1149 installerPackageName) == PackageManager.PERMISSION_GRANTED;
1150
1151 // For now only allow rollbacks for modules or for testing.
1152 return (isModule(packageName) && manageRollbacksGranted)
1153 || testManageRollbacksGranted;
1154 }
1155
1156 /**
1157 * Returns true if the package name is the name of a module.
1158 */
1159 private boolean isModule(String packageName) {
1160 PackageManager pm = mContext.getPackageManager();
1161 final ModuleInfo moduleInfo;
1162 try {
1163 moduleInfo = pm.getModuleInfo(packageName, 0);
1164 } catch (PackageManager.NameNotFoundException e) {
1165 return false;
1166 }
1167
1168 return moduleInfo != null;
1169 }
1170
1171 /**
Richard Uhlere95d0552018-12-27 15:03:41 +00001172 * Gets the version of the package currently installed.
1173 * Returns null if the package is not currently installed.
1174 */
Richard Uhlera7e9b2d2019-01-22 17:20:58 +00001175 private VersionedPackage getInstalledPackageVersion(String packageName) {
Richard Uhlere95d0552018-12-27 15:03:41 +00001176 PackageManager pm = mContext.getPackageManager();
1177 PackageInfo pkgInfo = null;
1178 try {
Richard Uhler82913b72019-04-01 13:02:31 +01001179 pkgInfo = getPackageInfo(packageName);
Richard Uhlere95d0552018-12-27 15:03:41 +00001180 } catch (PackageManager.NameNotFoundException e) {
1181 return null;
1182 }
1183
Richard Uhlera7e9b2d2019-01-22 17:20:58 +00001184 return new VersionedPackage(packageName, pkgInfo.getLongVersionCode());
1185 }
1186
Richard Uhler82913b72019-04-01 13:02:31 +01001187 /**
1188 * Gets PackageInfo for the given package.
1189 * Matches any user and apex. Returns null if no such package is
1190 * installed.
1191 */
1192 private PackageInfo getPackageInfo(String packageName)
1193 throws PackageManager.NameNotFoundException {
1194 PackageManager pm = mContext.getPackageManager();
1195 try {
1196 // The MATCH_ANY_USER flag doesn't mix well with the MATCH_APEX
1197 // flag, so make two separate attempts to get the package info.
1198 // We don't need both flags at the same time because we assume
1199 // apex files are always installed for all users.
1200 return pm.getPackageInfo(packageName, PackageManager.MATCH_ANY_USER);
1201 } catch (PackageManager.NameNotFoundException e) {
1202 return pm.getPackageInfo(packageName, PackageManager.MATCH_APEX);
1203 }
1204 }
1205
1206
Richard Uhlera7e9b2d2019-01-22 17:20:58 +00001207 private boolean packageVersionsEqual(VersionedPackage a, VersionedPackage b) {
Richard Uhlerb4bad062019-07-10 15:00:09 +01001208 return a != null && b != null
1209 && a.getPackageName().equals(b.getPackageName())
Richard Uhlera7e9b2d2019-01-22 17:20:58 +00001210 && a.getLongVersionCode() == b.getLongVersionCode();
Richard Uhlere95d0552018-12-27 15:03:41 +00001211 }
Richard Uhler2d7c7f0d2019-01-04 09:18:21 +00001212
1213 private class SessionCallback extends PackageInstaller.SessionCallback {
1214
1215 @Override
1216 public void onCreated(int sessionId) { }
1217
1218 @Override
1219 public void onBadgingChanged(int sessionId) { }
1220
1221 @Override
1222 public void onActiveChanged(int sessionId, boolean active) { }
1223
1224 @Override
1225 public void onProgressChanged(int sessionId, float progress) { }
1226
1227 @Override
1228 public void onFinished(int sessionId, boolean success) {
Richard Uhler47569702019-05-02 12:36:39 +01001229 NewRollback newRollback;
1230 synchronized (mLock) {
1231 newRollback = getNewRollbackForPackageSessionLocked(sessionId);
1232 if (newRollback != null) {
1233 mNewRollbacks.remove(newRollback);
1234 }
1235 }
1236
1237 if (newRollback != null) {
Richard Uhler91fc4db2019-07-19 14:55:01 +01001238 Rollback rollback = completeEnableRollback(newRollback, success);
Richard Uhler47569702019-05-02 12:36:39 +01001239 if (rollback != null && !rollback.isStaged()) {
1240 makeRollbackAvailable(rollback);
1241 }
Richard Uhler2d7c7f0d2019-01-04 09:18:21 +00001242 }
Narayan Kamathfcd4a042019-02-01 14:16:37 +00001243 }
1244 }
Richard Uhler2d7c7f0d2019-01-04 09:18:21 +00001245
Richard Uhler6f8a33b2019-02-26 10:40:36 +00001246 /**
1247 * Add a rollback to the list of rollbacks.
1248 * This should be called after rollback has been enabled for all packages
1249 * in the rollback. It does not make the rollback available yet.
1250 *
Richard Uhler91fc4db2019-07-19 14:55:01 +01001251 * @return the Rollback instance for a successfully enable-completed rollback,
Richard Uhler47569702019-05-02 12:36:39 +01001252 * or null on error.
Richard Uhler6f8a33b2019-02-26 10:40:36 +00001253 */
Richard Uhler91fc4db2019-07-19 14:55:01 +01001254 private Rollback completeEnableRollback(NewRollback newRollback, boolean success) {
1255 Rollback rollback = newRollback.rollback;
Richard Uhler47569702019-05-02 12:36:39 +01001256 if (!success) {
1257 // The install session was aborted, clean up the pending install.
Richard Uhler91fc4db2019-07-19 14:55:01 +01001258 deleteRollback(rollback);
Richard Uhler6f8a33b2019-02-26 10:40:36 +00001259 return null;
Narayan Kamathfcd4a042019-02-01 14:16:37 +00001260 }
shafik4831ad72019-05-03 17:36:42 +01001261 if (newRollback.isCancelled) {
shafik7ad57752019-06-05 14:59:57 +01001262 Slog.e(TAG, "Rollback has been cancelled by PackageManager");
Richard Uhler91fc4db2019-07-19 14:55:01 +01001263 deleteRollback(rollback);
shafik4831ad72019-05-03 17:36:42 +01001264 return null;
1265 }
Narayan Kamathfcd4a042019-02-01 14:16:37 +00001266
Richard Uhler91fc4db2019-07-19 14:55:01 +01001267 // It's safe to access rollback.info outside a synchronized block because
Richard Uhler47569702019-05-02 12:36:39 +01001268 // this is running on the handler thread and all changes to the
Richard Uhler91fc4db2019-07-19 14:55:01 +01001269 // rollback.info occur on the handler thread.
1270 if (rollback.info.getPackages().size() != newRollback.packageSessionIds.length) {
shafik7ad57752019-06-05 14:59:57 +01001271 Slog.e(TAG, "Failed to enable rollback for all packages in session.");
Richard Uhler91fc4db2019-07-19 14:55:01 +01001272 deleteRollback(rollback);
Richard Uhler6f8a33b2019-02-26 10:40:36 +00001273 return null;
Narayan Kamathfcd4a042019-02-01 14:16:37 +00001274 }
Narayan Kamathfcd4a042019-02-01 14:16:37 +00001275
Richard Uhler91fc4db2019-07-19 14:55:01 +01001276 saveRollback(rollback);
Richard Uhler035e9742019-01-09 13:11:07 +00001277 synchronized (mLock) {
Richard Uhler6f8a33b2019-02-26 10:40:36 +00001278 // Note: There is a small window of time between when
1279 // the session has been committed by the package
1280 // manager and when we make the rollback available
1281 // here. Presumably the window is small enough that
1282 // nobody will want to roll back the newly installed
1283 // package before we make the rollback available.
Richard Uhler91fc4db2019-07-19 14:55:01 +01001284 // TODO: We'll lose the rollback if the
Richard Uhler6f8a33b2019-02-26 10:40:36 +00001285 // device reboots between when the session is
1286 // committed and this point. Revisit this after
1287 // adding support for rollback of staged installs.
Richard Uhler91fc4db2019-07-19 14:55:01 +01001288 mRollbacks.add(rollback);
Richard Uhler035e9742019-01-09 13:11:07 +00001289 }
Richard Uhler6f8a33b2019-02-26 10:40:36 +00001290
Richard Uhler91fc4db2019-07-19 14:55:01 +01001291 return rollback;
Richard Uhler6f8a33b2019-02-26 10:40:36 +00001292 }
1293
Richard Uhler91fc4db2019-07-19 14:55:01 +01001294 private void makeRollbackAvailable(Rollback rollback) {
Richard Uhler6f8a33b2019-02-26 10:40:36 +00001295 // TODO: What if the rollback has since been expired, for example due
1296 // to a new package being installed. Won't this revive an expired
1297 // rollback? Consider adding a ROLLBACK_STATE_EXPIRED to address this.
1298 synchronized (mLock) {
Richard Uhler91fc4db2019-07-19 14:55:01 +01001299 rollback.state = Rollback.ROLLBACK_STATE_AVAILABLE;
1300 rollback.timestamp = Instant.now();
Richard Uhler6f8a33b2019-02-26 10:40:36 +00001301 }
Richard Uhler91fc4db2019-07-19 14:55:01 +01001302 saveRollback(rollback);
Richard Uhler6f8a33b2019-02-26 10:40:36 +00001303
1304 // TODO(zezeozue): Provide API to explicitly start observing instead
1305 // of doing this for all rollbacks. If we do this for all rollbacks,
1306 // should document in PackageInstaller.SessionParams#setEnableRollback
1307 // After enabling and commiting any rollback, observe packages and
1308 // prepare to rollback if packages crashes too frequently.
1309 List<String> packages = new ArrayList<>();
Richard Uhler91fc4db2019-07-19 14:55:01 +01001310 for (int i = 0; i < rollback.info.getPackages().size(); i++) {
1311 packages.add(rollback.info.getPackages().get(i).getPackageName());
Richard Uhler6f8a33b2019-02-26 10:40:36 +00001312 }
1313 mPackageHealthObserver.startObservingHealth(packages,
1314 mRollbackLifetimeDurationInMillis);
1315 scheduleExpiration(mRollbackLifetimeDurationInMillis);
Richard Uhler035e9742019-01-09 13:11:07 +00001316 }
Richard Uhlerb9d54472019-01-22 12:50:08 +00001317
Richard Uhler0a79b322019-01-23 13:51:07 +00001318 /*
Richard Uhler91fc4db2019-07-19 14:55:01 +01001319 * Returns the rollback with the given rollbackId, if any.
Richard Uhler0a79b322019-01-23 13:51:07 +00001320 */
Richard Uhler91fc4db2019-07-19 14:55:01 +01001321 private Rollback getRollbackForId(int rollbackId) {
Richard Uhler0a79b322019-01-23 13:51:07 +00001322 synchronized (mLock) {
Richard Uhler6f8a33b2019-02-26 10:40:36 +00001323 for (int i = 0; i < mRollbacks.size(); ++i) {
Richard Uhler91fc4db2019-07-19 14:55:01 +01001324 Rollback rollback = mRollbacks.get(i);
1325 if (rollback.info.getRollbackId() == rollbackId) {
1326 return rollback;
Richard Uhler0a79b322019-01-23 13:51:07 +00001327 }
1328 }
1329 }
Narayan Kamathc034fe92019-01-23 10:48:17 +00001330
1331 return null;
1332 }
1333
1334 /**
1335 * Returns the {@code PackageRollbackInfo} associated with {@code packageName} from
Richard Uhler91fc4db2019-07-19 14:55:01 +01001336 * a specified {@code Rollback}.
Narayan Kamathc034fe92019-01-23 10:48:17 +00001337 */
Richard Uhler91fc4db2019-07-19 14:55:01 +01001338 private static PackageRollbackInfo getPackageRollbackInfo(Rollback rollback,
Narayan Kamathc034fe92019-01-23 10:48:17 +00001339 String packageName) {
Richard Uhler91fc4db2019-07-19 14:55:01 +01001340 for (PackageRollbackInfo info : rollback.info.getPackages()) {
Narayan Kamathc034fe92019-01-23 10:48:17 +00001341 if (info.getPackageName().equals(packageName)) {
1342 return info;
1343 }
1344 }
1345
Richard Uhler0a79b322019-01-23 13:51:07 +00001346 return null;
1347 }
1348
Richard Uhlerb9d54472019-01-22 12:50:08 +00001349 @GuardedBy("mLock")
Richard Uhler47569702019-05-02 12:36:39 +01001350 private int allocateRollbackIdLocked() {
Richard Uhlerb9d54472019-01-22 12:50:08 +00001351 int n = 0;
1352 int rollbackId;
1353 do {
1354 rollbackId = mRandom.nextInt(Integer.MAX_VALUE - 1) + 1;
1355 if (!mAllocatedRollbackIds.get(rollbackId, false)) {
1356 mAllocatedRollbackIds.put(rollbackId, true);
1357 return rollbackId;
1358 }
1359 } while (n++ < 32);
1360
Richard Uhler47569702019-05-02 12:36:39 +01001361 throw new IllegalStateException("Failed to allocate rollback ID");
Richard Uhlerb9d54472019-01-22 12:50:08 +00001362 }
Nikita Ioffe952aa7b2019-01-28 19:49:56 +00001363
Richard Uhler91fc4db2019-07-19 14:55:01 +01001364 private void deleteRollback(Rollback rollback) {
1365 for (PackageRollbackInfo info : rollback.info.getPackages()) {
Oli Lan87a504d2019-08-09 10:25:49 +01001366 IntArray snapshottedUsers = info.getSnapshottedUsers();
1367 for (int i = 0; i < snapshottedUsers.size(); i++) {
1368 int userId = snapshottedUsers.get(i);
Richard Uhler91fc4db2019-07-19 14:55:01 +01001369 mAppDataRollbackHelper.destroyAppDataSnapshot(rollback.info.getRollbackId(),
Richard Uhlercca637a2019-02-27 11:50:48 +00001370 info, userId);
Nikita Ioffe952aa7b2019-01-28 19:49:56 +00001371 }
1372 }
Richard Uhler91fc4db2019-07-19 14:55:01 +01001373 mRollbackStore.deleteRollback(rollback);
Nikita Ioffe952aa7b2019-01-28 19:49:56 +00001374 }
Richard Uhler6f8a33b2019-02-26 10:40:36 +00001375
1376 /**
Richard Uhler91fc4db2019-07-19 14:55:01 +01001377 * Saves a rollback, swallowing any IOExceptions.
Richard Uhler6f8a33b2019-02-26 10:40:36 +00001378 * For those times when it's not obvious what to do about the IOException.
1379 * TODO: Double check we can't do a better job handling the IOException in
1380 * a cases where this method is called.
1381 */
Richard Uhler91fc4db2019-07-19 14:55:01 +01001382 private void saveRollback(Rollback rollback) {
Richard Uhler6f8a33b2019-02-26 10:40:36 +00001383 try {
Richard Uhler91fc4db2019-07-19 14:55:01 +01001384 mRollbackStore.saveRollback(rollback);
Richard Uhler6f8a33b2019-02-26 10:40:36 +00001385 } catch (IOException ioe) {
Richard Uhler91fc4db2019-07-19 14:55:01 +01001386 Slog.e(TAG, "Unable to save rollback for: "
1387 + rollback.info.getRollbackId(), ioe);
Richard Uhler6f8a33b2019-02-26 10:40:36 +00001388 }
1389 }
shafik60046002019-03-12 17:54:10 +00001390
1391 @Override
1392 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1393 IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
1394 synchronized (mLock) {
Richard Uhler91fc4db2019-07-19 14:55:01 +01001395 for (Rollback rollback : mRollbacks) {
1396 RollbackInfo info = rollback.info;
shafik60046002019-03-12 17:54:10 +00001397 ipw.println(info.getRollbackId() + ":");
1398 ipw.increaseIndent();
Richard Uhler91fc4db2019-07-19 14:55:01 +01001399 ipw.println("-state: " + rollback.getStateAsString());
1400 ipw.println("-timestamp: " + rollback.timestamp);
1401 if (rollback.stagedSessionId != -1) {
1402 ipw.println("-stagedSessionId: " + rollback.stagedSessionId);
shafik60046002019-03-12 17:54:10 +00001403 }
1404 ipw.println("-packages:");
1405 ipw.increaseIndent();
1406 for (PackageRollbackInfo pkg : info.getPackages()) {
1407 ipw.println(pkg.getPackageName()
1408 + " " + pkg.getVersionRolledBackFrom().getLongVersionCode()
1409 + " -> " + pkg.getVersionRolledBackTo().getLongVersionCode());
1410 }
1411 ipw.decreaseIndent();
Richard Uhler91fc4db2019-07-19 14:55:01 +01001412 if (rollback.state == Rollback.ROLLBACK_STATE_COMMITTED) {
shafik60046002019-03-12 17:54:10 +00001413 ipw.println("-causePackages:");
1414 ipw.increaseIndent();
1415 for (VersionedPackage cPkg : info.getCausePackages()) {
1416 ipw.println(cPkg.getPackageName() + " " + cPkg.getLongVersionCode());
1417 }
1418 ipw.decreaseIndent();
1419 ipw.println("-committedSessionId: " + info.getCommittedSessionId());
1420 }
1421 ipw.decreaseIndent();
1422 }
1423 }
1424 }
Richard Uhler1fc10c12019-03-18 11:38:46 +00001425
1426 private void enforceManageRollbacks(@NonNull String message) {
1427 if ((PackageManager.PERMISSION_GRANTED != mContext.checkCallingOrSelfPermission(
1428 Manifest.permission.MANAGE_ROLLBACKS))
1429 && (PackageManager.PERMISSION_GRANTED != mContext.checkCallingOrSelfPermission(
1430 Manifest.permission.TEST_MANAGE_ROLLBACKS))) {
1431 throw new SecurityException(message + " requires "
1432 + Manifest.permission.MANAGE_ROLLBACKS + " or "
1433 + Manifest.permission.TEST_MANAGE_ROLLBACKS);
1434 }
1435 }
Richard Uhler47569702019-05-02 12:36:39 +01001436
1437 private static class NewRollback {
Richard Uhler91fc4db2019-07-19 14:55:01 +01001438 public final Rollback rollback;
Richard Uhler47569702019-05-02 12:36:39 +01001439
1440 /**
shafik4831ad72019-05-03 17:36:42 +01001441 * This array holds all of the rollback tokens associated with package sessions included
1442 * in this rollback. This is used to identify which rollback should be cancelled in case
1443 * {@link PackageManager} sends an {@link Intent#ACTION_CANCEL_ENABLE_ROLLBACK} intent.
1444 */
1445 private final IntArray mTokens = new IntArray();
1446
1447 /**
Richard Uhler47569702019-05-02 12:36:39 +01001448 * Session ids for all packages in the install.
1449 * For multi-package sessions, this is the list of child session ids.
1450 * For normal sessions, this list is a single element with the normal
1451 * session id.
1452 */
1453 public final int[] packageSessionIds;
1454
shafik4831ad72019-05-03 17:36:42 +01001455 /**
Richard Uhler91fc4db2019-07-19 14:55:01 +01001456 * Flag to determine whether the rollback has been cancelled.
shafik4831ad72019-05-03 17:36:42 +01001457 *
Richard Uhler91fc4db2019-07-19 14:55:01 +01001458 * <p>Rollback could be invalidated and cancelled if RollbackManager receives
shafik4831ad72019-05-03 17:36:42 +01001459 * {@link Intent#ACTION_CANCEL_ENABLE_ROLLBACK} from {@link PackageManager}.
1460 *
1461 * <p>The main underlying assumption here is that if enabling the rollback times out, then
1462 * {@link PackageManager} will NOT send
1463 * {@link PackageInstaller.SessionCallback#onFinished(int, boolean)} before it broadcasts
1464 * {@link Intent#ACTION_CANCEL_ENABLE_ROLLBACK}.
1465 */
1466 public boolean isCancelled = false;
1467
Richard Uhler91fc4db2019-07-19 14:55:01 +01001468 NewRollback(Rollback rollback, int[] packageSessionIds) {
1469 this.rollback = rollback;
Richard Uhler47569702019-05-02 12:36:39 +01001470 this.packageSessionIds = packageSessionIds;
1471 }
shafik4831ad72019-05-03 17:36:42 +01001472
1473 public void addToken(int token) {
1474 mTokens.add(token);
1475 }
1476
1477 public boolean hasToken(int token) {
1478 return mTokens.indexOf(token) != -1;
1479 }
Richard Uhler47569702019-05-02 12:36:39 +01001480 }
1481
1482 NewRollback createNewRollbackLocked(PackageInstaller.SessionInfo parentSession) {
1483 int rollbackId = allocateRollbackIdLocked();
Richard Uhler91fc4db2019-07-19 14:55:01 +01001484 final Rollback rollback;
Richard Uhler47569702019-05-02 12:36:39 +01001485 int parentSessionId = parentSession.getSessionId();
1486
1487 if (parentSession.isStaged()) {
Richard Uhler91fc4db2019-07-19 14:55:01 +01001488 rollback = mRollbackStore.createStagedRollback(rollbackId, parentSessionId);
Richard Uhler47569702019-05-02 12:36:39 +01001489 } else {
Richard Uhler91fc4db2019-07-19 14:55:01 +01001490 rollback = mRollbackStore.createNonStagedRollback(rollbackId);
Richard Uhler47569702019-05-02 12:36:39 +01001491 }
1492
1493 int[] packageSessionIds;
1494 if (parentSession.isMultiPackage()) {
1495 packageSessionIds = parentSession.getChildSessionIds();
1496 } else {
1497 packageSessionIds = new int[]{parentSessionId};
1498 }
1499
Richard Uhler91fc4db2019-07-19 14:55:01 +01001500 return new NewRollback(rollback, packageSessionIds);
Richard Uhler47569702019-05-02 12:36:39 +01001501 }
1502
1503 /**
1504 * Returns the NewRollback associated with the given package session.
1505 * Returns null if no NewRollback is found for the given package
1506 * session.
1507 */
1508 NewRollback getNewRollbackForPackageSessionLocked(int packageSessionId) {
1509 // We expect mNewRollbacks to be a very small list; linear search
1510 // should be plenty fast.
Richard Uhler91fc4db2019-07-19 14:55:01 +01001511 for (NewRollback newRollback: mNewRollbacks) {
1512 for (int id : newRollback.packageSessionIds) {
Richard Uhler47569702019-05-02 12:36:39 +01001513 if (id == packageSessionId) {
Richard Uhler91fc4db2019-07-19 14:55:01 +01001514 return newRollback;
Richard Uhler47569702019-05-02 12:36:39 +01001515 }
1516 }
1517 }
1518 return null;
1519 }
Richard Uhlere95d0552018-12-27 15:03:41 +00001520}