summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Edgar Arriaga <edgararriaga@google.com> 2023-07-24 18:42:17 +0000
committer Edgar Arriaga <edgararriaga@google.com> 2023-11-15 19:56:38 +0000
commit99042d052a1a6a08a6cbc5263e2ae16d05c6d347 (patch)
tree755708ce51fc7623bd82430ed6392e25a6066179
parent586efa0cd6411fba931f7329be20520875b16b71 (diff)
Pin webview to memory
Bug: 307594624 Test: dumpsys pinner verify pinned ranges updating webview and enabling disabling flags. Change-Id: I5fe3f03f0a8c0eb5b101d75d1bf1449b54050519
-rw-r--r--core/res/OWNERS4
-rw-r--r--core/res/res/values/config.xml3
-rw-r--r--core/res/res/values/symbols.xml1
-rw-r--r--services/core/java/com/android/server/PinnerService.java315
-rw-r--r--services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java36
5 files changed, 286 insertions, 73 deletions
diff --git a/core/res/OWNERS b/core/res/OWNERS
index 0df7c2047bc1..46bd3175b4be 100644
--- a/core/res/OWNERS
+++ b/core/res/OWNERS
@@ -45,6 +45,10 @@ per-file res/values/config_device_idle.xml = file:/apex/jobscheduler/OWNERS
# Wear
per-file res/*-watch/* = file:/WEAR_OWNERS
+# Peformance
+per-file res/values/config.xml = file:/PERFORMANCE_OWNERS
+per-file res/values/symbols.xml = file:/PERFORMANCE_OWNERS
+
# PowerProfile
per-file res/xml/power_profile.xml = file:/BATTERY_STATS_OWNERS
per-file res/xml/power_profile_test.xml = file:/BATTERY_STATS_OWNERS
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index f827d28b26fd..a9d9bbf4f27e 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4336,6 +4336,9 @@
<!-- True if assistant app should be pinned via Pinner Service -->
<bool name="config_pinnerAssistantApp">false</bool>
+ <!-- Bytes that the PinnerService will pin for WebView -->
+ <integer name="config_pinnerWebviewPinBytes">0</integer>
+
<!-- Number of days preloaded file cache should be preserved on a device before it can be
deleted -->
<integer name="config_keepPreloadsMinDays">7</integer>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index e19d5486768a..afe293b57406 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3376,6 +3376,7 @@
<java-symbol type="bool" name="config_pinnerCameraApp" />
<java-symbol type="bool" name="config_pinnerHomeApp" />
<java-symbol type="bool" name="config_pinnerAssistantApp" />
+ <java-symbol type="integer" name="config_pinnerWebviewPinBytes" />
<java-symbol type="string" name="config_doubleTouchGestureEnableFile" />
diff --git a/services/core/java/com/android/server/PinnerService.java b/services/core/java/com/android/server/PinnerService.java
index 8cd5ce1f4ff8..e28933b44527 100644
--- a/services/core/java/com/android/server/PinnerService.java
+++ b/services/core/java/com/android/server/PinnerService.java
@@ -20,6 +20,8 @@ import static android.app.ActivityManager.UID_OBSERVER_ACTIVE;
import static android.app.ActivityManager.UID_OBSERVER_GONE;
import static android.os.Process.SYSTEM_UID;
+import static com.android.server.flags.Flags.pinWebview;
+
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -48,6 +50,7 @@ import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.os.SystemProperties;
+import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.DeviceConfig;
@@ -83,6 +86,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Method;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
@@ -110,11 +114,8 @@ public final class PinnerService extends SystemService {
private static final int KEY_ASSISTANT = 2;
// Pin using pinlist.meta when pinning apps.
- private static boolean PROP_PIN_PINLIST = SystemProperties.getBoolean(
- "pinner.use_pinlist", true);
- // Pin the whole odex/vdex/etc file when pinning apps.
- private static boolean PROP_PIN_ODEX = SystemProperties.getBoolean(
- "pinner.whole_odex", true);
+ private static boolean PROP_PIN_PINLIST =
+ SystemProperties.getBoolean("pinner.use_pinlist", true);
private static final int MAX_CAMERA_PIN_SIZE = 80 * (1 << 20); // 80MB max for camera app.
private static final int MAX_HOME_PIN_SIZE = 6 * (1 << 20); // 6MB max for home app.
@@ -134,8 +135,7 @@ public final class PinnerService extends SystemService {
private SearchManager mSearchManager;
/** The list of the statically pinned files. */
- @GuardedBy("this")
- private final ArrayList<PinnedFile> mPinnedFiles = new ArrayList<>();
+ @GuardedBy("this") private final ArrayMap<String, PinnedFile> mPinnedFiles = new ArrayMap<>();
/** The list of the pinned apps. This is a map from {@link AppKey} to a pinned app. */
@GuardedBy("this")
@@ -170,6 +170,7 @@ public final class PinnerService extends SystemService {
private final boolean mConfiguredToPinCamera;
private final boolean mConfiguredToPinHome;
private final boolean mConfiguredToPinAssistant;
+ private final int mConfiguredWebviewPinBytes;
private BinderService mBinderService;
private PinnerHandler mPinnerHandler = null;
@@ -228,6 +229,8 @@ public final class PinnerService extends SystemService {
com.android.internal.R.bool.config_pinnerHomeApp);
mConfiguredToPinAssistant = context.getResources().getBoolean(
com.android.internal.R.bool.config_pinnerAssistantApp);
+ mConfiguredWebviewPinBytes = context.getResources().getInteger(
+ com.android.internal.R.integer.config_pinnerWebviewPinBytes);
mPinKeys = createPinKeys();
mPinnerHandler = new PinnerHandler(BackgroundThread.get().getLooper());
@@ -317,7 +320,7 @@ public final class PinnerService extends SystemService {
public List<PinnedFileStats> dumpDataForStatsd() {
List<PinnedFileStats> pinnedFileStats = new ArrayList<>();
synchronized (PinnerService.this) {
- for (PinnedFile pinnedFile : mPinnedFiles) {
+ for (PinnedFile pinnedFile : mPinnedFiles.values()) {
pinnedFileStats.add(new PinnedFileStats(SYSTEM_UID, pinnedFile));
}
@@ -353,39 +356,17 @@ public final class PinnerService extends SystemService {
com.android.internal.R.array.config_defaultPinnerServiceFiles);
// Continue trying to pin each file even if we fail to pin some of them
for (String fileToPin : filesToPin) {
- PinnedFile pf = pinFile(fileToPin,
- Integer.MAX_VALUE,
- /*attemptPinIntrospection=*/false);
+ PinnedFile pf = pinFileInternal(fileToPin, Integer.MAX_VALUE,
+ /*attemptPinIntrospection=*/false);
if (pf == null) {
Slog.e(TAG, "Failed to pin file = " + fileToPin);
continue;
}
synchronized (this) {
- mPinnedFiles.add(pf);
- }
- if (fileToPin.endsWith(".jar") | fileToPin.endsWith(".apk")) {
- // Check whether the runtime has compilation artifacts to pin.
- String arch = VMRuntime.getInstructionSet(Build.SUPPORTED_ABIS[0]);
- String[] files = null;
- try {
- files = DexFile.getDexFileOutputPaths(fileToPin, arch);
- } catch (IOException ioe) { }
- if (files == null) {
- continue;
- }
- for (String file : files) {
- PinnedFile df = pinFile(file,
- Integer.MAX_VALUE,
- /*attemptPinIntrospection=*/false);
- if (df == null) {
- Slog.i(TAG, "Failed to pin ART file = " + file);
- continue;
- }
- synchronized (this) {
- mPinnedFiles.add(df);
- }
- }
+ mPinnedFiles.put(pf.fileName, pf);
}
+ pf.groupName = "xml-config";
+ pinOptimizedDexDependencies(pf, Integer.MAX_VALUE, null);
}
refreshPinAnonConfig();
@@ -482,7 +463,7 @@ public final class PinnerService extends SystemService {
pinnedAppFiles = new ArrayList<>(app.mFiles);
}
for (PinnedFile pinnedFile : pinnedAppFiles) {
- pinnedFile.close();
+ unpinFile(pinnedFile.fileName);
}
}
@@ -490,6 +471,19 @@ public final class PinnerService extends SystemService {
return ResolverActivity.class.getName().equals(info.name);
}
+ public int getWebviewPinQuota() {
+ if (!pinWebview()) {
+ return 0;
+ }
+ int quota = mConfiguredWebviewPinBytes;
+ int overrideQuota = SystemProperties.getInt("pinner.pin_webview_size", -1);
+ if (overrideQuota != -1) {
+ // Quota was overridden
+ quota = overrideQuota;
+ }
+ return quota;
+ }
+
private ApplicationInfo getCameraInfo(int userHandle) {
Intent cameraIntent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
ApplicationInfo info = getApplicationInfoForIntent(cameraIntent, userHandle,
@@ -863,7 +857,7 @@ public final class PinnerService extends SystemService {
continue;
}
- PinnedFile pf = pinFile(apk, apkPinSizeLimit, /*attemptPinIntrospection=*/true);
+ PinnedFile pf = pinFileInternal(apk, apkPinSizeLimit, /*attemptPinIntrospection=*/true);
if (pf == null) {
Slog.e(TAG, "Failed to pin " + apk);
continue;
@@ -877,40 +871,121 @@ public final class PinnerService extends SystemService {
}
apkPinSizeLimit -= pf.bytesPinned;
+ if (apk.equals(appInfo.sourceDir)) {
+ pinOptimizedDexDependencies(pf, apkPinSizeLimit, appInfo);
+ for (PinnedFile dep : pf.pinnedDeps) {
+ pinnedApp.mFiles.add(dep);
+ }
+ }
}
+ }
- // determine the ABI from either ApplicationInfo or Build
- String abi = appInfo.primaryCpuAbi != null ? appInfo.primaryCpuAbi :
- Build.SUPPORTED_ABIS[0];
- String arch = VMRuntime.getInstructionSet(abi);
- // get the path to the odex or oat file
- String baseCodePath = appInfo.getBaseCodePath();
- String[] files = null;
- try {
- files = DexFile.getDexFileOutputPaths(baseCodePath, arch);
- } catch (IOException ioe) {}
- if (files == null) {
- return;
+ /**
+ * Pin file or apk to memory.
+ *
+ * Prefer to use this method instead of {@link #pinFileInternal(String, int, boolean)} as it
+ * takes care of accounting and if pinning an apk, it also pins any extra optimized art files
+ * that related to the file but not within itself.
+ *
+ * @param fileToPin File to pin
+ * @param maxBytesToPin maximum quota allowed for pinning
+ * @return total bytes that were pinned.
+ */
+ public int pinFile(String fileToPin, int maxBytesToPin, @Nullable ApplicationInfo appInfo,
+ @Nullable String groupName) {
+ PinnedFile existingPin;
+ synchronized(this) {
+ existingPin = mPinnedFiles.get(fileToPin);
+ }
+ if (existingPin != null) {
+ if (existingPin.bytesPinned == maxBytesToPin) {
+ // Duplicate pin requesting same amount of bytes, lets just bail out.
+ return 0;
+ } else {
+ // User decided to pin a different amount of bytes than currently pinned
+ // so this is a valid pin request. Unpin the previous version before repining.
+ if (DEBUG) {
+ Slog.d(TAG, "Unpinning file prior to repin: " + fileToPin);
+ }
+ unpinFile(fileToPin);
+ }
}
- //not pinning the oat/odex is not a fatal error
- for (String file : files) {
- PinnedFile pf = pinFile(file, pinSizeLimit, /*attemptPinIntrospection=*/false);
- if (pf != null) {
- synchronized (this) {
- if (PROP_PIN_ODEX) {
- pinnedApp.mFiles.add(pf);
- }
+ boolean isApk = fileToPin.endsWith(".apk");
+ int bytesPinned = 0;
+ PinnedFile pf = pinFileInternal(fileToPin, maxBytesToPin,
+ /*attemptPinIntrospection=*/isApk);
+ if (pf == null) {
+ Slog.e(TAG, "Failed to pin file = " + fileToPin);
+ return 0;
+ }
+ pf.groupName = groupName != null ? groupName : "";
+
+ maxBytesToPin -= bytesPinned;
+ bytesPinned += pf.bytesPinned;
+
+ synchronized (this) {
+ mPinnedFiles.put(pf.fileName, pf);
+ }
+ if (maxBytesToPin > 0) {
+ pinOptimizedDexDependencies(pf, maxBytesToPin, appInfo);
+ }
+ return bytesPinned;
+ }
+
+ /**
+ * Pin any dependency optimized files generated by ART.
+ * @param pinnedFile An already pinned file whose dependencies we want pinned.
+ * @param maxBytesToPin Maximum amount of bytes to pin.
+ * @param appInfo Used to determine the ABI in case the application has one custom set, when set
+ * to null it will use the default supported ABI by the device.
+ * @return total bytes pinned.
+ */
+ private int pinOptimizedDexDependencies(
+ PinnedFile pinnedFile, int maxBytesToPin, @Nullable ApplicationInfo appInfo) {
+ if (pinnedFile == null) {
+ return 0;
+ }
+
+ int bytesPinned = 0;
+ if (pinnedFile.fileName.endsWith(".jar") | pinnedFile.fileName.endsWith(".apk")) {
+ String abi = null;
+ if (appInfo != null) {
+ abi = appInfo.primaryCpuAbi;
+ }
+ if (abi == null) {
+ abi = Build.SUPPORTED_ABIS[0];
+ }
+ // Check whether the runtime has compilation artifacts to pin.
+ String arch = VMRuntime.getInstructionSet(abi);
+ String[] files = null;
+ try {
+ files = DexFile.getDexFileOutputPaths(pinnedFile.fileName, arch);
+ } catch (IOException ioe) {
+ }
+ if (files == null) {
+ return bytesPinned;
+ }
+ for (String file : files) {
+ // Unpin if it was already pinned prior to re-pinning.
+ unpinFile(file);
+
+ PinnedFile df = pinFileInternal(file, Integer.MAX_VALUE,
+ /*attemptPinIntrospection=*/false);
+ if (df == null) {
+ Slog.i(TAG, "Failed to pin ART file = " + file);
+ return bytesPinned;
}
- if (DEBUG) {
- if (PROP_PIN_ODEX) {
- Slog.i(TAG, "Pinned " + pf.fileName);
- } else {
- Slog.i(TAG, "Pinned [skip] " + pf.fileName);
- }
+ df.groupName = pinnedFile.groupName;
+ pinnedFile.pinnedDeps.add(df);
+ maxBytesToPin -= df.bytesPinned;
+ bytesPinned += df.bytesPinned;
+ synchronized (this) {
+ mPinnedFiles.put(df.fileName, df);
}
}
}
+ return bytesPinned;
}
/** mlock length bytes of fileToPin in memory
@@ -950,9 +1025,12 @@ public final class PinnerService extends SystemService {
* zip in order to extract the
* @return Pinned memory resource owner thing or null on error
*/
- private static PinnedFile pinFile(String fileToPin,
- int maxBytesToPin,
- boolean attemptPinIntrospection) {
+ private static PinnedFile pinFileInternal(
+ String fileToPin, int maxBytesToPin, boolean attemptPinIntrospection) {
+ if (DEBUG) {
+ Slog.d(TAG, "pin file: " + fileToPin + " use-pinlist: " + attemptPinIntrospection);
+ }
+ Trace.beginSection("pinFile:" + fileToPin);
ZipFile fileAsZip = null;
InputStream pinRangeStream = null;
try {
@@ -964,8 +1042,6 @@ public final class PinnerService extends SystemService {
pinRangeStream = maybeOpenPinMetaInZip(fileAsZip, fileToPin);
}
- Slog.d(TAG, "pinRangeStream: " + pinRangeStream);
-
PinRangeSource pinRangeSource = (pinRangeStream != null)
? new PinRangeSourceStream(pinRangeStream)
: new PinRangeSourceStatic(0, Integer.MAX_VALUE /* will be clipped */);
@@ -973,6 +1049,7 @@ public final class PinnerService extends SystemService {
} finally {
safeClose(pinRangeStream);
safeClose(fileAsZip); // Also closes any streams we've opened
+ Trace.endSection();
}
}
@@ -1008,9 +1085,23 @@ public final class PinnerService extends SystemService {
return null;
}
+ // Looking at root directory is the old behavior but still some apps rely on it so keeping
+ // for backward compatibility. As doing a single item lookup is cheap in the root.
ZipEntry pinMetaEntry = zipFile.getEntry(PIN_META_FILENAME);
+
+ if (pinMetaEntry == null) {
+ // It is usually within an apk's control to include files in assets/ directory
+ // so this would be the expected point to have the pinlist.meta coming from.
+ // we explicitly avoid doing an exhaustive search because it may be expensive so
+ // prefer to have a good known location to retrieve the file.
+ pinMetaEntry = zipFile.getEntry("assets/" + PIN_META_FILENAME);
+ }
+
InputStream pinMetaStream = null;
if (pinMetaEntry != null) {
+ if (DEBUG) {
+ Slog.d(TAG, "Found pinlist.meta for " + fileName);
+ }
try {
pinMetaStream = zipFile.getInputStream(pinMetaEntry);
} catch (IOException ex) {
@@ -1019,6 +1110,10 @@ public final class PinnerService extends SystemService {
fileName),
ex);
}
+ } else {
+ Slog.w(TAG,
+ String.format(
+ "Could not find pinlist.meta for \"%s\": pinning as blob", fileName));
}
return pinMetaStream;
}
@@ -1155,6 +1250,49 @@ public final class PinnerService extends SystemService {
}
}
}
+ private List<PinnedFile> getAllPinsForGroup(String group) {
+ List<PinnedFile> filesInGroup;
+ synchronized (this) {
+ filesInGroup = mPinnedFiles.values()
+ .stream()
+ .filter(pf -> pf.groupName.equals(group))
+ .toList();
+ }
+ return filesInGroup;
+ }
+ public void unpinGroup(String group) {
+ List<PinnedFile> pinnedFiles = getAllPinsForGroup(group);
+ for (PinnedFile pf : pinnedFiles) {
+ unpinFile(pf.fileName);
+ }
+ }
+
+ public void unpinFile(String filename) {
+ PinnedFile pinnedFile;
+ synchronized (this) {
+ pinnedFile = mPinnedFiles.get(filename);
+ }
+ if (pinnedFile == null) {
+ // File not pinned, nothing to do.
+ return;
+ }
+ pinnedFile.close();
+ synchronized (this) {
+ if (DEBUG) {
+ Slog.d(TAG, "Unpinned file: " + filename);
+ }
+ mPinnedFiles.remove(pinnedFile.fileName);
+ for (PinnedFile dep : pinnedFile.pinnedDeps) {
+ if (dep == null) {
+ continue;
+ }
+ mPinnedFiles.remove(dep.fileName);
+ if (DEBUG) {
+ Slog.d(TAG, "Unpinned dependency: " + dep.fileName);
+ }
+ }
+ }
+ }
private static int clamp(int min, int value, int max) {
return Math.max(min, Math.min(value, max));
@@ -1204,13 +1342,11 @@ public final class PinnerService extends SystemService {
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+ HashSet<PinnedFile> shownPins = new HashSet<>();
+ HashSet<String> groups = new HashSet<>();
+
synchronized (PinnerService.this) {
long totalSize = 0;
- for (PinnedFile pinnedFile : mPinnedFiles) {
- pw.format("%s %s\n", pinnedFile.fileName, pinnedFile.bytesPinned);
- totalSize += pinnedFile.bytesPinned;
- }
- pw.println();
for (int key : mPinnedApps.keySet()) {
PinnedApp app = mPinnedApps.get(key);
pw.print(getNameForKey(key));
@@ -1220,8 +1356,32 @@ public final class PinnerService extends SystemService {
for (PinnedFile pf : mPinnedApps.get(key).mFiles) {
pw.print(" "); pw.format("%s %s\n", pf.fileName, pf.bytesPinned);
totalSize += pf.bytesPinned;
+ shownPins.add(pf);
}
}
+ pw.println();
+ for (PinnedFile pinnedFile : mPinnedFiles.values()) {
+ if (!groups.contains(pinnedFile.groupName)) {
+ groups.add(pinnedFile.groupName);
+ }
+ }
+ for (String group : groups) {
+ pw.print("Group:" + group);
+ pw.println();
+ List<PinnedFile> groupPins = mPinnedFiles.values()
+ .stream()
+ .filter(f -> f.groupName.equals(group))
+ .toList();
+ for (PinnedFile pinnedFile : groupPins) {
+ if (shownPins.contains(pinnedFile)) {
+ // Already showed in the dump and accounted for, skip.
+ continue;
+ }
+ pw.format(" %s %s\n", pinnedFile.fileName, pinnedFile.bytesPinned);
+ totalSize += pinnedFile.bytesPinned;
+ }
+ }
+ pw.println();
if (mPinAnonAddress != 0) {
pw.format("Pinned anon region: %s\n", mCurrentlyPinnedAnonSize);
}
@@ -1280,6 +1440,10 @@ public final class PinnerService extends SystemService {
final String fileName;
final int bytesPinned;
+ // User defined group name for pinner accounting
+ String groupName = "";
+ ArrayList<PinnedFile> pinnedDeps = new ArrayList<>();
+
PinnedFile(long address, int mapSize, String fileName, int bytesPinned) {
mAddress = address;
this.mapSize = mapSize;
@@ -1293,6 +1457,11 @@ public final class PinnerService extends SystemService {
safeMunmap(mAddress, mapSize);
mAddress = -1;
}
+ for (PinnedFile dep : pinnedDeps) {
+ if (dep != null) {
+ dep.close();
+ }
+ }
}
@Override
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
index 43d62aaa120a..cc2b9998ae33 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
@@ -17,6 +17,7 @@ package com.android.server.webkit;
import android.annotation.Nullable;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
@@ -29,8 +30,12 @@ import android.webkit.WebViewFactory;
import android.webkit.WebViewProviderInfo;
import android.webkit.WebViewProviderResponse;
+import com.android.server.LocalServices;
+import com.android.server.PinnerService;
+
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
/**
@@ -88,6 +93,8 @@ class WebViewUpdateServiceImpl {
private static final int MULTIPROCESS_SETTING_ON_VALUE = Integer.MAX_VALUE;
private static final int MULTIPROCESS_SETTING_OFF_VALUE = Integer.MIN_VALUE;
+ private static final String PIN_GROUP = "webview";
+
private final SystemInterface mSystemInterface;
private final Context mContext;
@@ -332,6 +339,34 @@ class WebViewUpdateServiceImpl {
return newPackage;
}
+ private void pinWebviewIfRequired(ApplicationInfo appInfo) {
+ PinnerService pinnerService = LocalServices.getService(PinnerService.class);
+ int webviewPinQuota = pinnerService.getWebviewPinQuota();
+ if (webviewPinQuota <= 0) {
+ return;
+ }
+
+ pinnerService.unpinGroup(PIN_GROUP);
+
+ ArrayList<String> apksToPin = new ArrayList<>();
+ boolean pinSharedFirst = appInfo.metaData.getBoolean("PIN_SHARED_LIBS_FIRST", true);
+ for (String sharedLib : appInfo.sharedLibraryFiles) {
+ apksToPin.add(sharedLib);
+ }
+ apksToPin.add(appInfo.sourceDir);
+ if (!pinSharedFirst) {
+ // We want to prioritize pinning of the native library that is most likely used by apps
+ // which in some build flavors live in the main apk and as a shared library for others.
+ Collections.reverse(apksToPin);
+ }
+ for (String apk : apksToPin) {
+ if (webviewPinQuota <= 0) {
+ break;
+ }
+ int bytesPinned = pinnerService.pinFile(apk, webviewPinQuota, appInfo, PIN_GROUP);
+ webviewPinQuota -= bytesPinned;
+ }
+ }
/**
* This is called when we change WebView provider, either when the current provider is
* updated or a new provider is chosen / takes precedence.
@@ -340,6 +375,7 @@ class WebViewUpdateServiceImpl {
synchronized (mLock) {
mAnyWebViewInstalled = true;
if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) {
+ pinWebviewIfRequired(newPackage.applicationInfo);
mCurrentWebViewPackage = newPackage;
// The relro creations might 'finish' (not start at all) before