• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package com.android.server.rollback;
18 
19 import static android.crashrecovery.flags.Flags.deprecateFlagsAndSettingsResets;
20 
21 import static com.android.server.rollback.RollbackManagerServiceImpl.sendFailure;
22 
23 import android.Manifest;
24 import android.annotation.AnyThread;
25 import android.annotation.IntDef;
26 import android.annotation.NonNull;
27 import android.annotation.Nullable;
28 import android.annotation.WorkerThread;
29 import android.content.Context;
30 import android.content.Intent;
31 import android.content.IntentSender;
32 import android.content.pm.PackageInstaller;
33 import android.content.pm.PackageManager;
34 import android.content.pm.PackageManagerInternal;
35 import android.content.pm.VersionedPackage;
36 import android.content.rollback.PackageRollbackInfo;
37 import android.content.rollback.RollbackInfo;
38 import android.content.rollback.RollbackManager;
39 import android.os.Binder;
40 import android.os.Handler;
41 import android.os.Looper;
42 import android.os.ParcelFileDescriptor;
43 import android.os.UserHandle;
44 import android.os.UserManager;
45 import android.os.ext.SdkExtensions;
46 import android.text.TextUtils;
47 import android.util.ArraySet;
48 import android.util.Slog;
49 import android.util.SparseIntArray;
50 
51 import com.android.internal.annotations.VisibleForTesting;
52 import com.android.internal.util.ArrayUtils;
53 import com.android.internal.util.IndentingPrintWriter;
54 import com.android.internal.util.Preconditions;
55 import com.android.server.LocalServices;
56 import com.android.server.crashrecovery.CrashRecoveryAdaptor;
57 import com.android.server.pm.pkg.AndroidPackage;
58 
59 import java.io.File;
60 import java.io.IOException;
61 import java.lang.annotation.Retention;
62 import java.lang.annotation.RetentionPolicy;
63 import java.text.ParseException;
64 import java.time.Instant;
65 import java.util.ArrayList;
66 import java.util.List;
67 import java.util.Objects;
68 import java.util.Set;
69 import java.util.function.Consumer;
70 
71 /**
72  * Information about a rollback available for a set of atomically installed packages.
73  *
74  * Threading model:
75  *
76  * Each method falls into one of the 2 categories:
77  * - @AnyThread annotates thread-safe methods.
78  * - @WorkerThread annotates methods that should be called from the worker thread only.
79  *
80  * In production code, the constructor is called on the worker thread of
81  * {@link RollbackManagerServiceImpl}. All method invocations should happen on this thread.
82  * Violation of thread invariants will trigger exceptions. In the case of unit tests, it is up to
83  * the tests to serialize all method calls to avoid race condition. No thread invariants are
84  * enforced in this case.
85  */
86 class Rollback {
87 
88     private static final String TAG = "RollbackManager";
89 
90     @IntDef(prefix = { "ROLLBACK_STATE_" }, value = {
91             ROLLBACK_STATE_ENABLING,
92             ROLLBACK_STATE_AVAILABLE,
93             ROLLBACK_STATE_COMMITTED,
94             ROLLBACK_STATE_DELETED
95     })
96     @Retention(RetentionPolicy.SOURCE)
97     @interface RollbackState {}
98 
99     /**
100      * The rollback is in the process of being enabled. It is not yet
101      * available for use.
102      */
103     static final int ROLLBACK_STATE_ENABLING = 0;
104 
105     /**
106      * The rollback is currently available.
107      */
108     static final int ROLLBACK_STATE_AVAILABLE = 1;
109 
110     /**
111      * The rollback has been committed.
112      */
113     static final int ROLLBACK_STATE_COMMITTED = 3;
114 
115     /**
116      * The rollback has been deleted.
117      */
118     static final int ROLLBACK_STATE_DELETED = 4;
119 
120     /**
121      * The session ID associate with this rollback. This is the parent session ID in the case of
122      * a multi-package session.
123      */
124     private final int mOriginalSessionId;
125 
126     /**
127      * The rollback info for this rollback.
128      */
129     public final RollbackInfo info;
130 
131     /**
132      * The directory where the rollback data is stored.
133      */
134     private final File mBackupDir;
135 
136     /**
137      * The time when the upgrade occurred, for purposes of expiring
138      * rollback data.
139      *
140      * The timestamp is not applicable for all rollback states, but we make
141      * sure to keep it non-null to avoid potential errors there.
142      */
143     private @NonNull Instant mTimestamp;
144 
145     /**
146      * The current state of the rollback.
147      * ENABLING, AVAILABLE, DELETED, or COMMITTED.
148      */
149     private @RollbackState int mState;
150 
151     /**
152      * The detailed description of the current state. For a DELETED state, it describes
153      * the reason why the rollback is deleted.
154      */
155     private @NonNull String mStateDescription = "";
156 
157     /**
158      * True if we are expecting the package manager to call restoreUserData
159      * for this rollback because it has just been committed but the rollback
160      * has not yet been fully applied.
161      */
162     private boolean mRestoreUserDataInProgress = false;
163 
164     /**
165      * The user that performed the install with rollback enabled.
166      */
167     private final int mUserId;
168 
169     /**
170      * The installer package name from the install session that enabled the rollback. May be null if
171      * that session did not set this value.
172      *
173      * If this is an empty string then the installer package name will be resolved by
174      * PackageManager.
175      */
176     @Nullable private final String mInstallerPackageName;
177 
178     /**
179      * Time after which rollback expires.
180      */
181     private long mRollbackLifetimeMillis = 0;
182 
183     /**
184      * Session ids for all packages in the install. For multi-package sessions, this is the list
185      * of child session ids. For normal sessions, this list is a single element with the normal
186      * session id.
187      */
188     private final int[] mPackageSessionIds;
189 
190     /**
191      * The extension versions supported at the time of rollback creation.
192      */
193     @NonNull private final SparseIntArray mExtensionVersions;
194 
195     /**
196      * The worker thread on which all method invocations should happen. It might be null in the
197      * case of unit tests where no thread invariants are enforced.
198      */
199     @Nullable private final Handler mHandler;
200 
201     /**
202      * Constructs a new, empty Rollback instance.
203      *
204      * @param rollbackId the id of the rollback.
205      * @param backupDir the directory where the rollback data is stored.
206      * @param originalSessionId the session id associated with this rollback.
207      * @param isStaged true if this is a staged rollback.
208      * @param userId the user that performed the install with rollback enabled.
209      * @param installerPackageName the installer package name from the original install session.
210      * @param packageSessionIds the session ids for all packages in the install.
211      * @param extensionVersions the extension versions supported at the time of rollback creation
212      */
Rollback(int rollbackId, File backupDir, int originalSessionId, boolean isStaged, int userId, String installerPackageName, int[] packageSessionIds, SparseIntArray extensionVersions)213     Rollback(int rollbackId, File backupDir, int originalSessionId, boolean isStaged, int userId,
214             String installerPackageName, int[] packageSessionIds,
215             SparseIntArray extensionVersions) {
216         this.info = new RollbackInfo(rollbackId,
217                 /* packages */ new ArrayList<>(),
218                 /* isStaged */ isStaged,
219                 /* causePackages */ new ArrayList<>(),
220                 /* committedSessionId */ -1,
221                 /* rollbackImpactLevel */ PackageManager.ROLLBACK_USER_IMPACT_LOW);
222         mUserId = userId;
223         mInstallerPackageName = installerPackageName;
224         mBackupDir = backupDir;
225         mOriginalSessionId = originalSessionId;
226         mState = ROLLBACK_STATE_ENABLING;
227         mTimestamp = Instant.now();
228         mPackageSessionIds = packageSessionIds != null ? packageSessionIds : new int[0];
229         mExtensionVersions = Objects.requireNonNull(extensionVersions);
230         mHandler = Looper.myLooper() != null ? new Handler(Looper.myLooper()) : null;
231     }
232 
233     /**
234      * Constructs a pre-populated Rollback instance.
235      */
Rollback(RollbackInfo info, File backupDir, Instant timestamp, int originalSessionId, @RollbackState int state, String stateDescription, boolean restoreUserDataInProgress, int userId, String installerPackageName, SparseIntArray extensionVersions)236     Rollback(RollbackInfo info, File backupDir, Instant timestamp, int originalSessionId,
237             @RollbackState int state, String stateDescription, boolean restoreUserDataInProgress,
238             int userId, String installerPackageName, SparseIntArray extensionVersions) {
239         this.info = info;
240         mUserId = userId;
241         mInstallerPackageName = installerPackageName;
242         mBackupDir = backupDir;
243         mTimestamp = timestamp;
244         mOriginalSessionId = originalSessionId;
245         mState = state;
246         mStateDescription = stateDescription;
247         mRestoreUserDataInProgress = restoreUserDataInProgress;
248         mExtensionVersions = Objects.requireNonNull(extensionVersions);
249         // TODO(b/120200473): Include this field during persistence. This field will be used to
250         // decide which rollback to expire when ACTION_PACKAGE_REPLACED is received. Note persisting
251         // this field is not backward compatible. We won't fix b/120200473 until S to minimize the
252         // impact.
253         mPackageSessionIds = new int[0];
254         mHandler = Looper.myLooper() != null ? new Handler(Looper.myLooper()) : null;
255     }
256 
assertInWorkerThread()257     private void assertInWorkerThread() {
258         Preconditions.checkState(mHandler == null || mHandler.getLooper().isCurrentThread());
259     }
260 
261     /**
262      * Whether the rollback is for rollback of a staged install.
263      */
264     @AnyThread
isStaged()265     boolean isStaged() {
266         return info.isStaged();
267     }
268 
269     /**
270      * Returns the directory in which rollback data should be stored.
271      */
272     @AnyThread
getBackupDir()273     File getBackupDir() {
274         return mBackupDir;
275     }
276 
277     /**
278      * Returns the time when the upgrade occurred, for purposes of expiring rollback data.
279      */
280     @WorkerThread
getTimestamp()281     Instant getTimestamp() {
282         assertInWorkerThread();
283         return mTimestamp;
284     }
285 
286     /**
287      * Sets the time at which upgrade occurred.
288      */
289     @WorkerThread
setTimestamp(Instant timestamp)290     void setTimestamp(Instant timestamp) {
291         assertInWorkerThread();
292         mTimestamp = timestamp;
293         RollbackStore.saveRollback(this);
294     }
295 
296     /**
297      * Sets rollback lifetime in milliseconds, for purposes of expiring rollback data.
298      */
299     @WorkerThread
setRollbackLifetimeMillis(long lifetimeMillis)300     void setRollbackLifetimeMillis(long lifetimeMillis) {
301         assertInWorkerThread();
302         mRollbackLifetimeMillis = lifetimeMillis;
303     }
304 
305     /**
306      * Returns rollback lifetime in milliseconds, for purposes of expiring rollback data.
307      */
308     @WorkerThread
getRollbackLifetimeMillis()309     long getRollbackLifetimeMillis() {
310         assertInWorkerThread();
311         return mRollbackLifetimeMillis;
312     }
313 
314     /**
315      * Returns the session ID associated with this rollback, or {@code -1} if unknown.
316      */
317     @AnyThread
getOriginalSessionId()318     int getOriginalSessionId() {
319         return mOriginalSessionId;
320     }
321 
322     /**
323      * Returns the ID of the user that performed the install with rollback enabled.
324      */
325     @AnyThread
getUserId()326     int getUserId() {
327         return mUserId;
328     }
329 
330     /**
331      * Returns the installer package name from the install session that enabled the rollback. In the
332      * case that this is called on a rollback from an older version, returns the empty string.
333      */
334     @AnyThread
getInstallerPackageName()335     @Nullable String getInstallerPackageName() {
336         return mInstallerPackageName;
337     }
338 
339     /**
340      * Returns the extension versions that were supported at the time that the rollback was created,
341      * as a mapping from SdkVersion to ExtensionVersion.
342      */
343     @AnyThread
getExtensionVersions()344     SparseIntArray getExtensionVersions() {
345         return mExtensionVersions;
346     }
347 
348     /**
349      * Returns true if the rollback is in the ENABLING state.
350      */
351     @WorkerThread
isEnabling()352     boolean isEnabling() {
353         assertInWorkerThread();
354         return mState == ROLLBACK_STATE_ENABLING;
355     }
356 
357     /**
358      * Returns true if the rollback is in the AVAILABLE state.
359      */
360     @WorkerThread
isAvailable()361     boolean isAvailable() {
362         assertInWorkerThread();
363         return mState == ROLLBACK_STATE_AVAILABLE;
364     }
365 
366     /**
367      * Returns true if the rollback is in the COMMITTED state.
368      */
369     @WorkerThread
isCommitted()370     boolean isCommitted() {
371         assertInWorkerThread();
372         return mState == ROLLBACK_STATE_COMMITTED;
373     }
374 
375     /**
376      * Returns true if the rollback is in the DELETED state.
377      */
378     @WorkerThread
isDeleted()379     boolean isDeleted() {
380         assertInWorkerThread();
381         return mState == ROLLBACK_STATE_DELETED;
382     }
383 
384     /**
385      * Saves this rollback to persistent storage.
386      */
387     @WorkerThread
saveRollback()388     void saveRollback() {
389         assertInWorkerThread();
390         RollbackStore.saveRollback(this);
391     }
392 
393     /**
394      * Enables this rollback for the provided package.
395      *
396      * @return boolean True if the rollback was enabled successfully for the specified package.
397      */
398     @WorkerThread
enableForPackage(String packageName, long newVersion, long installedVersion, boolean isApex, String sourceDir, String[] splitSourceDirs, int rollbackDataPolicy, @PackageManager.RollbackImpactLevel int rollbackImpactLevel)399     boolean enableForPackage(String packageName, long newVersion, long installedVersion,
400             boolean isApex, String sourceDir, String[] splitSourceDirs, int rollbackDataPolicy,
401             @PackageManager.RollbackImpactLevel int rollbackImpactLevel) {
402         assertInWorkerThread();
403         try {
404             RollbackStore.backupPackageCodePath(this, packageName, sourceDir);
405             if (!ArrayUtils.isEmpty(splitSourceDirs)) {
406                 for (String dir : splitSourceDirs) {
407                     RollbackStore.backupPackageCodePath(this, packageName, dir);
408                 }
409             }
410         } catch (IOException e) {
411             Slog.e(TAG, "Unable to copy package for rollback for " + packageName, e);
412             return false;
413         }
414 
415         PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(
416                 new VersionedPackage(packageName, newVersion),
417                 new VersionedPackage(packageName, installedVersion),
418                 new ArrayList<>() /* pendingBackups */, new ArrayList<>() /* pendingRestores */,
419                 isApex, false /* isApkInApex */, new ArrayList<>(), rollbackDataPolicy);
420 
421         info.getPackages().add(packageRollbackInfo);
422 
423         if (info.getRollbackImpactLevel() < rollbackImpactLevel) {
424             info.setRollbackImpactLevel(rollbackImpactLevel);
425         }
426         return true;
427     }
428 
429     /**
430      * Enables this rollback for the provided apk-in-apex.
431      *
432      * @return boolean True if the rollback was enabled successfully for the specified package.
433      */
434     @WorkerThread
enableForPackageInApex(String packageName, long installedVersion, int rollbackDataPolicy)435     boolean enableForPackageInApex(String packageName, long installedVersion,
436             int rollbackDataPolicy) {
437         assertInWorkerThread();
438         // TODO(b/147666157): Extract the new version number of apk-in-apex
439         // The new version for the apk-in-apex is set to 0 for now. If the package is then further
440         // updated via non-staged install flow, then RollbackManagerServiceImpl#onPackageReplaced()
441         // will be called and this rollback will be deleted. Other ways of package update have not
442         // been handled yet.
443         PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(
444                 new VersionedPackage(packageName, 0 /* newVersion */),
445                 new VersionedPackage(packageName, installedVersion),
446                 new ArrayList<>() /* pendingBackups */, new ArrayList<>() /* pendingRestores */,
447                 false /* isApex */, true /* isApkInApex */, new ArrayList<>(), rollbackDataPolicy);
448         info.getPackages().add(packageRollbackInfo);
449         return true;
450     }
451 
addAll(List<Integer> list, int[] arr)452     private static void addAll(List<Integer> list, int[] arr) {
453         for (int i = 0; i < arr.length; ++i) {
454             list.add(arr[i]);
455         }
456     }
457 
458     /**
459      * Snapshots user data for the provided package and user ids. Does nothing if this rollback is
460      * not in the ENABLING state.
461      */
462     @WorkerThread
snapshotUserData(String packageName, int[] userIds, AppDataRollbackHelper dataHelper)463     void snapshotUserData(String packageName, int[] userIds, AppDataRollbackHelper dataHelper) {
464         assertInWorkerThread();
465         if (!isEnabling()) {
466             return;
467         }
468 
469         for (PackageRollbackInfo pkgRollbackInfo : info.getPackages()) {
470             if (pkgRollbackInfo.getPackageName().equals(packageName)) {
471                 if (pkgRollbackInfo.getRollbackDataPolicy()
472                         == PackageManager.ROLLBACK_DATA_POLICY_RESTORE) {
473                     dataHelper.snapshotAppData(info.getRollbackId(), pkgRollbackInfo, userIds);
474                     addAll(pkgRollbackInfo.getSnapshottedUsers(), userIds);
475                     RollbackStore.saveRollback(this);
476                 }
477                 break;
478             }
479         }
480     }
481 
482     /**
483      * Commits the pending backups and restores for a given {@code userId}. If this rollback has a
484      * pending backup, it is updated with a mapping from {@code userId} to inode of the CE user data
485      * snapshot.
486      */
487     @WorkerThread
commitPendingBackupAndRestoreForUser(int userId, AppDataRollbackHelper dataHelper)488     void commitPendingBackupAndRestoreForUser(int userId, AppDataRollbackHelper dataHelper) {
489         assertInWorkerThread();
490         if (dataHelper.commitPendingBackupAndRestoreForUser(userId, this)) {
491             RollbackStore.saveRollback(this);
492         }
493     }
494 
495     /**
496      * Changes the state of the rollback to AVAILABLE. This also changes the timestamp to the
497      * current time and saves the rollback. Does nothing if this rollback is already in the
498      * DELETED state.
499      */
500     @WorkerThread
makeAvailable()501     void makeAvailable() {
502         assertInWorkerThread();
503         if (isDeleted()) {
504             Slog.w(TAG, "Cannot make deleted rollback available.");
505             return;
506         }
507         setState(ROLLBACK_STATE_AVAILABLE, "");
508         mTimestamp = Instant.now();
509         RollbackStore.saveRollback(this);
510     }
511 
512     /**
513      * Commits the rollback.
514      */
515     @WorkerThread
commit(final Context context, List<VersionedPackage> causePackages, String callerPackageName, IntentSender statusReceiver)516     void commit(final Context context, List<VersionedPackage> causePackages,
517             String callerPackageName, IntentSender statusReceiver) {
518         assertInWorkerThread();
519         if (!isAvailable()) {
520             sendFailure(context, statusReceiver,
521                     RollbackManager.STATUS_FAILURE_ROLLBACK_UNAVAILABLE,
522                     "Rollback unavailable");
523             return;
524         }
525 
526         if (containsApex() && wasCreatedAtLowerExtensionVersion()) {
527             PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
528             if (extensionVersionReductionWouldViolateConstraint(mExtensionVersions, pmi)) {
529                 sendFailure(context, statusReceiver, RollbackManager.STATUS_FAILURE,
530                         "Rollback may violate a minExtensionVersion constraint");
531                 return;
532             }
533         }
534 
535         // Get a context to use to install the downgraded version of the package.
536         Context pkgContext;
537         try {
538             pkgContext = context.createPackageContextAsUser(callerPackageName, 0,
539                     UserHandle.of(mUserId));
540         } catch (PackageManager.NameNotFoundException e) {
541             sendFailure(context, statusReceiver, RollbackManager.STATUS_FAILURE,
542                     "Invalid callerPackageName");
543             return;
544         }
545 
546         PackageManager pm = pkgContext.getPackageManager();
547         try {
548             PackageInstaller packageInstaller = pm.getPackageInstaller();
549             PackageInstaller.SessionParams parentParams = new PackageInstaller.SessionParams(
550                     PackageInstaller.SessionParams.MODE_FULL_INSTALL);
551             parentParams.setRequestDowngrade(true);
552             parentParams.setMultiPackage();
553             if (isStaged()) {
554                 parentParams.setStaged();
555             }
556             parentParams.setInstallReason(PackageManager.INSTALL_REASON_ROLLBACK);
557 
558             int parentSessionId = packageInstaller.createSession(parentParams);
559             PackageInstaller.Session parentSession = packageInstaller.openSession(
560                     parentSessionId);
561 
562             List<String> packageNames = new ArrayList<>(info.getPackages().size());
563             for (PackageRollbackInfo pkgRollbackInfo : info.getPackages()) {
564                 packageNames.add(pkgRollbackInfo.getPackageName());
565 
566                 if (pkgRollbackInfo.isApkInApex()) {
567                     // No need to issue a downgrade install request for apk-in-apex. It will
568                     // be rolled back when its parent apex is downgraded.
569                     continue;
570                 }
571                 PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
572                         PackageInstaller.SessionParams.MODE_FULL_INSTALL);
573                 String installerPackageName = mInstallerPackageName;
574                 if (TextUtils.isEmpty(mInstallerPackageName)) {
575                     installerPackageName = pm.getInstallerPackageName(
576                             pkgRollbackInfo.getPackageName());
577                 }
578                 if (installerPackageName != null) {
579                     params.setInstallerPackageName(installerPackageName);
580                 }
581                 params.setRequestDowngrade(true);
582                 params.setRequiredInstalledVersionCode(
583                         pkgRollbackInfo.getVersionRolledBackFrom().getLongVersionCode());
584                 params.setInstallReason(PackageManager.INSTALL_REASON_ROLLBACK);
585                 if (isStaged()) {
586                     params.setStaged();
587                 }
588                 if (pkgRollbackInfo.isApex()) {
589                     params.setInstallAsApex();
590                 }
591                 int sessionId = packageInstaller.createSession(params);
592                 PackageInstaller.Session session = packageInstaller.openSession(sessionId);
593                 File[] packageCodePaths = RollbackStore.getPackageCodePaths(
594                         this, pkgRollbackInfo.getPackageName());
595                 if (packageCodePaths == null) {
596                     sendFailure(context, statusReceiver, RollbackManager.STATUS_FAILURE,
597                             "Backup copy of package: "
598                                     + pkgRollbackInfo.getPackageName() + " is inaccessible");
599                     return;
600                 }
601 
602                 for (File packageCodePath : packageCodePaths) {
603                     try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(packageCodePath,
604                             ParcelFileDescriptor.MODE_READ_ONLY)) {
605                         final long token = Binder.clearCallingIdentity();
606                         try {
607                             boolean fallbackToCopy = false;
608                             try {
609                                 // Populate apk/apex files using hard links to avoid copy
610                                 session.stageViaHardLink(packageCodePath.getAbsolutePath());
611                             } catch (Exception ignore) {
612                                 fallbackToCopy = true;
613                             }
614                             if (fallbackToCopy) {
615                                 session.write(packageCodePath.getName(), 0,
616                                         packageCodePath.length(),
617                                         fd);
618                             }
619                         } finally {
620                             Binder.restoreCallingIdentity(token);
621                         }
622                     }
623                 }
624                 parentSession.addChildSessionId(sessionId);
625             }
626 
627             if (!deprecateFlagsAndSettingsResets()) {
628                 // Clear flags.
629                 CrashRecoveryAdaptor.rescuePartyResetDeviceConfigForPackages(packageNames);
630             }
631 
632             Consumer<Intent> onResult = result -> {
633                 mHandler.post(() -> {
634                     assertInWorkerThread();
635                     int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
636                             PackageInstaller.STATUS_FAILURE);
637                     if (status != PackageInstaller.STATUS_SUCCESS) {
638                         // Committing the rollback failed, but we still have all the info we
639                         // need to try rolling back again, so restore the rollback state to
640                         // how it was before we tried committing.
641                         // TODO: Should we just kill this rollback if commit failed?
642                         // Why would we expect commit not to fail again?
643                         // TODO: Could this cause a rollback to be resurrected
644                         // if it should otherwise have expired by now?
645                         setState(ROLLBACK_STATE_AVAILABLE, "Commit failed");
646                         mRestoreUserDataInProgress = false;
647                         info.setCommittedSessionId(-1);
648                         sendFailure(context, statusReceiver,
649                                 RollbackManager.STATUS_FAILURE_INSTALL,
650                                 "Rollback downgrade install failed: "
651                                         + result.getStringExtra(
652                                         PackageInstaller.EXTRA_STATUS_MESSAGE));
653                         return;
654                     }
655 
656                     if (!isStaged()) {
657                         // All calls to restoreUserData should have
658                         // completed by now for a non-staged install.
659                         mRestoreUserDataInProgress = false;
660                     }
661 
662                     info.getCausePackages().addAll(causePackages);
663                     RollbackStore.deletePackageCodePaths(this);
664                     RollbackStore.saveRollback(this);
665 
666                     // Send success.
667                     try {
668                         final Intent fillIn = new Intent();
669                         fillIn.putExtra(
670                                 RollbackManager.EXTRA_STATUS,
671                                 RollbackManager.STATUS_SUCCESS);
672                         statusReceiver.sendIntent(context, 0, fillIn, null, null);
673                     } catch (IntentSender.SendIntentException e) {
674                         // Nowhere to send the result back to, so don't bother.
675                     }
676 
677                     Intent broadcast = new Intent(Intent.ACTION_ROLLBACK_COMMITTED);
678 
679                     UserManager userManager = context.getSystemService(UserManager.class);
680                     for (UserHandle user : userManager.getUserHandles(true)) {
681                         context.sendBroadcastAsUser(broadcast,
682                                 user,
683                                 Manifest.permission.MANAGE_ROLLBACKS);
684                     }
685                 });
686             };
687 
688             final LocalIntentReceiver receiver = new LocalIntentReceiver(onResult);
689             setState(ROLLBACK_STATE_COMMITTED, "");
690             info.setCommittedSessionId(parentSessionId);
691             mRestoreUserDataInProgress = true;
692             parentSession.commit(receiver.getIntentSender());
693         } catch (IOException e) {
694             Slog.e(TAG, "Rollback failed", e);
695             sendFailure(context, statusReceiver, RollbackManager.STATUS_FAILURE,
696                     "IOException: " + e.toString());
697         }
698     }
699 
700     /**
701      * Restores user data for the specified package if this rollback is currently marked as
702      * having a restore in progress.
703      *
704      * @return boolean True if this rollback has a restore in progress and contains the specified
705      * package.
706      */
707     @WorkerThread
restoreUserDataForPackageIfInProgress(String packageName, int[] userIds, int appId, String seInfo, AppDataRollbackHelper dataHelper)708     boolean restoreUserDataForPackageIfInProgress(String packageName, int[] userIds, int appId,
709             String seInfo, AppDataRollbackHelper dataHelper) {
710         assertInWorkerThread();
711         if (!isRestoreUserDataInProgress()) {
712             return false;
713         }
714 
715         boolean foundPackage = false;
716         for (PackageRollbackInfo pkgRollbackInfo : info.getPackages()) {
717             if (pkgRollbackInfo.getPackageName().equals(packageName)) {
718                 foundPackage = true;
719                 boolean changedRollback = false;
720                 for (int userId : userIds) {
721                     changedRollback |= dataHelper.restoreAppData(
722                             info.getRollbackId(), pkgRollbackInfo, userId, appId, seInfo);
723                 }
724                 // We've updated metadata about this rollback, so save it to flash.
725                 if (changedRollback) {
726                     RollbackStore.saveRollback(this);
727                 }
728                 break;
729             }
730         }
731         return foundPackage;
732     }
733 
734     /**
735      * Deletes app data snapshots associated with this rollback, and moves to the DELETED state.
736      */
737     @WorkerThread
delete(AppDataRollbackHelper dataHelper, @NonNull String reason)738     void delete(AppDataRollbackHelper dataHelper, @NonNull String reason) {
739         assertInWorkerThread();
740         boolean containsApex = false;
741         Set<Integer> apexUsers = new ArraySet<>();
742         for (PackageRollbackInfo pkgInfo : info.getPackages()) {
743             List<Integer> snapshottedUsers = pkgInfo.getSnapshottedUsers();
744             if (pkgInfo.isApex()) {
745                 containsApex = true;
746                 apexUsers.addAll(snapshottedUsers);
747             } else {
748                 for (int i = 0; i < snapshottedUsers.size(); i++) {
749                     // Destroy app data snapshot.
750                     int userId = snapshottedUsers.get(i);
751 
752                     dataHelper.destroyAppDataSnapshot(info.getRollbackId(), pkgInfo, userId);
753                 }
754             }
755         }
756         if (containsApex) {
757             dataHelper.destroyApexDeSnapshots(info.getRollbackId());
758             for (int user : apexUsers) {
759                 dataHelper.destroyApexCeSnapshots(user, info.getRollbackId());
760             }
761         }
762 
763         RollbackStore.deleteRollback(this);
764         setState(ROLLBACK_STATE_DELETED, reason);
765     }
766 
767     /**
768      * Returns true if we are expecting the package manager to call restoreUserData for this
769      * rollback because it has just been committed but the rollback has not yet been fully applied.
770      */
771     @WorkerThread
isRestoreUserDataInProgress()772     boolean isRestoreUserDataInProgress() {
773         assertInWorkerThread();
774         return mRestoreUserDataInProgress;
775     }
776 
777     /**
778      * Sets whether we are expecting the package manager to call restoreUserData for this
779      * rollback because it has just been committed but the rollback has not yet been fully applied.
780      */
781     @WorkerThread
setRestoreUserDataInProgress(boolean restoreUserDataInProgress)782     void setRestoreUserDataInProgress(boolean restoreUserDataInProgress) {
783         assertInWorkerThread();
784         mRestoreUserDataInProgress = restoreUserDataInProgress;
785         RollbackStore.saveRollback(this);
786     }
787 
788     /**
789      * Returns true if this rollback includes the package with the provided {@code packageName}.
790      */
791     @WorkerThread
includesPackage(String packageName)792     boolean includesPackage(String packageName) {
793         assertInWorkerThread();
794         for (PackageRollbackInfo packageRollbackInfo : info.getPackages()) {
795             if (packageRollbackInfo.getPackageName().equals(packageName)) {
796                 return true;
797             }
798         }
799         return false;
800     }
801 
802     /**
803      * Returns true if this rollback includes the package with the provided {@code packageName}
804      * with a <i>version rolled back from</i> that is not {@code versionCode}.
805      */
806     @WorkerThread
includesPackageWithDifferentVersion(String packageName, long versionCode)807     boolean includesPackageWithDifferentVersion(String packageName, long versionCode) {
808         assertInWorkerThread();
809         for (PackageRollbackInfo pkgRollbackInfo : info.getPackages()) {
810             if (pkgRollbackInfo.getPackageName().equals(packageName)
811                     && pkgRollbackInfo.getVersionRolledBackFrom().getLongVersionCode()
812                     != versionCode) {
813                 return true;
814             }
815         }
816         return false;
817     }
818 
819     /**
820      * Returns a list containing the names of all the packages included in this rollback.
821      */
822     @WorkerThread
getPackageNames()823     List<String> getPackageNames() {
824         assertInWorkerThread();
825         List<String> result = new ArrayList<>();
826         for (PackageRollbackInfo pkgRollbackInfo : info.getPackages()) {
827             result.add(pkgRollbackInfo.getPackageName());
828         }
829         return result;
830     }
831 
832     /**
833      * Returns a list containing the names of all the apex packages included in this rollback.
834      */
835     @WorkerThread
getApexPackageNames()836     List<String> getApexPackageNames() {
837         assertInWorkerThread();
838         List<String> result = new ArrayList<>();
839         for (PackageRollbackInfo pkgRollbackInfo : info.getPackages()) {
840             if (pkgRollbackInfo.isApex()) {
841                 result.add(pkgRollbackInfo.getPackageName());
842             }
843         }
844         return result;
845     }
846 
847     /**
848      * Returns true if this rollback contains the provided {@code packageSessionId}.
849      */
850     @AnyThread
containsSessionId(int packageSessionId)851     boolean containsSessionId(int packageSessionId) {
852         for (int id : mPackageSessionIds) {
853             if (id == packageSessionId) {
854                 return true;
855             }
856         }
857         return false;
858     }
859 
860     /**
861      * Returns true if all packages in this rollback are enabled. We won't enable this rollback
862      * until all packages are enabled. Note we don't count apk-in-apex here since they are enabled
863      * automatically when the embedding apex is enabled.
864      */
865     @WorkerThread
allPackagesEnabled()866     boolean allPackagesEnabled() {
867         assertInWorkerThread();
868         int packagesWithoutApkInApex = 0;
869         for (PackageRollbackInfo rollbackInfo : info.getPackages()) {
870             if (!rollbackInfo.isApkInApex()) {
871                 packagesWithoutApkInApex++;
872             }
873         }
874         return packagesWithoutApkInApex == mPackageSessionIds.length;
875     }
876 
877     @AnyThread
rollbackStateToString(@ollbackState int state)878     static String rollbackStateToString(@RollbackState int state) {
879         switch (state) {
880             case Rollback.ROLLBACK_STATE_ENABLING: return "enabling";
881             case Rollback.ROLLBACK_STATE_AVAILABLE: return "available";
882             case Rollback.ROLLBACK_STATE_COMMITTED: return "committed";
883             case Rollback.ROLLBACK_STATE_DELETED: return "deleted";
884         }
885         throw new AssertionError("Invalid rollback state: " + state);
886     }
887 
888     @AnyThread
rollbackStateFromString(String state)889     static @RollbackState int rollbackStateFromString(String state)
890             throws ParseException {
891         switch (state) {
892             case "enabling": return Rollback.ROLLBACK_STATE_ENABLING;
893             case "available": return Rollback.ROLLBACK_STATE_AVAILABLE;
894             case "committed": return Rollback.ROLLBACK_STATE_COMMITTED;
895             case "deleted": return Rollback.ROLLBACK_STATE_DELETED;
896         }
897         throw new ParseException("Invalid rollback state: " + state, 0);
898     }
899 
900     @WorkerThread
getStateAsString()901     String getStateAsString() {
902         assertInWorkerThread();
903         return rollbackStateToString(mState);
904     }
905 
906     /**
907      * Returns true if there is an app installed that specifies a minExtensionVersion greater
908      * than what was present at the time this Rollback was created.
909      */
910     @VisibleForTesting
extensionVersionReductionWouldViolateConstraint( SparseIntArray rollbackExtVers, PackageManagerInternal pmi)911     static boolean extensionVersionReductionWouldViolateConstraint(
912             SparseIntArray rollbackExtVers, PackageManagerInternal pmi) {
913         if (rollbackExtVers.size() == 0) {
914             return false;
915         }
916         List<String> packages = pmi.getPackageList().getPackageNames();
917         for (int i = 0; i < packages.size(); i++) {
918             AndroidPackage pkg = pmi.getPackage(packages.get(i));
919             SparseIntArray minExtVers = pkg.getMinExtensionVersions();
920             if (minExtVers == null) {
921                 continue;
922             }
923             for (int j = 0; j < rollbackExtVers.size(); j++) {
924                 int minExt = minExtVers.get(rollbackExtVers.keyAt(j), -1);
925                 if (rollbackExtVers.valueAt(j) < minExt) {
926                     return true;
927                 }
928             }
929         }
930         return false;
931     }
932 
933     /**
934      * Returns true if for any SDK version, the extension version recorded at the time of rollback
935      * creation is lower than the current extension version.
936      */
937     @AnyThread
wasCreatedAtLowerExtensionVersion()938     private boolean wasCreatedAtLowerExtensionVersion() {
939         for (int i = 0; i < mExtensionVersions.size(); i++) {
940             if (SdkExtensions.getExtensionVersion(mExtensionVersions.keyAt(i))
941                     > mExtensionVersions.valueAt(i)) {
942                 return true;
943             }
944         }
945         return false;
946     }
947 
948     @AnyThread
containsApex()949     private boolean containsApex() {
950         for (PackageRollbackInfo pkgInfo : info.getPackages()) {
951             if (pkgInfo.isApex()) {
952                 return true;
953             }
954         }
955         return false;
956     }
957 
958     @WorkerThread
dump(IndentingPrintWriter ipw)959     void dump(IndentingPrintWriter ipw) {
960         assertInWorkerThread();
961         ipw.println(info.getRollbackId() + ":");
962         ipw.increaseIndent();
963         ipw.println("-state: " + getStateAsString());
964         ipw.println("-stateDescription: " + mStateDescription);
965         ipw.println("-timestamp: " + getTimestamp());
966         ipw.println("-rollbackLifetimeMillis: " + getRollbackLifetimeMillis());
967         ipw.println("-rollbackImpactLevel: " + info.getRollbackImpactLevel());
968         ipw.println("-isStaged: " + isStaged());
969         ipw.println("-originalSessionId: " + getOriginalSessionId());
970         ipw.println("-packages:");
971         ipw.increaseIndent();
972         for (PackageRollbackInfo pkg : info.getPackages()) {
973             ipw.println(pkg.getPackageName()
974                     + " " + pkg.getVersionRolledBackFrom().getLongVersionCode()
975                     + " -> " + pkg.getVersionRolledBackTo().getLongVersionCode()
976                     + " [" + pkg.getRollbackDataPolicy() + "]");
977         }
978         ipw.decreaseIndent();
979         if (isCommitted()) {
980             ipw.println("-causePackages:");
981             ipw.increaseIndent();
982             for (VersionedPackage cPkg : info.getCausePackages()) {
983                 ipw.println(cPkg.getPackageName() + " " + cPkg.getLongVersionCode());
984             }
985             ipw.decreaseIndent();
986             ipw.println("-committedSessionId: " + info.getCommittedSessionId());
987         }
988         if (mExtensionVersions.size() > 0) {
989             ipw.println("-extensionVersions:");
990             ipw.increaseIndent();
991             ipw.println(mExtensionVersions.toString());
992             ipw.decreaseIndent();
993         }
994         ipw.decreaseIndent();
995     }
996 
997     @WorkerThread
getStateDescription()998     String getStateDescription() {
999         assertInWorkerThread();
1000         return mStateDescription;
1001     }
1002 
1003     @VisibleForTesting
setState(@ollbackState int state, String description)1004     void setState(@RollbackState int state, String description) {
1005         assertInWorkerThread();
1006         mState = state;
1007         mStateDescription = description;
1008     }
1009 }
1010