diff options
15 files changed, 221 insertions, 40 deletions
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java index d2c0ee3f5196..779f570db3d6 100644 --- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java +++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java @@ -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; @@ -47,6 +48,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; @@ -362,20 +364,24 @@ 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, - ParcelFileDescriptor.MODE_READ_ONLY)) { - final long token = Binder.clearCallingIdentity(); - try { - session.write(packageCode.getName(), 0, packageCode.length(), fd); - } finally { - Binder.restoreCallingIdentity(token); + for (File packageCodePath : packageCodePaths) { + try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(packageCodePath, + ParcelFileDescriptor.MODE_READ_ONLY)) { + final long token = Binder.clearCallingIdentity(); + try { + session.write(packageCodePath.getName(), 0, packageCodePath.length(), + fd); + } finally { + Binder.restoreCallingIdentity(token); + } } } parentSession.addChildSessionId(sessionId); @@ -950,7 +956,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; diff --git a/services/core/java/com/android/server/rollback/RollbackStore.java b/services/core/java/com/android/server/rollback/RollbackStore.java index be904eacebff..bb4e89eca5da 100644 --- a/services/core/java/com/android/server/rollback/RollbackStore.java +++ b/services/core/java/com/android/server/rollback/RollbackStore.java @@ -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; } /** diff --git a/tests/RollbackTest/Android.mk b/tests/RollbackTest/Android.mk index 9e5d8cebf43a..db9376b844f9 100644 --- a/tests/RollbackTest/Android.mk +++ b/tests/RollbackTest/Android.mk @@ -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 := diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java index bd0881f18423..8ae615651666 100644 --- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java +++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java @@ -442,6 +442,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. */ diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java index def5b8ead283..f28714c87125 100644 --- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java +++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java @@ -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,12 +155,14 @@ class RollbackTestUtils { session = packageInstaller.openSession(sessionId); ClassLoader loader = RollbackTest.class.getClassLoader(); - try (OutputStream packageInSession = session.openWrite(resourceName, 0, -1); - InputStream is = loader.getResourceAsStream(resourceName);) { - byte[] buffer = new byte[4096]; - int n; - while ((n = is.read(buffer)) >= 0) { - packageInSession.write(buffer, 0, n); + for (String resourceName : resourceNames) { + try (OutputStream packageInSession = session.openWrite(resourceName, 0, -1); + InputStream is = loader.getResourceAsStream(resourceName);) { + byte[] buffer = new byte[4096]; + int n; + while ((n = is.read(buffer)) >= 0) { + packageInSession.write(buffer, 0, n); + } } } diff --git a/tests/RollbackTest/TestApp/ACrashingV2.xml b/tests/RollbackTest/TestApp/ACrashingV2.xml index 5708d2385f01..77bfd4e0f9a0 100644 --- a/tests/RollbackTest/TestApp/ACrashingV2.xml +++ b/tests/RollbackTest/TestApp/ACrashingV2.xml @@ -23,7 +23,6 @@ <uses-sdk android:minSdkVersion="19" /> <application android:label="Rollback Test App A v2"> - <meta-data android:name="version" android:value="2" /> <receiver android:name="com.android.tests.rollback.testapp.ProcessUserData" android:exported="true" /> <activity android:name="com.android.tests.rollback.testapp.CrashingMainActivity"> diff --git a/tests/RollbackTest/TestApp/Av1.xml b/tests/RollbackTest/TestApp/Av1.xml index 996d831013a8..63729fbaaf28 100644 --- a/tests/RollbackTest/TestApp/Av1.xml +++ b/tests/RollbackTest/TestApp/Av1.xml @@ -23,7 +23,6 @@ <uses-sdk android:minSdkVersion="19" /> <application android:label="Rollback Test App A v1"> - <meta-data android:name="version" android:value="1" /> <receiver android:name="com.android.tests.rollback.testapp.ProcessUserData" android:exported="true" /> <activity android:name="com.android.tests.rollback.testapp.MainActivity"> diff --git a/tests/RollbackTest/TestApp/Av2.xml b/tests/RollbackTest/TestApp/Av2.xml index 21c7260b570b..f0e909feabf3 100644 --- a/tests/RollbackTest/TestApp/Av2.xml +++ b/tests/RollbackTest/TestApp/Av2.xml @@ -23,7 +23,6 @@ <uses-sdk android:minSdkVersion="19" /> <application android:label="Rollback Test App A v2"> - <meta-data android:name="version" android:value="2" /> <receiver android:name="com.android.tests.rollback.testapp.ProcessUserData" android:exported="true" /> <activity android:name="com.android.tests.rollback.testapp.MainActivity"> diff --git a/tests/RollbackTest/TestApp/Bv1.xml b/tests/RollbackTest/TestApp/Bv1.xml index de0fd0d6d616..ca9c2ec47a20 100644 --- a/tests/RollbackTest/TestApp/Bv1.xml +++ b/tests/RollbackTest/TestApp/Bv1.xml @@ -23,7 +23,6 @@ <uses-sdk android:minSdkVersion="19" /> <application android:label="Rollback Test App B v1"> - <meta-data android:name="version" android:value="1" /> <receiver android:name="com.android.tests.rollback.testapp.ProcessUserData" android:exported="true" /> <activity android:name="com.android.tests.rollback.testapp.MainActivity"> diff --git a/tests/RollbackTest/TestApp/Bv2.xml b/tests/RollbackTest/TestApp/Bv2.xml index 6c2e66a3220c..bd3e6133f6f6 100644 --- a/tests/RollbackTest/TestApp/Bv2.xml +++ b/tests/RollbackTest/TestApp/Bv2.xml @@ -23,7 +23,6 @@ <uses-sdk android:minSdkVersion="19" /> <application android:label="Rollback Test App B v2"> - <meta-data android:name="version" android:value="2" /> <receiver android:name="com.android.tests.rollback.testapp.ProcessUserData" android:exported="true" /> <activity android:name="com.android.tests.rollback.testapp.MainActivity"> diff --git a/tests/RollbackTest/TestApp/res_v1/values-anydpi/values.xml b/tests/RollbackTest/TestApp/res_v1/values-anydpi/values.xml new file mode 100644 index 000000000000..90d3da2565cc --- /dev/null +++ b/tests/RollbackTest/TestApp/res_v1/values-anydpi/values.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<resources> + <integer name="split_version">1</integer> +</resources> diff --git a/tests/RollbackTest/TestApp/res_v1/values/values.xml b/tests/RollbackTest/TestApp/res_v1/values/values.xml new file mode 100644 index 000000000000..0447c74a79a6 --- /dev/null +++ b/tests/RollbackTest/TestApp/res_v1/values/values.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<resources> + <integer name="app_version">1</integer> + <integer name="split_version">0</integer> +</resources> diff --git a/tests/RollbackTest/TestApp/res_v2/values-anydpi/values.xml b/tests/RollbackTest/TestApp/res_v2/values-anydpi/values.xml new file mode 100644 index 000000000000..9a1aa7fd8461 --- /dev/null +++ b/tests/RollbackTest/TestApp/res_v2/values-anydpi/values.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<resources> + <integer name="split_version">2</integer> +</resources> diff --git a/tests/RollbackTest/TestApp/res_v2/values/values.xml b/tests/RollbackTest/TestApp/res_v2/values/values.xml new file mode 100644 index 000000000000..fd988f597f61 --- /dev/null +++ b/tests/RollbackTest/TestApp/res_v2/values/values.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<resources> + <integer name="app_version">2</integer> + <integer name="split_version">0</integer> +</resources> diff --git a/tests/RollbackTest/TestApp/src/com/android/tests/rollback/testapp/ProcessUserData.java b/tests/RollbackTest/TestApp/src/com/android/tests/rollback/testapp/ProcessUserData.java index fde6a83b6e56..38c658e795aa 100644 --- a/tests/RollbackTest/TestApp/src/com/android/tests/rollback/testapp/ProcessUserData.java +++ b/tests/RollbackTest/TestApp/src/com/android/tests/rollback/testapp/ProcessUserData.java @@ -19,9 +19,7 @@ package com.android.tests.rollback.testapp; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.os.Bundle; +import android.content.res.Resources; import java.io.File; import java.io.FileNotFoundException; @@ -35,6 +33,8 @@ import java.util.Scanner; */ public class ProcessUserData extends BroadcastReceiver { + private static final String TAG = "RollbackTestApp"; + /** * Exception thrown in case of issue with user data. */ @@ -66,14 +66,19 @@ public class ProcessUserData extends BroadcastReceiver { * @throws UserDataException in case of problems with app user data. */ public void processUserData(Context context) throws UserDataException { - int appVersion = 0; - try { - ApplicationInfo appInfo = context.getPackageManager().getApplicationInfo( - context.getPackageName(), PackageManager.GET_META_DATA); - Bundle bundle = appInfo.metaData; - appVersion = bundle.getInt("version"); - } catch (PackageManager.NameNotFoundException e) { - throw new UserDataException("Unable to get app version info", e); + Resources res = context.getResources(); + String packageName = context.getPackageName(); + + int appVersionId = res.getIdentifier("app_version", "integer", packageName); + int appVersion = res.getInteger(appVersionId); + + int splitVersionId = res.getIdentifier("split_version", "integer", packageName); + int splitVersion = res.getInteger(splitVersionId); + + // Make sure the app version and split versions are compatible. + if (appVersion != splitVersion) { + throw new UserDataException("Split version " + splitVersion + + " does not match app version " + appVersion); } // Read the version of the app's user data and ensure it is compatible |