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

Commit b9d54474 authored by Richard Uhler's avatar Richard Uhler Committed by Narayan Kamath
Browse files

Assign a rollbackId to all rollbacks.

To make it easier to specify what rollback to perform when a rollback is
executed.

Bug: 112431924
Test: atest RollbackTest
Change-Id: I860ad443848341fbb99169a05b084fa797c5e08c
parent daf5dc91
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -1688,6 +1688,7 @@ package android.content.rollback {
  public final class RollbackInfo implements android.os.Parcelable {
    method public int describeContents();
    method public int getRollbackId();
    method public void writeToParcel(android.os.Parcel, int);
    field public static final android.os.Parcelable.Creator<android.content.rollback.RollbackInfo> CREATOR;
    field public final android.content.rollback.PackageRollbackInfo targetPackage;
+17 −2
Original line number Diff line number Diff line
@@ -29,6 +29,11 @@ import android.os.Parcelable;
@SystemApi
public final class RollbackInfo implements Parcelable {

    /**
     * A unique identifier for the rollback.
     */
    private final int mRollbackId;

    /**
     * The package that needs to be rolled back.
     */
@@ -40,12 +45,21 @@ public final class RollbackInfo implements Parcelable {
    // staged installs is supported.

    /** @hide */
    public RollbackInfo(PackageRollbackInfo targetPackage) {
    public RollbackInfo(int rollbackId, PackageRollbackInfo targetPackage) {
        this.mRollbackId = rollbackId;
        this.targetPackage = targetPackage;
    }

    private RollbackInfo(Parcel in) {
        this.targetPackage = PackageRollbackInfo.CREATOR.createFromParcel(in);
        mRollbackId = in.readInt();
        targetPackage = PackageRollbackInfo.CREATOR.createFromParcel(in);
    }

    /**
     * Returns a unique identifier for this rollback.
     */
    public int getRollbackId() {
        return mRollbackId;
    }

    @Override
@@ -55,6 +69,7 @@ public final class RollbackInfo implements Parcelable {

    @Override
    public void writeToParcel(Parcel out, int flags) {
        out.writeInt(mRollbackId);
        targetPackage.writeToParcel(out, flags);
    }

+7 −1
Original line number Diff line number Diff line
@@ -28,6 +28,11 @@ import java.util.List;
 * packages.
 */
class RollbackData {
    /**
     * A unique identifier for this rollback.
     */
    public final int rollbackId;

    /**
     * The per-package rollback information.
     */
@@ -44,7 +49,8 @@ class RollbackData {
     */
    public Instant timestamp;

    RollbackData(File backupDir) {
    RollbackData(int rollbackId, File backupDir) {
        this.rollbackId = rollbackId;
        this.backupDir = backupDir;
    }
}
+40 −12
Original line number Diff line number Diff line
@@ -46,6 +46,7 @@ import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.storage.StorageManager;
import android.util.Log;
import android.util.SparseBooleanArray;

import com.android.internal.annotations.GuardedBy;
import com.android.server.LocalServices;
@@ -55,14 +56,16 @@ import com.android.server.pm.PackageManagerServiceUtils;

import java.io.File;
import java.io.IOException;
import java.security.SecureRandom;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.function.Consumer;

@@ -82,6 +85,13 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
    // mLock is held when they are called.
    private final Object mLock = new Object();

    // Used for generating rollback IDs.
    private final Random mRandom = new SecureRandom();

    // Set of allocated rollback ids
    @GuardedBy("mLock")
    private final SparseBooleanArray mAllocatedRollbackIds = new SparseBooleanArray();

    // Package rollback data for rollback-enabled installs that have not yet
    // been committed. Maps from sessionId to rollback data.
    @GuardedBy("mLock")
@@ -212,7 +222,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
            if (info.packageName.equals(packageName)) {
                // TODO: Once the RollbackInfo API supports info about
                // dependant packages, add that info here.
                return new RollbackInfo(info);
                return new RollbackInfo(data.rollbackId, info);
            }
        }
        return null;
@@ -282,16 +292,10 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
            return;
        }

        // Verify the latest rollback matches the version requested.
        // TODO: Check dependant packages too once RollbackInfo includes that
        // information.
        for (PackageRollbackInfo info : data.packages) {
            if (info.packageName.equals(targetPackageName)
                    && !rollback.targetPackage.higherVersion.equals(info.higherVersion)) {
                sendFailure(statusReceiver, "Rollback is out of date.");
        if (data.rollbackId != rollback.getRollbackId()) {
            sendFailure(statusReceiver, "Rollback for package is out of date");
            return;
        }
        }

        // Verify the RollbackData is up to date with what's installed on
        // device.
@@ -469,7 +473,15 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
    @GuardedBy("mLock")
    private void loadAllRollbackDataLocked() {
        mAvailableRollbacks = mRollbackStore.loadAvailableRollbacks();
        for (RollbackData data : mAvailableRollbacks) {
            mAllocatedRollbackIds.put(data.rollbackId, true);
        }

        mRecentlyExecutedRollbacks = mRollbackStore.loadRecentlyExecutedRollbacks();
        for (RollbackInfo info : mRecentlyExecutedRollbacks) {
            mAllocatedRollbackIds.put(info.getRollbackId(), true);
        }

        scheduleExpiration(0);
    }

@@ -732,7 +744,8 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
                mChildSessions.put(childSessionId, parentSessionId);
                data = mPendingRollbacks.get(parentSessionId);
                if (data == null) {
                    data = mRollbackStore.createAvailableRollback();
                    int rollbackId = allocateRollbackIdLocked();
                    data = mRollbackStore.createAvailableRollback(rollbackId);
                    mPendingRollbacks.put(parentSessionId, data);
                }
                data.packages.add(info);
@@ -912,4 +925,19 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
        }
        return null;
    }

    @GuardedBy("mLock")
    private int allocateRollbackIdLocked() throws IOException {
        int n = 0;
        int rollbackId;
        do {
            rollbackId = mRandom.nextInt(Integer.MAX_VALUE - 1) + 1;
            if (!mAllocatedRollbackIds.get(rollbackId, false)) {
                mAllocatedRollbackIds.put(rollbackId, true);
                return rollbackId;
            }
        } while (n++ < 32);

        throw new IOException("Failed to allocate rollback ID");
    }
}
+12 −7
Original line number Diff line number Diff line
@@ -29,7 +29,6 @@ import org.json.JSONObject;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.time.Instant;
import java.time.format.DateTimeParseException;
import java.util.ArrayList;
@@ -58,7 +57,7 @@ class RollbackStore {
    //                  base.apk
    //      recently_executed.json
    //
    // * XXX, YYY are random strings from Files.createTempDirectory
    // * XXX, YYY are the rollbackIds for the corresponding rollbacks.
    // * rollback.json contains all relevant metadata for the rollback. This
    //   file is not written until the rollback is made available.
    //
@@ -113,13 +112,14 @@ class RollbackStore {
                JSONArray array = object.getJSONArray("recentlyExecuted");
                for (int i = 0; i < array.length(); ++i) {
                    JSONObject element = array.getJSONObject(i);
                    int rollbackId = element.getInt("rollbackId");
                    String packageName = element.getString("packageName");
                    long higherVersionCode = element.getLong("higherVersionCode");
                    long lowerVersionCode = element.getLong("lowerVersionCode");
                    PackageRollbackInfo target = new PackageRollbackInfo(packageName,
                            new PackageRollbackInfo.PackageVersion(higherVersionCode),
                            new PackageRollbackInfo.PackageVersion(lowerVersionCode));
                    RollbackInfo rollback = new RollbackInfo(target);
                    RollbackInfo rollback = new RollbackInfo(rollbackId, target);
                    recentlyExecutedRollbacks.add(rollback);
                }
            } catch (IOException | JSONException e) {
@@ -135,9 +135,9 @@ class RollbackStore {
    /**
     * Creates a new RollbackData instance with backupDir assigned.
     */
    RollbackData createAvailableRollback() throws IOException {
        File backupDir = Files.createTempDirectory(mAvailableRollbacksDir.toPath(), null).toFile();
        return new RollbackData(backupDir);
    RollbackData createAvailableRollback(int rollbackId) throws IOException {
        File backupDir = new File(mAvailableRollbacksDir, Integer.toString(rollbackId));
        return new RollbackData(rollbackId, backupDir);
    }

    /**
@@ -162,6 +162,7 @@ class RollbackStore {
                infoJson.put("lowerVersionCode", info.lowerVersion.versionCode);
                packagesJson.put(infoJson);
            }
            dataJson.put("rollbackId", data.rollbackId);
            dataJson.put("packages", packagesJson);
            dataJson.put("timestamp", data.timestamp.toString());

@@ -195,6 +196,7 @@ class RollbackStore {
            for (int i = 0; i < recentlyExecutedRollbacks.size(); ++i) {
                RollbackInfo rollback = recentlyExecutedRollbacks.get(i);
                JSONObject element = new JSONObject();
                element.put("rollbackId", rollback.getRollbackId());
                element.put("packageName", rollback.targetPackage.packageName);
                element.put("higherVersionCode", rollback.targetPackage.higherVersion.versionCode);
                element.put("lowerVersionCode", rollback.targetPackage.lowerVersion.versionCode);
@@ -216,10 +218,13 @@ class RollbackStore {
     */
    private RollbackData loadRollbackData(File backupDir) throws IOException {
        try {
            RollbackData data = new RollbackData(backupDir);
            File rollbackJsonFile = new File(backupDir, "rollback.json");
            JSONObject dataJson = new JSONObject(
                    IoUtils.readFileAsString(rollbackJsonFile.getAbsolutePath()));

            int rollbackId = dataJson.getInt("rollbackId");
            RollbackData data = new RollbackData(rollbackId, backupDir);

            JSONArray packagesJson = dataJson.getJSONArray("packages");
            for (int i = 0; i < packagesJson.length(); ++i) {
                JSONObject infoJson = packagesJson.getJSONObject(i);