summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNarayan Kamath <narayan@google.com>2017-06-15 11:35:38 +0100
committerNarayan Kamath <narayan@google.com>2017-07-06 11:57:36 +0100
commitf9419f0f8524da4980726e06130a80e0fb226763 (patch)
tree25a46090eff09b9f258d5b255464ca8291cbe022
parentc276c79e83680c4d14e790ba002c14e4a7dacb49 (diff)
Allow splits to declare a classloader type.
Add an attribute android:classLoader which can be set to the name of the classloader the base or split wishes to use. For now, this can only be set to "dalvik.system.PathClassLoader" or "dalvik.system.DelegateLastClassLoader". The current implementation only allows the classloader to exist in the boot classpath but that restriction can be loosened in a future change if necessary. Test: cts-tradefed run cts-dev -m CtsAppSecurityHostTestCases Bug: 36044779 Change-Id: I5a51f7aa0c8c4c398d2f46129eb06785cc8e3c3f
-rw-r--r--api/current.txt1
-rw-r--r--api/system-current.txt1
-rw-r--r--api/test-current.txt1
-rw-r--r--cmds/pm/src/com/android/commands/pm/Pm.java2
-rw-r--r--core/java/android/app/ApplicationLoaders.java35
-rw-r--r--core/java/android/app/LoadedApk.java19
-rw-r--r--core/java/android/content/pm/ApplicationInfo.java19
-rw-r--r--core/java/android/content/pm/PackageParser.java49
-rw-r--r--core/java/com/android/internal/os/ClassLoaderFactory.java92
-rw-r--r--core/java/com/android/internal/os/PathClassLoaderFactory.java66
-rw-r--r--core/java/com/android/internal/os/ZygoteInit.java16
-rw-r--r--core/jni/Android.bp2
-rw-r--r--core/jni/AndroidRuntime.cpp4
-rw-r--r--core/jni/com_android_internal_os_ClassLoaderFactory.cpp (renamed from core/jni/com_android_internal_os_PathClassLoaderFactory.cpp)6
-rw-r--r--core/res/res/values/attrs_manifest.xml19
-rw-r--r--core/res/res/values/public.xml1
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java2
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerShellCommand.java2
18 files changed, 205 insertions, 132 deletions
diff --git a/api/current.txt b/api/current.txt
index 38bb68be55a9..cfec8d27258c 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -388,6 +388,7 @@ package android {
field public static final int childIndicatorRight = 16843024; // 0x1010110
field public static final int childIndicatorStart = 16843731; // 0x10103d3
field public static final int choiceMode = 16843051; // 0x101012b
+ field public static final int classLoader = 16844139; // 0x101056b
field public static final int clearTaskOnLaunch = 16842773; // 0x1010015
field public static final int clickable = 16842981; // 0x10100e5
field public static final int clipChildren = 16842986; // 0x10100ea
diff --git a/api/system-current.txt b/api/system-current.txt
index d98319cc9c79..de632fda8b23 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -511,6 +511,7 @@ package android {
field public static final int childIndicatorRight = 16843024; // 0x1010110
field public static final int childIndicatorStart = 16843731; // 0x10103d3
field public static final int choiceMode = 16843051; // 0x101012b
+ field public static final int classLoader = 16844139; // 0x101056b
field public static final int clearTaskOnLaunch = 16842773; // 0x1010015
field public static final int clickable = 16842981; // 0x10100e5
field public static final int clipChildren = 16842986; // 0x10100ea
diff --git a/api/test-current.txt b/api/test-current.txt
index 94bfe84c88d0..4616c0bd859a 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -388,6 +388,7 @@ package android {
field public static final int childIndicatorRight = 16843024; // 0x1010110
field public static final int childIndicatorStart = 16843731; // 0x10103d3
field public static final int choiceMode = 16843051; // 0x101012b
+ field public static final int classLoader = 16844139; // 0x101056b
field public static final int clearTaskOnLaunch = 16842773; // 0x1010015
field public static final int clickable = 16842981; // 0x10100e5
field public static final int clipChildren = 16842986; // 0x10100ea
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index ad989dee7b55..f0189c240a85 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -414,7 +414,7 @@ public final class Pm {
try {
ApkLite baseApk = PackageParser.parseApkLite(file, 0);
PackageLite pkgLite = new PackageLite(null, baseApk, null, null, null, null,
- null, null);
+ null, null, null);
params.sessionParams.setSize(
PackageHelper.calculateInstalledSize(pkgLite, false,
params.sessionParams.abiOverride));
diff --git a/core/java/android/app/ApplicationLoaders.java b/core/java/android/app/ApplicationLoaders.java
index 2062930929a2..b7c1f4e082e2 100644
--- a/core/java/android/app/ApplicationLoaders.java
+++ b/core/java/android/app/ApplicationLoaders.java
@@ -18,9 +18,8 @@ package android.app;
import android.os.Build;
import android.os.Trace;
-import android.text.TextUtils;
import android.util.ArrayMap;
-import com.android.internal.os.PathClassLoaderFactory;
+import com.android.internal.os.ClassLoaderFactory;
import dalvik.system.PathClassLoader;
/** @hide */
@@ -31,15 +30,16 @@ public class ApplicationLoaders {
ClassLoader getClassLoader(String zip, int targetSdkVersion, boolean isBundled,
String librarySearchPath, String libraryPermittedPath,
- ClassLoader parent) {
+ ClassLoader parent, String classLoaderName) {
// For normal usage the cache key used is the same as the zip path.
return getClassLoader(zip, targetSdkVersion, isBundled, librarySearchPath,
- libraryPermittedPath, parent, zip);
+ libraryPermittedPath, parent, zip, classLoaderName);
}
private ClassLoader getClassLoader(String zip, int targetSdkVersion, boolean isBundled,
String librarySearchPath, String libraryPermittedPath,
- ClassLoader parent, String cacheKey) {
+ ClassLoader parent, String cacheKey,
+ String classLoaderName) {
/*
* This is the parent we use if they pass "null" in. In theory
* this should be the "system" class loader; in practice we
@@ -66,28 +66,25 @@ public class ApplicationLoaders {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, zip);
- PathClassLoader pathClassloader = PathClassLoaderFactory.createClassLoader(
- zip,
- librarySearchPath,
- libraryPermittedPath,
- parent,
- targetSdkVersion,
- isBundled);
+ ClassLoader classloader = ClassLoaderFactory.createClassLoader(
+ zip, librarySearchPath, libraryPermittedPath, parent,
+ targetSdkVersion, isBundled, classLoaderName);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "setupVulkanLayerPath");
- setupVulkanLayerPath(pathClassloader, librarySearchPath);
+ setupVulkanLayerPath(classloader, librarySearchPath);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- mLoaders.put(cacheKey, pathClassloader);
- return pathClassloader;
+ mLoaders.put(cacheKey, classloader);
+ return classloader;
}
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, zip);
- PathClassLoader pathClassloader = new PathClassLoader(zip, parent);
+ ClassLoader loader = ClassLoaderFactory.createClassLoader(
+ zip, null, parent, classLoaderName);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- return pathClassloader;
+ return loader;
}
}
@@ -105,7 +102,7 @@ public class ApplicationLoaders {
// The cache key is passed separately to enable the stub WebView to be cached under the
// stub's APK path, when the actual package path is the donor APK.
return getClassLoader(packagePath, Build.VERSION.SDK_INT, false, libsPath, null, null,
- cacheKey);
+ cacheKey, null /* classLoaderName */);
}
private static native void setupVulkanLayerPath(ClassLoader classLoader, String librarySearchPath);
@@ -122,7 +119,7 @@ public class ApplicationLoaders {
baseDexClassLoader.addDexPath(dexPath);
}
- private final ArrayMap<String, ClassLoader> mLoaders = new ArrayMap<String, ClassLoader>();
+ private final ArrayMap<String, ClassLoader> mLoaders = new ArrayMap<>();
private static final ApplicationLoaders gApplicationLoaders = new ApplicationLoaders();
}
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 79e5407a17d3..b38be6626e50 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -97,7 +97,6 @@ public final class LoadedApk {
private String mAppDir;
private String mResDir;
private String[] mOverlayDirs;
- private String[] mSharedLibraries;
private String mDataDir;
private String mLibDir;
private File mDataDirFile;
@@ -116,6 +115,7 @@ public final class LoadedApk {
private String[] mSplitNames;
private String[] mSplitAppDirs;
private String[] mSplitResDirs;
+ private String[] mSplitClassLoaderNames;
private final ArrayMap<Context, ArrayMap<BroadcastReceiver, ReceiverDispatcher>> mReceivers
= new ArrayMap<>();
@@ -126,8 +126,6 @@ public final class LoadedApk {
private final ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>> mUnboundServices
= new ArrayMap<>();
- int mClientCount = 0;
-
Application getApplication() {
return mApplication;
}
@@ -192,8 +190,8 @@ public final class LoadedApk {
mResDir = null;
mSplitAppDirs = null;
mSplitResDirs = null;
+ mSplitClassLoaderNames = null;
mOverlayDirs = null;
- mSharedLibraries = null;
mDataDir = null;
mDataDirFile = null;
mDeviceProtectedDataDirFile = null;
@@ -324,7 +322,6 @@ public final class LoadedApk {
mAppDir = aInfo.sourceDir;
mResDir = aInfo.uid == myUid ? aInfo.sourceDir : aInfo.publicSourceDir;
mOverlayDirs = aInfo.resourceDirs;
- mSharedLibraries = aInfo.sharedLibraryFiles;
mDataDir = aInfo.dataDir;
mLibDir = aInfo.nativeLibraryDir;
mDataDirFile = FileUtils.newFileOrNull(aInfo.dataDir);
@@ -334,6 +331,7 @@ public final class LoadedApk {
mSplitNames = aInfo.splitNames;
mSplitAppDirs = aInfo.splitSourceDirs;
mSplitResDirs = aInfo.uid == myUid ? aInfo.splitSourceDirs : aInfo.splitPublicSourceDirs;
+ mSplitClassLoaderNames = aInfo.splitClassLoaderNames;
if (aInfo.requestsIsolatedSplitLoading() && !ArrayUtils.isEmpty(mSplitNames)) {
mSplitLoader = new SplitDependencyLoaderImpl(aInfo.splitDependencies);
@@ -530,7 +528,8 @@ public final class LoadedApk {
// Since we handled the special base case above, parentSplitIdx is always valid.
final ClassLoader parent = mCachedClassLoaders[parentSplitIdx];
mCachedClassLoaders[splitIdx] = ApplicationLoaders.getDefault().getClassLoader(
- mSplitAppDirs[splitIdx - 1], getTargetSdkVersion(), false, null, null, parent);
+ mSplitAppDirs[splitIdx - 1], getTargetSdkVersion(), false, null, null, parent,
+ mSplitClassLoaderNames[splitIdx - 1]);
Collections.addAll(splitPaths, mCachedResourcePaths[parentSplitIdx]);
splitPaths.add(mSplitResDirs[splitIdx - 1]);
@@ -650,8 +649,9 @@ public final class LoadedApk {
if (mClassLoader == null) {
StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
mClassLoader = ApplicationLoaders.getDefault().getClassLoader(
- "" /* codePath */, mApplicationInfo.targetSdkVersion, isBundledApp,
- librarySearchPath, libraryPermittedPath, mBaseClassLoader);
+ "" /* codePath */, mApplicationInfo.targetSdkVersion, isBundledApp,
+ librarySearchPath, libraryPermittedPath, mBaseClassLoader,
+ null /* classLoaderName */);
StrictMode.setThreadPolicy(oldPolicy);
}
@@ -678,7 +678,8 @@ public final class LoadedApk {
mClassLoader = ApplicationLoaders.getDefault().getClassLoader(zip,
mApplicationInfo.targetSdkVersion, isBundledApp, librarySearchPath,
- libraryPermittedPath, mBaseClassLoader);
+ libraryPermittedPath, mBaseClassLoader,
+ mApplicationInfo.classLoaderName);
StrictMode.setThreadPolicy(oldPolicy);
// Setup the class loader paths for profiling.
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 06f7916be4b6..0bfe56797d22 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -1005,6 +1005,12 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
}
}
+ /** @hide */
+ public String classLoaderName;
+
+ /** @hide */
+ public String[] splitClassLoaderNames;
+
public void dump(Printer pw, String prefix) {
dump(pw, prefix, DUMP_FLAG_ALL);
}
@@ -1056,6 +1062,13 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
pw.println(prefix + "sharedLibraryFiles=" + Arrays.toString(sharedLibraryFiles));
}
}
+ if (classLoaderName != null) {
+ pw.println(prefix + "classLoaderName=" + classLoaderName);
+ }
+ if (!ArrayUtils.isEmpty(splitClassLoaderNames)) {
+ pw.println(prefix + "splitClassLoaderNames=" + Arrays.toString(splitClassLoaderNames));
+ }
+
pw.println(prefix + "enabled=" + enabled
+ " minSdkVersion=" + minSdkVersion
+ " targetSdkVersion=" + targetSdkVersion
@@ -1178,6 +1191,8 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
networkSecurityConfigRes = orig.networkSecurityConfigRes;
category = orig.category;
targetSandboxVersion = orig.targetSandboxVersion;
+ classLoaderName = orig.classLoaderName;
+ splitClassLoaderNames = orig.splitClassLoaderNames;
}
public String toString() {
@@ -1246,6 +1261,8 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
dest.writeInt(networkSecurityConfigRes);
dest.writeInt(category);
dest.writeInt(targetSandboxVersion);
+ dest.writeString(classLoaderName);
+ dest.writeStringArray(splitClassLoaderNames);
}
public static final Parcelable.Creator<ApplicationInfo> CREATOR
@@ -1311,6 +1328,8 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
networkSecurityConfigRes = source.readInt();
category = source.readInt();
targetSandboxVersion = source.readInt();
+ classLoaderName = source.readString();
+ splitClassLoaderNames = source.readStringArray();
}
/**
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index e4f2fc1c5035..eb6e0d8bd4de 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -88,6 +88,7 @@ import android.view.Gravity;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.ClassLoaderFactory;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.XmlUtils;
@@ -415,9 +416,12 @@ public class PackageParser {
public final boolean extractNativeLibs;
public final boolean isolatedSplits;
+ public final String classLoaderName;
+ public final String[] splitClassLoaderNames;
+
public PackageLite(String codePath, ApkLite baseApk, String[] splitNames,
boolean[] isFeatureSplits, String[] usesSplitNames, String[] configForSplit,
- String[] splitCodePaths, int[] splitRevisionCodes) {
+ String[] splitCodePaths, int[] splitRevisionCodes, String[] splitClassLoaderNames) {
this.packageName = baseApk.packageName;
this.versionCode = baseApk.versionCode;
this.installLocation = baseApk.installLocation;
@@ -437,6 +441,9 @@ public class PackageParser {
this.use32bitAbi = baseApk.use32bitAbi;
this.extractNativeLibs = baseApk.extractNativeLibs;
this.isolatedSplits = baseApk.isolatedSplits;
+
+ this.classLoaderName = baseApk.classLoaderName;
+ this.splitClassLoaderNames = splitClassLoaderNames;
}
public List<String> getAllCodePaths() {
@@ -471,13 +478,14 @@ public class PackageParser {
public final boolean use32bitAbi;
public final boolean extractNativeLibs;
public final boolean isolatedSplits;
+ public final String classLoaderName;
public ApkLite(String codePath, String packageName, String splitName, boolean isFeatureSplit,
String configForSplit, String usesSplitName, int versionCode, int revisionCode,
int installLocation, List<VerifierInfo> verifiers, Signature[] signatures,
Certificate[][] certificates, boolean coreApp, boolean debuggable,
boolean multiArch, boolean use32bitAbi, boolean extractNativeLibs,
- boolean isolatedSplits) {
+ boolean isolatedSplits, String classLoaderName) {
this.codePath = codePath;
this.packageName = packageName;
this.splitName = splitName;
@@ -496,6 +504,7 @@ public class PackageParser {
this.use32bitAbi = use32bitAbi;
this.extractNativeLibs = extractNativeLibs;
this.isolatedSplits = isolatedSplits;
+ this.classLoaderName = classLoaderName;
}
}
@@ -863,7 +872,7 @@ public class PackageParser {
final ApkLite baseApk = parseApkLite(packageFile, flags);
final String packagePath = packageFile.getAbsolutePath();
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- return new PackageLite(packagePath, baseApk, null, null, null, null, null, null);
+ return new PackageLite(packagePath, baseApk, null, null, null, null, null, null, null);
}
static PackageLite parseClusterPackageLite(File packageDir, int flags)
@@ -926,6 +935,7 @@ public class PackageParser {
String[] configForSplits = null;
String[] splitCodePaths = null;
int[] splitRevisionCodes = null;
+ String[] splitClassLoaderNames = null;
if (size > 0) {
splitNames = new String[size];
isFeatureSplits = new boolean[size];
@@ -933,6 +943,7 @@ public class PackageParser {
configForSplits = new String[size];
splitCodePaths = new String[size];
splitRevisionCodes = new int[size];
+ splitClassLoaderNames = new String[size];
splitNames = apks.keySet().toArray(splitNames);
Arrays.sort(splitNames, sSplitNameComparator);
@@ -944,12 +955,13 @@ public class PackageParser {
configForSplits[i] = apk.configForSplit;
splitCodePaths[i] = apk.codePath;
splitRevisionCodes[i] = apk.revisionCode;
+ splitClassLoaderNames[i] = apk.classLoaderName;
}
}
final String codePath = packageDir.getAbsolutePath();
return new PackageLite(codePath, baseApk, splitNames, isFeatureSplits, usesSplitNames,
- configForSplits, splitCodePaths, splitRevisionCodes);
+ configForSplits, splitCodePaths, splitRevisionCodes, splitClassLoaderNames);
}
/**
@@ -1187,6 +1199,8 @@ public class PackageParser {
pkg.splitPrivateFlags = new int[num];
pkg.applicationInfo.splitNames = pkg.splitNames;
pkg.applicationInfo.splitDependencies = splitDependencies;
+ pkg.applicationInfo.classLoaderName = lite.classLoaderName;
+ pkg.applicationInfo.splitClassLoaderNames = lite.splitClassLoaderNames;
for (int i = 0; i < num; i++) {
final AssetManager splitAssets = assetLoader.getSplitAssetManager(i);
@@ -1697,7 +1711,7 @@ public class PackageParser {
}
final AttributeSet attrs = parser;
- return parseApkLite(apkPath, parser, attrs, flags, signatures, certificates);
+ return parseApkLite(apkPath, parser, attrs, signatures, certificates);
} catch (XmlPullParserException | IOException | RuntimeException e) {
Slog.w(TAG, "Failed to parse " + apkPath, e);
@@ -1784,7 +1798,7 @@ public class PackageParser {
}
private static ApkLite parseApkLite(String codePath, XmlPullParser parser, AttributeSet attrs,
- int flags, Signature[] signatures, Certificate[][] certificates)
+ Signature[] signatures, Certificate[][] certificates)
throws IOException, XmlPullParserException, PackageParserException {
final Pair<String, String> packageSplit = parsePackageSplitNames(parser, attrs);
@@ -1800,6 +1814,7 @@ public class PackageParser {
boolean isFeatureSplit = false;
String configForSplit = null;
String usesSplitName = null;
+ String classLoaderName = null;
for (int i = 0; i < attrs.getAttributeCount(); i++) {
final String attr = attrs.getAttributeName(i);
@@ -1856,6 +1871,14 @@ public class PackageParser {
if ("extractNativeLibs".equals(attr)) {
extractNativeLibs = attrs.getAttributeBooleanValue(i, true);
}
+ if ("classLoader".equals(attr)) {
+ classLoaderName = attrs.getAttributeValue(i);
+ if (!ClassLoaderFactory.isValidClassLoaderName(classLoaderName)) {
+ throw new PackageParserException(
+ PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+ "Invalid class loader name: " + classLoaderName);
+ }
+ }
}
} else if (TAG_USES_SPLIT.equals(parser.getName())) {
if (usesSplitName != null) {
@@ -1875,19 +1898,7 @@ public class PackageParser {
return new ApkLite(codePath, packageSplit.first, packageSplit.second, isFeatureSplit,
configForSplit, usesSplitName, versionCode, revisionCode, installLocation,
verifiers, signatures, certificates, coreApp, debuggable, multiArch, use32bitAbi,
- extractNativeLibs, isolatedSplits);
- }
-
- /**
- * Temporary.
- */
- static public Signature stringToSignature(String str) {
- final int N = str.length();
- byte[] sig = new byte[N];
- for (int i=0; i<N; i++) {
- sig[i] = (byte)str.charAt(i);
- }
- return new Signature(sig);
+ extractNativeLibs, isolatedSplits, classLoaderName);
}
/**
diff --git a/core/java/com/android/internal/os/ClassLoaderFactory.java b/core/java/com/android/internal/os/ClassLoaderFactory.java
new file mode 100644
index 000000000000..0c041f24b28d
--- /dev/null
+++ b/core/java/com/android/internal/os/ClassLoaderFactory.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import android.os.Trace;
+
+import dalvik.system.DelegateLastClassLoader;
+import dalvik.system.PathClassLoader;
+
+/**
+ * Creates class loaders.
+ *
+ * @hide
+ */
+public class ClassLoaderFactory {
+ // Unconstructable
+ private ClassLoaderFactory() {}
+
+ private static final String PATH_CLASS_LOADER_NAME = PathClassLoader.class.getName();
+ private static final String DELEGATE_LAST_CLASS_LOADER_NAME =
+ DelegateLastClassLoader.class.getName();
+
+ /**
+ * Returns true if {@code name} is a supported classloader. {@code name} must be a
+ * binary name of a class, as defined by {@code Class.getName}.
+ */
+ public static boolean isValidClassLoaderName(String name) {
+ return PATH_CLASS_LOADER_NAME.equals(name) ||
+ DELEGATE_LAST_CLASS_LOADER_NAME.equals(name);
+ }
+
+ /**
+ * Same as {@code createClassLoader} below, except that no associated namespace
+ * is created.
+ */
+ public static ClassLoader createClassLoader(String dexPath,
+ String librarySearchPath, ClassLoader parent, String classloaderName) {
+ if (classloaderName == null || PATH_CLASS_LOADER_NAME.equals(classloaderName)) {
+ return new PathClassLoader(dexPath, librarySearchPath, parent);
+ } else if (DELEGATE_LAST_CLASS_LOADER_NAME.equals(classloaderName)) {
+ return new DelegateLastClassLoader(dexPath, librarySearchPath, parent);
+ }
+
+ throw new AssertionError("Invalid classLoaderName: " + classloaderName);
+ }
+
+ /**
+ * Create a ClassLoader and initialize a linker-namespace for it.
+ */
+ public static ClassLoader createClassLoader(String dexPath,
+ String librarySearchPath, String libraryPermittedPath, ClassLoader parent,
+ int targetSdkVersion, boolean isNamespaceShared, String classloaderName) {
+
+ final ClassLoader classLoader = createClassLoader(dexPath, librarySearchPath, parent,
+ classloaderName);
+
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "createClassloaderNamespace");
+ String errorMessage = createClassloaderNamespace(classLoader,
+ targetSdkVersion,
+ librarySearchPath,
+ libraryPermittedPath,
+ isNamespaceShared);
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+
+ if (errorMessage != null) {
+ throw new UnsatisfiedLinkError("Unable to create namespace for the classloader " +
+ classLoader + ": " + errorMessage);
+ }
+
+ return classLoader;
+ }
+
+ private static native String createClassloaderNamespace(ClassLoader classLoader,
+ int targetSdkVersion,
+ String librarySearchPath,
+ String libraryPermittedPath,
+ boolean isNamespaceShared);
+}
diff --git a/core/java/com/android/internal/os/PathClassLoaderFactory.java b/core/java/com/android/internal/os/PathClassLoaderFactory.java
deleted file mode 100644
index 06a93b254bf5..000000000000
--- a/core/java/com/android/internal/os/PathClassLoaderFactory.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.os;
-
-import android.os.Trace;
-
-import dalvik.system.PathClassLoader;
-
-/**
- * Creates path class loaders.
- *
- * @hide
- */
-public class PathClassLoaderFactory {
- // Unconstructable
- private PathClassLoaderFactory() {}
-
- /**
- * Create a PathClassLoader and initialize a linker-namespace for it.
- *
- * @hide
- */
- public static PathClassLoader createClassLoader(String dexPath,
- String librarySearchPath,
- String libraryPermittedPath,
- ClassLoader parent,
- int targetSdkVersion,
- boolean isNamespaceShared) {
- PathClassLoader pathClassloader = new PathClassLoader(dexPath, librarySearchPath, parent);
-
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "createClassloaderNamespace");
- String errorMessage = createClassloaderNamespace(pathClassloader,
- targetSdkVersion,
- librarySearchPath,
- libraryPermittedPath,
- isNamespaceShared);
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
-
- if (errorMessage != null) {
- throw new UnsatisfiedLinkError("Unable to create namespace for the classloader " +
- pathClassloader + ": " + errorMessage);
- }
-
- return pathClassloader;
- }
-
- private static native String createClassloaderNamespace(ClassLoader classLoader,
- int targetSdkVersion,
- String librarySearchPath,
- String libraryPermittedPath,
- boolean isNamespaceShared);
-}
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 385976c187ed..77cd984bb4ef 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -24,7 +24,6 @@ import android.content.res.TypedArray;
import android.icu.impl.CacheValue;
import android.icu.text.DecimalFormatSymbols;
import android.icu.util.ULocale;
-import android.net.LocalServerSocket;
import android.opengl.EGL14;
import android.os.Build;
import android.os.IInstalld;
@@ -517,15 +516,12 @@ public class ZygoteInit {
* namespace, i.e., this classloader can access platform-private native libraries. The
* classloader will use java.library.path as the native library path.
*/
- static PathClassLoader createPathClassLoader(String classPath, int targetSdkVersion) {
- String libraryPath = System.getProperty("java.library.path");
-
- return PathClassLoaderFactory.createClassLoader(classPath,
- libraryPath,
- libraryPath,
- ClassLoader.getSystemClassLoader(),
- targetSdkVersion,
- true /* isNamespaceShared */);
+ static ClassLoader createPathClassLoader(String classPath, int targetSdkVersion) {
+ String libraryPath = System.getProperty("java.library.path");
+
+ return ClassLoaderFactory.createClassLoader(classPath, libraryPath, libraryPath,
+ ClassLoader.getSystemClassLoader(), targetSdkVersion, true /* isNamespaceShared */,
+ null /* classLoaderName */);
}
/**
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index c4533c36d0f2..44dfb33effce 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -185,8 +185,8 @@ cc_library_shared {
"android_content_res_Configuration.cpp",
"android_animation_PropertyValuesHolder.cpp",
"com_android_internal_net_NetworkStatsFactory.cpp",
+ "com_android_internal_os_ClassLoaderFactory.cpp",
"com_android_internal_os_FuseAppLoop.cpp",
- "com_android_internal_os_PathClassLoaderFactory.cpp",
"com_android_internal_os_Zygote.cpp",
"com_android_internal_util_VirtualRefBasePtr.cpp",
"com_android_internal_view_animation_NativeInterpolatorFactoryHelper.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 89e137b7c399..30d4e0276bf0 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -203,8 +203,8 @@ extern int register_android_content_res_Configuration(JNIEnv* env);
extern int register_android_animation_PropertyValuesHolder(JNIEnv *env);
extern int register_com_android_internal_content_NativeLibraryHelper(JNIEnv *env);
extern int register_com_android_internal_net_NetworkStatsFactory(JNIEnv *env);
+extern int register_com_android_internal_os_ClassLoaderFactory(JNIEnv* env);
extern int register_com_android_internal_os_FuseAppLoop(JNIEnv* env);
-extern int register_com_android_internal_os_PathClassLoaderFactory(JNIEnv* env);
extern int register_com_android_internal_os_Zygote(JNIEnv *env);
extern int register_com_android_internal_util_VirtualRefBasePtr(JNIEnv *env);
@@ -1402,7 +1402,7 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_net_TrafficStats),
REG_JNI(register_android_os_MemoryFile),
REG_JNI(register_android_os_SharedMemory),
- REG_JNI(register_com_android_internal_os_PathClassLoaderFactory),
+ REG_JNI(register_com_android_internal_os_ClassLoaderFactory),
REG_JNI(register_com_android_internal_os_Zygote),
REG_JNI(register_com_android_internal_util_VirtualRefBasePtr),
REG_JNI(register_android_hardware_Camera),
diff --git a/core/jni/com_android_internal_os_PathClassLoaderFactory.cpp b/core/jni/com_android_internal_os_ClassLoaderFactory.cpp
index a7a912cd0e05..7052e5f3ce4b 100644
--- a/core/jni/com_android_internal_os_PathClassLoaderFactory.cpp
+++ b/core/jni/com_android_internal_os_ClassLoaderFactory.cpp
@@ -39,13 +39,13 @@ static const JNINativeMethod g_methods[] = {
reinterpret_cast<void*>(createClassloaderNamespace_native) },
};
-static const char* const kPathClassLoaderFactoryPathName = "com/android/internal/os/PathClassLoaderFactory";
+static const char* const kClassLoaderFactoryPathName = "com/android/internal/os/ClassLoaderFactory";
namespace android
{
-int register_com_android_internal_os_PathClassLoaderFactory(JNIEnv* env) {
- return RegisterMethodsOrDie(env, kPathClassLoaderFactoryPathName, g_methods, NELEM(g_methods));
+int register_com_android_internal_os_ClassLoaderFactory(JNIEnv* env) {
+ return RegisterMethodsOrDie(env, kClassLoaderFactoryPathName, g_methods, NELEM(g_methods));
}
} // namespace android
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 01b8e1558fb3..33f69b47af36 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1051,6 +1051,22 @@
<p>The default value of this attribute is <code>false</code>. -->
<attr name="isolatedSplits" format="boolean" />
+ <!-- The classname of the classloader used to load the application's classes
+ from its APK. The APK in question can either be the 'base' APK or any
+ of the application's 'split' APKs if it's using a feature split.
+
+ <p>
+ The supported values for this attribute are
+ <code>dalvik.system.PathClassLoader</code> and
+ <code>dalvik.system.DelegateLastClassLoader</code>. If unspecified,
+ the default value of this attribute is <code>dalvik.system.PathClassLoader</code>.
+
+ If an unknown classloader is provided, a PackageParserException with cause
+ <code>PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED</code> will be
+ thrown and the app will not be installed.
+ -->
+ <attr name="classLoader" format="string" />
+
<!-- If set to <code>true</code>, indicates to the platform that this APK is
a 'feature' split and that it implicitly depends on the base APK. This distinguishes
this split APK from a 'configuration' split, which provides resource overrides
@@ -1485,6 +1501,9 @@
<enum name="productivity" value="7" />
</attr>
+ <!-- Declares the kind of classloader this application's classes must be loaded with -->
+ <attr name="classLoader" />
+
</declare-styleable>
<!-- The <code>permission</code> tag declares a security permission that can be
used to control access from other packages to specific components or
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 2a4881d6b167..634d79a92a4b 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2845,6 +2845,7 @@
<public-group type="attr" first-id="0x01010569">
<public name="showWhenLocked"/>
<public name="turnScreenOn"/>
+ <public name="classLoader" />
</public-group>
<public-group type="style" first-id="0x010302e0">
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 5c54ba803a05..58237713d793 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -927,7 +927,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
// This is kind of hacky; we're creating a half-parsed package that is
// straddled between the inherited and staged APKs.
final PackageLite pkg = new PackageLite(null, baseApk, null, null, null, null,
- splitPaths.toArray(new String[splitPaths.size()]), null);
+ splitPaths.toArray(new String[splitPaths.size()]), null, null);
final boolean isForwardLocked =
(params.installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0;
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 20d7b28c55e1..9765113b7957 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -175,7 +175,7 @@ class PackageManagerShellCommand extends ShellCommand {
try {
ApkLite baseApk = PackageParser.parseApkLite(file, 0);
PackageLite pkgLite = new PackageLite(null, baseApk, null, null, null, null,
- null, null);
+ null, null, null);
params.sessionParams.setSize(PackageHelper.calculateInstalledSize(
pkgLite, false, params.sessionParams.abiOverride));
} catch (PackageParserException | IOException e) {