Merge "Marked apks loaded by isolated processes as usedByOthers" into sc-dev
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 4faa18e..716394c 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -11871,10 +11871,10 @@
@Override
public void notifyDexLoad(String loadingPackageName, Map<String, String> classLoaderContextMap,
String loaderIsa) {
- if (PLATFORM_PACKAGE_NAME.equals(loadingPackageName)
- && Binder.getCallingUid() != Process.SYSTEM_UID) {
+ int callingUid = Binder.getCallingUid();
+ if (PLATFORM_PACKAGE_NAME.equals(loadingPackageName) && callingUid != Process.SYSTEM_UID) {
Slog.w(TAG, "Non System Server process reporting dex loads as system server. uid="
- + Binder.getCallingUid());
+ + callingUid);
// Do not record dex loads from processes pretending to be system server.
// Only the system server should be assigned the package "android", so reject calls
// that don't satisfy the constraint.
@@ -11885,6 +11885,7 @@
// in order to verify the expectations.
return;
}
+
int userId = UserHandle.getCallingUserId();
ApplicationInfo ai = getApplicationInfo(loadingPackageName, /*flags*/ 0, userId);
if (ai == null) {
@@ -11892,7 +11893,8 @@
+ loadingPackageName + ", user=" + userId);
return;
}
- mDexManager.notifyDexLoad(ai, classLoaderContextMap, loaderIsa, userId);
+ mDexManager.notifyDexLoad(ai, classLoaderContextMap, loaderIsa, userId,
+ Process.isIsolated(callingUid));
}
@Override
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index 37f3175..32ba26c 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -86,6 +86,11 @@
// However it can load verification data - thus we pick the "verify" compiler filter.
private static final String SYSTEM_SERVER_COMPILER_FILTER = "verify";
+ // The suffix we add to the package name when the loading happens in an isolated process.
+ // Note that the double dot creates and "invalid" package name which makes it clear that this
+ // is an artificially constructed name.
+ private static final String ISOLATED_PROCESS_PACKAGE_SUFFIX = "..isolated";
+
private final Context mContext;
// Maps package name to code locations.
@@ -166,12 +171,14 @@
* the class loader context that was used to load them.
* @param loaderIsa the ISA of the app loading the dex files
* @param loaderUserId the user id which runs the code loading the dex files
+ * @param loaderIsIsolatedProcess whether or not the loading process is isolated.
*/
public void notifyDexLoad(ApplicationInfo loadingAppInfo,
- Map<String, String> classLoaderContextMap, String loaderIsa, int loaderUserId) {
+ Map<String, String> classLoaderContextMap, String loaderIsa, int loaderUserId,
+ boolean loaderIsIsolatedProcess) {
try {
notifyDexLoadInternal(loadingAppInfo, classLoaderContextMap, loaderIsa,
- loaderUserId);
+ loaderUserId, loaderIsIsolatedProcess);
} catch (Exception e) {
Slog.w(TAG, "Exception while notifying dex load for package " +
loadingAppInfo.packageName, e);
@@ -181,7 +188,7 @@
@VisibleForTesting
/*package*/ void notifyDexLoadInternal(ApplicationInfo loadingAppInfo,
Map<String, String> classLoaderContextMap, String loaderIsa,
- int loaderUserId) {
+ int loaderUserId, boolean loaderIsIsolatedProcess) {
if (classLoaderContextMap == null) {
return;
}
@@ -195,22 +202,36 @@
return;
}
+ // If this load is coming from an isolated process we need to be able to prevent profile
+ // based optimizations. This is because isolated processes are sandboxed and can only read
+ // world readable files, so they need world readable optimization files. An
+ // example of such a package is webview.
+ //
+ // In order to prevent profile optimization we pretend that the load is coming from a
+ // different package, and so we assign a artificial name to the loading package making it
+ // clear that it comes from an isolated process. This blends well with the entire
+ // usedByOthers logic without needing to special handle isolated process in all dexopt
+ // layers.
+ String loadingPackageAmendedName = loadingAppInfo.packageName;
+ if (loaderIsIsolatedProcess) {
+ loadingPackageAmendedName += ISOLATED_PROCESS_PACKAGE_SUFFIX;
+ }
for (Map.Entry<String, String> mapping : classLoaderContextMap.entrySet()) {
String dexPath = mapping.getKey();
// Find the owning package name.
DexSearchResult searchResult = getDexPackage(loadingAppInfo, dexPath, loaderUserId);
if (DEBUG) {
- Slog.i(TAG, loadingAppInfo.packageName
- + " loads from " + searchResult + " : " + loaderUserId + " : " + dexPath);
+ Slog.i(TAG, loadingPackageAmendedName
+ + " loads from " + searchResult + " : " + loaderUserId + " : " + dexPath);
}
if (searchResult.mOutcome != DEX_SEARCH_NOT_FOUND) {
// TODO(calin): extend isUsedByOtherApps check to detect the cases where
// different apps share the same runtime. In that case we should not mark the dex
// file as isUsedByOtherApps. Currently this is a safe approximation.
- boolean isUsedByOtherApps = !loadingAppInfo.packageName.equals(
- searchResult.mOwningPackageName);
+ boolean isUsedByOtherApps =
+ !loadingPackageAmendedName.equals(searchResult.mOwningPackageName);
boolean primaryOrSplit = searchResult.mOutcome == DEX_SEARCH_FOUND_PRIMARY ||
searchResult.mOutcome == DEX_SEARCH_FOUND_SPLIT;
@@ -249,7 +270,7 @@
// async write to disk to make sure we don't loose the data in case of a reboot.
if (mPackageDexUsage.record(searchResult.mOwningPackageName,
dexPath, loaderUserId, loaderIsa, primaryOrSplit,
- loadingAppInfo.packageName, classLoaderContext, overwriteCLC)) {
+ loadingPackageAmendedName, classLoaderContext, overwriteCLC)) {
mPackageDexUsage.maybeWriteAsync();
}
}
@@ -749,7 +770,7 @@
dexPath, userId, isa, /*primaryOrSplit*/ false,
loadingPackage,
PackageDexUsage.VARIABLE_CLASS_LOADER_CONTEXT,
- /*overwriteCLC*/ false);
+ /*overwriteCLC=*/ false);
update |= newUpdate;
}
if (update) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/dex/DexManagerTests.java b/services/tests/mockingservicestests/src/com/android/server/pm/dex/DexManagerTests.java
index 2e0cadf..8abe46f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/dex/DexManagerTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/dex/DexManagerTests.java
@@ -410,6 +410,17 @@
}
@Test
+ public void testNotifyUsedByIsolatedProcess() {
+ // Bar loads its own apk but as isolatedProcess.
+ notifyDexLoad(mBarUser0, mBarUser0.getBaseAndSplitDexPaths(), mUser0,
+ /*isolatedProcess=*/ true);
+
+ // Bar is used by an isolated process and should be marked as usedByOtherApps
+ PackageUseInfo pui = getPackageUseInfo(mBarUser0);
+ assertIsUsedByOtherApps(mBarUser0, pui, true);
+ }
+
+ @Test
public void testNotifyPackageUpdatedCodeLocations() {
// Simulate a split update.
String newSplit = mBarUser0.replaceLastSplit();
@@ -545,7 +556,7 @@
List<String> classLoaders =
Arrays.asList(PATH_CLASS_LOADER_NAME, UNSUPPORTED_CLASS_LOADER_NAME);
List<String> classPaths = Arrays.asList(classPath, classPath);
- notifyDexLoad(mBarUser0, classLoaders, classPaths, mUser0);
+ notifyDexLoad(mBarUser0, classLoaders, classPaths, mUser0, /*isolatedProcess=*/ false);
assertNoUseInfo(mBarUser0);
@@ -664,7 +675,8 @@
expectedContexts[i] += contextSuffix;
}
- notifyDexLoad(mFooUser0, fooSecondaries, expectedContexts, mUser0);
+ notifyDexLoad(mFooUser0, fooSecondaries, expectedContexts, mUser0,
+ /*isolatedProcess=*/ false);
PackageUseInfo pui = getPackageUseInfo(mFooUser0);
assertIsUsedByOtherApps(mFooUser0, pui, false);
@@ -838,26 +850,32 @@
assertEquals(codePath, isUsedByOtherApps, pui.isUsedByOtherApps(codePath));
}
}
+
private void notifyDexLoad(TestData testData, List<String> dexPaths, int loaderUserId) {
+ notifyDexLoad(testData, dexPaths, loaderUserId, /*isolatedProcess=*/ false);
+ }
+
+ private void notifyDexLoad(TestData testData, List<String> dexPaths, int loaderUserId,
+ boolean isolatedProcess) {
// By default, assume a single class loader in the chain.
// This makes writing tests much easier.
List<String> classLoaders = Arrays.asList(testData.mClassLoader);
List<String> classPaths = dexPaths != null
? Arrays.<String>asList(String.join(File.pathSeparator, dexPaths)) : null;
- notifyDexLoad(testData, classLoaders, classPaths, loaderUserId);
+ notifyDexLoad(testData, classLoaders, classPaths, loaderUserId, isolatedProcess);
}
private void notifyDexLoad(TestData testData, List<String> classLoaders,
- List<String> classPaths, int loaderUserId) {
+ List<String> classPaths, int loaderUserId, boolean isolatedProcess) {
String[] classLoaderContexts = computeClassLoaderContexts(classLoaders, classPaths);
// We call the internal function so any exceptions thrown cause test failures.
List<String> dexPaths = classPaths != null
? Arrays.asList(classPaths.get(0).split(File.pathSeparator)) : Arrays.asList();
- notifyDexLoad(testData, dexPaths, classLoaderContexts, loaderUserId);
+ notifyDexLoad(testData, dexPaths, classLoaderContexts, loaderUserId, isolatedProcess);
}
private void notifyDexLoad(TestData testData, List<String> dexPaths,
- String[] classLoaderContexts, int loaderUserId) {
+ String[] classLoaderContexts, int loaderUserId, boolean isolatedProcess) {
assertTrue(dexPaths.size() == classLoaderContexts.length);
HashMap<String, String> dexPathMapping = new HashMap<>(dexPaths.size());
for (int i = 0; i < dexPaths.size(); i++) {
@@ -865,7 +883,7 @@
? classLoaderContexts[i] : PackageDexUsage.UNSUPPORTED_CLASS_LOADER_CONTEXT);
}
mDexManager.notifyDexLoadInternal(testData.mPackageInfo.applicationInfo, dexPathMapping,
- testData.mLoaderIsa, loaderUserId);
+ testData.mLoaderIsa, loaderUserId, isolatedProcess);
}
private String[] computeClassLoaderContexts(List<String> classLoaders,