ResourcesManager: Allow managed addition of library asset paths

This allows WebView to add itself to the ResourcesManager and
remain their even after configuration changes and multi-window
changes.

Bug:29112218
Change-Id: I2cb131ae2c61fb58c48babafdd46c1882be96aa9
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index a4688d1..f56a6ad 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -274,7 +274,7 @@
                 if (libDir.endsWith(".apk")) {
                     // Avoid opening files we know do not have resources,
                     // like code-only .jar files.
-                    if (assets.addAssetPath(libDir) == 0) {
+                    if (assets.addAssetPathAsSharedLibrary(libDir) == 0) {
                         Log.w(TAG, "Asset path '" + libDir +
                                 "' does not exist or contains no resources.");
                     }
@@ -330,6 +330,22 @@
     }
 
     /**
+     * Finds a cached ResourcesImpl object that matches the given ResourcesKey, or
+     * creates a new one and caches it for future use.
+     * @param key The key to match.
+     * @return a ResourcesImpl object matching the key.
+     */
+    private @NonNull ResourcesImpl findOrCreateResourcesImplForKeyLocked(
+            @NonNull ResourcesKey key) {
+        ResourcesImpl impl = findResourcesImplForKeyLocked(key);
+        if (impl == null) {
+            impl = createResourcesImpl(key);
+            mResourceImpls.put(key, new WeakReference<>(impl));
+        }
+        return impl;
+    }
+
+    /**
      * Find the ResourcesKey that this ResourcesImpl object is associated with.
      * @return the ResourcesKey or null if none was found.
      */
@@ -811,4 +827,75 @@
             Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
         }
     }
+
+    /**
+     * Appends the library asset path to any ResourcesImpl object that contains the main
+     * assetPath.
+     * @param assetPath The main asset path for which to add the library asset path.
+     * @param libAsset The library asset path to add.
+     */
+    public void appendLibAssetForMainAssetPath(String assetPath, String libAsset) {
+        synchronized (this) {
+            // 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 ResourcesImpl impl = mResourceImpls.valueAt(i).get();
+                final ResourcesKey key = mResourceImpls.keyAt(i);
+                if (impl != null && key.mResDir.equals(assetPath)) {
+                    if (!ArrayUtils.contains(key.mLibDirs, libAsset)) {
+                        final int newLibAssetCount = 1 +
+                                (key.mLibDirs != null ? key.mLibDirs.length : 0);
+                        final String[] newLibAssets = new String[newLibAssetCount];
+                        if (key.mLibDirs != null) {
+                            System.arraycopy(key.mLibDirs, 0, newLibAssets, 0, key.mLibDirs.length);
+                        }
+                        newLibAssets[newLibAssetCount - 1] = libAsset;
+
+                        updatedResourceKeys.put(impl, new ResourcesKey(
+                                key.mResDir,
+                                key.mSplitResDirs,
+                                key.mOverlayDirs,
+                                newLibAssets,
+                                key.mDisplayId,
+                                key.mOverrideConfiguration,
+                                key.mCompatInfo));
+                    }
+                }
+            }
+
+            // Bail early if there is no work to do.
+            if (updatedResourceKeys.isEmpty()) {
+                return;
+            }
+
+            // Update any references to ResourcesImpl that require reloading.
+            final int resourcesCount = mResourceReferences.size();
+            for (int i = 0; i < resourcesCount; i++) {
+                final Resources r = mResourceReferences.get(i).get();
+                if (r != null) {
+                    final ResourcesKey key = updatedResourceKeys.get(r.getImpl());
+                    if (key != null) {
+                        r.setImpl(findOrCreateResourcesImplForKeyLocked(key));
+                    }
+                }
+            }
+
+            // Update any references to ResourcesImpl that require reloading for each Activity.
+            for (ActivityResources activityResources : mActivityResourceReferences.values()) {
+                final int resCount = activityResources.activityResources.size();
+                for (int i = 0; i < resCount; i++) {
+                    final Resources r = activityResources.activityResources.get(i).get();
+                    if (r != null) {
+                        final ResourcesKey key = updatedResourceKeys.get(r.getImpl());
+                        if (key != null) {
+                            r.setImpl(findOrCreateResourcesImplForKeyLocked(key));
+                        }
+                    }
+                }
+            }
+        }
+    }
 }
diff --git a/core/java/android/webkit/WebViewDelegate.java b/core/java/android/webkit/WebViewDelegate.java
index 9e73af2..2cdff79 100644
--- a/core/java/android/webkit/WebViewDelegate.java
+++ b/core/java/android/webkit/WebViewDelegate.java
@@ -21,7 +21,9 @@
 import android.annotation.SystemApi;
 import android.app.ActivityThread;
 import android.app.Application;
+import android.app.ResourcesManager;
 import android.content.Context;
+import android.content.pm.ApplicationInfo;
 import android.content.res.Resources;
 import android.graphics.Canvas;
 import android.os.SystemProperties;
@@ -31,6 +33,8 @@
 import android.view.View;
 import android.view.ViewRootImpl;
 
+import com.android.internal.util.ArrayUtils;
+
 /**
  * Delegate used by the WebView provider implementation to access
  * the required framework functionality needed to implement a {@link WebView}.
@@ -177,7 +181,29 @@
      * Adds the WebView asset path to {@link android.content.res.AssetManager}.
      */
     public void addWebViewAssetPath(Context context) {
-        context.getAssets().addAssetPathAsSharedLibrary(
-                WebViewFactory.getLoadedPackageInfo().applicationInfo.sourceDir);
+        final String newAssetPath = WebViewFactory.getLoadedPackageInfo().applicationInfo.sourceDir;
+
+        final ApplicationInfo appInfo = context.getApplicationInfo();
+        final String[] libs = appInfo.sharedLibraryFiles;
+        if (!ArrayUtils.contains(libs, newAssetPath)) {
+            // Build the new library asset path list.
+            final int newLibAssetsCount = 1 + (libs != null ? libs.length : 0);
+            final String[] newLibAssets = new String[newLibAssetsCount];
+            if (libs != null) {
+                System.arraycopy(libs, 0, newLibAssets, 0, libs.length);
+            }
+            newLibAssets[newLibAssetsCount - 1] = newAssetPath;
+
+            // Update the ApplicationInfo object with the new list.
+            // We know this will persist and future Resources created via ResourcesManager
+            // will include the shared library because this ApplicationInfo comes from the
+            // underlying LoadedApk in ContextImpl, which does not change during the life of the
+            // application.
+            appInfo.sharedLibraryFiles = newLibAssets;
+
+            // Update existing Resources with the WebView library.
+            ResourcesManager.getInstance().appendLibAssetForMainAssetPath(
+                    appInfo.getBaseResourcePath(), newAssetPath);
+        }
     }
 }
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index d447a38..ceeb12b 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -6174,7 +6174,7 @@
     if (id >= 256) {
         LOG_ALWAYS_FATAL("Package id out of range");
         return NO_ERROR;
-    } else if (id == 0 || appAsLib || isSystemAsset) {
+    } else if (id == 0 || (id == 0x7f && appAsLib) || isSystemAsset) {
         // This is a library or a system asset, so assign an ID
         id = mNextPackageId++;
     }