Loading core/java/android/app/AppOpsManager.java +1 −3 Original line number Diff line number Diff line Loading @@ -2981,9 +2981,7 @@ public class AppOpsManager { new AppOpInfo.Builder(OP_ESTABLISH_VPN_MANAGER, OPSTR_ESTABLISH_VPN_MANAGER, "ESTABLISH_VPN_MANAGER").setDefaultMode(AppOpsManager.MODE_ALLOWED).build(), new AppOpInfo.Builder(OP_ACCESS_RESTRICTED_SETTINGS, OPSTR_ACCESS_RESTRICTED_SETTINGS, "ACCESS_RESTRICTED_SETTINGS").setDefaultMode( android.permission.flags.Flags.enhancedConfirmationModeApisEnabled() ? MODE_DEFAULT : MODE_ALLOWED) "ACCESS_RESTRICTED_SETTINGS").setDefaultMode(AppOpsManager.MODE_ALLOWED) .setDisableReset(true).setRestrictRead(true).build(), new AppOpInfo.Builder(OP_RECEIVE_AMBIENT_TRIGGER_AUDIO, OPSTR_RECEIVE_AMBIENT_TRIGGER_AUDIO, "RECEIVE_SOUNDTRIGGER_AUDIO").setDefaultMode(AppOpsManager.MODE_ALLOWED) Loading core/java/android/app/ResourcesManager.java +167 −95 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ import android.content.res.AssetManager; import android.content.res.CompatResources; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.content.res.Flags; import android.content.res.Resources; import android.content.res.ResourcesImpl; import android.content.res.ResourcesKey; Loading Loading @@ -138,16 +139,22 @@ public class ResourcesManager { private final ArrayMap<String, SharedLibraryAssets> mSharedLibAssetsMap = new ArrayMap<>(); @VisibleForTesting public ArrayMap<String, SharedLibraryAssets> getRegisteredResourcePaths() { return mSharedLibAssetsMap; } /** * The internal function to register the resources paths of a package (e.g. a shared library). * This will collect the package resources' paths from its ApplicationInfo and add them to all * existing and future contexts while the application is running. */ public void registerResourcePaths(@NonNull String uniqueId, @NonNull ApplicationInfo appInfo) { SharedLibraryAssets sharedLibAssets = new SharedLibraryAssets(appInfo.sourceDir, appInfo.splitSourceDirs, appInfo.sharedLibraryFiles, appInfo.resourceDirs, appInfo.overlayPaths); if (!Flags.registerResourcePaths()) { return; } final var sharedLibAssets = new SharedLibraryAssets(appInfo); synchronized (mLock) { if (mSharedLibAssetsMap.containsKey(uniqueId)) { Slog.v(TAG, "Package resources' paths for uniqueId: " + uniqueId Loading @@ -155,18 +162,37 @@ public class ResourcesManager { return; } mSharedLibAssetsMap.put(uniqueId, sharedLibAssets); appendLibAssetsLocked(sharedLibAssets.getAllAssetPaths()); Slog.v(TAG, "The following resources' paths have been added: " + Arrays.toString(sharedLibAssets.getAllAssetPaths())); appendLibAssetsLocked(sharedLibAssets); Slog.v(TAG, "The following library key has been added: " + sharedLibAssets.getResourcesKey()); } } /** * Apply the registered library paths to the passed impl object * @return the hash code for the current version of the registered paths */ public int updateResourceImplWithRegisteredLibs(@NonNull ResourcesImpl impl) { if (!Flags.registerResourcePaths()) { return 0; } final var collector = new PathCollector(null); final int size = mSharedLibAssetsMap.size(); for (int i = 0; i < size; i++) { final var libraryKey = mSharedLibAssetsMap.valueAt(i).getResourcesKey(); collector.appendKey(libraryKey); } impl.getAssets().addPresetApkKeys(extractApkKeys(collector.collectedKey())); return size; } private static class ApkKey { public static class ApkKey { public final String path; public final boolean sharedLib; public final boolean overlay; ApkKey(String path, boolean sharedLib, boolean overlay) { public ApkKey(String path, boolean sharedLib, boolean overlay) { this.path = path; this.sharedLib = sharedLib; this.overlay = overlay; Loading @@ -190,6 +216,12 @@ public class ResourcesManager { return this.path.equals(other.path) && this.sharedLib == other.sharedLib && this.overlay == other.overlay; } @Override public String toString() { return "ApkKey[" + (sharedLib ? "lib" : "app") + (overlay ? ", overlay" : "") + ": " + path + "]"; } } /** Loading Loading @@ -505,7 +537,10 @@ public class ResourcesManager { return "/data/resource-cache/" + path.substring(1).replace('/', '@') + "@idmap"; } private @NonNull ApkAssets loadApkAssets(@NonNull final ApkKey key) throws IOException { /** * Loads the ApkAssets object for the passed key, or picks the one from the cache if available. */ public @NonNull ApkAssets loadApkAssets(@NonNull final ApkKey key) throws IOException { ApkAssets apkAssets; // Optimistically check if this ApkAssets exists somewhere else. Loading Loading @@ -747,8 +782,8 @@ public class ResourcesManager { private @Nullable ResourcesImpl findOrCreateResourcesImplForKeyLocked( @NonNull ResourcesKey key, @Nullable ApkAssetsSupplier apkSupplier) { ResourcesImpl impl = findResourcesImplForKeyLocked(key); // ResourcesImpl also need to be recreated if its shared library count is not up-to-date. if (impl == null || impl.getSharedLibCount() != mSharedLibAssetsMap.size()) { // ResourcesImpl also need to be recreated if its shared library hash is not up-to-date. if (impl == null || impl.getAppliedSharedLibsHash() != mSharedLibAssetsMap.size()) { impl = createResourcesImpl(key, apkSupplier); if (impl != null) { mResourceImpls.put(key, new WeakReference<>(impl)); Loading Loading @@ -1533,55 +1568,108 @@ public class ResourcesManager { } } private void appendLibAssetsLocked(String[] libAssets) { synchronized (mLock) { // Record which ResourcesImpl need updating // (and what ResourcesKey they should update to). final ArrayMap<ResourcesImpl, ResourcesKey> updatedResourceKeys = new ArrayMap<>(); /** * A utility class to collect resources paths into a ResourcesKey object: * - Separates the libraries and the overlays into different sets as those are loaded in * different ways. * - Allows to start with an existing original key object, and copies all non-path related * properties into the final one. * - Preserves the path order while dropping all duplicates in an efficient manner. */ private static class PathCollector { public final ResourcesKey originalKey; public final ArrayList<String> orderedLibs = new ArrayList<>(); public final ArraySet<String> libsSet = new ArraySet<>(); public final ArrayList<String> orderedOverlays = new ArrayList<>(); public final ArraySet<String> overlaysSet = new ArraySet<>(); static void appendNewPath(@NonNull String path, @NonNull ArraySet<String> uniquePaths, @NonNull ArrayList<String> orderedPaths) { if (uniquePaths.add(path)) { orderedPaths.add(path); } } static void appendAllNewPaths(@Nullable String[] paths, @NonNull ArraySet<String> uniquePaths, @NonNull ArrayList<String> orderedPaths) { if (paths == null) return; for (int i = 0, size = paths.length; i < size; i++) { appendNewPath(paths[i], uniquePaths, orderedPaths); } } PathCollector(@Nullable ResourcesKey original) { originalKey = original; if (originalKey != null) { appendKey(originalKey); } } public void appendKey(@NonNull ResourcesKey key) { appendAllNewPaths(key.mLibDirs, libsSet, orderedLibs); appendAllNewPaths(key.mOverlayPaths, overlaysSet, orderedOverlays); } boolean isSameAsOriginal() { if (originalKey == null) { return orderedLibs.isEmpty() && orderedOverlays.isEmpty(); } return ((originalKey.mLibDirs == null && orderedLibs.isEmpty()) || (originalKey.mLibDirs != null && originalKey.mLibDirs.length == orderedLibs.size())) && ((originalKey.mOverlayPaths == null && orderedOverlays.isEmpty()) || (originalKey.mOverlayPaths != null && originalKey.mOverlayPaths.length == orderedOverlays.size())); } @NonNull ResourcesKey collectedKey() { return new ResourcesKey( originalKey == null ? null : originalKey.mResDir, originalKey == null ? null : originalKey.mSplitResDirs, orderedOverlays.toArray(new String[0]), orderedLibs.toArray(new String[0]), originalKey == null ? 0 : originalKey.mDisplayId, originalKey == null ? null : originalKey.mOverrideConfiguration, originalKey == null ? null : originalKey.mCompatInfo, originalKey == null ? null : originalKey.mLoaders); } } /** * Takes the original resources key and the one containing a set of library paths and overlays * to append, and combines them together. In case when the original key already contains all * those paths this function returns null, otherwise it makes a new ResourcesKey object. */ private @Nullable ResourcesKey createNewResourceKeyIfNeeded( @NonNull ResourcesKey original, @NonNull ResourcesKey library) { final var collector = new PathCollector(original); collector.appendKey(library); return collector.isSameAsOriginal() ? null : collector.collectedKey(); } /** * Append the newly registered shared library asset paths to all existing resources objects. */ private void appendLibAssetsLocked(@NonNull SharedLibraryAssets libAssets) { // Record the ResourcesImpl's that need updating, and what ResourcesKey they should // update to. final ArrayMap<ResourcesImpl, ResourcesKey> updatedResourceKeys = new ArrayMap<>(); final int implCount = mResourceImpls.size(); for (int i = 0; i < implCount; i++) { final ResourcesKey key = mResourceImpls.keyAt(i); final WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(i); final ResourcesImpl impl = weakImplRef != null ? weakImplRef.get() : null; if (impl == null) { Slog.w(TAG, "Found a ResourcesImpl which is null, skip it and continue to " + "append shared library assets for next ResourcesImpl."); Slog.w(TAG, "Found a null ResourcesImpl, skipped."); continue; } var newDirs = new ArrayList<String>(); var dirsSet = new ArraySet<String>(); if (key.mLibDirs != null) { final int dirsLength = key.mLibDirs.length; for (int k = 0; k < dirsLength; k++) { newDirs.add(key.mLibDirs[k]); dirsSet.add(key.mLibDirs[k]); } } final int assetsLength = libAssets.length; for (int j = 0; j < assetsLength; j++) { if (dirsSet.add(libAssets[j])) { newDirs.add(libAssets[j]); } } String[] newLibAssets = newDirs.toArray(new String[0]); if (!Arrays.equals(newLibAssets, key.mLibDirs)) { updatedResourceKeys.put(impl, new ResourcesKey( key.mResDir, key.mSplitResDirs, key.mOverlayPaths, newLibAssets, key.mDisplayId, key.mOverrideConfiguration, key.mCompatInfo, key.mLoaders)); final var newKey = createNewResourceKeyIfNeeded(key, libAssets.getResourcesKey()); if (newKey != null) { updatedResourceKeys.put(impl, newKey); } } redirectAllResourcesToNewImplLocked(updatedResourceKeys); } } private void applyNewResourceDirsLocked(@Nullable final String[] oldSourceDirs, @NonNull final ApplicationInfo appInfo) { Loading Loading @@ -1718,8 +1806,9 @@ public class ResourcesManager { } } // Another redirect function which will loop through all Resources and reload ResourcesImpl // if it needs a shared library asset paths update. // Another redirect function which will loop through all Resources in the process, even the ones // the app created outside of the regular Android Runtime, and reload their ResourcesImpl if it // needs a shared library asset paths update. private void redirectAllResourcesToNewImplLocked( @NonNull final ArrayMap<ResourcesImpl, ResourcesKey> updatedResourceKeys) { cleanupReferences(mAllResourceReferences, mAllResourceReferencesQueue); Loading Loading @@ -1830,52 +1919,35 @@ public class ResourcesManager { } } @VisibleForTesting public static class SharedLibraryAssets { private final String[] mAssetPaths; SharedLibraryAssets(String sourceDir, String[] splitSourceDirs, String[] sharedLibraryFiles, String[] resourceDirs, String[] overlayPaths) { mAssetPaths = collectAssetPaths(sourceDir, splitSourceDirs, sharedLibraryFiles, resourceDirs, overlayPaths); } private @NonNull String[] collectAssetPaths(String sourceDir, String[] splitSourceDirs, String[] sharedLibraryFiles, String[] resourceDirs, String[] overlayPaths) { final String[][] inputLists = { splitSourceDirs, sharedLibraryFiles, resourceDirs, overlayPaths }; final ArraySet<String> assetPathSet = new ArraySet<>(); final List<String> assetPathList = new ArrayList<>(); if (sourceDir != null) { assetPathSet.add(sourceDir); assetPathList.add(sourceDir); } for (int i = 0; i < inputLists.length; i++) { if (inputLists[i] != null) { for (int j = 0; j < inputLists[i].length; j++) { if (assetPathSet.add(inputLists[i][j])) { assetPathList.add(inputLists[i][j]); } } } } return assetPathList.toArray(new String[0]); private final ResourcesKey mResourcesKey; private SharedLibraryAssets(ApplicationInfo appInfo) { // We're loading all library's files as shared libs, regardless where they are in // its own ApplicationInfo. final var collector = new PathCollector(null); PathCollector.appendNewPath(appInfo.sourceDir, collector.libsSet, collector.orderedLibs); PathCollector.appendAllNewPaths(appInfo.splitSourceDirs, collector.libsSet, collector.orderedLibs); PathCollector.appendAllNewPaths(appInfo.sharedLibraryFiles, collector.libsSet, collector.orderedLibs); PathCollector.appendAllNewPaths(appInfo.resourceDirs, collector.overlaysSet, collector.orderedOverlays); PathCollector.appendAllNewPaths(appInfo.overlayPaths, collector.overlaysSet, collector.orderedOverlays); mResourcesKey = collector.collectedKey(); } /** * @return all the asset paths of this collected in this class. * @return the resources key for this library assets. */ public @NonNull String[] getAllAssetPaths() { return mAssetPaths; public @NonNull ResourcesKey getResourcesKey() { return mResourcesKey; } } public @NonNull ArrayMap<String, SharedLibraryAssets> getSharedLibAssetsMap() { return new ArrayMap<>(mSharedLibAssetsMap); } /** * Add all resources references to the list which is designed to help to append shared library * asset paths. This is invoked in Resources constructor to include all Resources instances. Loading core/java/android/content/res/AssetManager.java +91 −33 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package android.content.res; import static android.content.res.Resources.ID_NULL; import static android.app.ResourcesManager.ApkKey; import android.annotation.AnyRes; import android.annotation.ArrayRes; Loading @@ -26,12 +27,14 @@ import android.annotation.Nullable; import android.annotation.StringRes; import android.annotation.StyleRes; import android.annotation.TestApi; import android.app.ResourcesManager; import android.compat.annotation.UnsupportedAppUsage; import android.content.pm.ActivityInfo; import android.content.res.Configuration.NativeConfig; import android.content.res.loader.ResourcesLoader; import android.os.Build; import android.os.ParcelFileDescriptor; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; import android.util.SparseArray; Loading Loading @@ -264,7 +267,7 @@ public final class AssetManager implements AutoCloseable { } sSystemApkAssetsSet = new ArraySet<>(apkAssets); sSystemApkAssets = apkAssets.toArray(new ApkAssets[apkAssets.size()]); sSystemApkAssets = apkAssets.toArray(new ApkAssets[0]); if (sSystem == null) { sSystem = new AssetManager(true /*sentinel*/); } Loading Loading @@ -448,7 +451,7 @@ public final class AssetManager implements AutoCloseable { @Deprecated @UnsupportedAppUsage public int addAssetPath(String path) { return addAssetPathInternal(path, false /*overlay*/, false /*appAsLib*/); return addAssetPathInternal(List.of(new ApkKey(path, false, false)), false); } /** Loading @@ -458,7 +461,7 @@ public final class AssetManager implements AutoCloseable { @Deprecated @UnsupportedAppUsage public int addAssetPathAsSharedLibrary(String path) { return addAssetPathInternal(path, false /*overlay*/, true /*appAsLib*/); return addAssetPathInternal(List.of(new ApkKey(path, true, false)), false); } /** Loading @@ -468,55 +471,110 @@ public final class AssetManager implements AutoCloseable { @Deprecated @UnsupportedAppUsage public int addOverlayPath(String path) { return addAssetPathInternal(path, true /*overlay*/, false /*appAsLib*/); return addAssetPathInternal(List.of(new ApkKey(path, false, true)), false); } /** * @hide */ public void addSharedLibraryPaths(@NonNull String[] paths) { final int length = paths.length; for (int i = 0; i < length; i++) { addAssetPathInternal(paths[i], false, true); public void addPresetApkKeys(@NonNull List<ApkKey> keys) { addAssetPathInternal(keys, true); } private int addAssetPathInternal(List<ApkKey> apkKeys, boolean presetAssets) { Objects.requireNonNull(apkKeys, "apkKeys"); if (apkKeys.isEmpty()) { return 0; } private int addAssetPathInternal(String path, boolean overlay, boolean appAsLib) { Objects.requireNonNull(path, "path"); synchronized (this) { ensureOpenLocked(); final int count = mApkAssets.length; // See if we already have it loaded. for (int i = 0; i < count; i++) { if (mApkAssets[i].getAssetPath().equals(path)) { return i + 1; } // See if we already have some of the apkKeys loaded. final int originalAssetsCount = mApkAssets.length; // Getting an assets' path is a relatively expensive operation, cache them. final ArrayMap<String, Integer> assetPaths = new ArrayMap<>(originalAssetsCount); for (int i = 0; i < originalAssetsCount; i++) { assetPaths.put(mApkAssets[i].getAssetPath(), i); } final ApkAssets assets; try { if (overlay) { // TODO(b/70343104): This hardcoded path will be removed once // addAssetPathInternal is deleted. final String idmapPath = "/data/resource-cache/" + path.substring(1).replace('/', '@') + "@idmap"; assets = ApkAssets.loadOverlayFromPath(idmapPath, 0 /* flags */); final var newKeys = new ArrayList<ApkKey>(apkKeys.size()); int lastFoundIndex = -1; for (int i = 0, pathsSize = apkKeys.size(); i < pathsSize; i++) { final var key = apkKeys.get(i); final var index = assetPaths.get(key.path); if (index == null) { newKeys.add(key); } else { assets = ApkAssets.loadFromPath(path, appAsLib ? ApkAssets.PROPERTY_DYNAMIC : 0); lastFoundIndex = index; } } catch (IOException e) { return 0; } if (newKeys.isEmpty()) { return lastFoundIndex + 1; } mApkAssets = Arrays.copyOf(mApkAssets, count + 1); mApkAssets[count] = assets; nativeSetApkAssets(mObject, mApkAssets, true, false); final var newAssets = loadAssets(newKeys); if (newAssets.isEmpty()) { return 0; } mApkAssets = makeNewAssetsArrayLocked(newAssets); nativeSetApkAssets(mObject, mApkAssets, true, presetAssets); invalidateCachesLocked(-1); return count + 1; return originalAssetsCount + 1; } } /** * Insert the new assets preserving the correct order: all non-loader assets go before all * of the loader assets. */ @GuardedBy("this") private @NonNull ApkAssets[] makeNewAssetsArrayLocked( @NonNull ArrayList<ApkAssets> newNonLoaderAssets) { final int originalAssetsCount = mApkAssets.length; int firstLoaderIndex = originalAssetsCount; for (int i = 0; i < originalAssetsCount; i++) { if (mApkAssets[i].isForLoader()) { firstLoaderIndex = i; break; } } final int newAssetsSize = newNonLoaderAssets.size(); final var newAssetsArray = new ApkAssets[originalAssetsCount + newAssetsSize]; if (firstLoaderIndex > 0) { // This should always be true, but who knows... System.arraycopy(mApkAssets, 0, newAssetsArray, 0, firstLoaderIndex); } for (int i = 0; i < newAssetsSize; i++) { newAssetsArray[firstLoaderIndex + i] = newNonLoaderAssets.get(i); } if (originalAssetsCount > firstLoaderIndex) { System.arraycopy( mApkAssets, firstLoaderIndex, newAssetsArray, firstLoaderIndex + newAssetsSize, originalAssetsCount - firstLoaderIndex); } return newAssetsArray; } private static @NonNull ArrayList<ApkAssets> loadAssets(@NonNull ArrayList<ApkKey> keys) { final int pathsSize = keys.size(); final var loadedAssets = new ArrayList<ApkAssets>(pathsSize); final var resourcesManager = ResourcesManager.getInstance(); for (int i = 0; i < pathsSize; i++) { final var key = keys.get(i); try { // ResourcesManager has a cache of loaded assets, ensuring we don't open the same // file repeatedly, which is useful for the common overlays and registered // shared libraries. loadedAssets.add(resourcesManager.loadApkAssets(key)); } catch (IOException e) { Log.w(TAG, "Failed to load asset, key = " + key, e); } } return loadedAssets; } /** @hide */ Loading core/java/android/content/res/ResourcesImpl.java +8 −18 File changed.Preview size limit exceeded, changes collapsed. Show changes core/java/android/credentials/selection/IntentFactory.java +11 −1 Original line number Diff line number Diff line Loading @@ -232,7 +232,17 @@ public class IntentFactory { oemComponentName, PackageManager.ComponentInfoFlags.of( PackageManager.MATCH_SYSTEM_ONLY)); if (info.enabled && info.exported) { boolean oemComponentEnabled = info.enabled; int runtimeComponentEnabledState = context.getPackageManager() .getComponentEnabledSetting(oemComponentName); if (runtimeComponentEnabledState == PackageManager .COMPONENT_ENABLED_STATE_ENABLED) { oemComponentEnabled = true; } else if (runtimeComponentEnabledState == PackageManager .COMPONENT_ENABLED_STATE_DISABLED) { oemComponentEnabled = false; } if (oemComponentEnabled && info.exported) { intentResultBuilder.setOemUiUsageStatus(IntentCreationResult .OemUiUsageStatus.SUCCESS); Slog.i(TAG, Loading Loading
core/java/android/app/AppOpsManager.java +1 −3 Original line number Diff line number Diff line Loading @@ -2981,9 +2981,7 @@ public class AppOpsManager { new AppOpInfo.Builder(OP_ESTABLISH_VPN_MANAGER, OPSTR_ESTABLISH_VPN_MANAGER, "ESTABLISH_VPN_MANAGER").setDefaultMode(AppOpsManager.MODE_ALLOWED).build(), new AppOpInfo.Builder(OP_ACCESS_RESTRICTED_SETTINGS, OPSTR_ACCESS_RESTRICTED_SETTINGS, "ACCESS_RESTRICTED_SETTINGS").setDefaultMode( android.permission.flags.Flags.enhancedConfirmationModeApisEnabled() ? MODE_DEFAULT : MODE_ALLOWED) "ACCESS_RESTRICTED_SETTINGS").setDefaultMode(AppOpsManager.MODE_ALLOWED) .setDisableReset(true).setRestrictRead(true).build(), new AppOpInfo.Builder(OP_RECEIVE_AMBIENT_TRIGGER_AUDIO, OPSTR_RECEIVE_AMBIENT_TRIGGER_AUDIO, "RECEIVE_SOUNDTRIGGER_AUDIO").setDefaultMode(AppOpsManager.MODE_ALLOWED) Loading
core/java/android/app/ResourcesManager.java +167 −95 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ import android.content.res.AssetManager; import android.content.res.CompatResources; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.content.res.Flags; import android.content.res.Resources; import android.content.res.ResourcesImpl; import android.content.res.ResourcesKey; Loading Loading @@ -138,16 +139,22 @@ public class ResourcesManager { private final ArrayMap<String, SharedLibraryAssets> mSharedLibAssetsMap = new ArrayMap<>(); @VisibleForTesting public ArrayMap<String, SharedLibraryAssets> getRegisteredResourcePaths() { return mSharedLibAssetsMap; } /** * The internal function to register the resources paths of a package (e.g. a shared library). * This will collect the package resources' paths from its ApplicationInfo and add them to all * existing and future contexts while the application is running. */ public void registerResourcePaths(@NonNull String uniqueId, @NonNull ApplicationInfo appInfo) { SharedLibraryAssets sharedLibAssets = new SharedLibraryAssets(appInfo.sourceDir, appInfo.splitSourceDirs, appInfo.sharedLibraryFiles, appInfo.resourceDirs, appInfo.overlayPaths); if (!Flags.registerResourcePaths()) { return; } final var sharedLibAssets = new SharedLibraryAssets(appInfo); synchronized (mLock) { if (mSharedLibAssetsMap.containsKey(uniqueId)) { Slog.v(TAG, "Package resources' paths for uniqueId: " + uniqueId Loading @@ -155,18 +162,37 @@ public class ResourcesManager { return; } mSharedLibAssetsMap.put(uniqueId, sharedLibAssets); appendLibAssetsLocked(sharedLibAssets.getAllAssetPaths()); Slog.v(TAG, "The following resources' paths have been added: " + Arrays.toString(sharedLibAssets.getAllAssetPaths())); appendLibAssetsLocked(sharedLibAssets); Slog.v(TAG, "The following library key has been added: " + sharedLibAssets.getResourcesKey()); } } /** * Apply the registered library paths to the passed impl object * @return the hash code for the current version of the registered paths */ public int updateResourceImplWithRegisteredLibs(@NonNull ResourcesImpl impl) { if (!Flags.registerResourcePaths()) { return 0; } final var collector = new PathCollector(null); final int size = mSharedLibAssetsMap.size(); for (int i = 0; i < size; i++) { final var libraryKey = mSharedLibAssetsMap.valueAt(i).getResourcesKey(); collector.appendKey(libraryKey); } impl.getAssets().addPresetApkKeys(extractApkKeys(collector.collectedKey())); return size; } private static class ApkKey { public static class ApkKey { public final String path; public final boolean sharedLib; public final boolean overlay; ApkKey(String path, boolean sharedLib, boolean overlay) { public ApkKey(String path, boolean sharedLib, boolean overlay) { this.path = path; this.sharedLib = sharedLib; this.overlay = overlay; Loading @@ -190,6 +216,12 @@ public class ResourcesManager { return this.path.equals(other.path) && this.sharedLib == other.sharedLib && this.overlay == other.overlay; } @Override public String toString() { return "ApkKey[" + (sharedLib ? "lib" : "app") + (overlay ? ", overlay" : "") + ": " + path + "]"; } } /** Loading Loading @@ -505,7 +537,10 @@ public class ResourcesManager { return "/data/resource-cache/" + path.substring(1).replace('/', '@') + "@idmap"; } private @NonNull ApkAssets loadApkAssets(@NonNull final ApkKey key) throws IOException { /** * Loads the ApkAssets object for the passed key, or picks the one from the cache if available. */ public @NonNull ApkAssets loadApkAssets(@NonNull final ApkKey key) throws IOException { ApkAssets apkAssets; // Optimistically check if this ApkAssets exists somewhere else. Loading Loading @@ -747,8 +782,8 @@ public class ResourcesManager { private @Nullable ResourcesImpl findOrCreateResourcesImplForKeyLocked( @NonNull ResourcesKey key, @Nullable ApkAssetsSupplier apkSupplier) { ResourcesImpl impl = findResourcesImplForKeyLocked(key); // ResourcesImpl also need to be recreated if its shared library count is not up-to-date. if (impl == null || impl.getSharedLibCount() != mSharedLibAssetsMap.size()) { // ResourcesImpl also need to be recreated if its shared library hash is not up-to-date. if (impl == null || impl.getAppliedSharedLibsHash() != mSharedLibAssetsMap.size()) { impl = createResourcesImpl(key, apkSupplier); if (impl != null) { mResourceImpls.put(key, new WeakReference<>(impl)); Loading Loading @@ -1533,55 +1568,108 @@ public class ResourcesManager { } } private void appendLibAssetsLocked(String[] libAssets) { synchronized (mLock) { // Record which ResourcesImpl need updating // (and what ResourcesKey they should update to). final ArrayMap<ResourcesImpl, ResourcesKey> updatedResourceKeys = new ArrayMap<>(); /** * A utility class to collect resources paths into a ResourcesKey object: * - Separates the libraries and the overlays into different sets as those are loaded in * different ways. * - Allows to start with an existing original key object, and copies all non-path related * properties into the final one. * - Preserves the path order while dropping all duplicates in an efficient manner. */ private static class PathCollector { public final ResourcesKey originalKey; public final ArrayList<String> orderedLibs = new ArrayList<>(); public final ArraySet<String> libsSet = new ArraySet<>(); public final ArrayList<String> orderedOverlays = new ArrayList<>(); public final ArraySet<String> overlaysSet = new ArraySet<>(); static void appendNewPath(@NonNull String path, @NonNull ArraySet<String> uniquePaths, @NonNull ArrayList<String> orderedPaths) { if (uniquePaths.add(path)) { orderedPaths.add(path); } } static void appendAllNewPaths(@Nullable String[] paths, @NonNull ArraySet<String> uniquePaths, @NonNull ArrayList<String> orderedPaths) { if (paths == null) return; for (int i = 0, size = paths.length; i < size; i++) { appendNewPath(paths[i], uniquePaths, orderedPaths); } } PathCollector(@Nullable ResourcesKey original) { originalKey = original; if (originalKey != null) { appendKey(originalKey); } } public void appendKey(@NonNull ResourcesKey key) { appendAllNewPaths(key.mLibDirs, libsSet, orderedLibs); appendAllNewPaths(key.mOverlayPaths, overlaysSet, orderedOverlays); } boolean isSameAsOriginal() { if (originalKey == null) { return orderedLibs.isEmpty() && orderedOverlays.isEmpty(); } return ((originalKey.mLibDirs == null && orderedLibs.isEmpty()) || (originalKey.mLibDirs != null && originalKey.mLibDirs.length == orderedLibs.size())) && ((originalKey.mOverlayPaths == null && orderedOverlays.isEmpty()) || (originalKey.mOverlayPaths != null && originalKey.mOverlayPaths.length == orderedOverlays.size())); } @NonNull ResourcesKey collectedKey() { return new ResourcesKey( originalKey == null ? null : originalKey.mResDir, originalKey == null ? null : originalKey.mSplitResDirs, orderedOverlays.toArray(new String[0]), orderedLibs.toArray(new String[0]), originalKey == null ? 0 : originalKey.mDisplayId, originalKey == null ? null : originalKey.mOverrideConfiguration, originalKey == null ? null : originalKey.mCompatInfo, originalKey == null ? null : originalKey.mLoaders); } } /** * Takes the original resources key and the one containing a set of library paths and overlays * to append, and combines them together. In case when the original key already contains all * those paths this function returns null, otherwise it makes a new ResourcesKey object. */ private @Nullable ResourcesKey createNewResourceKeyIfNeeded( @NonNull ResourcesKey original, @NonNull ResourcesKey library) { final var collector = new PathCollector(original); collector.appendKey(library); return collector.isSameAsOriginal() ? null : collector.collectedKey(); } /** * Append the newly registered shared library asset paths to all existing resources objects. */ private void appendLibAssetsLocked(@NonNull SharedLibraryAssets libAssets) { // Record the ResourcesImpl's that need updating, and what ResourcesKey they should // update to. final ArrayMap<ResourcesImpl, ResourcesKey> updatedResourceKeys = new ArrayMap<>(); final int implCount = mResourceImpls.size(); for (int i = 0; i < implCount; i++) { final ResourcesKey key = mResourceImpls.keyAt(i); final WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(i); final ResourcesImpl impl = weakImplRef != null ? weakImplRef.get() : null; if (impl == null) { Slog.w(TAG, "Found a ResourcesImpl which is null, skip it and continue to " + "append shared library assets for next ResourcesImpl."); Slog.w(TAG, "Found a null ResourcesImpl, skipped."); continue; } var newDirs = new ArrayList<String>(); var dirsSet = new ArraySet<String>(); if (key.mLibDirs != null) { final int dirsLength = key.mLibDirs.length; for (int k = 0; k < dirsLength; k++) { newDirs.add(key.mLibDirs[k]); dirsSet.add(key.mLibDirs[k]); } } final int assetsLength = libAssets.length; for (int j = 0; j < assetsLength; j++) { if (dirsSet.add(libAssets[j])) { newDirs.add(libAssets[j]); } } String[] newLibAssets = newDirs.toArray(new String[0]); if (!Arrays.equals(newLibAssets, key.mLibDirs)) { updatedResourceKeys.put(impl, new ResourcesKey( key.mResDir, key.mSplitResDirs, key.mOverlayPaths, newLibAssets, key.mDisplayId, key.mOverrideConfiguration, key.mCompatInfo, key.mLoaders)); final var newKey = createNewResourceKeyIfNeeded(key, libAssets.getResourcesKey()); if (newKey != null) { updatedResourceKeys.put(impl, newKey); } } redirectAllResourcesToNewImplLocked(updatedResourceKeys); } } private void applyNewResourceDirsLocked(@Nullable final String[] oldSourceDirs, @NonNull final ApplicationInfo appInfo) { Loading Loading @@ -1718,8 +1806,9 @@ public class ResourcesManager { } } // Another redirect function which will loop through all Resources and reload ResourcesImpl // if it needs a shared library asset paths update. // Another redirect function which will loop through all Resources in the process, even the ones // the app created outside of the regular Android Runtime, and reload their ResourcesImpl if it // needs a shared library asset paths update. private void redirectAllResourcesToNewImplLocked( @NonNull final ArrayMap<ResourcesImpl, ResourcesKey> updatedResourceKeys) { cleanupReferences(mAllResourceReferences, mAllResourceReferencesQueue); Loading Loading @@ -1830,52 +1919,35 @@ public class ResourcesManager { } } @VisibleForTesting public static class SharedLibraryAssets { private final String[] mAssetPaths; SharedLibraryAssets(String sourceDir, String[] splitSourceDirs, String[] sharedLibraryFiles, String[] resourceDirs, String[] overlayPaths) { mAssetPaths = collectAssetPaths(sourceDir, splitSourceDirs, sharedLibraryFiles, resourceDirs, overlayPaths); } private @NonNull String[] collectAssetPaths(String sourceDir, String[] splitSourceDirs, String[] sharedLibraryFiles, String[] resourceDirs, String[] overlayPaths) { final String[][] inputLists = { splitSourceDirs, sharedLibraryFiles, resourceDirs, overlayPaths }; final ArraySet<String> assetPathSet = new ArraySet<>(); final List<String> assetPathList = new ArrayList<>(); if (sourceDir != null) { assetPathSet.add(sourceDir); assetPathList.add(sourceDir); } for (int i = 0; i < inputLists.length; i++) { if (inputLists[i] != null) { for (int j = 0; j < inputLists[i].length; j++) { if (assetPathSet.add(inputLists[i][j])) { assetPathList.add(inputLists[i][j]); } } } } return assetPathList.toArray(new String[0]); private final ResourcesKey mResourcesKey; private SharedLibraryAssets(ApplicationInfo appInfo) { // We're loading all library's files as shared libs, regardless where they are in // its own ApplicationInfo. final var collector = new PathCollector(null); PathCollector.appendNewPath(appInfo.sourceDir, collector.libsSet, collector.orderedLibs); PathCollector.appendAllNewPaths(appInfo.splitSourceDirs, collector.libsSet, collector.orderedLibs); PathCollector.appendAllNewPaths(appInfo.sharedLibraryFiles, collector.libsSet, collector.orderedLibs); PathCollector.appendAllNewPaths(appInfo.resourceDirs, collector.overlaysSet, collector.orderedOverlays); PathCollector.appendAllNewPaths(appInfo.overlayPaths, collector.overlaysSet, collector.orderedOverlays); mResourcesKey = collector.collectedKey(); } /** * @return all the asset paths of this collected in this class. * @return the resources key for this library assets. */ public @NonNull String[] getAllAssetPaths() { return mAssetPaths; public @NonNull ResourcesKey getResourcesKey() { return mResourcesKey; } } public @NonNull ArrayMap<String, SharedLibraryAssets> getSharedLibAssetsMap() { return new ArrayMap<>(mSharedLibAssetsMap); } /** * Add all resources references to the list which is designed to help to append shared library * asset paths. This is invoked in Resources constructor to include all Resources instances. Loading
core/java/android/content/res/AssetManager.java +91 −33 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package android.content.res; import static android.content.res.Resources.ID_NULL; import static android.app.ResourcesManager.ApkKey; import android.annotation.AnyRes; import android.annotation.ArrayRes; Loading @@ -26,12 +27,14 @@ import android.annotation.Nullable; import android.annotation.StringRes; import android.annotation.StyleRes; import android.annotation.TestApi; import android.app.ResourcesManager; import android.compat.annotation.UnsupportedAppUsage; import android.content.pm.ActivityInfo; import android.content.res.Configuration.NativeConfig; import android.content.res.loader.ResourcesLoader; import android.os.Build; import android.os.ParcelFileDescriptor; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; import android.util.SparseArray; Loading Loading @@ -264,7 +267,7 @@ public final class AssetManager implements AutoCloseable { } sSystemApkAssetsSet = new ArraySet<>(apkAssets); sSystemApkAssets = apkAssets.toArray(new ApkAssets[apkAssets.size()]); sSystemApkAssets = apkAssets.toArray(new ApkAssets[0]); if (sSystem == null) { sSystem = new AssetManager(true /*sentinel*/); } Loading Loading @@ -448,7 +451,7 @@ public final class AssetManager implements AutoCloseable { @Deprecated @UnsupportedAppUsage public int addAssetPath(String path) { return addAssetPathInternal(path, false /*overlay*/, false /*appAsLib*/); return addAssetPathInternal(List.of(new ApkKey(path, false, false)), false); } /** Loading @@ -458,7 +461,7 @@ public final class AssetManager implements AutoCloseable { @Deprecated @UnsupportedAppUsage public int addAssetPathAsSharedLibrary(String path) { return addAssetPathInternal(path, false /*overlay*/, true /*appAsLib*/); return addAssetPathInternal(List.of(new ApkKey(path, true, false)), false); } /** Loading @@ -468,55 +471,110 @@ public final class AssetManager implements AutoCloseable { @Deprecated @UnsupportedAppUsage public int addOverlayPath(String path) { return addAssetPathInternal(path, true /*overlay*/, false /*appAsLib*/); return addAssetPathInternal(List.of(new ApkKey(path, false, true)), false); } /** * @hide */ public void addSharedLibraryPaths(@NonNull String[] paths) { final int length = paths.length; for (int i = 0; i < length; i++) { addAssetPathInternal(paths[i], false, true); public void addPresetApkKeys(@NonNull List<ApkKey> keys) { addAssetPathInternal(keys, true); } private int addAssetPathInternal(List<ApkKey> apkKeys, boolean presetAssets) { Objects.requireNonNull(apkKeys, "apkKeys"); if (apkKeys.isEmpty()) { return 0; } private int addAssetPathInternal(String path, boolean overlay, boolean appAsLib) { Objects.requireNonNull(path, "path"); synchronized (this) { ensureOpenLocked(); final int count = mApkAssets.length; // See if we already have it loaded. for (int i = 0; i < count; i++) { if (mApkAssets[i].getAssetPath().equals(path)) { return i + 1; } // See if we already have some of the apkKeys loaded. final int originalAssetsCount = mApkAssets.length; // Getting an assets' path is a relatively expensive operation, cache them. final ArrayMap<String, Integer> assetPaths = new ArrayMap<>(originalAssetsCount); for (int i = 0; i < originalAssetsCount; i++) { assetPaths.put(mApkAssets[i].getAssetPath(), i); } final ApkAssets assets; try { if (overlay) { // TODO(b/70343104): This hardcoded path will be removed once // addAssetPathInternal is deleted. final String idmapPath = "/data/resource-cache/" + path.substring(1).replace('/', '@') + "@idmap"; assets = ApkAssets.loadOverlayFromPath(idmapPath, 0 /* flags */); final var newKeys = new ArrayList<ApkKey>(apkKeys.size()); int lastFoundIndex = -1; for (int i = 0, pathsSize = apkKeys.size(); i < pathsSize; i++) { final var key = apkKeys.get(i); final var index = assetPaths.get(key.path); if (index == null) { newKeys.add(key); } else { assets = ApkAssets.loadFromPath(path, appAsLib ? ApkAssets.PROPERTY_DYNAMIC : 0); lastFoundIndex = index; } } catch (IOException e) { return 0; } if (newKeys.isEmpty()) { return lastFoundIndex + 1; } mApkAssets = Arrays.copyOf(mApkAssets, count + 1); mApkAssets[count] = assets; nativeSetApkAssets(mObject, mApkAssets, true, false); final var newAssets = loadAssets(newKeys); if (newAssets.isEmpty()) { return 0; } mApkAssets = makeNewAssetsArrayLocked(newAssets); nativeSetApkAssets(mObject, mApkAssets, true, presetAssets); invalidateCachesLocked(-1); return count + 1; return originalAssetsCount + 1; } } /** * Insert the new assets preserving the correct order: all non-loader assets go before all * of the loader assets. */ @GuardedBy("this") private @NonNull ApkAssets[] makeNewAssetsArrayLocked( @NonNull ArrayList<ApkAssets> newNonLoaderAssets) { final int originalAssetsCount = mApkAssets.length; int firstLoaderIndex = originalAssetsCount; for (int i = 0; i < originalAssetsCount; i++) { if (mApkAssets[i].isForLoader()) { firstLoaderIndex = i; break; } } final int newAssetsSize = newNonLoaderAssets.size(); final var newAssetsArray = new ApkAssets[originalAssetsCount + newAssetsSize]; if (firstLoaderIndex > 0) { // This should always be true, but who knows... System.arraycopy(mApkAssets, 0, newAssetsArray, 0, firstLoaderIndex); } for (int i = 0; i < newAssetsSize; i++) { newAssetsArray[firstLoaderIndex + i] = newNonLoaderAssets.get(i); } if (originalAssetsCount > firstLoaderIndex) { System.arraycopy( mApkAssets, firstLoaderIndex, newAssetsArray, firstLoaderIndex + newAssetsSize, originalAssetsCount - firstLoaderIndex); } return newAssetsArray; } private static @NonNull ArrayList<ApkAssets> loadAssets(@NonNull ArrayList<ApkKey> keys) { final int pathsSize = keys.size(); final var loadedAssets = new ArrayList<ApkAssets>(pathsSize); final var resourcesManager = ResourcesManager.getInstance(); for (int i = 0; i < pathsSize; i++) { final var key = keys.get(i); try { // ResourcesManager has a cache of loaded assets, ensuring we don't open the same // file repeatedly, which is useful for the common overlays and registered // shared libraries. loadedAssets.add(resourcesManager.loadApkAssets(key)); } catch (IOException e) { Log.w(TAG, "Failed to load asset, key = " + key, e); } } return loadedAssets; } /** @hide */ Loading
core/java/android/content/res/ResourcesImpl.java +8 −18 File changed.Preview size limit exceeded, changes collapsed. Show changes
core/java/android/credentials/selection/IntentFactory.java +11 −1 Original line number Diff line number Diff line Loading @@ -232,7 +232,17 @@ public class IntentFactory { oemComponentName, PackageManager.ComponentInfoFlags.of( PackageManager.MATCH_SYSTEM_ONLY)); if (info.enabled && info.exported) { boolean oemComponentEnabled = info.enabled; int runtimeComponentEnabledState = context.getPackageManager() .getComponentEnabledSetting(oemComponentName); if (runtimeComponentEnabledState == PackageManager .COMPONENT_ENABLED_STATE_ENABLED) { oemComponentEnabled = true; } else if (runtimeComponentEnabledState == PackageManager .COMPONENT_ENABLED_STATE_DISABLED) { oemComponentEnabled = false; } if (oemComponentEnabled && info.exported) { intentResultBuilder.setOemUiUsageStatus(IntentCreationResult .OemUiUsageStatus.SUCCESS); Slog.i(TAG, Loading