Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4831ad70 authored by shafik's avatar shafik
Browse files

Fail to enable rollback if enable rollback times out

Make PackageManager send a ACTION_CANCEL_ENABLE_ROLLBACK intent to
RollbackManager. RollbackManager marks the relevant rollback as invalid.
Allow enable rollback to continue as usual, before making the rollback
available, RollbackManager checks whether it's valid. If it's not, the
rollback data is deleted.

Add a test case for expired rollback enabling attempt in RollbackTest.

Test: atest RollbackTest#testEnableRollbackTimeoutFailsRollback
Test: manual -
      * Set ENABLE_ROLLBACK_TIMEOUT_MILLIS to 1 ms using DeviceConfig
      * Install a mainline module with rollback enabled
      * adb shell dumpsys rollback
      * observe that no rollback was made available
Fixes: 131679409

Change-Id: Iaa4dbff002b820aff1fc3e1b985f129cf5ebe2e6
parent 78cbd8f8
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -2444,6 +2444,18 @@ public class Intent implements Parcelable, Cloneable {
    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
    public static final String ACTION_PACKAGE_ENABLE_ROLLBACK =
            "android.intent.action.PACKAGE_ENABLE_ROLLBACK";
    /**
     * Broadcast Action: Sent to the system rollback manager when the rollback for a certain
     * package needs to be cancelled.
     *
     * <p class="note">This intent is sent by PackageManagerService to notify RollbackManager
     * that enabling a specific rollback has timed out.
     *
     * @hide
     */
    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
    public static final String ACTION_CANCEL_ENABLE_ROLLBACK =
            "android.intent.action.CANCEL_ENABLE_ROLLBACK";
    /**
     * Broadcast Action: A rollback has been committed.
     *
+2 −2
Original line number Diff line number Diff line
@@ -40,7 +40,7 @@ import java.util.List;
 * used to initiate rollback of those packages for a limited time period after
 * upgrade.
 *
 * @see PackageInstaller.SessionParams#setEnableRollback()
 * @see PackageInstaller.SessionParams#setEnableRollback(boolean)
 * @hide
 */
@SystemApi @TestApi
@@ -52,7 +52,7 @@ public final class RollbackManager {
    /**
     * Lifetime duration of rollback packages in millis. A rollback will be available for
     * at most that duration of time after a package is installed with
     * {@link PackageInstaller.SessionParams#setEnableRollback()}.
     * {@link PackageInstaller.SessionParams#setEnableRollback(boolean)}.
     *
     * <p>If flag value is negative, the default value will be assigned.
     *
+1 −0
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@
    <protected-broadcast android:name="android.intent.action.PACKAGE_FULLY_REMOVED" />
    <protected-broadcast android:name="android.intent.action.PACKAGE_CHANGED" />
    <protected-broadcast android:name="android.intent.action.PACKAGE_ENABLE_ROLLBACK" />
    <protected-broadcast android:name="android.intent.action.CANCEL_ENABLE_ROLLBACK" />
    <protected-broadcast android:name="android.intent.action.ROLLBACK_COMMITTED" />
    <protected-broadcast android:name="android.intent.action.PACKAGE_RESTARTED" />
    <protected-broadcast android:name="android.intent.action.PACKAGE_DATA_CLEARED" />
+9 −0
Original line number Diff line number Diff line
@@ -1744,6 +1744,15 @@ public class PackageManagerService extends IPackageManager.Stub
                        Trace.asyncTraceEnd(
                                TRACE_TAG_PACKAGE_MANAGER, "enable_rollback", enableRollbackToken);
                        params.handleRollbackEnabled();
                        Intent rollbackTimeoutIntent = new Intent(
                                Intent.ACTION_CANCEL_ENABLE_ROLLBACK);
                        rollbackTimeoutIntent.putExtra(
                                PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_TOKEN,
                                enableRollbackToken);
                        rollbackTimeoutIntent.addFlags(
                                Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
                        mContext.sendBroadcastAsUser(rollbackTimeoutIntent, UserHandle.SYSTEM,
                                android.Manifest.permission.PACKAGE_ROLLBACK_AGENT);
                    }
                    break;
                }
+59 −3
Original line number Diff line number Diff line
@@ -184,7 +184,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {

                    getHandler().post(() -> {
                        boolean success = enableRollback(installFlags, newPackageCodePath,
                                installedUsers, user);
                                installedUsers, user, token);
                        int ret = PackageManagerInternal.ENABLE_ROLLBACK_SUCCEEDED;
                        if (!success) {
                            ret = PackageManagerInternal.ENABLE_ROLLBACK_FAILED;
@@ -203,6 +203,27 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
            }
        }, enableRollbackFilter, null, getHandler());

        IntentFilter enableRollbackTimedOutFilter = new IntentFilter();
        enableRollbackTimedOutFilter.addAction(Intent.ACTION_CANCEL_ENABLE_ROLLBACK);

        mContext.registerReceiver(new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                if (Intent.ACTION_CANCEL_ENABLE_ROLLBACK.equals(intent.getAction())) {
                    int token = intent.getIntExtra(
                            PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_TOKEN, -1);
                    synchronized (mLock) {
                        for (NewRollback rollback : mNewRollbacks) {
                            if (rollback.hasToken(token)) {
                                rollback.isCancelled = true;
                                return;
                            }
                        }
                    }
                }
            }
        }, enableRollbackTimedOutFilter, null, getHandler());

        registerTimeChangeReceiver();
    }

@@ -807,10 +828,11 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
     * @param newPackageCodePath path to the package about to be installed.
     * @param installedUsers the set of users for which a given package is installed.
     * @param user the user that owns the install session to enable rollback on.
     * @param token the distinct rollback token sent by package manager.
     * @return true if enabling the rollback succeeds, false otherwise.
     */
    private boolean enableRollback(int installFlags, File newPackageCodePath,
            int[] installedUsers, @UserIdInt int user) {
            int[] installedUsers, @UserIdInt int user, int token) {

        // Find the session id associated with this install.
        // TODO: It would be nice if package manager or package installer told
@@ -903,6 +925,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
                mNewRollbacks.add(newRollback);
            }
        }
        newRollback.addToken(token);

        return enableRollbackForPackageSession(newRollback.data, packageSession,
                installedUsers, /* snapshotUserData*/ true);
@@ -1005,7 +1028,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
    public void restoreUserData(String packageName, int[] userIds, int appId, long ceDataInode,
            String seInfo, int token) {
        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
            throw new SecurityException("restoureUserData may only be called by the system.");
            throw new SecurityException("restoreUserData may only be called by the system.");
        }

        getHandler().post(() -> {
@@ -1261,6 +1284,11 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
            deleteRollback(data);
            return null;
        }
        if (newRollback.isCancelled) {
            Log.e(TAG, "Rollback has been cancelled by PackageManager");
            deleteRollback(data);
            return null;
        }

        // It's safe to access data.info outside a synchronized block because
        // this is running on the handler thread and all changes to the
@@ -1440,6 +1468,13 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
    private static class NewRollback {
        public final RollbackData data;

        /**
         * This array holds all of the rollback tokens associated with package sessions included
         * in this rollback. This is used to identify which rollback should be cancelled in case
         * {@link PackageManager} sends an {@link Intent#ACTION_CANCEL_ENABLE_ROLLBACK} intent.
         */
        private final IntArray mTokens = new IntArray();

        /**
         * Session ids for all packages in the install.
         * For multi-package sessions, this is the list of child session ids.
@@ -1448,10 +1483,31 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
         */
        public final int[] packageSessionIds;

        /**
         * Flag to determine whether the RollbackData has been cancelled.
         *
         * <p>RollbackData could be invalidated and cancelled if RollbackManager receives
         * {@link Intent#ACTION_CANCEL_ENABLE_ROLLBACK} from {@link PackageManager}.
         *
         * <p>The main underlying assumption here is that if enabling the rollback times out, then
         * {@link PackageManager} will NOT send
         * {@link PackageInstaller.SessionCallback#onFinished(int, boolean)} before it broadcasts
         * {@link Intent#ACTION_CANCEL_ENABLE_ROLLBACK}.
         */
        public boolean isCancelled = false;

        NewRollback(RollbackData data, int[] packageSessionIds) {
            this.data = data;
            this.packageSessionIds = packageSessionIds;
        }

        public void addToken(int token) {
            mTokens.add(token);
        }

        public boolean hasToken(int token) {
            return mTokens.indexOf(token) != -1;
        }
    }

    NewRollback createNewRollbackLocked(PackageInstaller.SessionInfo parentSession) {
Loading