2 * Copyright (C) 2018 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.server.rollback;
19 import android.Manifest;
20 import android.annotation.AnyThread;
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.annotation.WorkerThread;
24 import android.app.AppOpsManager;
25 import android.content.BroadcastReceiver;
26 import android.content.Context;
27 import android.content.Intent;
28 import android.content.IntentFilter;
29 import android.content.IntentSender;
30 import android.content.pm.ApplicationInfo;
31 import android.content.pm.ModuleInfo;
32 import android.content.pm.PackageInfo;
33 import android.content.pm.PackageInstaller;
34 import android.content.pm.PackageManager;
35 import android.content.pm.PackageManagerInternal;
36 import android.content.pm.PackageParser;
37 import android.content.pm.ParceledListSlice;
38 import android.content.pm.UserInfo;
39 import android.content.pm.VersionedPackage;
40 import android.content.pm.parsing.ApkLiteParseUtils;
41 import android.content.pm.parsing.result.ParseResult;
42 import android.content.pm.parsing.result.ParseTypeImpl;
43 import android.content.rollback.IRollbackManager;
44 import android.content.rollback.RollbackInfo;
45 import android.content.rollback.RollbackManager;
46 import android.os.Binder;
47 import android.os.Build;
48 import android.os.Environment;
49 import android.os.Handler;
50 import android.os.HandlerExecutor;
51 import android.os.HandlerThread;
52 import android.os.Process;
53 import android.os.SystemClock;
54 import android.os.UserHandle;
55 import android.os.UserManager;
56 import android.os.ext.SdkExtensions;
57 import android.provider.DeviceConfig;
58 import android.util.IntArray;
59 import android.util.Log;
60 import android.util.LongArrayQueue;
61 import android.util.Slog;
62 import android.util.SparseBooleanArray;
63 import android.util.SparseIntArray;
65 import com.android.internal.annotations.GuardedBy;
66 import com.android.internal.util.DumpUtils;
67 import com.android.internal.util.IndentingPrintWriter;
68 import com.android.server.LocalServices;
69 import com.android.server.PackageWatchdog;
70 import com.android.server.SystemConfig;
71 import com.android.server.Watchdog;
72 import com.android.server.pm.ApexManager;
73 import com.android.server.pm.Installer;
76 import java.io.FileDescriptor;
77 import java.io.PrintWriter;
78 import java.security.SecureRandom;
79 import java.time.Instant;
80 import java.time.temporal.ChronoUnit;
81 import java.util.ArrayList;
82 import java.util.Arrays;
83 import java.util.HashSet;
84 import java.util.Iterator;
85 import java.util.List;
86 import java.util.Random;
88 import java.util.concurrent.CountDownLatch;
89 import java.util.concurrent.Executor;
90 import java.util.concurrent.LinkedBlockingQueue;
91 import java.util.concurrent.TimeUnit;
94 * Implementation of service that manages APK level rollbacks.
98 * - @AnyThread annotates thread-safe methods.
99 * - @WorkerThread annotates methods that should be called from the handler thread only.
101 class RollbackManagerServiceImpl extends IRollbackManager.Stub {
103 private static final String TAG = "RollbackManager";
104 private static final boolean LOCAL_LOGV = Log.isLoggable(TAG, Log.VERBOSE);
106 // Rollbacks expire after 14 days.
107 private static final long DEFAULT_ROLLBACK_LIFETIME_DURATION_MILLIS =
108 TimeUnit.DAYS.toMillis(14);
110 // Lock used to synchronize accesses to in-memory rollback data
111 // structures. By convention, methods with the suffix "Locked" require
112 // mLock is held when they are called.
113 private final Object mLock = new Object();
115 // No need for guarding with lock because value is only accessed in handler thread
116 // and the value will be written on boot complete. Initialization here happens before
117 // handler threads are running so that's fine.
118 private long mRollbackLifetimeDurationInMillis = DEFAULT_ROLLBACK_LIFETIME_DURATION_MILLIS;
120 private static final long HANDLER_THREAD_TIMEOUT_DURATION_MILLIS =
121 TimeUnit.MINUTES.toMillis(10);
123 // Used for generating rollback IDs.
124 private final Random mRandom = new SecureRandom();
126 // Set of allocated rollback ids
128 private final SparseBooleanArray mAllocatedRollbackIds = new SparseBooleanArray();
130 // The list of all rollbacks, including available and committed rollbacks.
132 private final List<Rollback> mRollbacks;
134 // Apk sessions from a staged session with no matching rollback.
136 private final IntArray mOrphanedApkSessionIds = new IntArray();
138 private final RollbackStore mRollbackStore;
140 private final Context mContext;
141 private final HandlerThread mHandlerThread;
142 private final Executor mExecutor;
143 private final Installer mInstaller;
144 private final RollbackPackageHealthObserver mPackageHealthObserver;
145 private final AppDataRollbackHelper mAppDataRollbackHelper;
146 private final Runnable mRunExpiration = this::runExpiration;
148 // The # of milli-seconds to sleep for each received ACTION_PACKAGE_ENABLE_ROLLBACK.
149 // Used by #blockRollbackManager to test timeout in enabling rollbacks.
150 // Accessed on the handler thread only.
151 private final LongArrayQueue mSleepDuration = new LongArrayQueue();
153 // This field stores the difference in Millis between the uptime (millis since device
154 // has booted) and current time (device wall clock) - it's used to update rollback
155 // timestamps when the time is changed, by the user or by change of timezone.
156 // No need for guarding with lock because value is only accessed in handler thread.
157 private long mRelativeBootTime = calculateRelativeBootTime();
159 RollbackManagerServiceImpl(Context context) {
161 // Note that we're calling onStart here because this object is only constructed on
162 // SystemService#onStart.
163 mInstaller = new Installer(mContext);
164 mInstaller.onStart();
166 mRollbackStore = new RollbackStore(new File(Environment.getDataDirectory(), "rollback"));
168 mPackageHealthObserver = new RollbackPackageHealthObserver(mContext);
169 mAppDataRollbackHelper = new AppDataRollbackHelper(mInstaller);
171 // Load rollback data from device storage.
172 synchronized (mLock) {
173 mRollbacks = mRollbackStore.loadRollbacks();
174 if (!context.getPackageManager().isDeviceUpgrading()) {
175 for (Rollback rollback : mRollbacks) {
176 mAllocatedRollbackIds.put(rollback.info.getRollbackId(), true);
179 // Delete rollbacks when build fingerprint has changed.
180 for (Rollback rollback : mRollbacks) {
181 rollback.delete(mAppDataRollbackHelper);
187 // Kick off and start monitoring the handler thread.
188 mHandlerThread = new HandlerThread("RollbackManagerServiceHandler");
189 mHandlerThread.start();
190 Watchdog.getInstance().addThread(getHandler(), HANDLER_THREAD_TIMEOUT_DURATION_MILLIS);
191 mExecutor = new HandlerExecutor(getHandler());
193 for (UserInfo userInfo : UserManager.get(mContext).getUsers(true)) {
194 registerUserCallbacks(userInfo.getUserHandle());
197 IntentFilter enableRollbackFilter = new IntentFilter();
198 enableRollbackFilter.addAction(Intent.ACTION_PACKAGE_ENABLE_ROLLBACK);
200 enableRollbackFilter.addDataType("application/vnd.android.package-archive");
201 } catch (IntentFilter.MalformedMimeTypeException e) {
202 Slog.e(TAG, "addDataType", e);
205 mContext.registerReceiver(new BroadcastReceiver() {
207 public void onReceive(Context context, Intent intent) {
208 if (Intent.ACTION_PACKAGE_ENABLE_ROLLBACK.equals(intent.getAction())) {
209 int token = intent.getIntExtra(
210 PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_TOKEN, -1);
211 int sessionId = intent.getIntExtra(
212 PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_SESSION_ID, -1);
214 queueSleepIfNeeded();
216 getHandler().post(() -> {
217 boolean success = enableRollback(sessionId);
218 int ret = PackageManagerInternal.ENABLE_ROLLBACK_SUCCEEDED;
220 ret = PackageManagerInternal.ENABLE_ROLLBACK_FAILED;
223 PackageManagerInternal pm = LocalServices.getService(
224 PackageManagerInternal.class);
225 pm.setEnableRollbackCode(token, ret);
228 // We're handling the ordered broadcast. Abort the
229 // broadcast because there is no need for it to go to
234 }, enableRollbackFilter, null, getHandler());
236 IntentFilter enableRollbackTimedOutFilter = new IntentFilter();
237 enableRollbackTimedOutFilter.addAction(Intent.ACTION_CANCEL_ENABLE_ROLLBACK);
239 mContext.registerReceiver(new BroadcastReceiver() {
241 public void onReceive(Context context, Intent intent) {
242 if (Intent.ACTION_CANCEL_ENABLE_ROLLBACK.equals(intent.getAction())) {
243 int sessionId = intent.getIntExtra(
244 PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_SESSION_ID, -1);
246 Slog.v(TAG, "broadcast=ACTION_CANCEL_ENABLE_ROLLBACK id=" + sessionId);
248 synchronized (mLock) {
249 Rollback rollback = getRollbackForSessionLocked(sessionId);
250 if (rollback != null && rollback.isEnabling()) {
251 mRollbacks.remove(rollback);
252 rollback.delete(mAppDataRollbackHelper);
257 }, enableRollbackTimedOutFilter, null, getHandler());
259 IntentFilter userAddedIntentFilter = new IntentFilter(Intent.ACTION_USER_ADDED);
260 mContext.registerReceiver(new BroadcastReceiver() {
262 public void onReceive(Context context, Intent intent) {
263 if (Intent.ACTION_USER_ADDED.equals(intent.getAction())) {
264 final int newUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
265 if (newUserId == -1) {
268 registerUserCallbacks(UserHandle.of(newUserId));
271 }, userAddedIntentFilter, null, getHandler());
273 registerTimeChangeReceiver();
277 private void registerUserCallbacks(UserHandle user) {
278 Context context = getContextAsUser(user);
279 if (context == null) {
280 Slog.e(TAG, "Unable to register user callbacks for user " + user);
284 context.getPackageManager().getPackageInstaller()
285 .registerSessionCallback(new SessionCallback(), getHandler());
287 IntentFilter filter = new IntentFilter();
288 filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
289 filter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED);
290 filter.addDataScheme("package");
291 context.registerReceiver(new BroadcastReceiver() {
293 public void onReceive(Context context, Intent intent) {
294 String action = intent.getAction();
295 if (Intent.ACTION_PACKAGE_REPLACED.equals(action)) {
296 String packageName = intent.getData().getSchemeSpecificPart();
298 Slog.v(TAG, "broadcast=ACTION_PACKAGE_REPLACED" + " pkg=" + packageName);
300 onPackageReplaced(packageName);
302 if (Intent.ACTION_PACKAGE_FULLY_REMOVED.equals(action)) {
303 String packageName = intent.getData().getSchemeSpecificPart();
304 Slog.i(TAG, "broadcast=ACTION_PACKAGE_FULLY_REMOVED pkg=" + packageName);
305 onPackageFullyRemoved(packageName);
308 }, filter, null, getHandler());
312 public ParceledListSlice getAvailableRollbacks() {
313 enforceManageRollbacks("getAvailableRollbacks");
314 synchronized (mLock) {
315 List<RollbackInfo> rollbacks = new ArrayList<>();
316 for (int i = 0; i < mRollbacks.size(); ++i) {
317 Rollback rollback = mRollbacks.get(i);
318 if (rollback.isAvailable()) {
319 rollbacks.add(rollback.info);
322 return new ParceledListSlice<>(rollbacks);
327 public ParceledListSlice<RollbackInfo> getRecentlyCommittedRollbacks() {
328 enforceManageRollbacks("getRecentlyCommittedRollbacks");
330 synchronized (mLock) {
331 List<RollbackInfo> rollbacks = new ArrayList<>();
332 for (int i = 0; i < mRollbacks.size(); ++i) {
333 Rollback rollback = mRollbacks.get(i);
334 if (rollback.isCommitted()) {
335 rollbacks.add(rollback.info);
338 return new ParceledListSlice<>(rollbacks);
343 public void commitRollback(int rollbackId, ParceledListSlice causePackages,
344 String callerPackageName, IntentSender statusReceiver) {
345 enforceManageRollbacks("commitRollback");
347 final int callingUid = Binder.getCallingUid();
348 AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class);
349 appOps.checkPackage(callingUid, callerPackageName);
351 getHandler().post(() ->
352 commitRollbackInternal(rollbackId, causePackages.getList(),
353 callerPackageName, statusReceiver));
357 private void registerTimeChangeReceiver() {
358 final BroadcastReceiver timeChangeIntentReceiver = new BroadcastReceiver() {
360 public void onReceive(Context context, Intent intent) {
361 final long oldRelativeBootTime = mRelativeBootTime;
362 mRelativeBootTime = calculateRelativeBootTime();
363 final long timeDifference = mRelativeBootTime - oldRelativeBootTime;
365 synchronized (mLock) {
366 Iterator<Rollback> iter = mRollbacks.iterator();
367 while (iter.hasNext()) {
368 Rollback rollback = iter.next();
369 rollback.setTimestamp(
370 rollback.getTimestamp().plusMillis(timeDifference));
375 final IntentFilter filter = new IntentFilter();
376 filter.addAction(Intent.ACTION_TIME_CHANGED);
377 mContext.registerReceiver(timeChangeIntentReceiver, filter,
378 null /* broadcastPermission */, getHandler());
382 private static long calculateRelativeBootTime() {
383 return System.currentTimeMillis() - SystemClock.elapsedRealtime();
387 * Performs the actual work to commit a rollback.
388 * The work is done on the current thread. This may be a long running
392 private void commitRollbackInternal(int rollbackId, List<VersionedPackage> causePackages,
393 String callerPackageName, IntentSender statusReceiver) {
394 Slog.i(TAG, "commitRollback id=" + rollbackId + " caller=" + callerPackageName);
396 Rollback rollback = getRollbackForId(rollbackId);
397 if (rollback == null) {
399 mContext, statusReceiver, RollbackManager.STATUS_FAILURE_ROLLBACK_UNAVAILABLE,
400 "Rollback unavailable");
403 rollback.commit(mContext, causePackages, callerPackageName, statusReceiver);
407 public void reloadPersistedData() {
408 mContext.enforceCallingOrSelfPermission(
409 Manifest.permission.TEST_MANAGE_ROLLBACKS,
410 "reloadPersistedData");
412 CountDownLatch latch = new CountDownLatch(1);
413 getHandler().post(() -> {
414 synchronized (mLock) {
416 mRollbacks.addAll(mRollbackStore.loadRollbacks());
423 } catch (InterruptedException ie) {
424 throw new IllegalStateException("RollbackManagerHandlerThread interrupted");
429 public void expireRollbackForPackage(String packageName) {
430 mContext.enforceCallingOrSelfPermission(
431 Manifest.permission.TEST_MANAGE_ROLLBACKS,
432 "expireRollbackForPackage");
433 synchronized (mLock) {
434 Iterator<Rollback> iter = mRollbacks.iterator();
435 while (iter.hasNext()) {
436 Rollback rollback = iter.next();
437 if (rollback.includesPackage(packageName)) {
439 rollback.delete(mAppDataRollbackHelper);
446 public void blockRollbackManager(long millis) {
447 mContext.enforceCallingOrSelfPermission(
448 Manifest.permission.TEST_MANAGE_ROLLBACKS,
449 "blockRollbackManager");
450 getHandler().post(() -> {
451 mSleepDuration.addLast(millis);
456 private void queueSleepIfNeeded() {
457 if (mSleepDuration.size() == 0) {
460 long millis = mSleepDuration.removeFirst();
464 getHandler().post(() -> {
466 Thread.sleep(millis);
467 } catch (InterruptedException e) {
468 throw new IllegalStateException("RollbackManagerHandlerThread interrupted");
473 void onUnlockUser(int userId) {
475 Slog.v(TAG, "onUnlockUser id=" + userId);
477 // In order to ensure that no package begins running while a backup or restore is taking
478 // place, onUnlockUser must remain blocked until all pending backups and restores have
480 CountDownLatch latch = new CountDownLatch(1);
481 getHandler().post(() -> {
482 final List<Rollback> rollbacks;
483 synchronized (mLock) {
484 rollbacks = new ArrayList<>(mRollbacks);
487 for (int i = 0; i < rollbacks.size(); i++) {
488 Rollback rollback = rollbacks.get(i);
489 rollback.commitPendingBackupAndRestoreForUser(userId, mAppDataRollbackHelper);
494 destroyCeSnapshotsForExpiredRollbacks(userId);
499 } catch (InterruptedException ie) {
500 throw new IllegalStateException("RollbackManagerHandlerThread interrupted");
505 private void destroyCeSnapshotsForExpiredRollbacks(int userId) {
506 int[] rollbackIds = new int[mRollbacks.size()];
507 for (int i = 0; i < rollbackIds.length; i++) {
508 rollbackIds[i] = mRollbacks.get(i).info.getRollbackId();
510 ApexManager.getInstance().destroyCeSnapshotsNotSpecified(userId, rollbackIds);
512 mInstaller.destroyCeSnapshotsNotSpecified(userId, rollbackIds);
513 } catch (Installer.InstallerException ie) {
514 Slog.e(TAG, "Failed to delete snapshots for user: " + userId, ie);
519 private void updateRollbackLifetimeDurationInMillis() {
520 mRollbackLifetimeDurationInMillis = DeviceConfig.getLong(
521 DeviceConfig.NAMESPACE_ROLLBACK_BOOT,
522 RollbackManager.PROPERTY_ROLLBACK_LIFETIME_MILLIS,
523 DEFAULT_ROLLBACK_LIFETIME_DURATION_MILLIS);
524 if (mRollbackLifetimeDurationInMillis < 0) {
525 mRollbackLifetimeDurationInMillis = DEFAULT_ROLLBACK_LIFETIME_DURATION_MILLIS;
527 Slog.d(TAG, "mRollbackLifetimeDurationInMillis=" + mRollbackLifetimeDurationInMillis);
532 void onBootCompleted() {
533 DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_ROLLBACK_BOOT,
534 mExecutor, properties -> updateRollbackLifetimeDurationInMillis());
536 getHandler().post(() -> {
537 updateRollbackLifetimeDurationInMillis();
540 // Check to see if any rollback-enabled staged sessions or staged
541 // rollback sessions been applied.
542 List<Rollback> enabling = new ArrayList<>();
543 List<Rollback> restoreInProgress = new ArrayList<>();
544 Set<String> apexPackageNames = new HashSet<>();
545 synchronized (mLock) {
546 Iterator<Rollback> iter = mRollbacks.iterator();
547 while (iter.hasNext()) {
548 Rollback rollback = iter.next();
549 if (!rollback.isStaged()) {
550 // We only care about staged rollbacks here
554 PackageInstaller.SessionInfo session = mContext.getPackageManager()
555 .getPackageInstaller().getSessionInfo(rollback.getStagedSessionId());
556 if (session == null || session.isStagedSessionFailed()) {
558 rollback.delete(mAppDataRollbackHelper);
562 if (session.isStagedSessionApplied()) {
563 if (rollback.isEnabling()) {
564 enabling.add(rollback);
565 } else if (rollback.isRestoreUserDataInProgress()) {
566 restoreInProgress.add(rollback);
569 apexPackageNames.addAll(rollback.getApexPackageNames());
573 for (Rollback rollback : enabling) {
574 makeRollbackAvailable(rollback);
577 for (Rollback rollback : restoreInProgress) {
578 rollback.setRestoreUserDataInProgress(false);
581 for (String apexPackageName : apexPackageNames) {
582 // We will not recieve notifications when an apex is updated,
583 // so check now in case any rollbacks ought to be expired. The
584 // onPackagedReplace function is safe to call if the package
585 // hasn't actually been updated.
586 onPackageReplaced(apexPackageName);
589 synchronized (mLock) {
590 mOrphanedApkSessionIds.clear();
593 mPackageHealthObserver.onBootCompletedAsync();
598 * Called when a package has been replaced with a different version.
599 * Removes all backups for the package not matching the currently
600 * installed package version.
603 private void onPackageReplaced(String packageName) {
604 // TODO: Could this end up incorrectly deleting a rollback for a
605 // package that is about to be installed?
606 long installedVersion = getInstalledPackageVersion(packageName);
608 synchronized (mLock) {
609 Iterator<Rollback> iter = mRollbacks.iterator();
610 while (iter.hasNext()) {
611 Rollback rollback = iter.next();
612 // TODO: Should we remove rollbacks in the ENABLING state here?
613 if ((rollback.isEnabling() || rollback.isAvailable())
614 && rollback.includesPackageWithDifferentVersion(packageName,
617 rollback.delete(mAppDataRollbackHelper);
624 * Called when a package has been completely removed from the device.
625 * Removes all backups and rollback history for the given package.
628 private void onPackageFullyRemoved(String packageName) {
629 expireRollbackForPackage(packageName);
633 * Notifies an IntentSender of failure.
635 * @param statusReceiver where to send the failure
636 * @param status the RollbackManager.STATUS_* code with the failure.
637 * @param message the failure message.
640 static void sendFailure(Context context, IntentSender statusReceiver,
641 @RollbackManager.Status int status, String message) {
642 Slog.e(TAG, message);
644 final Intent fillIn = new Intent();
645 fillIn.putExtra(RollbackManager.EXTRA_STATUS, status);
646 fillIn.putExtra(RollbackManager.EXTRA_STATUS_MESSAGE, message);
647 statusReceiver.sendIntent(context, 0, fillIn, null, null);
648 } catch (IntentSender.SendIntentException e) {
649 // Nowhere to send the result back to, so don't bother.
653 // Check to see if anything needs expiration, and if so, expire it.
654 // Schedules future expiration as appropriate.
656 private void runExpiration() {
657 getHandler().removeCallbacks(mRunExpiration);
659 Instant now = Instant.now();
660 Instant oldest = null;
661 synchronized (mLock) {
662 Iterator<Rollback> iter = mRollbacks.iterator();
663 while (iter.hasNext()) {
664 Rollback rollback = iter.next();
665 if (!rollback.isAvailable()) {
668 Instant rollbackTimestamp = rollback.getTimestamp();
671 .plusMillis(mRollbackLifetimeDurationInMillis))) {
672 Slog.i(TAG, "runExpiration id=" + rollback.info.getRollbackId());
674 rollback.delete(mAppDataRollbackHelper);
675 } else if (oldest == null || oldest.isAfter(rollbackTimestamp)) {
676 oldest = rollbackTimestamp;
681 if (oldest != null) {
682 long delay = now.until(
683 oldest.plusMillis(mRollbackLifetimeDurationInMillis), ChronoUnit.MILLIS);
684 getHandler().postDelayed(mRunExpiration, delay);
689 private Handler getHandler() {
690 return mHandlerThread.getThreadHandler();
694 private Context getContextAsUser(UserHandle user) {
696 return mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user);
697 } catch (PackageManager.NameNotFoundException e) {
703 * Called via broadcast by the package manager when a package is being
704 * staged for install with rollback enabled. Called before the package has
707 * @param sessionId the id of the install session
708 * @return true if enabling the rollback succeeds, false otherwise.
711 private boolean enableRollback(int sessionId) {
713 Slog.v(TAG, "enableRollback sessionId=" + sessionId);
716 PackageInstaller installer = mContext.getPackageManager().getPackageInstaller();
717 PackageInstaller.SessionInfo packageSession = installer.getSessionInfo(sessionId);
718 if (packageSession == null) {
719 Slog.e(TAG, "Unable to find session for enabled rollback.");
723 PackageInstaller.SessionInfo parentSession = packageSession.hasParentSessionId()
724 ? installer.getSessionInfo(packageSession.getParentSessionId()) : packageSession;
725 if (parentSession == null) {
726 Slog.e(TAG, "Unable to find parent session for enabled rollback.");
730 // Check to see if this is the apk session for a staged session with
732 synchronized (mLock) {
733 for (int i = 0; i < mRollbacks.size(); ++i) {
734 Rollback rollback = mRollbacks.get(i);
735 if (rollback.getApkSessionId() == parentSession.getSessionId()) {
736 // This is the apk session for a staged session with rollback enabled. We do
737 // not need to create a new rollback for this session.
743 // Check to see if this is the apk session for a staged session for which rollback was
745 synchronized (mLock) {
746 if (mOrphanedApkSessionIds.indexOf(parentSession.getSessionId()) != -1) {
747 Slog.w(TAG, "Not enabling rollback for apk as no matching staged session "
748 + "rollback exists");
753 Rollback newRollback;
754 synchronized (mLock) {
755 // See if we already have a Rollback that contains this package
756 // session. If not, create a new Rollback for the parent session
757 // that we will use for all the packages in the session.
758 newRollback = getRollbackForSessionLocked(packageSession.getSessionId());
759 if (newRollback == null) {
760 newRollback = createNewRollbackLocked(parentSession);
764 return enableRollbackForPackageSession(newRollback, packageSession);
768 * Do code and userdata backups to enable rollback of the given session.
769 * In case of multiPackage sessions, <code>session</code> should be one of
770 * the child sessions, not the parent session.
772 * @return true on success, false on failure.
775 private boolean enableRollbackForPackageSession(Rollback rollback,
776 PackageInstaller.SessionInfo session) {
777 // TODO: Don't attempt to enable rollback for split installs.
778 final int installFlags = session.installFlags;
779 if ((installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) == 0) {
780 Slog.e(TAG, "Rollback is not enabled.");
783 if ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) {
784 Slog.e(TAG, "Rollbacks not supported for instant app install");
788 if (session.resolvedBaseCodePath == null) {
789 Slog.e(TAG, "Session code path has not been resolved.");
793 // Get information about the package to be installed.
794 ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
795 ParseResult<PackageParser.ApkLite> parseResult = ApkLiteParseUtils.parseApkLite(
796 input.reset(), new File(session.resolvedBaseCodePath), 0);
797 if (parseResult.isError()) {
798 Slog.e(TAG, "Unable to parse new package: " + parseResult.getErrorMessage(),
799 parseResult.getException());
802 PackageParser.ApkLite newPackage = parseResult.getResult();
804 String packageName = newPackage.packageName;
805 Slog.i(TAG, "Enabling rollback for install of " + packageName
806 + ", session:" + session.sessionId);
808 String installerPackageName = session.getInstallerPackageName();
809 if (!enableRollbackAllowed(installerPackageName, packageName)) {
810 Slog.e(TAG, "Installer " + installerPackageName
811 + " is not allowed to enable rollback on " + packageName);
815 final boolean isApex = ((installFlags & PackageManager.INSTALL_APEX) != 0);
817 // Get information about the currently installed package.
818 final PackageInfo pkgInfo;
820 pkgInfo = getPackageInfo(packageName);
821 } catch (PackageManager.NameNotFoundException e) {
822 // TODO: Support rolling back fresh package installs rather than
823 // fail here. Test this case.
824 Slog.e(TAG, packageName + " is not installed");
829 // Check if this apex contains apks inside it. If true, then they should be added as
830 // a RollbackPackageInfo into this rollback
831 final PackageManagerInternal pmi = LocalServices.getService(
832 PackageManagerInternal.class);
833 List<String> apksInApex = pmi.getApksInApex(packageName);
834 for (String apkInApex : apksInApex) {
835 // Get information about the currently installed package.
836 final PackageInfo apkPkgInfo;
838 apkPkgInfo = getPackageInfo(apkInApex);
839 } catch (PackageManager.NameNotFoundException e) {
840 // TODO: Support rolling back fresh package installs rather than
841 // fail here. Test this case.
842 Slog.e(TAG, apkInApex + " is not installed");
845 if (!rollback.enableForPackageInApex(
846 apkInApex, apkPkgInfo.getLongVersionCode(), session.rollbackDataPolicy)) {
853 * The order is important here! Always enable the embedded apk-in-apex (if any) before
854 * enabling the embedding apex. Otherwise the rollback object might be in an inconsistent
855 * state where an embedding apex is successfully enabled while one of its embedded
856 * apk-in-apex failed. Note {@link Rollback#allPackagesEnabled()} won't behave correctly if
857 * a rollback object is inconsistent because it doesn't count apk-in-apex.
859 ApplicationInfo appInfo = pkgInfo.applicationInfo;
860 return rollback.enableForPackage(packageName, newPackage.versionCode,
861 pkgInfo.getLongVersionCode(), isApex, appInfo.sourceDir,
862 appInfo.splitSourceDirs, session.rollbackDataPolicy);
866 public void snapshotAndRestoreUserData(String packageName, int[] userIds, int appId,
867 long ceDataInode, String seInfo, int token) {
868 if (Binder.getCallingUid() != Process.SYSTEM_UID) {
869 throw new SecurityException(
870 "snapshotAndRestoreUserData may only be called by the system.");
873 getHandler().post(() -> {
874 snapshotUserDataInternal(packageName, userIds);
875 restoreUserDataInternal(packageName, userIds, appId, seInfo);
876 // When this method is called as part of the install flow, a positive token number is
877 // passed to it. Need to notify the PackageManager when we are done.
879 final PackageManagerInternal pmi = LocalServices.getService(
880 PackageManagerInternal.class);
881 pmi.finishPackageInstall(token, false);
887 private void snapshotUserDataInternal(String packageName, int[] userIds) {
889 Slog.v(TAG, "snapshotUserData pkg=" + packageName
890 + " users=" + Arrays.toString(userIds));
892 synchronized (mLock) {
893 for (int i = 0; i < mRollbacks.size(); i++) {
894 Rollback rollback = mRollbacks.get(i);
895 rollback.snapshotUserData(packageName, userIds, mAppDataRollbackHelper);
901 private void restoreUserDataInternal(
902 String packageName, int[] userIds, int appId, String seInfo) {
904 Slog.v(TAG, "restoreUserData pkg=" + packageName
905 + " users=" + Arrays.toString(userIds));
907 synchronized (mLock) {
908 for (int i = 0; i < mRollbacks.size(); ++i) {
909 Rollback rollback = mRollbacks.get(i);
910 if (rollback.restoreUserDataForPackageIfInProgress(
911 packageName, userIds, appId, seInfo, mAppDataRollbackHelper)) {
919 public int notifyStagedSession(int sessionId) {
920 if (Binder.getCallingUid() != Process.SYSTEM_UID) {
921 throw new SecurityException("notifyStagedSession may only be called by the system.");
923 final LinkedBlockingQueue<Integer> result = new LinkedBlockingQueue<>();
925 // NOTE: We post this runnable on the RollbackManager's binder thread because we'd prefer
926 // to preserve the invariant that all operations that modify state happen there.
927 getHandler().post(() -> {
928 PackageInstaller installer = mContext.getPackageManager().getPackageInstaller();
930 final PackageInstaller.SessionInfo session = installer.getSessionInfo(sessionId);
931 if (session == null) {
932 Slog.e(TAG, "No matching install session for: " + sessionId);
937 Rollback newRollback;
938 synchronized (mLock) {
939 newRollback = createNewRollbackLocked(session);
942 if (!session.isMultiPackage()) {
943 if (!enableRollbackForPackageSession(newRollback, session)) {
944 Slog.e(TAG, "Unable to enable rollback for session: " + sessionId);
947 for (int childSessionId : session.getChildSessionIds()) {
948 final PackageInstaller.SessionInfo childSession =
949 installer.getSessionInfo(childSessionId);
950 if (childSession == null) {
951 Slog.e(TAG, "No matching child install session for: " + childSessionId);
954 if (!enableRollbackForPackageSession(newRollback, childSession)) {
955 Slog.e(TAG, "Unable to enable rollback for session: " + sessionId);
961 if (!completeEnableRollback(newRollback)) {
964 result.offer(newRollback.info.getRollbackId());
969 return result.take();
970 } catch (InterruptedException ie) {
971 Slog.e(TAG, "Interrupted while waiting for notifyStagedSession response");
977 public void notifyStagedApkSession(int originalSessionId, int apkSessionId) {
978 if (Binder.getCallingUid() != Process.SYSTEM_UID) {
979 throw new SecurityException("notifyStagedApkSession may only be called by the system.");
981 getHandler().post(() -> {
982 Rollback rollback = null;
983 synchronized (mLock) {
984 for (int i = 0; i < mRollbacks.size(); ++i) {
985 Rollback candidate = mRollbacks.get(i);
986 if (candidate.getStagedSessionId() == originalSessionId) {
987 rollback = candidate;
991 if (rollback == null) {
992 // Did not find rollback matching originalSessionId.
993 Slog.e(TAG, "notifyStagedApkSession did not find rollback for session "
995 + ". Adding orphaned apk session " + apkSessionId);
996 mOrphanedApkSessionIds.add(apkSessionId);
1000 if (rollback != null) {
1001 rollback.setApkSessionId(apkSessionId);
1007 * Returns true if the installer is allowed to enable rollback for the
1008 * given named package, false otherwise.
1011 private boolean enableRollbackAllowed(String installerPackageName, String packageName) {
1012 if (installerPackageName == null) {
1016 PackageManager pm = mContext.getPackageManager();
1017 boolean manageRollbacksGranted = pm.checkPermission(
1018 Manifest.permission.MANAGE_ROLLBACKS,
1019 installerPackageName) == PackageManager.PERMISSION_GRANTED;
1021 boolean testManageRollbacksGranted = pm.checkPermission(
1022 Manifest.permission.TEST_MANAGE_ROLLBACKS,
1023 installerPackageName) == PackageManager.PERMISSION_GRANTED;
1025 // For now only allow rollbacks for modules or for testing.
1026 return (isRollbackWhitelisted(packageName) && manageRollbacksGranted)
1027 || testManageRollbacksGranted;
1031 * Returns true is this package is eligible for enabling rollback.
1034 private boolean isRollbackWhitelisted(String packageName) {
1035 // TODO: Remove #isModule when the white list is ready.
1036 return SystemConfig.getInstance().getRollbackWhitelistedPackages().contains(packageName)
1037 || isModule(packageName);
1040 * Returns true if the package name is the name of a module.
1043 private boolean isModule(String packageName) {
1044 PackageManager pm = mContext.getPackageManager();
1045 final ModuleInfo moduleInfo;
1047 moduleInfo = pm.getModuleInfo(packageName, 0);
1048 } catch (PackageManager.NameNotFoundException e) {
1052 return moduleInfo != null;
1056 * Gets the version of the package currently installed.
1057 * Returns -1 if the package is not currently installed.
1060 private long getInstalledPackageVersion(String packageName) {
1061 PackageInfo pkgInfo;
1063 pkgInfo = getPackageInfo(packageName);
1064 } catch (PackageManager.NameNotFoundException e) {
1068 return pkgInfo.getLongVersionCode();
1072 * Gets PackageInfo for the given package. Matches any user and apex.
1074 * @throws PackageManager.NameNotFoundException if no such package is installed.
1077 private PackageInfo getPackageInfo(String packageName)
1078 throws PackageManager.NameNotFoundException {
1079 PackageManager pm = mContext.getPackageManager();
1081 // The MATCH_ANY_USER flag doesn't mix well with the MATCH_APEX
1082 // flag, so make two separate attempts to get the package info.
1083 // We don't need both flags at the same time because we assume
1084 // apex files are always installed for all users.
1085 return pm.getPackageInfo(packageName, PackageManager.MATCH_ANY_USER);
1086 } catch (PackageManager.NameNotFoundException e) {
1087 return pm.getPackageInfo(packageName, PackageManager.MATCH_APEX);
1092 private class SessionCallback extends PackageInstaller.SessionCallback {
1095 public void onCreated(int sessionId) { }
1098 public void onBadgingChanged(int sessionId) { }
1101 public void onActiveChanged(int sessionId, boolean active) { }
1104 public void onProgressChanged(int sessionId, float progress) { }
1107 public void onFinished(int sessionId, boolean success) {
1109 Slog.v(TAG, "SessionCallback.onFinished id=" + sessionId + " success=" + success);
1114 synchronized (mLock) {
1115 rollback = getRollbackForSessionLocked(sessionId);
1117 if (rollback != null && !rollback.isStaged() && rollback.isEnabling()
1118 && rollback.notifySessionWithSuccess()
1119 && completeEnableRollback(rollback)) {
1120 makeRollbackAvailable(rollback);
1123 synchronized (mLock) {
1124 Rollback rollback = getRollbackForSessionLocked(sessionId);
1125 if (rollback != null && rollback.isEnabling()) {
1126 Slog.w(TAG, "Delete rollback id=" + rollback.info.getRollbackId()
1127 + " for failed session id=" + sessionId);
1128 mRollbacks.remove(rollback);
1129 rollback.delete(mAppDataRollbackHelper);
1137 * Persist a rollback as enable-completed. It does not make the rollback available yet.
1138 * This rollback will be deleted and removed from {@link #mRollbacks} should any error happens.
1140 * @return {code true} if {code rollback} is successfully enable-completed,
1141 * or {code false} otherwise.
1144 private boolean completeEnableRollback(Rollback rollback) {
1146 Slog.v(TAG, "completeEnableRollback id=" + rollback.info.getRollbackId());
1149 // We are checking if number of packages (excluding apk-in-apex) we enabled for rollback is
1150 // equal to the number of sessions we are installing, to ensure we didn't skip enabling
1151 // of any sessions. If we successfully enable an apex, then we can assume we enabled
1152 // rollback for the embedded apk-in-apex, if any.
1153 if (!rollback.allPackagesEnabled()) {
1154 Slog.e(TAG, "Failed to enable rollback for all packages in session.");
1155 mRollbacks.remove(rollback);
1156 rollback.delete(mAppDataRollbackHelper);
1160 // Note: There is a small window of time between when
1161 // the session has been committed by the package
1162 // manager and when we make the rollback available
1163 // here. Presumably the window is small enough that
1164 // nobody will want to roll back the newly installed
1165 // package before we make the rollback available.
1166 // TODO: We'll lose the rollback if the
1167 // device reboots between when the session is
1168 // committed and this point. Revisit this after
1169 // adding support for rollback of staged installs.
1170 rollback.saveRollback();
1176 @GuardedBy("rollback.getLock")
1177 private void makeRollbackAvailable(Rollback rollback) {
1178 Slog.i(TAG, "makeRollbackAvailable id=" + rollback.info.getRollbackId());
1179 rollback.makeAvailable();
1181 // TODO(zezeozue): Provide API to explicitly start observing instead
1182 // of doing this for all rollbacks. If we do this for all rollbacks,
1183 // should document in PackageInstaller.SessionParams#setEnableRollback
1184 // After enabling and committing any rollback, observe packages and
1185 // prepare to rollback if packages crashes too frequently.
1186 mPackageHealthObserver.startObservingHealth(rollback.getPackageNames(),
1187 mRollbackLifetimeDurationInMillis);
1192 * Returns the rollback with the given rollbackId, if any.
1195 private Rollback getRollbackForId(int rollbackId) {
1196 synchronized (mLock) {
1197 for (int i = 0; i < mRollbacks.size(); ++i) {
1198 Rollback rollback = mRollbacks.get(i);
1199 if (rollback.info.getRollbackId() == rollbackId) {
1210 private int allocateRollbackIdLocked() {
1214 rollbackId = mRandom.nextInt(Integer.MAX_VALUE - 1) + 1;
1215 if (!mAllocatedRollbackIds.get(rollbackId, false)) {
1216 mAllocatedRollbackIds.put(rollbackId, true);
1221 throw new IllegalStateException("Failed to allocate rollback ID");
1225 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1226 if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
1228 IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
1229 synchronized (mLock) {
1230 for (Rollback rollback : mRollbacks) {
1234 PackageWatchdog.getInstance(mContext).dump(ipw);
1239 private void enforceManageRollbacks(@NonNull String message) {
1240 if ((PackageManager.PERMISSION_GRANTED != mContext.checkCallingOrSelfPermission(
1241 Manifest.permission.MANAGE_ROLLBACKS))
1242 && (PackageManager.PERMISSION_GRANTED != mContext.checkCallingOrSelfPermission(
1243 Manifest.permission.TEST_MANAGE_ROLLBACKS))) {
1244 throw new SecurityException(message + " requires "
1245 + Manifest.permission.MANAGE_ROLLBACKS + " or "
1246 + Manifest.permission.TEST_MANAGE_ROLLBACKS);
1251 * Creates and returns a Rollback according to the given SessionInfo
1252 * and adds it to {@link #mRollbacks}.
1256 private Rollback createNewRollbackLocked(PackageInstaller.SessionInfo parentSession) {
1257 int rollbackId = allocateRollbackIdLocked();
1259 if (parentSession.getUser() == UserHandle.ALL) {
1260 userId = UserHandle.USER_SYSTEM;
1262 userId = parentSession.getUser().getIdentifier();
1264 String installerPackageName = parentSession.getInstallerPackageName();
1265 final Rollback rollback;
1266 int parentSessionId = parentSession.getSessionId();
1269 Slog.v(TAG, "createNewRollback id=" + rollbackId
1270 + " user=" + userId + " installer=" + installerPackageName);
1273 int[] packageSessionIds;
1274 if (parentSession.isMultiPackage()) {
1275 packageSessionIds = parentSession.getChildSessionIds();
1277 packageSessionIds = new int[]{parentSessionId};
1280 if (parentSession.isStaged()) {
1281 rollback = mRollbackStore.createStagedRollback(rollbackId, parentSessionId, userId,
1282 installerPackageName, packageSessionIds, getExtensionVersions());
1284 rollback = mRollbackStore.createNonStagedRollback(rollbackId, userId,
1285 installerPackageName, packageSessionIds, getExtensionVersions());
1288 mRollbacks.add(rollback);
1292 private SparseIntArray getExtensionVersions() {
1293 // This list must be updated whenever the current API level is increased, or should be
1294 // replaced when we have another way of determining the relevant SDK versions.
1295 final int[] relevantSdkVersions = { Build.VERSION_CODES.R };
1297 SparseIntArray result = new SparseIntArray(relevantSdkVersions.length);
1298 for (int i = 0; i < relevantSdkVersions.length; i++) {
1299 result.put(relevantSdkVersions[i],
1300 SdkExtensions.getExtensionVersion(relevantSdkVersions[i]));
1306 * Returns the Rollback associated with the given session if parent or child session id matches.
1307 * Returns null if not found.
1312 private Rollback getRollbackForSessionLocked(int sessionId) {
1313 // We expect mRollbacks to be a very small list; linear search should be plenty fast.
1314 for (int i = 0; i < mRollbacks.size(); ++i) {
1315 Rollback rollback = mRollbacks.get(i);
1316 if (rollback.getStagedSessionId() == sessionId
1317 || rollback.containsSessionId(sessionId)) {