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

Commit 209523a5 authored by Brandon Liu's avatar Brandon Liu
Browse files

Register resources paths for shared library in runtime

Functionality implementation for Resources.registerResourcePaths()
API. Load resources paths for a shared library and add these
resources to all existing and future contexts while app is running.

Bug: b/324000040
Test: Added and verified affected tests pass.
Change-Id: I7ef6c8ec6f82884760ace4ab711bf480b949a5ee
parent 1c5039f8
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -483,6 +483,13 @@ java_aconfig_library {
    defaults: ["framework-minus-apex-aconfig-java-defaults"],
}

java_aconfig_library {
    name: "android.content.res.flags-aconfig-java-host",
    aconfig_declarations: "android.content.res.flags-aconfig",
    host_supported: true,
    defaults: ["framework-minus-apex-aconfig-java-defaults"],
}

// Media BetterTogether
aconfig_declarations {
    name: "com.android.media.flags.bettertogether-aconfig",
+137 −0
Original line number Diff line number Diff line
@@ -124,6 +124,32 @@ public class ResourcesManager {
     */
    private LocaleConfig mLocaleConfig = new LocaleConfig(LocaleList.getEmptyLocaleList());

    private final ArrayMap<String, SharedLibraryAssets> mSharedLibAssetsMap =
            new ArrayMap<>();

    /**
     * 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);

        synchronized (mLock) {
            if (mSharedLibAssetsMap.containsKey(uniqueId)) {
                Slog.v(TAG, "Package resources' paths for uniqueId: " + uniqueId
                        + " has already been registered, this is a no-op.");
                return;
            }
            mSharedLibAssetsMap.put(uniqueId, sharedLibAssets);
            appendLibAssetsLocked(sharedLibAssets.getAllAssetPaths());
            Slog.v(TAG, "The following resources' paths have been added: "
                    + Arrays.toString(sharedLibAssets.getAllAssetPaths()));
        }
    }

    private static class ApkKey {
        public final String path;
        public final boolean sharedLib;
@@ -278,6 +304,21 @@ public class ResourcesManager {
    public ResourcesManager() {
    }

    /**
     * Inject a customized ResourcesManager instance for testing, return the old ResourcesManager
     * instance.
     */
    @UnsupportedAppUsage
    @VisibleForTesting
    public static ResourcesManager setInstance(ResourcesManager resourcesManager) {
        synchronized (ResourcesManager.class) {
            ResourcesManager oldResourceManager = sResourcesManager;
            sResourcesManager = resourcesManager;
            return oldResourceManager;
        }

    }

    @UnsupportedAppUsage
    public static ResourcesManager getInstance() {
        synchronized (ResourcesManager.class) {
@@ -1480,6 +1521,56 @@ 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<>();

            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.");
                    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));
                }
            }

            redirectResourcesToNewImplLocked(updatedResourceKeys);
        }
    }

    private void applyNewResourceDirsLocked(@Nullable final String[] oldSourceDirs,
            @NonNull final ApplicationInfo appInfo) {
        try {
@@ -1689,4 +1780,50 @@ public class ResourcesManager {
            }
        }
    }

    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]);
        }

        /**
         * @return all the asset paths of this collected in this class.
         */
        public @NonNull String[] getAllAssetPaths() {
            return mAssetPaths;
        }
    }

    public @NonNull ArrayMap<String, SharedLibraryAssets> getSharedLibAssetsMap() {
        return new ArrayMap<>(mSharedLibAssetsMap);
    }
}
+10 −0
Original line number Diff line number Diff line
@@ -471,6 +471,16 @@ public final class AssetManager implements AutoCloseable {
        return addAssetPathInternal(path, true /*overlay*/, false /*appAsLib*/);
    }

    /**
     * @hide
     */
    public void addSharedLibraryPaths(@NonNull String[] paths) {
        final int length = paths.length;
        for (int i = 0; i < length; i++) {
            addAssetPathInternal(paths[i], false, true);
        }
    }

    private int addAssetPathInternal(String path, boolean overlay, boolean appAsLib) {
        Objects.requireNonNull(path, "path");
        synchronized (this) {
+7 −1
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@ import android.annotation.StyleRes;
import android.annotation.StyleableRes;
import android.annotation.XmlRes;
import android.app.Application;
import android.app.ResourcesManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.pm.ActivityInfo;
@@ -2854,6 +2855,11 @@ public class Resources {
    @FlaggedApi(android.content.res.Flags.FLAG_REGISTER_RESOURCE_PATHS)
    public static void registerResourcePaths(@NonNull String uniqueId,
            @NonNull ApplicationInfo appInfo) {
        throw new UnsupportedOperationException("The implementation has not been done yet.");
        if (Flags.registerResourcePaths()) {
            ResourcesManager.getInstance().registerResourcePaths(uniqueId, appInfo);
        } else {
            throw new UnsupportedOperationException("Flag " + Flags.FLAG_REGISTER_RESOURCE_PATHS
                    + " is disabled.");
        }
    }
}
+10 −0
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ import android.annotation.StyleRes;
import android.annotation.StyleableRes;
import android.app.LocaleConfig;
import android.app.ResourcesManager;
import android.app.ResourcesManager.SharedLibraryAssets;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.pm.ActivityInfo;
import android.content.pm.ActivityInfo.Config;
@@ -47,6 +48,7 @@ import android.os.Build;
import android.os.LocaleList;
import android.os.ParcelFileDescriptor;
import android.os.Trace;
import android.util.ArrayMap;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -197,6 +199,14 @@ public class ResourcesImpl {
    public ResourcesImpl(@NonNull AssetManager assets, @Nullable DisplayMetrics metrics,
            @Nullable Configuration config, @NonNull DisplayAdjustments displayAdjustments) {
        mAssets = assets;
        if (Flags.registerResourcePaths()) {
            ArrayMap<String, SharedLibraryAssets> sharedLibMap =
                    ResourcesManager.getInstance().getSharedLibAssetsMap();
            final int size = sharedLibMap.size();
            for (int i = 0; i < size; i++) {
                assets.addSharedLibraryPaths(sharedLibMap.valueAt(i).getAllAssetPaths());
            }
        }
        mMetrics.setToDefaults();
        mDisplayAdjustments = displayAdjustments;
        mConfiguration.setToDefaults();
Loading