Merge "Revert "Revert "[DexLoadReporter] Report classloader contexts di..."" am: 1ee3e70cf3
Change-Id: I5645d6c8f666e2e29e753cad5f216f9d7ab335d2
This commit is contained in:
@@ -28,9 +28,8 @@ import dalvik.system.VMRuntime;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -87,50 +86,32 @@ import java.util.Set;
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void report(List<ClassLoader> classLoadersChain, List<String> classPaths) {
|
public void report(Map<String, String> classLoaderContextMap) {
|
||||||
if (classLoadersChain.size() != classPaths.size()) {
|
if (classLoaderContextMap.isEmpty()) {
|
||||||
Slog.wtf(TAG, "Bad call to DexLoadReporter: argument size mismatch");
|
Slog.wtf(TAG, "Bad call to DexLoadReporter: empty classLoaderContextMap");
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (classPaths.isEmpty()) {
|
|
||||||
Slog.wtf(TAG, "Bad call to DexLoadReporter: empty dex paths");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The first element of classPaths is the list of dex files that should be registered.
|
|
||||||
// The classpath is represented as a list of dex files separated by File.pathSeparator.
|
|
||||||
String[] dexPathsForRegistration = classPaths.get(0).split(File.pathSeparator);
|
|
||||||
if (dexPathsForRegistration.length == 0) {
|
|
||||||
// No dex files to register.
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notify the package manager about the dex loads unconditionally.
|
// Notify the package manager about the dex loads unconditionally.
|
||||||
// The load might be for either a primary or secondary dex file.
|
// The load might be for either a primary or secondary dex file.
|
||||||
notifyPackageManager(classLoadersChain, classPaths);
|
notifyPackageManager(classLoaderContextMap);
|
||||||
// Check for secondary dex files and register them for profiling if possible.
|
// Check for secondary dex files and register them for profiling if possible.
|
||||||
// Note that we only register the dex paths belonging to the first class loader.
|
// Note that we only register the dex paths belonging to the first class loader.
|
||||||
registerSecondaryDexForProfiling(dexPathsForRegistration);
|
registerSecondaryDexForProfiling(classLoaderContextMap.keySet());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void notifyPackageManager(List<ClassLoader> classLoadersChain,
|
private void notifyPackageManager(Map<String, String> classLoaderContextMap) {
|
||||||
List<String> classPaths) {
|
|
||||||
// Get the class loader names for the binder call.
|
// Get the class loader names for the binder call.
|
||||||
List<String> classLoadersNames = new ArrayList<>(classPaths.size());
|
|
||||||
for (ClassLoader classLoader : classLoadersChain) {
|
|
||||||
classLoadersNames.add(classLoader.getClass().getName());
|
|
||||||
}
|
|
||||||
String packageName = ActivityThread.currentPackageName();
|
String packageName = ActivityThread.currentPackageName();
|
||||||
try {
|
try {
|
||||||
ActivityThread.getPackageManager().notifyDexLoad(
|
ActivityThread.getPackageManager().notifyDexLoad(packageName,
|
||||||
packageName, classLoadersNames, classPaths,
|
classLoaderContextMap, VMRuntime.getRuntime().vmInstructionSet());
|
||||||
VMRuntime.getRuntime().vmInstructionSet());
|
|
||||||
} catch (RemoteException re) {
|
} catch (RemoteException re) {
|
||||||
Slog.e(TAG, "Failed to notify PM about dex load for package " + packageName, re);
|
Slog.e(TAG, "Failed to notify PM about dex load for package " + packageName, re);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void registerSecondaryDexForProfiling(String[] dexPaths) {
|
private void registerSecondaryDexForProfiling(Set<String> dexPaths) {
|
||||||
if (!SystemProperties.getBoolean("dalvik.vm.dexopt.secondary", false)) {
|
if (!SystemProperties.getBoolean("dalvik.vm.dexopt.secondary", false)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -529,19 +529,12 @@ interface IPackageManager {
|
|||||||
* Notify the package manager that a list of dex files have been loaded.
|
* Notify the package manager that a list of dex files have been loaded.
|
||||||
*
|
*
|
||||||
* @param loadingPackageName the name of the package who performs the load
|
* @param loadingPackageName the name of the package who performs the load
|
||||||
* @param classLoadersNames the names of the class loaders present in the loading chain. The
|
* @param classLoaderContextMap a map from file paths to dex files that have been loaded to
|
||||||
* list encodes the class loader chain in the natural order. The first class loader has
|
* the class loader context that was used to load them.
|
||||||
* the second one as its parent and so on. The dex files present in the class path of the
|
|
||||||
* first class loader will be recorded in the usage file.
|
|
||||||
* @param classPaths the class paths corresponding to the class loaders names from
|
|
||||||
* {@param classLoadersNames}. The the first element corresponds to the first class loader
|
|
||||||
* and so on. A classpath is represented as a list of dex files separated by
|
|
||||||
* {@code File.pathSeparator}, or null if the class loader's classpath is not known.
|
|
||||||
* The dex files found in the first class path will be recorded in the usage file.
|
|
||||||
* @param loaderIsa the ISA of the loader process
|
* @param loaderIsa the ISA of the loader process
|
||||||
*/
|
*/
|
||||||
oneway void notifyDexLoad(String loadingPackageName, in List<String> classLoadersNames,
|
oneway void notifyDexLoad(String loadingPackageName,
|
||||||
in List<String> classPaths, String loaderIsa);
|
in Map<String, String> classLoaderContextMap, String loaderIsa);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register an application dex module with the package manager.
|
* Register an application dex module with the package manager.
|
||||||
|
|||||||
@@ -9764,8 +9764,8 @@ public class PackageManagerService extends IPackageManager.Stub
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void notifyDexLoad(String loadingPackageName, List<String> classLoaderNames,
|
public void notifyDexLoad(String loadingPackageName, Map<String, String> classLoaderContextMap,
|
||||||
List<String> classPaths, String loaderIsa) {
|
String loaderIsa) {
|
||||||
int userId = UserHandle.getCallingUserId();
|
int userId = UserHandle.getCallingUserId();
|
||||||
ApplicationInfo ai = getApplicationInfo(loadingPackageName, /*flags*/ 0, userId);
|
ApplicationInfo ai = getApplicationInfo(loadingPackageName, /*flags*/ 0, userId);
|
||||||
if (ai == null) {
|
if (ai == null) {
|
||||||
@@ -9773,7 +9773,7 @@ public class PackageManagerService extends IPackageManager.Stub
|
|||||||
+ loadingPackageName + ", user=" + userId);
|
+ loadingPackageName + ", user=" + userId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mDexManager.notifyDexLoad(ai, classLoaderNames, classPaths, loaderIsa, userId);
|
mDexManager.notifyDexLoad(ai, classLoaderContextMap, loaderIsa, userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -44,6 +44,8 @@ import com.android.server.pm.PackageDexOptimizer;
|
|||||||
import com.android.server.pm.PackageManagerService;
|
import com.android.server.pm.PackageManagerService;
|
||||||
import com.android.server.pm.PackageManagerServiceUtils;
|
import com.android.server.pm.PackageManagerServiceUtils;
|
||||||
|
|
||||||
|
import dalvik.system.VMRuntime;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@@ -143,22 +145,15 @@ public class DexManager {
|
|||||||
* return as fast as possible.
|
* return as fast as possible.
|
||||||
*
|
*
|
||||||
* @param loadingAppInfo the package performing the load
|
* @param loadingAppInfo the package performing the load
|
||||||
* @param classLoadersNames the names of the class loaders present in the loading chain. The
|
* @param classLoaderContextMap a map from file paths to dex files that have been loaded to
|
||||||
* list encodes the class loader chain in the natural order. The first class loader has
|
* the class loader context that was used to load them.
|
||||||
* the second one as its parent and so on. The dex files present in the class path of the
|
|
||||||
* first class loader will be recorded in the usage file.
|
|
||||||
* @param classPaths the class paths corresponding to the class loaders names from
|
|
||||||
* {@param classLoadersNames}. The the first element corresponds to the first class loader
|
|
||||||
* and so on. A classpath is represented as a list of dex files separated by
|
|
||||||
* {@code File.pathSeparator}, or null if the class loader's classpath is not known.
|
|
||||||
* The dex files found in the first class path will be recorded in the usage file.
|
|
||||||
* @param loaderIsa the ISA of the app loading the dex files
|
* @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 loaderUserId the user id which runs the code loading the dex files
|
||||||
*/
|
*/
|
||||||
public void notifyDexLoad(ApplicationInfo loadingAppInfo, List<String> classLoadersNames,
|
public void notifyDexLoad(ApplicationInfo loadingAppInfo,
|
||||||
List<String> classPaths, String loaderIsa, int loaderUserId) {
|
Map<String, String> classLoaderContextMap, String loaderIsa, int loaderUserId) {
|
||||||
try {
|
try {
|
||||||
notifyDexLoadInternal(loadingAppInfo, classLoadersNames, classPaths, loaderIsa,
|
notifyDexLoadInternal(loadingAppInfo, classLoaderContextMap, loaderIsa,
|
||||||
loaderUserId);
|
loaderUserId);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Slog.w(TAG, "Exception while notifying dex load for package " +
|
Slog.w(TAG, "Exception while notifying dex load for package " +
|
||||||
@@ -168,46 +163,23 @@ public class DexManager {
|
|||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
/*package*/ void notifyDexLoadInternal(ApplicationInfo loadingAppInfo,
|
/*package*/ void notifyDexLoadInternal(ApplicationInfo loadingAppInfo,
|
||||||
List<String> classLoaderNames, List<String> classPaths, String loaderIsa,
|
Map<String, String> classLoaderContextMap, String loaderIsa,
|
||||||
int loaderUserId) {
|
int loaderUserId) {
|
||||||
if (classLoaderNames.size() != classPaths.size()) {
|
if (classLoaderContextMap == null) {
|
||||||
Slog.wtf(TAG, "Bad call to noitfyDexLoad: args have different size");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (classLoaderNames.isEmpty()) {
|
if (classLoaderContextMap.isEmpty()) {
|
||||||
Slog.wtf(TAG, "Bad call to notifyDexLoad: class loaders list is empty");
|
Slog.wtf(TAG, "Bad call to notifyDexLoad: class loaders list is empty");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!PackageManagerServiceUtils.checkISA(loaderIsa)) {
|
if (!PackageManagerServiceUtils.checkISA(loaderIsa)) {
|
||||||
Slog.w(TAG, "Loading dex files " + classPaths + " in unsupported ISA: " +
|
Slog.w(TAG, "Loading dex files " + classLoaderContextMap.keySet()
|
||||||
loaderIsa + "?");
|
+ " in unsupported ISA: " + loaderIsa + "?");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The first classpath should never be null because the first classloader
|
for (Map.Entry<String, String> mapping : classLoaderContextMap.entrySet()) {
|
||||||
// should always be an instance of BaseDexClassLoader.
|
String dexPath = mapping.getKey();
|
||||||
String firstClassPath = classPaths.get(0);
|
|
||||||
if (firstClassPath == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// The classpath is represented as a list of dex files separated by File.pathSeparator.
|
|
||||||
String[] dexPathsToRegister = firstClassPath.split(File.pathSeparator);
|
|
||||||
|
|
||||||
// Encode the class loader contexts for the dexPathsToRegister.
|
|
||||||
String[] classLoaderContexts = DexoptUtils.processContextForDexLoad(
|
|
||||||
classLoaderNames, classPaths);
|
|
||||||
|
|
||||||
// A null classLoaderContexts means that there are unsupported class loaders in the
|
|
||||||
// chain.
|
|
||||||
if (classLoaderContexts == null) {
|
|
||||||
if (DEBUG) {
|
|
||||||
Slog.i(TAG, loadingAppInfo.packageName +
|
|
||||||
" uses unsupported class loader in " + classLoaderNames);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int dexPathIndex = 0;
|
|
||||||
for (String dexPath : dexPathsToRegister) {
|
|
||||||
// Find the owning package name.
|
// Find the owning package name.
|
||||||
DexSearchResult searchResult = getDexPackage(loadingAppInfo, dexPath, loaderUserId);
|
DexSearchResult searchResult = getDexPackage(loadingAppInfo, dexPath, loaderUserId);
|
||||||
|
|
||||||
@@ -229,7 +201,6 @@ public class DexManager {
|
|||||||
// If the dex file is the primary apk (or a split) and not isUsedByOtherApps
|
// If the dex file is the primary apk (or a split) and not isUsedByOtherApps
|
||||||
// do not record it. This case does not bring any new usable information
|
// do not record it. This case does not bring any new usable information
|
||||||
// and can be safely skipped.
|
// and can be safely skipped.
|
||||||
dexPathIndex++;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -239,13 +210,13 @@ public class DexManager {
|
|||||||
searchResult.mOwningPackageName, loadingAppInfo.packageName);
|
searchResult.mOwningPackageName, loadingAppInfo.packageName);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (classLoaderContexts != null) {
|
String classLoaderContext = mapping.getValue();
|
||||||
|
if (classLoaderContext != null
|
||||||
|
&& VMRuntime.isValidClassLoaderContext(classLoaderContext)) {
|
||||||
// Record dex file usage. If the current usage is a new pattern (e.g. new
|
// Record dex file usage. If the current usage is a new pattern (e.g. new
|
||||||
// secondary, or UsedByOtherApps), record will return true and we trigger an
|
// secondary, or UsedByOtherApps), record will return true and we trigger an
|
||||||
// async write to disk to make sure we don't loose the data in case of a reboot.
|
// async write to disk to make sure we don't loose the data in case of a reboot.
|
||||||
|
|
||||||
String classLoaderContext = classLoaderContexts[dexPathIndex];
|
|
||||||
if (mPackageDexUsage.record(searchResult.mOwningPackageName,
|
if (mPackageDexUsage.record(searchResult.mOwningPackageName,
|
||||||
dexPath, loaderUserId, loaderIsa, isUsedByOtherApps, primaryOrSplit,
|
dexPath, loaderUserId, loaderIsa, isUsedByOtherApps, primaryOrSplit,
|
||||||
loadingAppInfo.packageName, classLoaderContext)) {
|
loadingAppInfo.packageName, classLoaderContext)) {
|
||||||
@@ -259,7 +230,6 @@ public class DexManager {
|
|||||||
Slog.i(TAG, "Could not find owning package for dex file: " + dexPath);
|
Slog.i(TAG, "Could not find owning package for dex file: " + dexPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dexPathIndex++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -83,8 +83,9 @@ public class PackageDexUsage extends AbstractStatsBase<Void> {
|
|||||||
"=UnknownClassLoaderContext=";
|
"=UnknownClassLoaderContext=";
|
||||||
|
|
||||||
// The marker used for unsupported class loader contexts (no longer written, may occur in old
|
// The marker used for unsupported class loader contexts (no longer written, may occur in old
|
||||||
// files so discarded on read).
|
// files so discarded on read). Note: this matches
|
||||||
private static final String UNSUPPORTED_CLASS_LOADER_CONTEXT =
|
// ClassLoaderContext::kUnsupportedClassLoaderContextEncoding in the runtime.
|
||||||
|
/*package*/ static final String UNSUPPORTED_CLASS_LOADER_CONTEXT =
|
||||||
"=UnsupportedClassLoaderContext=";
|
"=UnsupportedClassLoaderContext=";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -133,6 +134,9 @@ public class PackageDexUsage extends AbstractStatsBase<Void> {
|
|||||||
if (classLoaderContext == null) {
|
if (classLoaderContext == null) {
|
||||||
throw new IllegalArgumentException("Null classLoaderContext");
|
throw new IllegalArgumentException("Null classLoaderContext");
|
||||||
}
|
}
|
||||||
|
if (classLoaderContext.equals(UNSUPPORTED_CLASS_LOADER_CONTEXT)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
synchronized (mPackageUseInfoMap) {
|
synchronized (mPackageUseInfoMap) {
|
||||||
PackageUseInfo packageUseInfo = mPackageUseInfoMap.get(owningPackageName);
|
PackageUseInfo packageUseInfo = mPackageUseInfoMap.get(owningPackageName);
|
||||||
@@ -843,10 +847,11 @@ public class PackageDexUsage extends AbstractStatsBase<Void> {
|
|||||||
boolean updateLoadingPackages = mLoadingPackages.addAll(dexUseInfo.mLoadingPackages);
|
boolean updateLoadingPackages = mLoadingPackages.addAll(dexUseInfo.mLoadingPackages);
|
||||||
|
|
||||||
String oldClassLoaderContext = mClassLoaderContext;
|
String oldClassLoaderContext = mClassLoaderContext;
|
||||||
if (UNKNOWN_CLASS_LOADER_CONTEXT.equals(mClassLoaderContext)) {
|
if (isUnknownOrUnsupportedContext(mClassLoaderContext)) {
|
||||||
// Can happen if we read a previous version.
|
// Can happen if we read a previous version.
|
||||||
mClassLoaderContext = dexUseInfo.mClassLoaderContext;
|
mClassLoaderContext = dexUseInfo.mClassLoaderContext;
|
||||||
} else if (!Objects.equals(mClassLoaderContext, dexUseInfo.mClassLoaderContext)) {
|
} else if (!isUnknownOrUnsupportedContext(dexUseInfo.mClassLoaderContext)
|
||||||
|
&& !Objects.equals(mClassLoaderContext, dexUseInfo.mClassLoaderContext)) {
|
||||||
// We detected a context change.
|
// We detected a context change.
|
||||||
mClassLoaderContext = VARIABLE_CLASS_LOADER_CONTEXT;
|
mClassLoaderContext = VARIABLE_CLASS_LOADER_CONTEXT;
|
||||||
}
|
}
|
||||||
@@ -857,6 +862,13 @@ public class PackageDexUsage extends AbstractStatsBase<Void> {
|
|||||||
|| !Objects.equals(oldClassLoaderContext, mClassLoaderContext);
|
|| !Objects.equals(oldClassLoaderContext, mClassLoaderContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean isUnknownOrUnsupportedContext(String context) {
|
||||||
|
// TODO: Merge UNKNOWN_CLASS_LOADER_CONTEXT & UNSUPPORTED_CLASS_LOADER_CONTEXT cases
|
||||||
|
// into UNSUPPORTED_CLASS_LOADER_CONTEXT.
|
||||||
|
return UNKNOWN_CLASS_LOADER_CONTEXT.equals(context)
|
||||||
|
|| UNSUPPORTED_CLASS_LOADER_CONTEXT.equals(context);
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isUsedByOtherApps() {
|
public boolean isUsedByOtherApps() {
|
||||||
return mIsUsedByOtherApps;
|
return mIsUsedByOtherApps;
|
||||||
}
|
}
|
||||||
@@ -878,7 +890,7 @@ public class PackageDexUsage extends AbstractStatsBase<Void> {
|
|||||||
public boolean isUnknownClassLoaderContext() {
|
public boolean isUnknownClassLoaderContext() {
|
||||||
// The class loader context may be unknown if we loaded the data from a previous version
|
// The class loader context may be unknown if we loaded the data from a previous version
|
||||||
// which didn't save the context.
|
// which didn't save the context.
|
||||||
return UNKNOWN_CLASS_LOADER_CONTEXT.equals(mClassLoaderContext);
|
return isUnknownOrUnsupportedContext(mClassLoaderContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isVariableClassLoaderContext() {
|
public boolean isVariableClassLoaderContext() {
|
||||||
|
|||||||
@@ -532,6 +532,61 @@ public class DexManagerTests {
|
|||||||
assertHasDclInfo(mBarUser0, mBarUser0, secondaries);
|
assertHasDclInfo(mBarUser0, mBarUser0, secondaries);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPrimaryAndSecondaryDexLoad() {
|
||||||
|
// Foo loads both primary and secondary dexes
|
||||||
|
List<String> fooSecondaries = mFooUser0.getSecondaryDexPaths();
|
||||||
|
List<String> fooDexes = new ArrayList<>(mFooUser0.getBaseAndSplitDexPaths());
|
||||||
|
int primaryCount = fooDexes.size();
|
||||||
|
fooDexes.addAll(fooSecondaries);
|
||||||
|
|
||||||
|
notifyDexLoad(mFooUser0, fooDexes, mUser0);
|
||||||
|
|
||||||
|
PackageUseInfo pui = getPackageUseInfo(mFooUser0);
|
||||||
|
assertIsUsedByOtherApps(mFooUser0, pui, false);
|
||||||
|
assertEquals(fooSecondaries.size(), pui.getDexUseInfoMap().size());
|
||||||
|
|
||||||
|
// Below we want to verify that the secondary dex files within fooDexes have been correctly
|
||||||
|
// reported and their class loader contexts were correctly recorded.
|
||||||
|
//
|
||||||
|
// In order to achieve this we first use DexoptUtils.processContextForDexLoad to compute the
|
||||||
|
// class loader contexts for all the dex files.
|
||||||
|
String[] allClassLoaderContexts = DexoptUtils.processContextForDexLoad(
|
||||||
|
Arrays.asList(mFooUser0.mClassLoader),
|
||||||
|
Arrays.asList(String.join(File.pathSeparator, fooDexes)));
|
||||||
|
// Next we filter out the class loader contexts corresponding to non-secondary dex files.
|
||||||
|
String[] secondaryClassLoaderContexts = Arrays.copyOfRange(allClassLoaderContexts,
|
||||||
|
primaryCount, allClassLoaderContexts.length);
|
||||||
|
assertSecondaryUse(mFooUser0, pui, fooSecondaries, /*isUsedByOtherApps*/false, mUser0,
|
||||||
|
secondaryClassLoaderContexts);
|
||||||
|
|
||||||
|
assertHasDclInfo(mFooUser0, mFooUser0, fooSecondaries);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNotifySecondary_withSharedLibrary() {
|
||||||
|
// Foo loads its own secondary files.
|
||||||
|
List<String> fooSecondaries = mFooUser0.getSecondaryDexPaths();
|
||||||
|
|
||||||
|
String contextSuffix = "{PCL[/system/framework/org.apache.http.legacy.jar]}";
|
||||||
|
String[] expectedContexts = DexoptUtils.processContextForDexLoad(
|
||||||
|
Arrays.asList(mFooUser0.mClassLoader),
|
||||||
|
Arrays.asList(String.join(File.pathSeparator, fooSecondaries)));
|
||||||
|
for (int i = 0; i < expectedContexts.length; i++) {
|
||||||
|
expectedContexts[i] += contextSuffix;
|
||||||
|
}
|
||||||
|
|
||||||
|
notifyDexLoad(mFooUser0, fooSecondaries, expectedContexts, mUser0);
|
||||||
|
|
||||||
|
PackageUseInfo pui = getPackageUseInfo(mFooUser0);
|
||||||
|
assertIsUsedByOtherApps(mFooUser0, pui, false);
|
||||||
|
assertEquals(fooSecondaries.size(), pui.getDexUseInfoMap().size());
|
||||||
|
assertSecondaryUse(mFooUser0, pui, fooSecondaries, /*isUsedByOtherApps*/false, mUser0,
|
||||||
|
expectedContexts);
|
||||||
|
|
||||||
|
assertHasDclInfo(mFooUser0, mFooUser0, fooSecondaries);
|
||||||
|
}
|
||||||
|
|
||||||
private void assertSecondaryUse(TestData testData, PackageUseInfo pui,
|
private void assertSecondaryUse(TestData testData, PackageUseInfo pui,
|
||||||
List<String> secondaries, boolean isUsedByOtherApps, int ownerUserId,
|
List<String> secondaries, boolean isUsedByOtherApps, int ownerUserId,
|
||||||
String[] expectedContexts) {
|
String[] expectedContexts) {
|
||||||
@@ -572,17 +627,43 @@ public class DexManagerTests {
|
|||||||
// By default, assume a single class loader in the chain.
|
// By default, assume a single class loader in the chain.
|
||||||
// This makes writing tests much easier.
|
// This makes writing tests much easier.
|
||||||
List<String> classLoaders = Arrays.asList(testData.mClassLoader);
|
List<String> classLoaders = Arrays.asList(testData.mClassLoader);
|
||||||
List<String> classPaths = (dexPaths == null)
|
List<String> classPaths = dexPaths != null
|
||||||
? Arrays.asList((String) null)
|
? Arrays.<String>asList(String.join(File.pathSeparator, dexPaths)) : null;
|
||||||
: Arrays.asList(String.join(File.pathSeparator, dexPaths));
|
|
||||||
notifyDexLoad(testData, classLoaders, classPaths, loaderUserId);
|
notifyDexLoad(testData, classLoaders, classPaths, loaderUserId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void notifyDexLoad(TestData testData, List<String> classLoaders,
|
private void notifyDexLoad(TestData testData, List<String> classLoaders,
|
||||||
List<String> classPaths, int loaderUserId) {
|
List<String> classPaths, int loaderUserId) {
|
||||||
|
String[] classLoaderContexts = computeClassLoaderContexts(classLoaders, classPaths);
|
||||||
// We call the internal function so any exceptions thrown cause test failures.
|
// We call the internal function so any exceptions thrown cause test failures.
|
||||||
mDexManager.notifyDexLoadInternal(testData.mPackageInfo.applicationInfo, classLoaders,
|
List<String> dexPaths = classPaths != null
|
||||||
classPaths, testData.mLoaderIsa, loaderUserId);
|
? Arrays.asList(classPaths.get(0).split(File.pathSeparator)) : Arrays.asList();
|
||||||
|
notifyDexLoad(testData, dexPaths, classLoaderContexts, loaderUserId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void notifyDexLoad(TestData testData, List<String> dexPaths,
|
||||||
|
String[] classLoaderContexts, int loaderUserId) {
|
||||||
|
assertTrue(dexPaths.size() == classLoaderContexts.length);
|
||||||
|
HashMap<String, String> dexPathMapping = new HashMap<>(dexPaths.size());
|
||||||
|
for (int i = 0; i < dexPaths.size(); i++) {
|
||||||
|
dexPathMapping.put(dexPaths.get(i), classLoaderContexts != null
|
||||||
|
? classLoaderContexts[i] : PackageDexUsage.UNSUPPORTED_CLASS_LOADER_CONTEXT);
|
||||||
|
}
|
||||||
|
mDexManager.notifyDexLoadInternal(testData.mPackageInfo.applicationInfo, dexPathMapping,
|
||||||
|
testData.mLoaderIsa, loaderUserId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String[] computeClassLoaderContexts(List<String> classLoaders,
|
||||||
|
List<String> classPaths) {
|
||||||
|
if (classPaths == null) {
|
||||||
|
return new String[0];
|
||||||
|
}
|
||||||
|
String[] results = DexoptUtils.processContextForDexLoad(classLoaders, classPaths);
|
||||||
|
if (results == null) {
|
||||||
|
results = new String[classPaths.get(0).split(File.pathSeparator).length];
|
||||||
|
Arrays.fill(results, PackageDexUsage.UNSUPPORTED_CLASS_LOADER_CONTEXT);
|
||||||
|
}
|
||||||
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
private PackageUseInfo getPackageUseInfo(TestData testData) {
|
private PackageUseInfo getPackageUseInfo(TestData testData) {
|
||||||
|
|||||||
Reference in New Issue
Block a user