diff --git a/cmds/idmap2/libidmap2/ResourceMapping.cpp b/cmds/idmap2/libidmap2/ResourceMapping.cpp index 229628c7dd8b..407478945151 100644 --- a/cmds/idmap2/libidmap2/ResourceMapping.cpp +++ b/cmds/idmap2/libidmap2/ResourceMapping.cpp @@ -33,6 +33,8 @@ namespace android::idmap2 { namespace { +#define REWRITE_PACKAGE(resid, package_id) \ + (((resid)&0x00ffffffU) | (((uint32_t)(package_id)) << 24U)) #define EXTRACT_PACKAGE(resid) ((0xff000000 & (resid)) >> 24) std::string ConcatPolicies(const std::vector& policies) { @@ -154,6 +156,7 @@ Result ResourceMapping::CreateResourceMapping(const AssetManage return Error("root element is not tag"); } + const uint8_t target_package_id = target_package->GetPackageId(); const uint8_t overlay_package_id = overlay_package->GetPackageId(); auto overlay_it_end = root_it.end(); for (auto overlay_it = root_it.begin(); overlay_it != overlay_it_end; ++overlay_it) { @@ -187,6 +190,9 @@ Result ResourceMapping::CreateResourceMapping(const AssetManage continue; } + // Retrieve the compile-time resource id of the target resource. + target_id = REWRITE_PACKAGE(target_id, target_package_id); + if (overlay_resource->dataType == Res_value::TYPE_STRING) { overlay_resource->data += string_pool_offset; } @@ -214,6 +220,7 @@ Result ResourceMapping::CreateResourceMappingLegacy( const AssetManager2* target_am, const AssetManager2* overlay_am, const LoadedPackage* target_package, const LoadedPackage* overlay_package) { ResourceMapping resource_mapping; + const uint8_t target_package_id = target_package->GetPackageId(); const auto end = overlay_package->end(); for (auto iter = overlay_package->begin(); iter != end; ++iter) { const ResourceId overlay_resid = *iter; @@ -225,11 +232,14 @@ Result ResourceMapping::CreateResourceMappingLegacy( // Find the resource with the same type and entry name within the target package. const std::string full_name = base::StringPrintf("%s:%s", target_package->GetPackageName().c_str(), name->c_str()); - const ResourceId target_resource = target_am->GetResourceId(full_name); + ResourceId target_resource = target_am->GetResourceId(full_name); if (target_resource == 0U) { continue; } + // Retrieve the compile-time resource id of the target resource. + target_resource = REWRITE_PACKAGE(target_resource, target_package_id); + resource_mapping.AddMapping(target_resource, Res_value::TYPE_REFERENCE, overlay_resid, /* rewrite_overlay_reference */ false); } diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java index aa115984b5e1..d23754e9d433 100644 --- a/core/java/android/app/ResourcesManager.java +++ b/core/java/android/app/ResourcesManager.java @@ -482,19 +482,6 @@ public class ResourcesManager { } } - if (key.mOverlayDirs != null) { - for (final String idmapPath : key.mOverlayDirs) { - try { - builder.addApkAssets(loadApkAssets(idmapPath, false /*sharedLib*/, - true /*overlay*/)); - } catch (IOException e) { - Log.w(TAG, "failed to add overlay path " + idmapPath); - - // continue. - } - } - } - if (key.mLibDirs != null) { for (final String libDir : key.mLibDirs) { if (libDir.endsWith(".apk")) { @@ -513,6 +500,19 @@ public class ResourcesManager { } } + if (key.mOverlayDirs != null) { + for (final String idmapPath : key.mOverlayDirs) { + try { + builder.addApkAssets(loadApkAssets(idmapPath, false /*sharedLib*/, + true /*overlay*/)); + } catch (IOException e) { + Log.w(TAG, "failed to add overlay path " + idmapPath); + + // continue. + } + } + } + return builder.build(); } diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 32803ab8f859..87acbc146f49 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -7803,7 +7803,7 @@ public class PackageParser { ai.category = FallbackCategoryProvider.getFallbackCategory(ai.packageName); } ai.seInfoUser = SELinuxUtil.assignSeinfoUser(state); - ai.resourceDirs = state.overlayPaths; + ai.resourceDirs = state.getAllOverlayPaths(); ai.icon = (sUseRoundIcon && ai.roundIconRes != 0) ? ai.roundIconRes : ai.iconRes; } diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java index f0f6753fc93e..da7623a1426e 100644 --- a/core/java/android/content/pm/PackageUserState.java +++ b/core/java/android/content/pm/PackageUserState.java @@ -46,6 +46,8 @@ import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.util.Arrays; +import java.util.LinkedHashSet; +import java.util.Map; import java.util.Objects; /** @@ -77,7 +79,9 @@ public class PackageUserState { public ArraySet disabledComponents; public ArraySet enabledComponents; - public String[] overlayPaths; + private String[] overlayPaths; + private ArrayMap sharedLibraryOverlayPaths; // Lib name to overlay paths + private String[] cachedOverlayPaths; @UnsupportedAppUsage public PackageUserState() { @@ -112,9 +116,33 @@ public class PackageUserState { enabledComponents = ArrayUtils.cloneOrNull(o.enabledComponents); overlayPaths = o.overlayPaths == null ? null : Arrays.copyOf(o.overlayPaths, o.overlayPaths.length); + if (o.sharedLibraryOverlayPaths != null) { + sharedLibraryOverlayPaths = new ArrayMap<>(o.sharedLibraryOverlayPaths); + } harmfulAppWarning = o.harmfulAppWarning; } + public String[] getOverlayPaths() { + return overlayPaths; + } + + public void setOverlayPaths(String[] paths) { + overlayPaths = paths; + cachedOverlayPaths = null; + } + + public Map getSharedLibraryOverlayPaths() { + return sharedLibraryOverlayPaths; + } + + public void setSharedLibraryOverlayPaths(String library, String[] paths) { + if (sharedLibraryOverlayPaths == null) { + sharedLibraryOverlayPaths = new ArrayMap<>(); + } + sharedLibraryOverlayPaths.put(library, paths); + cachedOverlayPaths = null; + } + /** * Test if this package is installed. */ @@ -235,6 +263,38 @@ public class PackageUserState { return isComponentEnabled; } + public String[] getAllOverlayPaths() { + if (overlayPaths == null && sharedLibraryOverlayPaths == null) { + return null; + } + + if (cachedOverlayPaths != null) { + return cachedOverlayPaths; + } + + final LinkedHashSet paths = new LinkedHashSet<>(); + if (overlayPaths != null) { + final int N = overlayPaths.length; + for (int i = 0; i < N; i++) { + paths.add(overlayPaths[i]); + } + } + + if (sharedLibraryOverlayPaths != null) { + for (String[] libOverlayPaths : sharedLibraryOverlayPaths.values()) { + if (libOverlayPaths != null) { + final int N = libOverlayPaths.length; + for (int i = 0; i < N; i++) { + paths.add(libOverlayPaths[i]); + } + } + } + } + + cachedOverlayPaths = paths.toArray(new String[0]); + return cachedOverlayPaths; + } + @Override final public boolean equals(Object obj) { if (!(obj instanceof PackageUserState)) { diff --git a/core/java/android/content/pm/parsing/PackageInfoUtils.java b/core/java/android/content/pm/parsing/PackageInfoUtils.java index f2cf9a484c6e..73a8d2a7dc0f 100644 --- a/core/java/android/content/pm/parsing/PackageInfoUtils.java +++ b/core/java/android/content/pm/parsing/PackageInfoUtils.java @@ -41,9 +41,11 @@ import android.content.pm.parsing.ComponentParseUtils.ParsedActivity; import android.content.pm.parsing.ComponentParseUtils.ParsedInstrumentation; import android.content.pm.parsing.ComponentParseUtils.ParsedPermission; import android.content.pm.parsing.ComponentParseUtils.ParsedPermissionGroup; +import android.util.ArraySet; import com.android.internal.util.ArrayUtils; +import java.util.LinkedHashSet; import java.util.Set; /** @hide */ @@ -545,7 +547,7 @@ public class PackageInfoUtils { ai.category = FallbackCategoryProvider.getFallbackCategory(ai.packageName); } ai.seInfoUser = SELinuxUtil.assignSeinfoUser(state); - ai.resourceDirs = state.overlayPaths; + ai.resourceDirs = state.getAllOverlayPaths(); ai.icon = (PackageParser.sUseRoundIcon && ai.roundIconRes != 0) ? ai.roundIconRes : ai.iconRes; } diff --git a/core/tests/overlaytests/remount/TEST_MAPPING b/core/tests/overlaytests/remount/TEST_MAPPING new file mode 100644 index 000000000000..54dd4310bd6e --- /dev/null +++ b/core/tests/overlaytests/remount/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "presubmit": [ + { + "name" : "OverlayRemountedTest" + } + ] +} \ No newline at end of file diff --git a/core/tests/overlaytests/remount/host/Android.bp b/core/tests/overlaytests/remount/host/Android.bp new file mode 100644 index 000000000000..3825c55f3b4b --- /dev/null +++ b/core/tests/overlaytests/remount/host/Android.bp @@ -0,0 +1,28 @@ +// 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. + +java_test_host { + name: "OverlayRemountedTest", + srcs: ["src/**/*.java"], + libs: [ + "tradefed", + "junit", + ], + test_suites: ["general-tests"], + java_resources: [ + ":OverlayRemountedTest_SharedLibrary", + ":OverlayRemountedTest_SharedLibraryOverlay", + ":OverlayRemountedTest_Target", + ], +} diff --git a/core/tests/overlaytests/remount/host/AndroidTest.xml b/core/tests/overlaytests/remount/host/AndroidTest.xml new file mode 100644 index 000000000000..11eadf1a4659 --- /dev/null +++ b/core/tests/overlaytests/remount/host/AndroidTest.xml @@ -0,0 +1,29 @@ + + + + + diff --git a/core/tests/overlaytests/remount/host/src/com/android/overlaytest/remounted/OverlayHostTest.java b/core/tests/overlaytests/remount/host/src/com/android/overlaytest/remounted/OverlayHostTest.java new file mode 100644 index 000000000000..84af18710fe6 --- /dev/null +++ b/core/tests/overlaytests/remount/host/src/com/android/overlaytest/remounted/OverlayHostTest.java @@ -0,0 +1,114 @@ +/* + * 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. + */ + +package com.android.overlaytest.remounted; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import com.android.tradefed.device.DeviceNotAvailableException; +import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; + +import org.junit.Rule; +import org.junit.rules.RuleChain; +import org.junit.rules.TemporaryFolder; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class OverlayHostTest extends BaseHostJUnit4Test { + private static final long TIME_OUT_MS = 30000; + private static final String RES_INSTRUMENTATION_ARG = "res"; + private static final String OVERLAY_INSTRUMENTATION_ARG = "overlays"; + private static final String RESOURCES_TYPE_SUFFIX = "_type"; + private static final String RESOURCES_DATA_SUFFIX = "_data"; + + public final TemporaryFolder mTemporaryFolder = new TemporaryFolder(); + public final SystemPreparer mPreparer = new SystemPreparer(mTemporaryFolder, this::getDevice); + + @Rule + public final RuleChain ruleChain = RuleChain.outerRule(mTemporaryFolder).around(mPreparer); + private Map mLastResults; + + /** + * Retrieves the values of the resources in the test package. The test package must use the + * {@link com.android.overlaytest.remounted.target.ResourceRetrievalRunner} instrumentation. + **/ + void retrieveResource(String testPackageName, List requiredOverlayPaths, + String... resourceNames) throws DeviceNotAvailableException { + final HashMap args = new HashMap<>(); + if (!requiredOverlayPaths.isEmpty()) { + // Enclose the require overlay paths in quotes so the arguments will be string arguments + // rather than file arguments. + args.put(OVERLAY_INSTRUMENTATION_ARG, + String.format("\"%s\"", String.join(" ", requiredOverlayPaths))); + } + + if (resourceNames.length == 0) { + throw new IllegalArgumentException("Must specify at least one resource to retrieve."); + } + + // Pass the names of the resources to retrieve into the test as one string. + args.put(RES_INSTRUMENTATION_ARG, + String.format("\"%s\"", String.join(" ", resourceNames))); + + runDeviceTests(getDevice(), null, testPackageName, null, null, null, TIME_OUT_MS, + TIME_OUT_MS, TIME_OUT_MS, false, false, args); + + // Retrieve the results of the most recently run test. + mLastResults = (getLastDeviceRunResults().getRunMetrics() == mLastResults) ? null : + getLastDeviceRunResults().getRunMetrics(); + } + + /** Returns the base resource directories of the specified packages. */ + List getPackagePaths(String... packageNames) + throws DeviceNotAvailableException { + final ArrayList paths = new ArrayList<>(); + for (String packageName : packageNames) { + // Use the package manager shell command to find the path of the package. + final String result = getDevice().executeShellCommand( + String.format("pm dump %s | grep \"resourcePath=\"", packageName)); + assertNotNull("Failed to find path for package " + packageName, result); + int splitIndex = result.indexOf('='); + assertTrue(splitIndex >= 0); + paths.add(result.substring(splitIndex + 1).trim()); + } + return paths; + } + + /** Builds the full name of a resource in the form package:type/entry. */ + String resourceName(String pkg, String type, String entry) { + return String.format("%s:%s/%s", pkg, type, entry); + } + + /** + * Asserts that the type and data of a a previously retrieved is the same as expected. + * @param resourceName the full name of the resource in the form package:type/entry + * @param type the expected {@link android.util.TypedValue} type of the resource + * @param data the expected value of the resource when coerced to a string using + * {@link android.util.TypedValue#coerceToString()} + **/ + void assertResource(String resourceName, int type, String data) { + assertNotNull("Failed to get test results", mLastResults); + assertNotEquals("No resource values were retrieved", mLastResults.size(), 0); + assertEquals("" + type, mLastResults.get(resourceName + RESOURCES_TYPE_SUFFIX)); + assertEquals("" + data, mLastResults.get(resourceName + RESOURCES_DATA_SUFFIX)); + } +} diff --git a/core/tests/overlaytests/remount/host/src/com/android/overlaytest/remounted/OverlaySharedLibraryTest.java b/core/tests/overlaytests/remount/host/src/com/android/overlaytest/remounted/OverlaySharedLibraryTest.java new file mode 100644 index 000000000000..4939e160612e --- /dev/null +++ b/core/tests/overlaytests/remount/host/src/com/android/overlaytest/remounted/OverlaySharedLibraryTest.java @@ -0,0 +1,90 @@ +/* + * 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. + */ + +package com.android.overlaytest.remounted; + +import com.android.tradefed.device.DeviceNotAvailableException; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Collections; +import java.util.List; + +@RunWith(DeviceJUnit4ClassRunner.class) +public class OverlaySharedLibraryTest extends OverlayHostTest { + private static final String TARGET_APK = "OverlayRemountedTest_Target.apk"; + private static final String TARGET_PACKAGE = "com.android.overlaytest.remounted.target"; + private static final String SHARED_LIBRARY_APK = + "OverlayRemountedTest_SharedLibrary.apk"; + private static final String SHARED_LIBRARY_PACKAGE = + "com.android.overlaytest.remounted.shared_library"; + private static final String SHARED_LIBRARY_OVERLAY_APK = + "OverlayRemountedTest_SharedLibraryOverlay.apk"; + private static final String SHARED_LIBRARY_OVERLAY_PACKAGE = + "com.android.overlaytest.remounted.shared_library.overlay"; + + @Test + public void testSharedLibrary() throws Exception { + final String targetResource = resourceName(TARGET_PACKAGE, "bool", + "uses_shared_library_overlaid"); + final String libraryResource = resourceName(SHARED_LIBRARY_PACKAGE, "bool", + "shared_library_overlaid"); + + mPreparer.pushResourceFile(SHARED_LIBRARY_APK, "/product/app/SharedLibrary.apk") + .installResourceApk(SHARED_LIBRARY_OVERLAY_APK, SHARED_LIBRARY_OVERLAY_PACKAGE) + .reboot() + .setOverlayEnabled(SHARED_LIBRARY_OVERLAY_PACKAGE, false) + .installResourceApk(TARGET_APK, TARGET_PACKAGE); + + // The shared library resource is not currently overlaid. + retrieveResource(Collections.emptyList(), targetResource, libraryResource); + assertResource(targetResource, 0x12 /* TYPE_INT_BOOLEAN */, "false"); + assertResource(libraryResource, 0x12 /* TYPE_INT_BOOLEAN */, "false"); + + // Overlay the shared library resource. + mPreparer.setOverlayEnabled(SHARED_LIBRARY_OVERLAY_PACKAGE, true); + retrieveResource(getPackagePaths(SHARED_LIBRARY_OVERLAY_PACKAGE), targetResource, + libraryResource); + assertResource(targetResource, 0x12 /* TYPE_INT_BOOLEAN */, "true"); + assertResource(libraryResource, 0x12 /* TYPE_INT_BOOLEAN */, "true"); + } + + @Test + public void testSharedLibraryPreEnabled() throws Exception { + final String targetResource = resourceName(TARGET_PACKAGE, "bool", + "uses_shared_library_overlaid"); + final String libraryResource = resourceName(SHARED_LIBRARY_PACKAGE, "bool", + "shared_library_overlaid"); + + mPreparer.pushResourceFile(SHARED_LIBRARY_APK, "/product/app/SharedLibrary.apk") + .installResourceApk(SHARED_LIBRARY_OVERLAY_APK, SHARED_LIBRARY_OVERLAY_PACKAGE) + .setOverlayEnabled(SHARED_LIBRARY_OVERLAY_PACKAGE, true) + .reboot() + .installResourceApk(TARGET_APK, TARGET_PACKAGE); + + retrieveResource(getPackagePaths(SHARED_LIBRARY_OVERLAY_PACKAGE), targetResource, + libraryResource); + assertResource(targetResource, 0x12 /* TYPE_INT_BOOLEAN */, "true"); + assertResource(libraryResource, 0x12 /* TYPE_INT_BOOLEAN */, "true"); + } + + private void retrieveResource(List requiredOverlayPaths, String... resourceNames) + throws DeviceNotAvailableException { + retrieveResource(TARGET_PACKAGE, requiredOverlayPaths, resourceNames); + } +} diff --git a/core/tests/overlaytests/remount/host/src/com/android/overlaytest/remounted/SystemPreparer.java b/core/tests/overlaytests/remount/host/src/com/android/overlaytest/remounted/SystemPreparer.java new file mode 100644 index 000000000000..7028f2f1d554 --- /dev/null +++ b/core/tests/overlaytests/remount/host/src/com/android/overlaytest/remounted/SystemPreparer.java @@ -0,0 +1,158 @@ +/* + * 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. + */ + +package com.android.overlaytest.remounted; + +import static org.junit.Assert.assertTrue; + +import static java.util.concurrent.TimeUnit.MILLISECONDS; + +import com.android.tradefed.device.DeviceNotAvailableException; +import com.android.tradefed.device.ITestDevice; + +import org.junit.Assert; +import org.junit.rules.ExternalResource; +import org.junit.rules.TemporaryFolder; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.FutureTask; +import java.util.concurrent.TimeoutException; + +class SystemPreparer extends ExternalResource { + private static final long REBOOT_SLEEP_MS = 30000; + private static final long OVERLAY_ENABLE_TIMEOUT_MS = 20000; + + // The paths of the files pushed onto the device through this rule. + private ArrayList mPushedFiles = new ArrayList<>(); + + // The package names of packages installed through this rule. + private ArrayList mInstalledPackages = new ArrayList<>(); + + private final TemporaryFolder mHostTempFolder; + private final DeviceProvider mDeviceProvider; + + SystemPreparer(TemporaryFolder hostTempFolder, DeviceProvider deviceProvider) { + mHostTempFolder = hostTempFolder; + mDeviceProvider = deviceProvider; + } + + /** Copies a file within the host test jar to a path on device. */ + SystemPreparer pushResourceFile(String resourcePath, + String outputPath) throws DeviceNotAvailableException, IOException { + final ITestDevice device = mDeviceProvider.getDevice(); + assertTrue(device.pushFile(copyResourceToTemp(resourcePath), outputPath)); + mPushedFiles.add(outputPath); + return this; + } + + /** Installs an APK within the host test jar onto the device. */ + SystemPreparer installResourceApk(String resourcePath, String packageName) + throws DeviceNotAvailableException, IOException { + final ITestDevice device = mDeviceProvider.getDevice(); + final File tmpFile = copyResourceToTemp(resourcePath); + final String result = device.installPackage(tmpFile, true); + Assert.assertNull(result); + mInstalledPackages.add(packageName); + return this; + } + + /** Sets the enable state of an overlay pacakage. */ + SystemPreparer setOverlayEnabled(String packageName, boolean enabled) + throws ExecutionException, TimeoutException { + final ITestDevice device = mDeviceProvider.getDevice(); + + // Wait for the overlay to change its enabled state. + final FutureTask enabledListener = new FutureTask<>(() -> { + while (true) { + device.executeShellCommand(String.format("cmd overlay %s %s", + enabled ? "enable" : "disable", packageName)); + + final String pattern = (enabled ? "[x]" : "[ ]") + " " + packageName; + if (device.executeShellCommand("cmd overlay list").contains(pattern)) { + return true; + } + } + }); + + final Executor executor = (cmd) -> new Thread(cmd).start(); + executor.execute(enabledListener); + try { + enabledListener.get(OVERLAY_ENABLE_TIMEOUT_MS, MILLISECONDS); + } catch (InterruptedException ignored) { + } + + return this; + } + + /** Restarts the device and waits until after boot is completed. */ + SystemPreparer reboot() throws DeviceNotAvailableException { + final ITestDevice device = mDeviceProvider.getDevice(); + device.executeShellCommand("stop"); + device.executeShellCommand("start"); + try { + // Sleep until the device is ready for test execution. + Thread.sleep(REBOOT_SLEEP_MS); + } catch (InterruptedException ignored) { + } + + return this; + } + + /** Copies a file within the host test jar to a temporary file on the host machine. */ + private File copyResourceToTemp(String resourcePath) throws IOException { + final File tempFile = mHostTempFolder.newFile(resourcePath); + final ClassLoader classLoader = getClass().getClassLoader(); + try (InputStream assetIs = classLoader.getResource(resourcePath).openStream(); + FileOutputStream assetOs = new FileOutputStream(tempFile)) { + if (assetIs == null) { + throw new IllegalStateException("Failed to find resource " + resourcePath); + } + + int b; + while ((b = assetIs.read()) >= 0) { + assetOs.write(b); + } + } + + return tempFile; + } + + /** Removes installed packages and files that were pushed to the device. */ + @Override + protected void after() { + final ITestDevice device = mDeviceProvider.getDevice(); + try { + for (final String file : mPushedFiles) { + device.deleteFile(file); + } + for (final String packageName : mInstalledPackages) { + device.uninstallPackage(packageName); + } + } catch (DeviceNotAvailableException e) { + Assert.fail(e.toString()); + } + } + + interface DeviceProvider { + ITestDevice getDevice(); + } +} diff --git a/core/tests/overlaytests/remount/host/test-apps/SharedLibrary/Android.bp b/core/tests/overlaytests/remount/host/test-apps/SharedLibrary/Android.bp new file mode 100644 index 000000000000..ffb0572c3fd0 --- /dev/null +++ b/core/tests/overlaytests/remount/host/test-apps/SharedLibrary/Android.bp @@ -0,0 +1,19 @@ +// 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. + +android_test_helper_app { + name: "OverlayRemountedTest_SharedLibrary", + sdk_version: "current", + aaptflags: ["--shared-lib"], +} diff --git a/core/tests/overlaytests/remount/host/test-apps/SharedLibrary/AndroidManifest.xml b/core/tests/overlaytests/remount/host/test-apps/SharedLibrary/AndroidManifest.xml new file mode 100644 index 000000000000..06e3f6a99410 --- /dev/null +++ b/core/tests/overlaytests/remount/host/test-apps/SharedLibrary/AndroidManifest.xml @@ -0,0 +1,23 @@ + + + + + + + + \ No newline at end of file diff --git a/core/tests/overlaytests/remount/host/test-apps/SharedLibrary/res/values/overlayable.xml b/core/tests/overlaytests/remount/host/test-apps/SharedLibrary/res/values/overlayable.xml new file mode 100644 index 000000000000..1b06f6d7530b --- /dev/null +++ b/core/tests/overlaytests/remount/host/test-apps/SharedLibrary/res/values/overlayable.xml @@ -0,0 +1,24 @@ + + + + + + + + + + diff --git a/core/tests/overlaytests/remount/host/test-apps/SharedLibrary/res/values/public.xml b/core/tests/overlaytests/remount/host/test-apps/SharedLibrary/res/values/public.xml new file mode 100644 index 000000000000..5b9db163a274 --- /dev/null +++ b/core/tests/overlaytests/remount/host/test-apps/SharedLibrary/res/values/public.xml @@ -0,0 +1,20 @@ + + + + + + \ No newline at end of file diff --git a/core/tests/overlaytests/remount/host/test-apps/SharedLibrary/res/values/values.xml b/core/tests/overlaytests/remount/host/test-apps/SharedLibrary/res/values/values.xml new file mode 100644 index 000000000000..2dc47a7ecf61 --- /dev/null +++ b/core/tests/overlaytests/remount/host/test-apps/SharedLibrary/res/values/values.xml @@ -0,0 +1,20 @@ + + + + + false + diff --git a/core/tests/overlaytests/remount/host/test-apps/SharedLibraryOverlay/Android.bp b/core/tests/overlaytests/remount/host/test-apps/SharedLibraryOverlay/Android.bp new file mode 100644 index 000000000000..0d29aec909d5 --- /dev/null +++ b/core/tests/overlaytests/remount/host/test-apps/SharedLibraryOverlay/Android.bp @@ -0,0 +1,18 @@ +// 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. + +android_test_helper_app { + name: "OverlayRemountedTest_SharedLibraryOverlay", + sdk_version: "current", +} diff --git a/core/tests/overlaytests/remount/host/test-apps/SharedLibraryOverlay/AndroidManifest.xml b/core/tests/overlaytests/remount/host/test-apps/SharedLibraryOverlay/AndroidManifest.xml new file mode 100644 index 000000000000..53a4e61949da --- /dev/null +++ b/core/tests/overlaytests/remount/host/test-apps/SharedLibraryOverlay/AndroidManifest.xml @@ -0,0 +1,23 @@ + + + + + + + \ No newline at end of file diff --git a/core/tests/overlaytests/remount/host/test-apps/SharedLibraryOverlay/res/values/values.xml b/core/tests/overlaytests/remount/host/test-apps/SharedLibraryOverlay/res/values/values.xml new file mode 100644 index 000000000000..f66448a8d991 --- /dev/null +++ b/core/tests/overlaytests/remount/host/test-apps/SharedLibraryOverlay/res/values/values.xml @@ -0,0 +1,20 @@ + + + + + true + diff --git a/core/tests/overlaytests/remount/target/Android.bp b/core/tests/overlaytests/remount/target/Android.bp new file mode 100644 index 000000000000..83f9f28b3f48 --- /dev/null +++ b/core/tests/overlaytests/remount/target/Android.bp @@ -0,0 +1,20 @@ +// Copyright (C) 2018 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. + +android_test_helper_app { + name: "OverlayRemountedTest_Target", + srcs: ["src/**/*.java"], + sdk_version: "test_current", + libs: ["OverlayRemountedTest_SharedLibrary"], +} diff --git a/core/tests/overlaytests/remount/target/AndroidManifest.xml b/core/tests/overlaytests/remount/target/AndroidManifest.xml new file mode 100644 index 000000000000..32fec43593f8 --- /dev/null +++ b/core/tests/overlaytests/remount/target/AndroidManifest.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + diff --git a/core/tests/overlaytests/remount/target/res/values/values.xml b/core/tests/overlaytests/remount/target/res/values/values.xml new file mode 100644 index 000000000000..b5f444a5eb72 --- /dev/null +++ b/core/tests/overlaytests/remount/target/res/values/values.xml @@ -0,0 +1,20 @@ + + + + + @sharedlib:bool/shared_library_overlaid + diff --git a/core/tests/overlaytests/remount/target/src/com/android/overlaytest/remounted/target/ResourceRetrievalRunner.java b/core/tests/overlaytests/remount/target/src/com/android/overlaytest/remounted/target/ResourceRetrievalRunner.java new file mode 100644 index 000000000000..2e4c211d6a0a --- /dev/null +++ b/core/tests/overlaytests/remount/target/src/com/android/overlaytest/remounted/target/ResourceRetrievalRunner.java @@ -0,0 +1,140 @@ +/* + * 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. + */ + +package com.android.overlaytest.remounted.target; + +import android.app.Activity; +import android.app.Instrumentation; +import android.content.res.Resources; +import android.os.Bundle; +import android.util.Log; +import android.util.TypedValue; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.concurrent.Executor; +import java.util.concurrent.FutureTask; +import java.util.concurrent.TimeUnit; + +/** + * An {@link Instrumentation} that retrieves the value of specified resources within the + * application. + **/ +public class ResourceRetrievalRunner extends Instrumentation { + private static final String TAG = ResourceRetrievalRunner.class.getSimpleName(); + + // A list of whitespace separated resource names of which to retrieve the resource values. + private static final String RESOURCE_LIST_TAG = "res"; + + // A list of whitespace separated overlay package paths that must be present before retrieving + // resource values. + private static final String REQUIRED_OVERLAYS_LIST_TAG = "overlays"; + + // The suffixes of the keys returned from the instrumentation. To retrieve the type of a + // resource looked up with the instrumentation, append the {@link #RESOURCES_TYPE_SUFFIX} suffix + // to the end of the name of the resource. For the value of a resource, use + // {@link #RESOURCES_DATA_SUFFIX} instead. + private static final String RESOURCES_TYPE_SUFFIX = "_type"; + private static final String RESOURCES_DATA_SUFFIX = "_data"; + + // The amount of time in seconds to wait for the overlays to be present in the AssetManager. + private static final int OVERLAY_PATH_TIMEOUT = 60; + + private final ArrayList mResourceNames = new ArrayList<>(); + private final ArrayList mOverlayPaths = new ArrayList<>(); + private final Bundle mResult = new Bundle(); + + /** + * Receives the instrumentation arguments and runs the resource retrieval. + * The entry with key {@link #RESOURCE_LIST_TAG} in the {@link Bundle} arguments is a + * whitespace separated string of resource names of which to retrieve the resource values. + * The entry with key {@link #REQUIRED_OVERLAYS_LIST_TAG} in the {@link Bundle} arguments is a + * whitespace separated string of overlay package paths prefixes that must be present before + * retrieving the resource values. + */ + @Override + public void onCreate(Bundle arguments) { + super.onCreate(arguments); + mResourceNames.addAll(Arrays.asList(arguments.getString(RESOURCE_LIST_TAG).split(" "))); + if (arguments.containsKey(REQUIRED_OVERLAYS_LIST_TAG)) { + mOverlayPaths.addAll(Arrays.asList( + arguments.getString(REQUIRED_OVERLAYS_LIST_TAG).split(" "))); + } + start(); + } + + @Override + public void onStart() { + final Resources res = getContext().getResources(); + res.getAssets().setResourceResolutionLoggingEnabled(true); + + if (!mOverlayPaths.isEmpty()) { + Log.d(TAG, String.format("Waiting for overlay paths [%s]", + String.join(",", mOverlayPaths))); + + // Wait for all required overlays to be present in the AssetManager. + final FutureTask overlayListener = new FutureTask<>(() -> { + while (!mOverlayPaths.isEmpty()) { + final String[] apkPaths = res.getAssets().getApkPaths(); + for (String path : apkPaths) { + for (String overlayPath : mOverlayPaths) { + if (path.startsWith(overlayPath)) { + mOverlayPaths.remove(overlayPath); + break; + } + } + } + } + return true; + }); + + try { + final Executor executor = (t) -> new Thread(t).start(); + executor.execute(overlayListener); + overlayListener.get(OVERLAY_PATH_TIMEOUT, TimeUnit.SECONDS); + } catch (Exception e) { + Log.e(TAG, String.format("Failed to wait for required overlays [%s]", + String.join(",", mOverlayPaths)), e); + finish(Activity.RESULT_CANCELED, mResult); + } + } + + // Retrieve the values for each resource passed in. + final TypedValue typedValue = new TypedValue(); + for (final String resourceName : mResourceNames) { + try { + final int resId = res.getIdentifier(resourceName, null, null); + res.getValue(resId, typedValue, true); + Log.d(TAG, String.format("Resolution for 0x%s: %s", Integer.toHexString(resId), + res.getAssets().getLastResourceResolution())); + } catch (Resources.NotFoundException e) { + Log.e(TAG, "Failed to retrieve value for resource " + resourceName, e); + finish(Activity.RESULT_CANCELED, mResult); + } + + putValue(resourceName, typedValue); + } + + finish(Activity.RESULT_OK, mResult); + } + + private void putValue(String resourceName, TypedValue value) { + mResult.putInt(resourceName + RESOURCES_TYPE_SUFFIX, value.type); + final CharSequence textValue = value.coerceToString(); + mResult.putString(resourceName + RESOURCES_DATA_SUFFIX, + textValue == null ? "null" : textValue.toString()); + } +} diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index 2c6be41052e8..8cfd2d8ca696 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -141,7 +141,10 @@ void AssetManager2::BuildDynamicRefTable() { // to take effect. const auto& loaded_idmap = apk_assets->GetLoadedIdmap(); auto target_package_iter = apk_assets_package_ids.find(loaded_idmap->TargetApkPath()); - if (target_package_iter != apk_assets_package_ids.end()) { + if (target_package_iter == apk_assets_package_ids.end()) { + LOG(INFO) << "failed to find target package for overlay " + << loaded_idmap->OverlayApkPath(); + } else { const uint8_t target_package_id = target_package_iter->second; const uint8_t target_idx = package_ids_[target_package_id]; CHECK(target_idx != 0xff) << "overlay added to apk_assets_package_ids but does not" @@ -591,7 +594,7 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri if (resource_resolution_logging_enabled_) { last_resolution_.steps.push_back( Resolution::Step{Resolution::Step::Type::OVERLAID, overlay_result.config.toString(), - &package_group.packages_[0].loaded_package_->GetPackageName()}); + overlay_result.package_name}); } } } diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java index 312dd46fbc73..41653e9a85ba 100644 --- a/services/core/java/android/content/pm/PackageManagerInternal.java +++ b/services/core/java/android/content/pm/PackageManagerInternal.java @@ -41,6 +41,7 @@ import com.android.server.pm.PackageList; import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Collection; import java.util.List; import java.util.function.Consumer; @@ -478,10 +479,12 @@ public abstract class PackageManagerInternal { * will be disabled. Pass in null or an empty list to disable * all overlays. The order of the items is significant if several * overlays modify the same resource. + * @param outUpdatedPackageNames An output list that contains the package names of packages + * affected by the update of enabled overlays. * @return true if all packages names were known by the package manager, false otherwise */ public abstract boolean setEnabledOverlayPackages(int userId, String targetPackageName, - List overlayPackageNames); + List overlayPackageNames, Collection outUpdatedPackageNames); /** * Resolves an activity intent, allowing instant apps to be resolved. diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java index b782ca96ae88..3c31f6a7f0d7 100644 --- a/services/core/java/com/android/server/om/OverlayManagerService.java +++ b/services/core/java/com/android/server/om/OverlayManagerService.java @@ -83,6 +83,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; @@ -925,7 +926,7 @@ public final class OverlayManagerService extends SystemService { /** * Updates the target packages' set of enabled overlays in PackageManager. */ - private void updateOverlayPaths(int userId, List targetPackageNames) { + private ArrayList updateOverlayPaths(int userId, List targetPackageNames) { try { traceBegin(TRACE_TAG_RRO, "OMS#updateOverlayPaths " + targetPackageNames); if (DEBUG) { @@ -955,6 +956,7 @@ public final class OverlayManagerService extends SystemService { } } + final HashSet updatedPackages = new HashSet<>(); final int n = targetPackageNames.size(); for (int i = 0; i < n; i++) { final String targetPackageName = targetPackageNames.get(i); @@ -965,11 +967,13 @@ public final class OverlayManagerService extends SystemService { } if (!pm.setEnabledOverlayPackages( - userId, targetPackageName, pendingChanges.get(targetPackageName))) { + userId, targetPackageName, pendingChanges.get(targetPackageName), + updatedPackages)) { Slog.e(TAG, String.format("Failed to change enabled overlays for %s user %d", targetPackageName, userId)); } } + return new ArrayList<>(updatedPackages); } finally { traceEnd(TRACE_TAG_RRO); } @@ -980,10 +984,10 @@ public final class OverlayManagerService extends SystemService { } private void updateAssets(final int userId, List targetPackageNames) { - updateOverlayPaths(userId, targetPackageNames); final IActivityManager am = ActivityManager.getService(); try { - am.scheduleApplicationInfoChanged(targetPackageNames, userId); + final ArrayList updatedPaths = updateOverlayPaths(userId, targetPackageNames); + am.scheduleApplicationInfoChanged(updatedPaths, userId); } catch (RemoteException e) { // Intentionally left empty. } diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java index 019c9528f8ab..9623542a2900 100644 --- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java +++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java @@ -685,7 +685,7 @@ final class OverlayManagerServiceImpl { // Static RROs targeting to "android", ie framework-res.apk, are handled by native layers. if (targetPackage != null && overlayPackage != null && !("android".equals(targetPackageName) - && overlayPackage.isStaticOverlayPackage())) { + && overlayPackage.isStaticOverlayPackage())) { mIdmapManager.createIdmap(targetPackage, overlayPackage, userId); } @@ -703,9 +703,9 @@ final class OverlayManagerServiceImpl { if (currentState != newState) { if (DEBUG) { Slog.d(TAG, String.format("%s:%d: %s -> %s", - overlayPackageName, userId, - OverlayInfo.stateToString(currentState), - OverlayInfo.stateToString(newState))); + overlayPackageName, userId, + OverlayInfo.stateToString(currentState), + OverlayInfo.stateToString(newState))); } modified |= mSettings.setState(overlayPackageName, userId, newState); } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 6bd9c4847a77..d29ec4cae00c 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -11595,6 +11595,23 @@ public class PackageManagerService extends IPackageManager.Stub return false; } SharedLibraryInfo libraryInfo = versionedLib.valueAt(libIdx); + + // Remove the shared library overlays from its dependent packages. + for (int currentUserId : UserManagerService.getInstance().getUserIds()) { + final List dependents = getPackagesUsingSharedLibraryLPr( + libraryInfo, 0, currentUserId); + if (dependents == null) { + continue; + } + for (VersionedPackage dependentPackage : dependents) { + final PackageSetting ps = mSettings.mPackages.get( + dependentPackage.getPackageName()); + if (ps != null) { + ps.setOverlayPathsForLibrary(libraryInfo.getName(), null, currentUserId); + } + } + } + versionedLib.remove(version); if (versionedLib.size() <= 0) { mSharedLibraries.remove(name); @@ -15200,6 +15217,29 @@ public class PackageManagerService extends IPackageManager.Stub // upcoming call to mSettings.writeLPr(). } } + + // Retrieve the overlays for shared libraries of the package. + if (pkg.getUsesLibraryInfos() != null) { + for (SharedLibraryInfo sharedLib : pkg.getUsesLibraryInfos()) { + for (int currentUserId : UserManagerService.getInstance().getUserIds()) { + if (!sharedLib.isDynamic()) { + // TODO(146804378): Support overlaying static shared libraries + continue; + } + final PackageSetting libPs = mSettings.mPackages.get( + sharedLib.getPackageName()); + if (libPs == null) { + continue; + } + final String[] overlayPaths = libPs.getOverlayPaths(currentUserId); + if (overlayPaths != null) { + ps.setOverlayPathsForLibrary(sharedLib.getName(), + Arrays.asList(overlayPaths), currentUserId); + } + } + } + } + // It's implied that when a user requests installation, they want the app to be // installed and enabled. if (userId != UserHandle.USER_ALL) { @@ -23226,9 +23266,11 @@ public class PackageManagerService extends IPackageManager.Stub @Override public boolean setEnabledOverlayPackages(int userId, @NonNull String targetPackageName, - @Nullable List overlayPackageNames) { + @Nullable List overlayPackageNames, + @NonNull Collection outUpdatedPackageNames) { synchronized (mLock) { - if (targetPackageName == null || mPackages.get(targetPackageName) == null) { + final AndroidPackage targetPkg = mPackages.get(targetPackageName); + if (targetPackageName == null || targetPkg == null) { Slog.e(TAG, "failed to find package " + targetPackageName); return false; } @@ -23247,8 +23289,41 @@ public class PackageManagerService extends IPackageManager.Stub } } + ArraySet updatedPackageNames = null; + if (targetPkg.getLibraryNames() != null) { + // Set the overlay paths for dependencies of the shared library. + updatedPackageNames = new ArraySet<>(); + for (String libName : targetPkg.getLibraryNames()) { + final SharedLibraryInfo info = getSharedLibraryInfoLPr(libName, + SharedLibraryInfo.VERSION_UNDEFINED); + if (info == null) { + continue; + } + final List dependents = getPackagesUsingSharedLibraryLPr( + info, 0, userId); + if (dependents == null) { + continue; + } + for (VersionedPackage dependent : dependents) { + final PackageSetting ps = mSettings.mPackages.get( + dependent.getPackageName()); + if (ps == null) { + continue; + } + ps.setOverlayPathsForLibrary(libName, overlayPaths, userId); + updatedPackageNames.add(dependent.getPackageName()); + } + } + } + final PackageSetting ps = mSettings.mPackages.get(targetPackageName); ps.setOverlayPaths(overlayPaths, userId); + + outUpdatedPackageNames.add(targetPackageName); + if (updatedPackageNames != null) { + outUpdatedPackageNames.addAll(updatedPackageNames); + } + return true; } } diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java index 0c0b93b18f84..f1ac0afa5dfd 100644 --- a/services/core/java/com/android/server/pm/PackageSettingBase.java +++ b/services/core/java/com/android/server/pm/PackageSettingBase.java @@ -41,6 +41,7 @@ import com.android.internal.util.Preconditions; import java.io.File; import java.util.Arrays; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Set; @@ -312,12 +313,21 @@ public abstract class PackageSettingBase extends SettingBase { } void setOverlayPaths(List overlayPaths, int userId) { - modifyUserState(userId).overlayPaths = overlayPaths == null ? null : - overlayPaths.toArray(new String[overlayPaths.size()]); + modifyUserState(userId).setOverlayPaths(overlayPaths == null ? null : + overlayPaths.toArray(new String[overlayPaths.size()])); } String[] getOverlayPaths(int userId) { - return readUserState(userId).overlayPaths; + return readUserState(userId).getOverlayPaths(); + } + + void setOverlayPathsForLibrary(String libName, List overlayPaths, int userId) { + modifyUserState(userId).setSharedLibraryOverlayPaths(libName, + overlayPaths == null ? null : overlayPaths.toArray(new String[0])); + } + + Map getOverlayPathsForLibrary(int userId) { + return readUserState(userId).getSharedLibraryOverlayPaths(); } /** Only use for testing. Do NOT use in production code. */ diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index f9a336166825..c799a52559c4 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -4725,6 +4725,22 @@ public final class Settings { } } + Map sharedLibraryOverlayPaths = + ps.getOverlayPathsForLibrary(user.id); + if (sharedLibraryOverlayPaths != null) { + for (Map.Entry libOverlayPaths : + sharedLibraryOverlayPaths.entrySet()) { + if (libOverlayPaths.getValue() == null) { + continue; + } + pw.print(prefix); pw.print(" "); + pw.print(libOverlayPaths.getKey()); pw.println(" overlay paths:"); + for (String path : libOverlayPaths.getValue()) { + pw.print(prefix); pw.print(" "); pw.println(path); + } + } + } + String lastDisabledAppCaller = ps.getLastDisabledAppCaller(user.id); if (lastDisabledAppCaller != null) { pw.print(prefix); pw.print(" lastDisabledCaller: "); diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp index 4555caafb478..5b6935bafe71 100644 --- a/tools/aapt2/cmd/Link.cpp +++ b/tools/aapt2/cmd/Link.cpp @@ -895,7 +895,7 @@ class Linker { // android:versionCode from the framework AndroidManifest.xml. ExtractCompileSdkVersions(asset_source->GetAssetManager()); } - } else if (asset_source->IsPackageDynamic(entry.first)) { + } else if (asset_source->IsPackageDynamic(entry.first, entry.second)) { final_table_.included_packages_[entry.first] = entry.second; } } diff --git a/tools/aapt2/process/SymbolTable.cpp b/tools/aapt2/process/SymbolTable.cpp index 83e20b5833b9..897fa80ffedb 100644 --- a/tools/aapt2/process/SymbolTable.cpp +++ b/tools/aapt2/process/SymbolTable.cpp @@ -245,7 +245,8 @@ std::map AssetManagerSymbolSource::GetAssignedPackageIds() return package_map; } -bool AssetManagerSymbolSource::IsPackageDynamic(uint32_t packageId) const { +bool AssetManagerSymbolSource::IsPackageDynamic(uint32_t packageId, + const std::string& package_name) const { if (packageId == 0) { return true; } @@ -253,7 +254,7 @@ bool AssetManagerSymbolSource::IsPackageDynamic(uint32_t packageId) const { for (const std::unique_ptr& assets : apk_assets_) { for (const std::unique_ptr& loaded_package : assets->GetLoadedArsc()->GetPackages()) { - if (packageId == loaded_package->GetPackageId() && loaded_package->IsDynamic()) { + if (package_name == loaded_package->GetPackageName() && loaded_package->IsDynamic()) { return true; } } @@ -328,12 +329,12 @@ std::unique_ptr AssetManagerSymbolSource::FindByName( bool found = false; ResourceId res_id = 0; uint32_t type_spec_flags; + ResourceName real_name; // There can be mangled resources embedded within other packages. Here we will // look into each package and look-up the mangled name until we find the resource. asset_manager_.ForEachPackage([&](const std::string& package_name, uint8_t id) -> bool { - ResourceName real_name(name.package, name.type, name.entry); - + real_name = ResourceName(name.package, name.type, name.entry); if (package_name != name.package) { real_name.entry = mangled_entry; real_name.package = package_name; @@ -353,12 +354,12 @@ std::unique_ptr AssetManagerSymbolSource::FindByName( } std::unique_ptr s; - if (name.type == ResourceType::kAttr) { + if (real_name.type == ResourceType::kAttr) { s = LookupAttributeInTable(asset_manager_, res_id); } else { s = util::make_unique(); s->id = res_id; - s->is_dynamic = IsPackageDynamic(ResourceId(res_id).package_id()); + s->is_dynamic = IsPackageDynamic(ResourceId(res_id).package_id(), real_name.package); } if (s) { @@ -406,7 +407,7 @@ std::unique_ptr AssetManagerSymbolSource::FindById( } else { s = util::make_unique(); s->id = id; - s->is_dynamic = IsPackageDynamic(ResourceId(id).package_id()); + s->is_dynamic = IsPackageDynamic(ResourceId(id).package_id(), name.package); } if (s) { diff --git a/tools/aapt2/process/SymbolTable.h b/tools/aapt2/process/SymbolTable.h index 6997cd6714a8..06eaf63ad442 100644 --- a/tools/aapt2/process/SymbolTable.h +++ b/tools/aapt2/process/SymbolTable.h @@ -194,7 +194,7 @@ class AssetManagerSymbolSource : public ISymbolSource { bool AddAssetPath(const android::StringPiece& path); std::map GetAssignedPackageIds() const; - bool IsPackageDynamic(uint32_t packageId) const; + bool IsPackageDynamic(uint32_t packageId, const std::string& package_name) const; std::unique_ptr FindByName( const ResourceName& name) override;