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

Commit 5178b7aa authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "RollbackManager: Backup and restore split apks."

parents bda1b8f6 ab009ea4
Loading
Loading
Loading
Loading
+23 −11
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
@@ -48,6 +49,7 @@ import android.util.SparseBooleanArray;
import android.util.SparseLongArray;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.ArrayUtils;
import com.android.server.LocalServices;
import com.android.server.pm.Installer;

@@ -411,22 +413,26 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
                int sessionId = packageInstaller.createSession(params);
                PackageInstaller.Session session = packageInstaller.openSession(sessionId);

                File packageCode = RollbackStore.getPackageCode(data, info.getPackageName());
                if (packageCode == null) {
                File[] packageCodePaths = RollbackStore.getPackageCodePaths(
                        data, info.getPackageName());
                if (packageCodePaths == null) {
                    sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE,
                            "Backup copy of package code inaccessible");
                            "Backup copy of package inaccessible");
                    return;
                }

                try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(packageCode,
                for (File packageCodePath : packageCodePaths) {
                    try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(packageCodePath,
                                ParcelFileDescriptor.MODE_READ_ONLY)) {
                        final long token = Binder.clearCallingIdentity();
                        try {
                        session.write(packageCode.getName(), 0, packageCode.length(), fd);
                            session.write(packageCodePath.getName(), 0, packageCodePath.length(),
                                    fd);
                        } finally {
                            Binder.restoreCallingIdentity(token);
                        }
                    }
                }
                parentSession.addChildSessionId(sessionId);
            }

@@ -999,7 +1005,13 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
        }

        try {
            RollbackStore.backupPackageCode(data, packageName, pkgInfo.applicationInfo.sourceDir);
            ApplicationInfo appInfo = pkgInfo.applicationInfo;
            RollbackStore.backupPackageCodePath(data, packageName, appInfo.sourceDir);
            if (!ArrayUtils.isEmpty(appInfo.splitSourceDirs)) {
                for (String sourceDir : appInfo.splitSourceDirs) {
                    RollbackStore.backupPackageCodePath(data, packageName, sourceDir);
                }
            }
        } catch (IOException e) {
            Log.e(TAG, "Unable to copy package for rollback for " + packageName, e);
            return false;
+9 −7
Original line number Diff line number Diff line
@@ -234,9 +234,11 @@ class RollbackStore {
    }

    /**
     * Creates a backup copy of the apk or apex for a package.
     * Creates a backup copy of an apk or apex for a package.
     * For packages containing splits, this method should be called for each
     * of the package's split apks in addition to the base apk.
     */
    static void backupPackageCode(RollbackData data, String packageName, String codePath)
    static void backupPackageCodePath(RollbackData data, String packageName, String codePath)
            throws IOException {
        File sourceFile = new File(codePath);
        File targetDir = new File(data.backupDir, packageName);
@@ -248,16 +250,16 @@ class RollbackStore {
    }

    /**
     * Returns the apk or apex file backed up for the given package.
     * Returns null if none found.
     * Returns the apk or apex files backed up for the given package.
     * Includes the base apk and any splits. Returns null if none found.
     */
    static File getPackageCode(RollbackData data, String packageName) {
    static File[] getPackageCodePaths(RollbackData data, String packageName) {
        File targetDir = new File(data.backupDir, packageName);
        File[] files = targetDir.listFiles();
        if (files == null || files.length != 1) {
        if (files == null || files.length == 0) {
            return null;
        }
        return files[0];
        return files;
    }

    /**
+41 −0
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
LOCAL_SDK_VERSION := current
LOCAL_SRC_FILES := $(call all-java-files-under, TestApp/src)
LOCAL_MANIFEST_FILE := TestApp/Av1.xml
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/TestApp/res_v1
LOCAL_PACKAGE_NAME := RollbackTestAppAv1
include $(BUILD_PACKAGE)
ROLLBACK_TEST_APP_AV1 := $(LOCAL_INSTALLED_MODULE)
@@ -32,6 +33,7 @@ LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
LOCAL_SDK_VERSION := current
LOCAL_SRC_FILES := $(call all-java-files-under, TestApp/src)
LOCAL_MANIFEST_FILE := TestApp/Av2.xml
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/TestApp/res_v2
LOCAL_PACKAGE_NAME := RollbackTestAppAv2
include $(BUILD_PACKAGE)
ROLLBACK_TEST_APP_AV2 := $(LOCAL_INSTALLED_MODULE)
@@ -43,6 +45,7 @@ LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
LOCAL_SDK_VERSION := current
LOCAL_SRC_FILES := $(call all-java-files-under, TestApp/src)
LOCAL_MANIFEST_FILE := TestApp/ACrashingV2.xml
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/TestApp/res_v2
LOCAL_PACKAGE_NAME := RollbackTestAppACrashingV2
include $(BUILD_PACKAGE)
ROLLBACK_TEST_APP_A_CRASHING_V2 := $(LOCAL_INSTALLED_MODULE)
@@ -54,6 +57,7 @@ LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
LOCAL_SDK_VERSION := current
LOCAL_SRC_FILES := $(call all-java-files-under, TestApp/src)
LOCAL_MANIFEST_FILE := TestApp/Bv1.xml
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/TestApp/res_v1
LOCAL_PACKAGE_NAME := RollbackTestAppBv1
include $(BUILD_PACKAGE)
ROLLBACK_TEST_APP_BV1 := $(LOCAL_INSTALLED_MODULE)
@@ -65,10 +69,39 @@ LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
LOCAL_SDK_VERSION := current
LOCAL_SRC_FILES := $(call all-java-files-under, TestApp/src)
LOCAL_MANIFEST_FILE := TestApp/Bv2.xml
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/TestApp/res_v2
LOCAL_PACKAGE_NAME := RollbackTestAppBv2
include $(BUILD_PACKAGE)
ROLLBACK_TEST_APP_BV2 := $(LOCAL_INSTALLED_MODULE)

# RollbackTestAppASplitV1.apk
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
LOCAL_SDK_VERSION := current
LOCAL_SRC_FILES := $(call all-java-files-under, TestApp/src)
LOCAL_MANIFEST_FILE := TestApp/Av1.xml
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/TestApp/res_v1
LOCAL_PACKAGE_NAME := RollbackTestAppASplitV1
LOCAL_PACKAGE_SPLITS := anydpi
include $(BUILD_PACKAGE)
ROLLBACK_TEST_APP_A_SPLIT_V1 := $(LOCAL_INSTALLED_MODULE)
ROLLBACK_TEST_APP_A_SPLIT_V1_SPLIT := $(installed_apk_splits)

# RollbackTestAppASplitV2.apk
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
LOCAL_SDK_VERSION := current
LOCAL_SRC_FILES := $(call all-java-files-under, TestApp/src)
LOCAL_MANIFEST_FILE := TestApp/Av2.xml
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/TestApp/res_v2
LOCAL_PACKAGE_NAME := RollbackTestAppASplitV2
LOCAL_PACKAGE_SPLITS := anydpi
include $(BUILD_PACKAGE)
ROLLBACK_TEST_APP_A_SPLIT_V2 := $(LOCAL_INSTALLED_MODULE)
ROLLBACK_TEST_APP_A_SPLIT_V2_SPLIT := $(installed_apk_splits)

# RollbackTest
include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(call all-java-files-under, RollbackTest/src)
@@ -82,6 +115,10 @@ LOCAL_JAVA_RESOURCE_FILES := \
  $(ROLLBACK_TEST_APP_A_CRASHING_V2) \
  $(ROLLBACK_TEST_APP_BV1) \
  $(ROLLBACK_TEST_APP_BV2) \
  $(ROLLBACK_TEST_APP_A_SPLIT_V1) \
  $(ROLLBACK_TEST_APP_A_SPLIT_V1_SPLIT) \
  $(ROLLBACK_TEST_APP_A_SPLIT_V2) \
  $(ROLLBACK_TEST_APP_A_SPLIT_V2_SPLIT) \
  $(ROLLBACK_TEST_APEX_V1) \
  $(ROLLBACK_TEST_APEX_V2)
LOCAL_MANIFEST_FILE := RollbackTest/AndroidManifest.xml
@@ -103,5 +140,9 @@ include $(BUILD_HOST_JAVA_LIBRARY)
ROLLBACK_TEST_APP_AV1 :=
ROLLBACK_TEST_APP_AV2 :=
ROLLBACK_TEST_APP_A_CRASHING_V2 :=
ROLLBACK_TEST_APP_A_SPLIT_V1 :=
ROLLBACK_TEST_APP_A_SPLIT_V1_SPLIT :=
ROLLBACK_TEST_APP_A_SPLIT_V2 :=
ROLLBACK_TEST_APP_A_SPLIT_V2_SPLIT :=
ROLLBACK_TEST_APP_BV1 :=
ROLLBACK_TEST_APP_BV2 :=
+33 −0
Original line number Diff line number Diff line
@@ -441,6 +441,39 @@ public class RollbackTest {
        }
    }

    /**
     * Test rollback of apks involving splits.
     */
    @Test
    public void testRollbackWithSplits() throws Exception {
        try {
            RollbackTestUtils.adoptShellPermissionIdentity(
                    Manifest.permission.INSTALL_PACKAGES,
                    Manifest.permission.DELETE_PACKAGES,
                    Manifest.permission.MANAGE_ROLLBACKS);

            RollbackTestUtils.uninstall(TEST_APP_A);
            RollbackTestUtils.installSplit(false,
                    "RollbackTestAppASplitV1.apk",
                    "RollbackTestAppASplitV1_anydpi.apk");
            processUserData(TEST_APP_A);

            RollbackTestUtils.installSplit(true,
                    "RollbackTestAppASplitV2.apk",
                    "RollbackTestAppASplitV2_anydpi.apk");
            processUserData(TEST_APP_A);

            RollbackManager rm = RollbackTestUtils.getRollbackManager();
            RollbackInfo rollback = getUniqueRollbackInfoForPackage(
                    rm.getAvailableRollbacks(), TEST_APP_A);
            assertNotNull(rollback);
            RollbackTestUtils.rollback(rollback.getRollbackId());
            processUserData(TEST_APP_A);
        } finally {
            RollbackTestUtils.dropShellPermissionIdentity();
        }
    }

    /**
     * Test restrictions on rollback broadcast sender.
     * A random app should not be able to send a ROLLBACK_COMMITTED broadcast.
+21 −6
Original line number Diff line number Diff line
@@ -130,6 +130,19 @@ class RollbackTestUtils {
     */
    static void install(String resourceName, boolean enableRollback)
            throws InterruptedException, IOException {
        installSplit(enableRollback, resourceName);
    }

    /**
     * Installs the apk with the given name and its splits.
     *
     * @param enableRollback if rollback should be enabled.
     * @param resourceNames names of class loader resources for the apk and
     *        its splits to install.
     * @throws AssertionError if the installation fails.
     */
    static void installSplit(boolean enableRollback, String... resourceNames)
            throws InterruptedException, IOException {
        Context context = InstrumentationRegistry.getContext();
        PackageInstaller.Session session = null;
        PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller();
@@ -142,6 +155,7 @@ class RollbackTestUtils {
        session = packageInstaller.openSession(sessionId);

        ClassLoader loader = RollbackTest.class.getClassLoader();
        for (String resourceName : resourceNames) {
            try (OutputStream packageInSession = session.openWrite(resourceName, 0, -1);
                    InputStream is = loader.getResourceAsStream(resourceName);) {
                byte[] buffer = new byte[4096];
@@ -150,6 +164,7 @@ class RollbackTestUtils {
                    packageInSession.write(buffer, 0, n);
                }
            }
        }

        // Commit the session (this will start the installation workflow).
        session.commit(LocalIntentSender.getIntentSender());
Loading