diff options
25 files changed, 1106 insertions, 153 deletions
diff --git a/api/current.txt b/api/current.txt index 16cfff2d84b8..c5bde1d62998 100644 --- a/api/current.txt +++ b/api/current.txt @@ -739,6 +739,7 @@ package android { field public static final int isScrollContainer = 16843342; // 0x101024e field public static final int isSticky = 16843335; // 0x1010247 field public static final int isolatedProcess = 16843689; // 0x10103a9 + field public static final int isolatedSplits = 16844109; // 0x101054d field public static final int itemBackground = 16843056; // 0x1010130 field public static final int itemIconDisabledAlpha = 16843057; // 0x1010131 field public static final int itemPadding = 16843565; // 0x101032d @@ -8356,6 +8357,7 @@ package android.content { method public abstract int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int); method public abstract deprecated void clearWallpaper() throws java.io.IOException; method public abstract android.content.Context createConfigurationContext(android.content.res.Configuration); + method public abstract android.content.Context createContextForSplit(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException; method public abstract android.content.Context createDeviceProtectedStorageContext(); method public abstract android.content.Context createDisplayContext(android.view.Display); method public abstract android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException; @@ -8554,6 +8556,7 @@ package android.content { method public int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int); method public deprecated void clearWallpaper() throws java.io.IOException; method public android.content.Context createConfigurationContext(android.content.res.Configuration); + method public android.content.Context createContextForSplit(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException; method public android.content.Context createDeviceProtectedStorageContext(); method public android.content.Context createDisplayContext(android.view.Display); method public android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException; @@ -9738,6 +9741,7 @@ package android.content.pm { field public int requiresSmallestWidthDp; field public java.lang.String[] sharedLibraryFiles; field public java.lang.String sourceDir; + field public java.lang.String[] splitNames; field public java.lang.String[] splitPublicSourceDirs; field public java.lang.String[] splitSourceDirs; field public int targetSdkVersion; @@ -9820,6 +9824,7 @@ package android.content.pm { field public boolean handleProfiling; field public java.lang.String publicSourceDir; field public java.lang.String sourceDir; + field public java.lang.String[] splitNames; field public java.lang.String[] splitPublicSourceDirs; field public java.lang.String[] splitSourceDirs; field public java.lang.String targetPackage; @@ -29952,6 +29957,7 @@ package android.os { method public final android.util.SizeF readSizeF(); method public final android.util.SparseArray readSparseArray(java.lang.ClassLoader); method public final android.util.SparseBooleanArray readSparseBooleanArray(); + method public final android.util.SparseIntArray readSparseIntArray(); method public final java.lang.String readString(); method public final void readStringArray(java.lang.String[]); method public final void readStringList(java.util.List<java.lang.String>); @@ -29996,6 +30002,7 @@ package android.os { method public final void writeSizeF(android.util.SizeF); method public final void writeSparseArray(android.util.SparseArray<java.lang.Object>); method public final void writeSparseBooleanArray(android.util.SparseBooleanArray); + method public final void writeSparseIntArray(android.util.SparseIntArray); method public final void writeString(java.lang.String); method public final void writeStringArray(java.lang.String[]); method public final void writeStringList(java.util.List<java.lang.String>); @@ -39236,6 +39243,7 @@ package android.test.mock { method public int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int); method public void clearWallpaper(); method public android.content.Context createConfigurationContext(android.content.res.Configuration); + method public android.content.Context createContextForSplit(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException; method public android.content.Context createDeviceProtectedStorageContext(); method public android.content.Context createDisplayContext(android.view.Display); method public android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException; diff --git a/api/system-current.txt b/api/system-current.txt index 066f9ce6366e..9da65a30be79 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -850,6 +850,7 @@ package android { field public static final int isScrollContainer = 16843342; // 0x101024e field public static final int isSticky = 16843335; // 0x1010247 field public static final int isolatedProcess = 16843689; // 0x10103a9 + field public static final int isolatedSplits = 16844109; // 0x101054d field public static final int itemBackground = 16843056; // 0x1010130 field public static final int itemIconDisabledAlpha = 16843057; // 0x1010131 field public static final int itemPadding = 16843565; // 0x101032d @@ -8738,6 +8739,7 @@ package android.content { method public abstract int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int); method public abstract deprecated void clearWallpaper() throws java.io.IOException; method public abstract android.content.Context createConfigurationContext(android.content.res.Configuration); + method public abstract android.content.Context createContextForSplit(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException; method public abstract android.content.Context createCredentialProtectedStorageContext(); method public abstract android.content.Context createDeviceProtectedStorageContext(); method public abstract android.content.Context createDisplayContext(android.view.Display); @@ -8948,6 +8950,7 @@ package android.content { method public int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int); method public deprecated void clearWallpaper() throws java.io.IOException; method public android.content.Context createConfigurationContext(android.content.res.Configuration); + method public android.content.Context createContextForSplit(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException; method public android.content.Context createCredentialProtectedStorageContext(); method public android.content.Context createDeviceProtectedStorageContext(); method public android.content.Context createDisplayContext(android.view.Display); @@ -10153,6 +10156,7 @@ package android.content.pm { field public int requiresSmallestWidthDp; field public java.lang.String[] sharedLibraryFiles; field public java.lang.String sourceDir; + field public java.lang.String[] splitNames; field public java.lang.String[] splitPublicSourceDirs; field public java.lang.String[] splitSourceDirs; field public int targetSdkVersion; @@ -10270,6 +10274,7 @@ package android.content.pm { field public boolean handleProfiling; field public java.lang.String publicSourceDir; field public java.lang.String sourceDir; + field public java.lang.String[] splitNames; field public java.lang.String[] splitPublicSourceDirs; field public java.lang.String[] splitSourceDirs; field public java.lang.String targetPackage; @@ -32668,6 +32673,7 @@ package android.os { method public final android.util.SizeF readSizeF(); method public final android.util.SparseArray readSparseArray(java.lang.ClassLoader); method public final android.util.SparseBooleanArray readSparseBooleanArray(); + method public final android.util.SparseIntArray readSparseIntArray(); method public final java.lang.String readString(); method public final void readStringArray(java.lang.String[]); method public final void readStringList(java.util.List<java.lang.String>); @@ -32712,6 +32718,7 @@ package android.os { method public final void writeSizeF(android.util.SizeF); method public final void writeSparseArray(android.util.SparseArray<java.lang.Object>); method public final void writeSparseBooleanArray(android.util.SparseBooleanArray); + method public final void writeSparseIntArray(android.util.SparseIntArray); method public final void writeString(java.lang.String); method public final void writeStringArray(java.lang.String[]); method public final void writeStringList(java.util.List<java.lang.String>); @@ -42587,6 +42594,7 @@ package android.test.mock { method public int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int); method public void clearWallpaper(); method public android.content.Context createConfigurationContext(android.content.res.Configuration); + method public android.content.Context createContextForSplit(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException; method public android.content.Context createCredentialProtectedStorageContext(); method public android.content.Context createDeviceProtectedStorageContext(); method public android.content.Context createDisplayContext(android.view.Display); diff --git a/api/test-current.txt b/api/test-current.txt index f0c628ceadac..c9f1d1c8f716 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -739,6 +739,7 @@ package android { field public static final int isScrollContainer = 16843342; // 0x101024e field public static final int isSticky = 16843335; // 0x1010247 field public static final int isolatedProcess = 16843689; // 0x10103a9 + field public static final int isolatedSplits = 16844109; // 0x101054d field public static final int itemBackground = 16843056; // 0x1010130 field public static final int itemIconDisabledAlpha = 16843057; // 0x1010131 field public static final int itemPadding = 16843565; // 0x101032d @@ -8379,6 +8380,7 @@ package android.content { method public abstract int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int); method public abstract deprecated void clearWallpaper() throws java.io.IOException; method public abstract android.content.Context createConfigurationContext(android.content.res.Configuration); + method public abstract android.content.Context createContextForSplit(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException; method public abstract android.content.Context createDeviceProtectedStorageContext(); method public abstract android.content.Context createDisplayContext(android.view.Display); method public abstract android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException; @@ -8578,6 +8580,7 @@ package android.content { method public int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int); method public deprecated void clearWallpaper() throws java.io.IOException; method public android.content.Context createConfigurationContext(android.content.res.Configuration); + method public android.content.Context createContextForSplit(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException; method public android.content.Context createDeviceProtectedStorageContext(); method public android.content.Context createDisplayContext(android.view.Display); method public android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException; @@ -9765,6 +9768,7 @@ package android.content.pm { field public int requiresSmallestWidthDp; field public java.lang.String[] sharedLibraryFiles; field public java.lang.String sourceDir; + field public java.lang.String[] splitNames; field public java.lang.String[] splitPublicSourceDirs; field public java.lang.String[] splitSourceDirs; field public int targetSdkVersion; @@ -9847,6 +9851,7 @@ package android.content.pm { field public boolean handleProfiling; field public java.lang.String publicSourceDir; field public java.lang.String sourceDir; + field public java.lang.String[] splitNames; field public java.lang.String[] splitPublicSourceDirs; field public java.lang.String[] splitSourceDirs; field public java.lang.String targetPackage; @@ -30063,6 +30068,7 @@ package android.os { method public final android.util.SizeF readSizeF(); method public final android.util.SparseArray readSparseArray(java.lang.ClassLoader); method public final android.util.SparseBooleanArray readSparseBooleanArray(); + method public final android.util.SparseIntArray readSparseIntArray(); method public final java.lang.String readString(); method public final void readStringArray(java.lang.String[]); method public final void readStringList(java.util.List<java.lang.String>); @@ -30107,6 +30113,7 @@ package android.os { method public final void writeSizeF(android.util.SizeF); method public final void writeSparseArray(android.util.SparseArray<java.lang.Object>); method public final void writeSparseBooleanArray(android.util.SparseBooleanArray); + method public final void writeSparseIntArray(android.util.SparseIntArray); method public final void writeString(java.lang.String); method public final void writeStringArray(java.lang.String[]); method public final void writeStringList(java.util.List<java.lang.String>); @@ -39357,6 +39364,7 @@ package android.test.mock { method public int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int); method public void clearWallpaper(); method public android.content.Context createConfigurationContext(android.content.res.Configuration); + method public android.content.Context createContextForSplit(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException; method public android.content.Context createDeviceProtectedStorageContext(); method public android.content.Context createDisplayContext(android.view.Display); method public android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException; diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java index ac5fea36ad2b..7015381dc4fb 100644 --- a/cmds/pm/src/com/android/commands/pm/Pm.java +++ b/cmds/pm/src/com/android/commands/pm/Pm.java @@ -408,7 +408,7 @@ public final class Pm { if (file.isFile()) { try { ApkLite baseApk = PackageParser.parseApkLite(file, 0); - PackageLite pkgLite = new PackageLite(null, baseApk, null, null, null); + PackageLite pkgLite = new PackageLite(null, baseApk, null, null, null, null); params.sessionParams.setSize( PackageHelper.calculateInstalledSize(pkgLite, false, params.sessionParams.abiOverride)); diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index d814ddcc7f27..34eaa0b91b34 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -2616,9 +2616,10 @@ public final class ActivityThread { r.activityInfo.targetActivity); } + ContextImpl appContext = createBaseContextForActivity(r); Activity activity = null; try { - java.lang.ClassLoader cl = r.packageInfo.getClassLoader(); + java.lang.ClassLoader cl = appContext.getClassLoader(); activity = mInstrumentation.newActivity( cl, component.getClassName(), r.intent); StrictMode.incrementExpectedActivityCount(activity.getClass()); @@ -2647,7 +2648,6 @@ public final class ActivityThread { + ", dir=" + r.packageInfo.getAppDir()); if (activity != null) { - Context appContext = createBaseContextForActivity(r, activity); CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager()); Configuration config = new Configuration(mCompatConfiguration); if (r.overrideConfig != null) { @@ -2661,6 +2661,7 @@ public final class ActivityThread { r.mPendingRemoveWindow = null; r.mPendingRemoveWindowManager = null; } + appContext.setOuterContext(activity); activity.attach(appContext, this, getInstrumentation(), r.token, r.ident, app, r.intent, r.activityInfo, title, r.parent, r.embeddedID, r.lastNonConfigurationInstances, config, @@ -2736,8 +2737,8 @@ public final class ActivityThread { return activity; } - private Context createBaseContextForActivity(ActivityClientRecord r, final Activity activity) { - int displayId = Display.DEFAULT_DISPLAY; + private ContextImpl createBaseContextForActivity(ActivityClientRecord r) { + final int displayId; try { displayId = ActivityManager.getService().getActivityDisplayId(r.token); } catch (RemoteException e) { @@ -2745,9 +2746,7 @@ public final class ActivityThread { } ContextImpl appContext = ContextImpl.createActivityContext( - this, r.packageInfo, r.token, displayId, r.overrideConfig); - appContext.setOuterContext(activity); - Context baseContext = appContext; + this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig); final DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance(); // For debugging purposes, if the activity's package name contains the value of @@ -2760,12 +2759,12 @@ public final class ActivityThread { if (id != Display.DEFAULT_DISPLAY) { Display display = dm.getCompatibleDisplay(id, appContext.getDisplayAdjustments(id)); - baseContext = appContext.createDisplayContext(display); + appContext = (ContextImpl) appContext.createDisplayContext(display); break; } } } - return baseContext; + return appContext; } private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) { @@ -3119,9 +3118,16 @@ public final class ActivityThread { IActivityManager mgr = ActivityManager.getService(); + Application app; BroadcastReceiver receiver; + ContextImpl context; try { - java.lang.ClassLoader cl = packageInfo.getClassLoader(); + app = packageInfo.makeApplication(false, mInstrumentation); + context = (ContextImpl) app.getBaseContext(); + if (data.info.splitName != null) { + context = (ContextImpl) context.createContextForSplit(data.info.splitName); + } + java.lang.ClassLoader cl = context.getClassLoader(); data.intent.setExtrasClassLoader(cl); data.intent.prepareToEnterProcess(); data.setExtrasClassLoader(cl); @@ -3136,8 +3142,6 @@ public final class ActivityThread { } try { - Application app = packageInfo.makeApplication(false, mInstrumentation); - if (localLOGV) Slog.v( TAG, "Performing receive of " + data.intent + ": app=" + app @@ -3146,7 +3150,6 @@ public final class ActivityThread { + ", comp=" + data.intent.getComponent().toShortString() + ", dir=" + packageInfo.getAppDir()); - ContextImpl context = (ContextImpl)app.getBaseContext(); sCurrentBroadcastIntent.set(data.intent); receiver.setPendingResult(data); receiver.onReceive(context.getReceiverRestrictedContext(), @@ -6031,6 +6034,15 @@ public final class ActivityThread { info.name); return null; } + + if (info.splitName != null) { + try { + c = c.createContextForSplit(info.splitName); + } catch (NameNotFoundException e) { + throw new RuntimeException(e); + } + } + try { final java.lang.ClassLoader cl = c.getClassLoader(); localProvider = (ContentProvider)cl. diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index d37888d76802..9db2b9237b02 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -32,6 +32,7 @@ import android.content.IntentSender; import android.content.ReceiverCallNotAllowedException; import android.content.ServiceConnection; import android.content.SharedPreferences; +import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; @@ -58,21 +59,27 @@ import android.os.Looper; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.Trace; import android.os.UserHandle; import android.os.storage.IStorageManager; import android.system.ErrnoException; import android.system.Os; import android.system.OsConstants; +import android.text.TextUtils; import android.util.AndroidRuntimeException; import android.util.ArrayMap; +import android.util.IntArray; import android.util.Log; import android.util.Slog; +import android.util.SparseBooleanArray; import android.view.Display; import android.view.DisplayAdjustments; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.Preconditions; +import dalvik.system.PathClassLoader; + import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; @@ -80,6 +87,8 @@ import java.io.FileOutputStream; import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Objects; class ReceiverRestrictedContext extends ContextWrapper { @@ -147,6 +156,7 @@ class ContextImpl extends Context { final ActivityThread mMainThread; final LoadedApk mPackageInfo; + private ClassLoader mClassLoader; private final IBinder mActivityToken; @@ -272,8 +282,7 @@ class ContextImpl extends Context { @Override public ClassLoader getClassLoader() { - return mPackageInfo != null ? - mPackageInfo.getClassLoader() : ClassLoader.getSystemClassLoader(); + return mClassLoader != null ? mClassLoader : (mPackageInfo != null ? mPackageInfo.getClassLoader() : ClassLoader.getSystemClassLoader()); } @Override @@ -1887,7 +1896,7 @@ class ContextImpl extends Context { pi.getResDir(), pi.getSplitResDirs(), pi.getOverlayDirs(), - pi.getApplicationInfo().sharedLibraryFiles, + pi.getSharedLibraries(), displayId, overrideConfig, compatInfo, @@ -1901,7 +1910,8 @@ class ContextImpl extends Context { flags | CONTEXT_REGISTER_PACKAGE); if (pi != null) { ContextImpl c = new ContextImpl(this, mMainThread, pi, mActivityToken, - new UserHandle(UserHandle.getUserId(application.uid)), flags); + new UserHandle(UserHandle.getUserId(application.uid)), flags, + null); final int displayId = mDisplay != null ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY; @@ -1930,13 +1940,15 @@ class ContextImpl extends Context { if (packageName.equals("system") || packageName.equals("android")) { // The system resources are loaded in every application, so we can safely copy // the context without reloading Resources. - return new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken, user, flags); + return new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken, user, flags, + null); } LoadedApk pi = mMainThread.getPackageInfo(packageName, mResources.getCompatibilityInfo(), flags | CONTEXT_REGISTER_PACKAGE, user.getIdentifier()); if (pi != null) { - ContextImpl c = new ContextImpl(this, mMainThread, pi, mActivityToken, user, flags); + ContextImpl c = new ContextImpl(this, mMainThread, pi, mActivityToken, user, flags, + null); final int displayId = mDisplay != null ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY; @@ -1954,13 +1966,42 @@ class ContextImpl extends Context { } @Override + public Context createContextForSplit(String splitName) throws NameNotFoundException { + if (!mPackageInfo.getApplicationInfo().requestsIsolatedSplitLoading()) { + // All Splits are always loaded. + return this; + } + + final ClassLoader classLoader = mPackageInfo.getSplitClassLoader(splitName); + final String[] paths = mPackageInfo.getSplitPaths(splitName); + + final ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, + mActivityToken, mUser, mFlags, classLoader); + + final int displayId = mDisplay != null + ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY; + + context.mResources = ResourcesManager.getInstance().getResources( + mActivityToken, + mPackageInfo.getResDir(), + paths, + mPackageInfo.getOverlayDirs(), + mPackageInfo.getApplicationInfo().sharedLibraryFiles, + displayId, + null, + mPackageInfo.getCompatibilityInfo(), + classLoader); + return context; + } + + @Override public Context createConfigurationContext(Configuration overrideConfiguration) { if (overrideConfiguration == null) { throw new IllegalArgumentException("overrideConfiguration must not be null"); } ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken, - mUser, mFlags); + mUser, mFlags, mClassLoader); final int displayId = mDisplay != null ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY; context.mResources = createResources(mActivityToken, mPackageInfo, displayId, @@ -1975,7 +2016,7 @@ class ContextImpl extends Context { } ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken, - mUser, mFlags); + mUser, mFlags, mClassLoader); final int displayId = display.getDisplayId(); context.mResources = createResources(mActivityToken, mPackageInfo, displayId, null, @@ -1988,14 +2029,16 @@ class ContextImpl extends Context { public Context createDeviceProtectedStorageContext() { final int flags = (mFlags & ~Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE) | Context.CONTEXT_DEVICE_PROTECTED_STORAGE; - return new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken, mUser, flags); + return new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken, mUser, flags, + mClassLoader); } @Override public Context createCredentialProtectedStorageContext() { final int flags = (mFlags & ~Context.CONTEXT_DEVICE_PROTECTED_STORAGE) | Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE; - return new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken, mUser, flags); + return new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken, mUser, flags, + mClassLoader); } @Override @@ -2082,8 +2125,9 @@ class ContextImpl extends Context { static ContextImpl createSystemContext(ActivityThread mainThread) { LoadedApk packageInfo = new LoadedApk(mainThread); - ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, 0); - context.mResources = packageInfo.getResources(mainThread); + ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, 0, + null); + context.mResources = packageInfo.getResources(); context.mResources.updateConfiguration(context.mResourcesManager.getConfiguration(), context.mResourcesManager.getDisplayMetrics()); return context; @@ -2091,18 +2135,35 @@ class ContextImpl extends Context { static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) { if (packageInfo == null) throw new IllegalArgumentException("packageInfo"); - ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, 0); - context.mResources = packageInfo.getResources(mainThread); + ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, 0, + null); + context.mResources = packageInfo.getResources(); return context; } static ContextImpl createActivityContext(ActivityThread mainThread, - LoadedApk packageInfo, IBinder activityToken, int displayId, + LoadedApk packageInfo, ActivityInfo activityInfo, IBinder activityToken, int displayId, Configuration overrideConfiguration) { if (packageInfo == null) throw new IllegalArgumentException("packageInfo"); + String[] splitDirs = packageInfo.getSplitResDirs(); + ClassLoader classLoader = packageInfo.getClassLoader(); + + if (packageInfo.getApplicationInfo().requestsIsolatedSplitLoading()) { + Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "SplitDependencies"); + try { + classLoader = packageInfo.getSplitClassLoader(activityInfo.splitName); + splitDirs = packageInfo.getSplitPaths(activityInfo.splitName); + } catch (NameNotFoundException e) { + // Nothing above us can handle a NameNotFoundException, better crash. + throw new RuntimeException(e); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_RESOURCES); + } + } + ContextImpl context = new ContextImpl(null, mainThread, packageInfo, activityToken, null, - 0); + 0, classLoader); // Clamp display ID to DEFAULT_DISPLAY if it is INVALID_DISPLAY. displayId = (displayId != Display.INVALID_DISPLAY) ? displayId : Display.DEFAULT_DISPLAY; @@ -2117,20 +2178,21 @@ class ContextImpl extends Context { // will be rebased upon. context.mResources = resourcesManager.createBaseActivityResources(activityToken, packageInfo.getResDir(), - packageInfo.getSplitResDirs(), + splitDirs, packageInfo.getOverlayDirs(), - packageInfo.getApplicationInfo().sharedLibraryFiles, + packageInfo.getSharedLibraries(), displayId, overrideConfiguration, compatInfo, - packageInfo.getClassLoader()); + classLoader); context.mDisplay = resourcesManager.getAdjustedDisplay(displayId, context.mResources.getDisplayAdjustments()); return context; } private ContextImpl(ContextImpl container, ActivityThread mainThread, - LoadedApk packageInfo, IBinder activityToken, UserHandle user, int flags) { + LoadedApk packageInfo, IBinder activityToken, UserHandle user, int flags, + ClassLoader classLoader) { mOuterContext = this; // If creator didn't specify which storage to use, use the default @@ -2155,6 +2217,7 @@ class ContextImpl extends Context { mUser = user; mPackageInfo = packageInfo; + mClassLoader = classLoader; mResourcesManager = ResourcesManager.getInstance(); if (container != null) { diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java index 4ab07432ad1f..17f5eddd0af6 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -27,9 +27,11 @@ import android.content.ServiceConnection; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; +import android.content.pm.split.SplitDependencyLoaderHelper; import android.content.res.AssetManager; import android.content.res.CompatibilityInfo; import android.content.res.Resources; +import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.os.FileUtils; @@ -41,23 +43,22 @@ import android.os.StrictMode; import android.os.SystemProperties; import android.os.Trace; import android.os.UserHandle; -import android.system.Os; -import android.system.OsConstants; -import android.system.ErrnoException; import android.text.TextUtils; import android.util.AndroidRuntimeException; import android.util.ArrayMap; import android.util.Log; import android.util.Slog; import android.util.SparseArray; +import android.util.SparseIntArray; import android.view.Display; import android.view.DisplayAdjustments; +import com.android.internal.util.ArrayUtils; + import dalvik.system.BaseDexClassLoader; import dalvik.system.VMRuntime; import java.io.File; -import java.io.FileDescriptor; import java.io.IOException; import java.io.InputStream; import java.lang.ref.WeakReference; @@ -65,13 +66,12 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URL; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Enumeration; import java.util.List; import java.util.Objects; -import libcore.io.IoUtils; - final class IntentReceiverLeaked extends AndroidRuntimeException { public IntentReceiverLeaked(String msg) { super(msg); @@ -97,8 +97,6 @@ public final class LoadedApk { private ApplicationInfo mApplicationInfo; private String mAppDir; private String mResDir; - private String[] mSplitAppDirs; - private String[] mSplitResDirs; private String[] mOverlayDirs; private String[] mSharedLibraries; private String mDataDir; @@ -116,14 +114,18 @@ public final class LoadedApk { private ClassLoader mClassLoader; private Application mApplication; + private String[] mSplitNames; + private String[] mSplitAppDirs; + private String[] mSplitResDirs; + private final ArrayMap<Context, ArrayMap<BroadcastReceiver, ReceiverDispatcher>> mReceivers - = new ArrayMap<Context, ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>>(); + = new ArrayMap<>(); private final ArrayMap<Context, ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>> mUnregisteredReceivers - = new ArrayMap<Context, ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>>(); + = new ArrayMap<>(); private final ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>> mServices - = new ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>>(); + = new ArrayMap<>(); private final ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>> mUnboundServices - = new ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>>(); + = new ArrayMap<>(); int mClientCount = 0; @@ -300,9 +302,18 @@ public final class LoadedApk { synchronized (this) { createOrUpdateClassLoaderLocked(addedPaths); if (mResources != null) { - mResources = mActivityThread.getTopLevelResources(mResDir, mSplitResDirs, - mOverlayDirs, mApplicationInfo.sharedLibraryFiles, Display.DEFAULT_DISPLAY, - this); + final String[] splitPaths; + try { + splitPaths = getSplitPaths(null); + } catch (PackageManager.NameNotFoundException e) { + // This should NEVER fail. + throw new AssertionError("null split not found"); + } + + mResources = ResourcesManager.getInstance().getResources(null, mResDir, + splitPaths, mOverlayDirs, mSharedLibraries, + Display.DEFAULT_DISPLAY, null, getCompatibilityInfo(), + getClassLoader()); } } } @@ -313,8 +324,6 @@ public final class LoadedApk { mApplicationInfo = aInfo; mAppDir = aInfo.sourceDir; mResDir = aInfo.uid == myUid ? aInfo.sourceDir : aInfo.publicSourceDir; - mSplitAppDirs = aInfo.splitSourceDirs; - mSplitResDirs = aInfo.uid == myUid ? aInfo.splitSourceDirs : aInfo.splitPublicSourceDirs; mOverlayDirs = aInfo.resourceDirs; mSharedLibraries = aInfo.sharedLibraryFiles; mDataDir = aInfo.dataDir; @@ -322,19 +331,28 @@ public final class LoadedApk { mDataDirFile = FileUtils.newFileOrNull(aInfo.dataDir); mDeviceProtectedDataDirFile = FileUtils.newFileOrNull(aInfo.deviceProtectedDataDir); mCredentialProtectedDataDirFile = FileUtils.newFileOrNull(aInfo.credentialProtectedDataDir); + + mSplitNames = aInfo.splitNames; + mSplitAppDirs = aInfo.splitSourceDirs; + mSplitResDirs = aInfo.uid == myUid ? aInfo.splitSourceDirs : aInfo.splitPublicSourceDirs; + + if (aInfo.requestsIsolatedSplitLoading() && !ArrayUtils.isEmpty(mSplitNames)) { + mSplitLoader = new SplitDependencyLoader(aInfo.splitDependencies); + } } public static void makePaths(ActivityThread activityThread, ApplicationInfo aInfo, List<String> outZipPaths, List<String> outLibPaths) { final String appDir = aInfo.sourceDir; - final String[] splitAppDirs = aInfo.splitSourceDirs; final String libDir = aInfo.nativeLibraryDir; final String[] sharedLibraries = aInfo.sharedLibraryFiles; outZipPaths.clear(); outZipPaths.add(appDir); - if (splitAppDirs != null) { - Collections.addAll(outZipPaths, splitAppDirs); + + // Do not load all available splits if the app requested isolated split loading. + if (aInfo.splitSourceDirs != null && !aInfo.requestsIsolatedSplitLoading()) { + Collections.addAll(outZipPaths, aInfo.splitSourceDirs); } if (outLibPaths != null) { @@ -367,13 +385,18 @@ public final class LoadedApk { || appDir.equals(instrumentedAppDir)) { outZipPaths.clear(); outZipPaths.add(instrumentationAppDir); - if (instrumentationSplitAppDirs != null) { - Collections.addAll(outZipPaths, instrumentationSplitAppDirs); - } - if (!instrumentationAppDir.equals(instrumentedAppDir)) { - outZipPaths.add(instrumentedAppDir); - if (instrumentedSplitAppDirs != null) { - Collections.addAll(outZipPaths, instrumentedSplitAppDirs); + + // Only add splits if the app did not request isolated split loading. + if (!aInfo.requestsIsolatedSplitLoading()) { + if (instrumentationSplitAppDirs != null) { + Collections.addAll(outZipPaths, instrumentationSplitAppDirs); + } + + if (!instrumentationAppDir.equals(instrumentedAppDir)) { + outZipPaths.add(instrumentedAppDir); + if (instrumentedSplitAppDirs != null) { + Collections.addAll(outZipPaths, instrumentedSplitAppDirs); + } } } @@ -399,7 +422,7 @@ public final class LoadedApk { // will be added to zipPaths that shouldn't be part of the library path. if (aInfo.primaryCpuAbi != null) { // Add fake libs into the library search path if we target prior to N. - if (aInfo.targetSdkVersion <= 23) { + if (aInfo.targetSdkVersion < Build.VERSION_CODES.N) { outLibPaths.add("/system/fake-libs" + (VMRuntime.is64BitAbi(aInfo.primaryCpuAbi) ? "64" : "")); } @@ -434,6 +457,116 @@ public final class LoadedApk { } } + private class SplitDependencyLoader + extends SplitDependencyLoaderHelper<PackageManager.NameNotFoundException> { + private String[] mCachedBaseResourcePath; + private final String[][] mCachedResourcePaths; + private final ClassLoader[] mCachedSplitClassLoaders; + + SplitDependencyLoader(SparseIntArray dependencies) { + super(dependencies); + mCachedResourcePaths = new String[mSplitNames.length][]; + mCachedSplitClassLoaders = new ClassLoader[mSplitNames.length]; + } + + @Override + protected boolean isSplitCached(int splitIdx) { + if (splitIdx != -1) { + return mCachedSplitClassLoaders[splitIdx] != null; + } + return mClassLoader != null && mCachedBaseResourcePath != null; + } + + private void addAllConfigSplits(String splitName, ArrayList<String> outAssetPaths) { + for (int i = 0; i < mSplitNames.length; i++) { + if (isConfigurationSplitOf(mSplitNames[i], splitName)) { + outAssetPaths.add(mSplitResDirs[i]); + } + } + } + + @Override + protected void constructSplit(int splitIdx, int parentSplitIdx) throws + PackageManager.NameNotFoundException { + final ArrayList<String> splitPaths = new ArrayList<>(); + if (splitIdx == -1) { + createOrUpdateClassLoaderLocked(null); + addAllConfigSplits(null, splitPaths); + mCachedBaseResourcePath = splitPaths.toArray(new String[splitPaths.size()]); + return; + } + + final ClassLoader parent; + if (parentSplitIdx == -1) { + // The parent is the base APK, so use its ClassLoader as parent + // and its configuration splits as part of our own too. + parent = mClassLoader; + Collections.addAll(splitPaths, mCachedBaseResourcePath); + } else { + parent = mCachedSplitClassLoaders[parentSplitIdx]; + Collections.addAll(splitPaths, mCachedResourcePaths[parentSplitIdx]); + } + + mCachedSplitClassLoaders[splitIdx] = ApplicationLoaders.getDefault().getClassLoader( + mSplitAppDirs[splitIdx], getTargetSdkVersion(), false, null, null, parent); + + splitPaths.add(mSplitResDirs[splitIdx]); + addAllConfigSplits(mSplitNames[splitIdx], splitPaths); + mCachedResourcePaths[splitIdx] = splitPaths.toArray(new String[splitPaths.size()]); + } + + private int ensureSplitLoaded(String splitName) + throws PackageManager.NameNotFoundException { + final int idx; + if (splitName == null) { + idx = -1; + } else { + idx = Arrays.binarySearch(mSplitNames, splitName); + if (idx < 0) { + throw new PackageManager.NameNotFoundException( + "Split name '" + splitName + "' is not installed"); + } + } + + loadDependenciesForSplit(idx); + return idx; + } + + ClassLoader getClassLoaderForSplit(String splitName) + throws PackageManager.NameNotFoundException { + final int idx = ensureSplitLoaded(splitName); + if (idx < 0) { + return mClassLoader; + } + return mCachedSplitClassLoaders[idx]; + } + + String[] getSplitPathsForSplit(String splitName) + throws PackageManager.NameNotFoundException { + final int idx = ensureSplitLoaded(splitName); + if (idx < 0) { + return mCachedBaseResourcePath; + } + return mCachedResourcePaths[idx]; + } + } + + private SplitDependencyLoader mSplitLoader; + + ClassLoader getSplitClassLoader(String splitName) throws PackageManager.NameNotFoundException { + if (mSplitLoader == null) { + return mClassLoader; + } + return mSplitLoader.getClassLoaderForSplit(splitName); + } + + String[] getSplitPaths(String splitName) throws PackageManager.NameNotFoundException { + if (mSplitLoader == null) { + return mSplitResDirs; + } + return mSplitLoader.getSplitPathsForSplit(splitName); + } + private void createOrUpdateClassLoaderLocked(List<String> addedPaths) { if (mPackageName.equals("android")) { // Note: This branch is taken for system server and we don't need to setup @@ -790,6 +923,10 @@ public final class LoadedApk { return mOverlayDirs; } + public String[] getSharedLibraries() { + return mSharedLibraries; + } + public String getDataDir() { return mDataDir; } @@ -806,14 +943,24 @@ public final class LoadedApk { return mCredentialProtectedDataDirFile; } - public AssetManager getAssets(ActivityThread mainThread) { - return getResources(mainThread).getAssets(); + public AssetManager getAssets() { + return getResources().getAssets(); } - public Resources getResources(ActivityThread mainThread) { + public Resources getResources() { if (mResources == null) { - mResources = mainThread.getTopLevelResources(mResDir, mSplitResDirs, mOverlayDirs, - mApplicationInfo.sharedLibraryFiles, Display.DEFAULT_DISPLAY, this); + final String[] splitPaths; + try { + splitPaths = getSplitPaths(null); + } catch (PackageManager.NameNotFoundException e) { + // This should never fail. + throw new AssertionError("null split not found"); + } + + mResources = ResourcesManager.getInstance().getResources(null, mResDir, + splitPaths, mOverlayDirs, mSharedLibraries, + Display.DEFAULT_DISPLAY, null, getCompatibilityInfo(), + getClassLoader()); } return mResources; } @@ -870,8 +1017,7 @@ public final class LoadedApk { } // Rewrite the R 'constants' for all library apks. - SparseArray<String> packageIdentifiers = getAssets(mActivityThread) - .getAssignedPackageIdentifiers(); + SparseArray<String> packageIdentifiers = getAssets().getAssignedPackageIdentifiers(); final int N = packageIdentifiers.size(); for (int i = 0; i < N; i++) { final int id = packageIdentifiers.keyAt(i); diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 38e6fbeb007f..f00f60512f16 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -4253,6 +4253,20 @@ public abstract class Context { int flags) throws PackageManager.NameNotFoundException; /** + * Return a new Context object for the given split name. The new Context has a ClassLoader and + * Resources object that can access the split's and all of its dependencies' code/resources. + * Each call to this method returns a new instance of a Context object; + * Context objects are not shared, however common state (ClassLoader, other Resources for + * the same split) may be so the Context itself can be fairly lightweight. + * + * @param splitName The name of the split to include, as declared in the split's + * <code>AndroidManifest.xml</code>. + * @return A {@link Context} with the given split's code and/or resources loaded. + */ + public abstract Context createContextForSplit(String splitName) + throws PackageManager.NameNotFoundException; + + /** * Get the userId associated with this context * @return user id * diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java index b131eccbe48a..546bfc404d56 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -16,7 +16,6 @@ package android.content; -import android.annotation.Nullable; import android.annotation.SystemApi; import android.app.IApplicationThread; import android.app.IServiceConnection; @@ -826,6 +825,13 @@ public class ContextWrapper extends Context { /** @hide */ @Override + public Context createContextForSplit(String splitName) + throws PackageManager.NameNotFoundException { + return mBase.createContextForSplit(splitName); + } + + /** @hide */ + @Override public int getUserId() { return mBase.getUserId(); } diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index 04ab2394b0d1..3d9ba96b6795 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -31,6 +31,7 @@ import android.os.Parcelable; import android.os.UserHandle; import android.text.TextUtils; import android.util.Printer; +import android.util.SparseIntArray; import com.android.internal.util.ArrayUtils; @@ -558,6 +559,14 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { public static final int PRIVATE_FLAG_STATIC_SHARED_LIBRARY = 1 << 13; /** + * Value for {@linl #privateFlags}: When set, the application will only have its splits loaded + * if they are required to load a component. Splits can be loaded on demand using the + * {@link Context#createContextForSplit(String)} API. + * @hide + */ + public static final int PRIVATE_FLAG_ISOLATED_SPLIT_LOADING = 1 << 14; + + /** * Private/hidden flags. See {@code PRIVATE_FLAG_...} constants. * {@hide} */ @@ -607,8 +616,12 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { public String publicSourceDir; /** - * Full paths to zero or more split APKs that, when combined with the base - * APK defined in {@link #sourceDir}, form a complete application. + * The names of all installed split APKs, ordered lexicographically. + */ + public String[] splitNames; + + /** + * Full paths to zero or more split APKs, indexed by the same order as {@link #splitNames}. */ public String[] splitSourceDirs; @@ -616,14 +629,35 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * Full path to the publicly available parts of {@link #splitSourceDirs}, * including resources and manifest. This may be different from * {@link #splitSourceDirs} if an application is forward locked. + * + * @see #splitSourceDirs */ public String[] splitPublicSourceDirs; /** - * Full paths to the locations of extra resource packages this application - * uses. This field is only used if there are extra resource packages, - * otherwise it is null. - * + * Maps the dependencies between split APKs. All splits implicitly depend on the base APK. + * + * Available since platform version O. + * + * Only populated if the application opts in to isolated split loading via the + * {@link android.R.attr.isolatedSplits} attribute in the <manifest> tag of the app's + * AndroidManifest.xml. + * + * The keys and values are all indices into the {@link #splitNames}, {@link #splitSourceDirs}, + * and {@link #splitPublicSourceDirs} arrays. + * Each key represents a split and its value is its parent split. + * Cycles do not exist because they are illegal and screened for during installation. + * + * May be null if no splits are installed, or if no dependencies exist between them. + * @hide + */ + public SparseIntArray splitDependencies; + + /** + * Full paths to the locations of extra resource packages (runtime overlays) + * this application uses. This field is only used if there are extra resource + * packages, otherwise it is null. + * * {@hide} */ public String[] resourceDirs; @@ -1058,8 +1092,10 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { scanPublicSourceDir = orig.scanPublicSourceDir; sourceDir = orig.sourceDir; publicSourceDir = orig.publicSourceDir; + splitNames = orig.splitNames; splitSourceDirs = orig.splitSourceDirs; splitPublicSourceDirs = orig.splitPublicSourceDirs; + splitDependencies = orig.splitDependencies; nativeLibraryDir = orig.nativeLibraryDir; secondaryNativeLibraryDir = orig.secondaryNativeLibraryDir; nativeLibraryRootDir = orig.nativeLibraryRootDir; @@ -1098,6 +1134,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { return 0; } + @SuppressWarnings("unchecked") public void writeToParcel(Parcel dest, int parcelableFlags) { super.writeToParcel(dest, parcelableFlags); dest.writeString(taskAffinity); @@ -1115,8 +1152,10 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { dest.writeString(scanPublicSourceDir); dest.writeString(sourceDir); dest.writeString(publicSourceDir); + dest.writeStringArray(splitNames); dest.writeStringArray(splitSourceDirs); dest.writeStringArray(splitPublicSourceDirs); + dest.writeSparseIntArray(splitDependencies); dest.writeString(nativeLibraryDir); dest.writeString(secondaryNativeLibraryDir); dest.writeString(nativeLibraryRootDir); @@ -1155,6 +1194,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { } }; + @SuppressWarnings("unchecked") private ApplicationInfo(Parcel source) { super(source); taskAffinity = source.readString(); @@ -1172,8 +1212,10 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { scanPublicSourceDir = source.readString(); sourceDir = source.readString(); publicSourceDir = source.readString(); + splitNames = source.readStringArray(); splitSourceDirs = source.readStringArray(); splitPublicSourceDirs = source.readStringArray(); + splitDependencies = source.readSparseIntArray(); nativeLibraryDir = source.readString(); secondaryNativeLibraryDir = source.readString(); nativeLibraryRootDir = source.readString(); @@ -1363,6 +1405,15 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { } /** + * Returns true if the app has declared in its manifest that it wants its split APKs to be + * loaded into isolated Contexts, with their own ClassLoaders and Resources objects. + * @hide + */ + public boolean requestsIsolatedSplitLoading() { + return (privateFlags & ApplicationInfo.PRIVATE_FLAG_ISOLATED_SPLIT_LOADING) != 0; + } + + /** * @hide */ public boolean isStaticSharedLibrary() { diff --git a/core/java/android/content/pm/ComponentInfo.java b/core/java/android/content/pm/ComponentInfo.java index b091d7ed4168..53be9537d00d 100644 --- a/core/java/android/content/pm/ComponentInfo.java +++ b/core/java/android/content/pm/ComponentInfo.java @@ -45,6 +45,12 @@ public class ComponentInfo extends PackageItemInfo { public String processName; /** + * The name of the split in which this component is declared. + * Null if the component was declared in the base APK. + */ + public String splitName; + + /** * A string resource identifier (in the package's resources) containing * a user-readable description of the component. From the "description" * attribute or, if not set, 0. @@ -53,7 +59,7 @@ public class ComponentInfo extends PackageItemInfo { /** * Indicates whether or not this component may be instantiated. Note that this value can be - * overriden by the one in its parent {@link ApplicationInfo}. + * overridden by the one in its parent {@link ApplicationInfo}. */ public boolean enabled = true; @@ -72,11 +78,6 @@ public class ComponentInfo extends PackageItemInfo { */ public boolean directBootAware = false; - /** - * The name of the split that contains the code for this component. - */ - public String splitName; - /** @removed */ @Deprecated public boolean encryptionAware = false; @@ -88,6 +89,7 @@ public class ComponentInfo extends PackageItemInfo { super(orig); applicationInfo = orig.applicationInfo; processName = orig.processName; + splitName = orig.splitName; descriptionRes = orig.descriptionRes; enabled = orig.enabled; exported = orig.exported; @@ -168,6 +170,9 @@ public class ComponentInfo extends PackageItemInfo { if (processName != null && !packageName.equals(processName)) { pw.println(prefix + "processName=" + processName); } + if (splitName != null) { + pw.println(prefix + "splitName=" + splitName); + } pw.println(prefix + "enabled=" + enabled + " exported=" + exported + " directBootAware=" + directBootAware); if (descriptionRes != 0) { @@ -200,6 +205,7 @@ public class ComponentInfo extends PackageItemInfo { applicationInfo.writeToParcel(dest, parcelableFlags); } dest.writeString(processName); + dest.writeString(splitName); dest.writeInt(descriptionRes); dest.writeInt(enabled ? 1 : 0); dest.writeInt(exported ? 1 : 0); @@ -213,6 +219,7 @@ public class ComponentInfo extends PackageItemInfo { applicationInfo = ApplicationInfo.CREATOR.createFromParcel(source); } processName = source.readString(); + splitName = source.readString(); descriptionRes = source.readInt(); enabled = (source.readInt() != 0); exported = (source.readInt() != 0); diff --git a/core/java/android/content/pm/InstrumentationInfo.java b/core/java/android/content/pm/InstrumentationInfo.java index 9d88cdd8d484..a135d8f11acd 100644 --- a/core/java/android/content/pm/InstrumentationInfo.java +++ b/core/java/android/content/pm/InstrumentationInfo.java @@ -18,6 +18,8 @@ package android.content.pm; import android.os.Parcel; import android.os.Parcelable; +import android.util.SparseArray; +import android.util.SparseIntArray; /** * Information you can retrieve about a particular piece of test @@ -44,8 +46,12 @@ public class InstrumentationInfo extends PackageItemInfo implements Parcelable { public String publicSourceDir; /** - * Full paths to zero or more split APKs that, when combined with the base - * APK defined in {@link #sourceDir}, form a complete application. + * The names of all installed split APKs, ordered lexicographically. + */ + public String[] splitNames; + + /** + * Full paths to zero or more split APKs, indexed by the same order as {@link #splitNames}. */ public String[] splitSourceDirs; @@ -53,10 +59,31 @@ public class InstrumentationInfo extends PackageItemInfo implements Parcelable { * Full path to the publicly available parts of {@link #splitSourceDirs}, * including resources and manifest. This may be different from * {@link #splitSourceDirs} if an application is forward locked. + * + * @see #splitSourceDirs */ public String[] splitPublicSourceDirs; /** + * Maps the dependencies between split APKs. All splits implicitly depend on the base APK. + * + * Available since platform version O. + * + * Only populated if the application opts in to isolated split loading via the + * {@link android.R.attr.isolatedSplits} attribute in the <manifest> tag of the app's + * AndroidManifest.xml. + * + * The keys and values are all indices into the {@link #splitNames}, {@link #splitSourceDirs}, + * and {@link #splitPublicSourceDirs} arrays. + * Each key represents a split and its value is its parent split. + * Cycles do not exist because they are illegal and screened for during installation. + * + * May be null if no splits are installed, or if no dependencies exist between them. + * @hide + */ + public SparseIntArray splitDependencies; + + /** * Full path to a directory assigned to the package for its persistent data. */ public String dataDir; @@ -88,8 +115,10 @@ public class InstrumentationInfo extends PackageItemInfo implements Parcelable { targetPackage = orig.targetPackage; sourceDir = orig.sourceDir; publicSourceDir = orig.publicSourceDir; + splitNames = orig.splitNames; splitSourceDirs = orig.splitSourceDirs; splitPublicSourceDirs = orig.splitPublicSourceDirs; + splitDependencies = orig.splitDependencies; dataDir = orig.dataDir; deviceProtectedDataDir = orig.deviceProtectedDataDir; credentialProtectedDataDir = orig.credentialProtectedDataDir; @@ -114,8 +143,10 @@ public class InstrumentationInfo extends PackageItemInfo implements Parcelable { dest.writeString(targetPackage); dest.writeString(sourceDir); dest.writeString(publicSourceDir); + dest.writeStringArray(splitNames); dest.writeStringArray(splitSourceDirs); dest.writeStringArray(splitPublicSourceDirs); + dest.writeSparseIntArray(splitDependencies); dest.writeString(dataDir); dest.writeString(deviceProtectedDataDir); dest.writeString(credentialProtectedDataDir); @@ -135,13 +166,16 @@ public class InstrumentationInfo extends PackageItemInfo implements Parcelable { } }; + @SuppressWarnings("unchecked") private InstrumentationInfo(Parcel source) { super(source); targetPackage = source.readString(); sourceDir = source.readString(); publicSourceDir = source.readString(); + splitNames = source.readStringArray(); splitSourceDirs = source.readStringArray(); splitPublicSourceDirs = source.readStringArray(); + splitDependencies = source.readSparseIntArray(); dataDir = source.readString(); deviceProtectedDataDir = source.readString(); credentialProtectedDataDir = source.readString(); @@ -156,8 +190,10 @@ public class InstrumentationInfo extends PackageItemInfo implements Parcelable { ai.packageName = packageName; ai.sourceDir = sourceDir; ai.publicSourceDir = publicSourceDir; + ai.splitNames = splitNames; ai.splitSourceDirs = splitSourceDirs; ai.splitPublicSourceDirs = splitPublicSourceDirs; + ai.splitDependencies = splitDependencies; ai.dataDir = dataDir; ai.deviceProtectedDataDir = deviceProtectedDataDir; ai.credentialProtectedDataDir = credentialProtectedDataDir; diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index cd51bce4a47b..8223726322a5 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -49,6 +49,9 @@ import android.app.ActivityManager; import android.content.ComponentName; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.split.SplitAssetDependencyLoader; +import android.content.pm.split.SplitAssetLoader; +import android.content.pm.split.DefaultSplitAssetLoader; import android.content.res.AssetManager; import android.content.res.Configuration; import android.content.res.Resources; @@ -74,6 +77,7 @@ import android.util.DisplayMetrics; import android.util.Log; import android.util.Pair; import android.util.Slog; +import android.util.SparseIntArray; import android.util.TypedValue; import android.util.apk.ApkSignatureSchemeV2Verifier; import android.util.jar.StrictJarFile; @@ -106,6 +110,7 @@ import java.security.spec.InvalidKeySpecException; import java.security.spec.X509EncodedKeySpec; import java.util.ArrayList; import java.util.Arrays; +import java.util.BitSet; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; @@ -155,6 +160,7 @@ public class PackageParser { private static final String TAG_MANIFEST = "manifest"; private static final String TAG_APPLICATION = "application"; + private static final String TAG_PACKAGE_VERIFIER = "package-verifier"; private static final String TAG_OVERLAY = "overlay"; private static final String TAG_KEY_SETS = "key-sets"; private static final String TAG_PERMISSION_GROUP = "permission-group"; @@ -178,6 +184,7 @@ public class PackageParser { private static final String TAG_EAT_COMMENT = "eat-comment"; private static final String TAG_PACKAGE = "package"; private static final String TAG_RESTRICT_UPDATE = "restrict-update"; + private static final String TAG_USES_SPLIT = "uses-split"; /** * Bit mask of all the valid bits that can be set in restartOnConfigChanges. @@ -352,6 +359,9 @@ public class PackageParser { /** Names of any split APKs, ordered by parsed splitName */ public final String[] splitNames; + /** Dependencies of any split APKs, ordered by parsed splitName */ + public final String[] usesSplitNames; + /** * Path where this package was found on disk. For monolithic packages * this is path to single base APK file; for cluster packages this is @@ -374,14 +384,17 @@ public class PackageParser { public final boolean multiArch; public final boolean use32bitAbi; public final boolean extractNativeLibs; + public final boolean isolatedSplits; public PackageLite(String codePath, ApkLite baseApk, String[] splitNames, - String[] splitCodePaths, int[] splitRevisionCodes) { + String[] usesSplitNames, String[] splitCodePaths, + int[] splitRevisionCodes) { this.packageName = baseApk.packageName; this.versionCode = baseApk.versionCode; this.installLocation = baseApk.installLocation; this.verifiers = baseApk.verifiers; this.splitNames = splitNames; + this.usesSplitNames = usesSplitNames; this.codePath = codePath; this.baseCodePath = baseApk.codePath; this.splitCodePaths = splitCodePaths; @@ -392,6 +405,7 @@ public class PackageParser { this.multiArch = baseApk.multiArch; this.use32bitAbi = baseApk.use32bitAbi; this.extractNativeLibs = baseApk.extractNativeLibs; + this.isolatedSplits = baseApk.isolatedSplits; } public List<String> getAllCodePaths() { @@ -411,6 +425,7 @@ public class PackageParser { public final String codePath; public final String packageName; public final String splitName; + public final String usesSplitName; public final int versionCode; public final int revisionCode; public final int installLocation; @@ -422,15 +437,17 @@ public class PackageParser { public final boolean multiArch; public final boolean use32bitAbi; public final boolean extractNativeLibs; + public final boolean isolatedSplits; - public ApkLite(String codePath, String packageName, String splitName, int versionCode, - int revisionCode, int installLocation, List<VerifierInfo> verifiers, + public ApkLite(String codePath, String packageName, String splitName, 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 extractNativeLibs, boolean isolatedSplits) { this.codePath = codePath; this.packageName = packageName; this.splitName = splitName; + this.usesSplitName = usesSplitName; this.versionCode = versionCode; this.revisionCode = revisionCode; this.installLocation = installLocation; @@ -442,6 +459,7 @@ public class PackageParser { this.multiArch = multiArch; this.use32bitAbi = use32bitAbi; this.extractNativeLibs = extractNativeLibs; + this.isolatedSplits = isolatedSplits; } } @@ -492,7 +510,7 @@ public class PackageParser { return isApkPath(file.getName()); } - private static boolean isApkPath(String path) { + public static boolean isApkPath(String path) { return path.endsWith(".apk"); } @@ -738,23 +756,23 @@ public class PackageParser { public static PackageLite parsePackageLite(File packageFile, int flags) throws PackageParserException { if (packageFile.isDirectory()) { - return parseClusterPackageLite(packageFile, flags, null); + return parseClusterPackageLite(packageFile, flags); } else { - return parseMonolithicPackageLite(packageFile, flags, null); + return parseMonolithicPackageLite(packageFile, flags); } } - private static PackageLite parseMonolithicPackageLite(File packageFile, int flags, - AssetManager cachedAssetManager) throws PackageParserException { + private static PackageLite parseMonolithicPackageLite(File packageFile, int flags) + throws PackageParserException { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parseApkLite"); - final ApkLite baseApk = parseApkLite(packageFile, flags, cachedAssetManager); + 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); + return new PackageLite(packagePath, baseApk, null, null, null, null); } - private static PackageLite parseClusterPackageLite(File packageDir, int flags, - AssetManager cachedAssetManager) throws PackageParserException { + private static PackageLite parseClusterPackageLite(File packageDir, int flags) + throws PackageParserException { final File[] files = packageDir.listFiles(); if (ArrayUtils.isEmpty(files)) { throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, @@ -768,7 +786,7 @@ public class PackageParser { final ArrayMap<String, ApkLite> apks = new ArrayMap<>(); for (File file : files) { if (isApkFile(file)) { - final ApkLite lite = parseApkLite(file, flags, cachedAssetManager); + final ApkLite lite = parseApkLite(file, flags); // Assert that all package names and version codes are // consistent with the first one we encounter. @@ -808,10 +826,12 @@ public class PackageParser { final int size = apks.size(); String[] splitNames = null; + String[] usesSplitNames = null; String[] splitCodePaths = null; int[] splitRevisionCodes = null; if (size > 0) { splitNames = new String[size]; + usesSplitNames = new String[size]; splitCodePaths = new String[size]; splitRevisionCodes = new int[size]; @@ -819,13 +839,15 @@ public class PackageParser { Arrays.sort(splitNames, sSplitNameComparator); for (int i = 0; i < size; i++) { - splitCodePaths[i] = apks.get(splitNames[i]).codePath; - splitRevisionCodes[i] = apks.get(splitNames[i]).revisionCode; + final ApkLite apk = apks.get(splitNames[i]); + usesSplitNames[i] = apk.usesSplitName; + splitCodePaths[i] = apk.codePath; + splitRevisionCodes[i] = apk.revisionCode; } } final String codePath = packageDir.getAbsolutePath(); - return new PackageLite(codePath, baseApk, splitNames, splitCodePaths, + return new PackageLite(codePath, baseApk, splitNames, usesSplitNames, splitCodePaths, splitRevisionCodes); } @@ -1004,6 +1026,42 @@ public class PackageParser { } } + private static SparseIntArray buildSplitDependencyTree(PackageLite pkg) + throws PackageParserException { + SparseIntArray splitDependencies = new SparseIntArray(); + for (int splitIdx = 0; splitIdx < pkg.splitNames.length; splitIdx++) { + final String splitDependency = pkg.usesSplitNames[splitIdx]; + if (splitDependency != null) { + final int depIdx = Arrays.binarySearch(pkg.splitNames, splitDependency); + if (depIdx < 0) { + throw new PackageParserException( + PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST, + "Split '" + pkg.splitNames[splitIdx] + "' requires split '" + + splitDependency + "', which is missing."); + } + splitDependencies.put(splitIdx, depIdx); + } + } + + // Verify that there are no cycles. + final BitSet bitset = new BitSet(); + for (int i = 0; i < splitDependencies.size(); i++) { + int splitIdx = splitDependencies.keyAt(i); + + bitset.clear(); + while (splitIdx != -1) { + if (bitset.get(splitIdx)) { + throw new PackageParserException( + PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST, + "Cycle detected in split dependencies."); + } + bitset.set(splitIdx); + splitIdx = splitDependencies.get(splitIdx, -1); + } + } + return splitDependencies.size() != 0 ? splitDependencies : null; + } + /** * Parse all APKs contained in the given directory, treating them as a * single package. This also performs sanity checking, such as requiring @@ -1014,25 +1072,24 @@ public class PackageParser { * must be done separately in {@link #collectCertificates(Package, int)}. */ private Package parseClusterPackage(File packageDir, int flags) throws PackageParserException { - final AssetManager assets = newConfiguredAssetManager(); - final PackageLite lite = parseClusterPackageLite(packageDir, 0, assets); - + final PackageLite lite = parseClusterPackageLite(packageDir, 0); if (mOnlyCoreApps && !lite.coreApp) { throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, "Not a coreApp: " + packageDir); } - try { - // Load all splits into the AssetManager (base has already been loaded earlier) - // so that resources can be overriden when parsing the manifests. - loadApkIntoAssetManager(assets, lite.baseCodePath, flags); - - if (!ArrayUtils.isEmpty(lite.splitCodePaths)) { - for (String path : lite.splitCodePaths) { - loadApkIntoAssetManager(assets, path, flags); - } - } + // Build the split dependency tree. + SparseIntArray splitDependencies = null; + final SplitAssetLoader assetLoader; + if (lite.isolatedSplits && !ArrayUtils.isEmpty(lite.splitNames)) { + splitDependencies = buildSplitDependencyTree(lite); + assetLoader = new SplitAssetDependencyLoader(lite, splitDependencies, flags); + } else { + assetLoader = new DefaultSplitAssetLoader(lite, flags); + } + try { + final AssetManager assets = assetLoader.getBaseAssetManager(); final File baseApk = new File(lite.baseCodePath); final Package pkg = parseBaseApk(baseApk, assets, flags); if (pkg == null) { @@ -1047,9 +1104,12 @@ public class PackageParser { pkg.splitRevisionCodes = lite.splitRevisionCodes; pkg.splitFlags = new int[num]; pkg.splitPrivateFlags = new int[num]; + pkg.applicationInfo.splitNames = pkg.splitNames; + pkg.applicationInfo.splitDependencies = splitDependencies; for (int i = 0; i < num; i++) { - parseSplitApk(pkg, i, assets, flags); + final AssetManager splitAssets = assetLoader.getSplitAssetManager(i); + parseSplitApk(pkg, i, splitAssets, flags); } } @@ -1057,7 +1117,7 @@ public class PackageParser { pkg.setUse32bitAbi(lite.use32bitAbi); return pkg; } finally { - IoUtils.closeQuietly(assets); + IoUtils.closeQuietly(assetLoader); } } @@ -1074,7 +1134,7 @@ public class PackageParser { @Deprecated public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException { final AssetManager assets = newConfiguredAssetManager(); - final PackageLite lite = parseMonolithicPackageLite(apkFile, flags, assets); + final PackageLite lite = parseMonolithicPackageLite(apkFile, flags); if (mOnlyCoreApps) { if (!lite.coreApp) { throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, @@ -1168,7 +1228,7 @@ public class PackageParser { final int cookie = loadApkIntoAssetManager(assets, apkPath, flags); - Resources res = null; + final Resources res; XmlResourceParser parser = null; try { res = new Resources(assets, mMetrics, null); @@ -1225,7 +1285,7 @@ public class PackageParser { } String tagName = parser.getName(); - if (tagName.equals("application")) { + if (tagName.equals(TAG_APPLICATION)) { if (foundApp) { if (RIGID_PARSER) { outError[0] = "<manifest> has more than one <application>"; @@ -1523,17 +1583,12 @@ public class PackageParser { */ public static ApkLite parseApkLite(File apkFile, int flags) throws PackageParserException { - return parseApkLite(apkFile, flags, null); - } - - private static ApkLite parseApkLite(File apkFile, int flags, - @Nullable AssetManager cachedAssetManager) throws PackageParserException { final String apkPath = apkFile.getAbsolutePath(); AssetManager assets = null; XmlResourceParser parser = null; try { - assets = cachedAssetManager == null ? newConfiguredAssetManager() : cachedAssetManager; + assets = newConfiguredAssetManager(); int cookie = assets.addAssetPath(apkPath); if (cookie == 0) { throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, @@ -1543,7 +1598,6 @@ public class PackageParser { final DisplayMetrics metrics = new DisplayMetrics(); metrics.setToDefaults(); - final Resources res = new Resources(assets, metrics, null); parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME); final Signature[] signatures; @@ -1565,16 +1619,14 @@ public class PackageParser { } final AttributeSet attrs = parser; - return parseApkLite(apkPath, res, parser, attrs, flags, signatures, certificates); + return parseApkLite(apkPath, parser, attrs, flags, signatures, certificates); } catch (XmlPullParserException | IOException | RuntimeException e) { throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION, "Failed to parse " + apkPath, e); } finally { IoUtils.closeQuietly(parser); - if (cachedAssetManager == null) { - IoUtils.closeQuietly(assets); - } + IoUtils.closeQuietly(assets); } } @@ -1652,9 +1704,9 @@ public class PackageParser { (splitName != null) ? splitName.intern() : splitName); } - private static ApkLite parseApkLite(String codePath, Resources res, XmlPullParser parser, - AttributeSet attrs, int flags, Signature[] signatures, Certificate[][] certificates) - throws IOException, XmlPullParserException, PackageParserException { + private static ApkLite parseApkLite(String codePath, XmlPullParser parser, AttributeSet attrs, + int flags, Signature[] signatures, Certificate[][] certificates) + throws IOException, XmlPullParserException, PackageParserException { final Pair<String, String> packageSplit = parsePackageSplitNames(parser, attrs); int installLocation = PARSE_DEFAULT_INSTALL_LOCATION; @@ -1665,6 +1717,8 @@ public class PackageParser { boolean multiArch = false; boolean use32bitAbi = false; boolean extractNativeLibs = true; + boolean isolatedSplits = false; + String usesSplitName = null; for (int i = 0; i < attrs.getAttributeCount(); i++) { final String attr = attrs.getAttributeName(i); @@ -1677,6 +1731,8 @@ public class PackageParser { revisionCode = attrs.getAttributeIntValue(i, 0); } else if (attr.equals("coreApp")) { coreApp = attrs.getAttributeBooleanValue(i, false); + } else if (attr.equals("isolatedSplits")) { + isolatedSplits = attrs.getAttributeBooleanValue(i, false); } } @@ -1691,14 +1747,16 @@ public class PackageParser { continue; } - if (parser.getDepth() == searchDepth && "package-verifier".equals(parser.getName())) { - final VerifierInfo verifier = parseVerifier(res, parser, attrs, flags); + if (parser.getDepth() != searchDepth) { + continue; + } + + if (TAG_PACKAGE_VERIFIER.equals(parser.getName())) { + final VerifierInfo verifier = parseVerifier(attrs); if (verifier != null) { verifiers.add(verifier); } - } - - if (parser.getDepth() == searchDepth && "application".equals(parser.getName())) { + } else if (TAG_APPLICATION.equals(parser.getName())) { for (int i = 0; i < attrs.getAttributeCount(); ++i) { final String attr = attrs.getAttributeName(i); if ("debuggable".equals(attr)) { @@ -1714,12 +1772,25 @@ public class PackageParser { extractNativeLibs = attrs.getAttributeBooleanValue(i, true); } } + } else if (TAG_USES_SPLIT.equals(parser.getName())) { + if (usesSplitName != null) { + Slog.w(TAG, "Only one <uses-split> permitted. Ignoring others."); + continue; + } + + usesSplitName = attrs.getAttributeValue(ANDROID_RESOURCES, "name"); + if (usesSplitName == null) { + throw new PackageParserException( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + "<uses-split> tag requires 'android:name' attribute"); + } } } - return new ApkLite(codePath, packageSplit.first, packageSplit.second, versionCode, - revisionCode, installLocation, verifiers, signatures, certificates, coreApp, - debuggable, multiArch, use32bitAbi, extractNativeLibs); + return new ApkLite(codePath, packageSplit.first, packageSplit.second, usesSplitName, + versionCode, revisionCode, installLocation, verifiers, signatures, + certificates, coreApp, debuggable, multiArch, use32bitAbi, extractNativeLibs, + isolatedSplits); } /** @@ -1940,6 +2011,10 @@ public class PackageParser { pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_EPHEMERAL; } + if (sa.getBoolean(com.android.internal.R.styleable.AndroidManifest_isolatedSplits, false)) { + pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_ISOLATED_SPLIT_LOADING; + } + // Resource boolean are -1, so 1 means we don't know the value. int supportsSmallScreens = 1; int supportsNormalScreens = 1; @@ -3657,6 +3732,8 @@ public class PackageParser { continue; } + ComponentInfo parsedComponent = null; + String tagName = parser.getName(); if (tagName.equals("activity")) { Activity a = parseActivity(owner, res, parser, flags, outError, false, @@ -3667,6 +3744,7 @@ public class PackageParser { } owner.activities.add(a); + parsedComponent = a.info; } else if (tagName.equals("receiver")) { Activity a = parseActivity(owner, res, parser, flags, outError, true, false); @@ -3676,6 +3754,7 @@ public class PackageParser { } owner.receivers.add(a); + parsedComponent = a.info; } else if (tagName.equals("service")) { Service s = parseService(owner, res, parser, flags, outError); @@ -3685,6 +3764,7 @@ public class PackageParser { } owner.services.add(s); + parsedComponent = s.info; } else if (tagName.equals("provider")) { Provider p = parseProvider(owner, res, parser, flags, outError); @@ -3694,6 +3774,7 @@ public class PackageParser { } owner.providers.add(p); + parsedComponent = p.info; } else if (tagName.equals("activity-alias")) { Activity a = parseActivityAlias(owner, res, parser, flags, outError); @@ -3703,6 +3784,7 @@ public class PackageParser { } owner.activities.add(a); + parsedComponent = a.info; } else if (parser.getName().equals("meta-data")) { // note: application meta-data is stored off to the side, so it can @@ -3769,6 +3851,14 @@ public class PackageParser { return false; } } + + if (parsedComponent != null && parsedComponent.splitName == null) { + // If the loaded component did not specify a split, inherit the split name + // based on the split it is defined in. + // This is used to later load the correct split when starting this + // component. + parsedComponent.splitName = owner.splitNames[splitIndex]; + } } return true; @@ -5039,18 +5129,23 @@ public class PackageParser { return data; } - private static VerifierInfo parseVerifier(Resources res, XmlPullParser parser, - AttributeSet attrs, int flags) { - final TypedArray sa = res.obtainAttributes(attrs, - com.android.internal.R.styleable.AndroidManifestPackageVerifier); - - final String packageName = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestPackageVerifier_name); + private static VerifierInfo parseVerifier(AttributeSet attrs) { + String packageName = null; + String encodedPublicKey = null; - final String encodedPublicKey = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestPackageVerifier_publicKey); + final int attrCount = attrs.getAttributeCount(); + for (int i = 0; i < attrCount; i++) { + final int attrResId = attrs.getAttributeNameResource(i); + switch (attrResId) { + case com.android.internal.R.attr.name: + packageName = attrs.getAttributeValue(i); + break; - sa.recycle(); + case com.android.internal.R.attr.publicKey: + encodedPublicKey = attrs.getAttributeValue(i); + break; + } + } if (packageName == null || packageName.length() == 0) { Slog.i(TAG, "verifier package name was null; skipping"); diff --git a/core/java/android/content/pm/split/DefaultSplitAssetLoader.java b/core/java/android/content/pm/split/DefaultSplitAssetLoader.java new file mode 100644 index 000000000000..5a9966d6e18b --- /dev/null +++ b/core/java/android/content/pm/split/DefaultSplitAssetLoader.java @@ -0,0 +1,100 @@ +/* + * 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 android.content.pm.split; + +import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST; +import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK; + +import android.content.pm.PackageParser; +import android.content.res.AssetManager; +import android.os.Build; + +import com.android.internal.util.ArrayUtils; + +import libcore.io.IoUtils; + +/** + * Loads the base and split APKs into a single AssetManager. + * @hide + */ +public class DefaultSplitAssetLoader implements SplitAssetLoader { + private final String mBaseCodePath; + private final String[] mSplitCodePaths; + private final int mFlags; + + private AssetManager mCachedAssetManager; + + public DefaultSplitAssetLoader(PackageParser.PackageLite pkg, int flags) { + mBaseCodePath = pkg.baseCodePath; + mSplitCodePaths = pkg.splitCodePaths; + mFlags = flags; + } + + private static void loadApkIntoAssetManager(AssetManager assets, String apkPath, int flags) + throws PackageParser.PackageParserException { + if ((flags & PackageParser.PARSE_MUST_BE_APK) != 0 && !PackageParser.isApkPath(apkPath)) { + throw new PackageParser.PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, + "Invalid package file: " + apkPath); + } + + if (assets.addAssetPath(apkPath) == 0) { + throw new PackageParser.PackageParserException( + INSTALL_PARSE_FAILED_BAD_MANIFEST, + "Failed adding asset path: " + apkPath); + } + } + + @Override + public AssetManager getBaseAssetManager() throws PackageParser.PackageParserException { + if (mCachedAssetManager != null) { + return mCachedAssetManager; + } + + AssetManager assets = new AssetManager(); + try { + assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + Build.VERSION.RESOURCES_SDK_INT); + loadApkIntoAssetManager(assets, mBaseCodePath, mFlags); + + if (!ArrayUtils.isEmpty(mSplitCodePaths)) { + for (String apkPath : mSplitCodePaths) { + loadApkIntoAssetManager(assets, apkPath, mFlags); + } + } + + mCachedAssetManager = assets; + assets = null; + return mCachedAssetManager; + } finally { + if (assets != null) { + IoUtils.closeQuietly(assets); + } + } + } + + @Override + public AssetManager getSplitAssetManager(int splitIdx) + throws PackageParser.PackageParserException { + return getBaseAssetManager(); + } + + @Override + public void close() throws Exception { + if (mCachedAssetManager != null) { + IoUtils.closeQuietly(mCachedAssetManager); + } + } +} diff --git a/core/java/android/content/pm/split/SplitAssetDependencyLoader.java b/core/java/android/content/pm/split/SplitAssetDependencyLoader.java new file mode 100644 index 000000000000..3ad45b6e2555 --- /dev/null +++ b/core/java/android/content/pm/split/SplitAssetDependencyLoader.java @@ -0,0 +1,153 @@ +/* + * 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 android.content.pm.split; + +import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST; +import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK; + +import android.content.pm.PackageParser; +import android.content.res.AssetManager; +import android.os.Build; +import android.util.SparseIntArray; + +import libcore.io.IoUtils; + +import java.util.ArrayList; +import java.util.Collections; + +/** + * Loads AssetManagers for splits and their dependencies. This SplitAssetLoader implementation + * is to be used when an application opts-in to isolated split loading. + * @hide + */ +public class SplitAssetDependencyLoader + extends SplitDependencyLoaderHelper<PackageParser.PackageParserException> + implements SplitAssetLoader { + private static final int BASE_ASSET_PATH_IDX = -1; + private final String mBasePath; + private final String[] mSplitNames; + private final String[] mSplitPaths; + private final int mFlags; + + private String[] mCachedBasePaths; + private AssetManager mCachedBaseAssetManager; + + private String[][] mCachedSplitPaths; + private AssetManager[] mCachedAssetManagers; + + public SplitAssetDependencyLoader(PackageParser.PackageLite pkg, SparseIntArray dependencies, + int flags) { + super(dependencies); + mBasePath = pkg.baseCodePath; + mSplitNames = pkg.splitNames; + mSplitPaths = pkg.splitCodePaths; + mFlags = flags; + mCachedBasePaths = null; + mCachedBaseAssetManager = null; + mCachedSplitPaths = new String[mSplitNames.length][]; + mCachedAssetManagers = new AssetManager[mSplitNames.length]; + } + + @Override + protected boolean isSplitCached(int splitIdx) { + if (splitIdx != -1) { + return mCachedAssetManagers[splitIdx] != null; + } + return mCachedBaseAssetManager != null; + } + + // Adds all non-code configuration splits for this split name. The split name is expected + // to represent a feature split. + private void addAllConfigSplits(String splitName, ArrayList<String> outAssetPaths) { + for (int i = 0; i < mSplitNames.length; i++) { + if (isConfigurationSplitOf(mSplitNames[i], splitName)) { + outAssetPaths.add(mSplitPaths[i]); + } + } + } + + private static AssetManager createAssetManagerWithPaths(String[] assetPaths, int flags) + throws PackageParser.PackageParserException { + final AssetManager assets = new AssetManager(); + try { + assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + Build.VERSION.RESOURCES_SDK_INT); + + for (String assetPath : assetPaths) { + if ((flags & PackageParser.PARSE_MUST_BE_APK) != 0 && + !PackageParser.isApkPath(assetPath)) { + throw new PackageParser.PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, + "Invalid package file: " + assetPath); + } + + if (assets.addAssetPath(assetPath) == 0) { + throw new PackageParser.PackageParserException( + INSTALL_PARSE_FAILED_BAD_MANIFEST, + "Failed adding asset path: " + assetPath); + } + } + return assets; + } catch (Throwable e) { + IoUtils.closeQuietly(assets); + throw e; + } + } + + @Override + protected void constructSplit(int splitIdx, int parentSplitIdx) throws + PackageParser.PackageParserException { + final ArrayList<String> assetPaths = new ArrayList<>(); + if (splitIdx == BASE_ASSET_PATH_IDX) { + assetPaths.add(mBasePath); + addAllConfigSplits(null, assetPaths); + mCachedBasePaths = assetPaths.toArray(new String[assetPaths.size()]); + mCachedBaseAssetManager = createAssetManagerWithPaths(mCachedBasePaths, mFlags); + return; + } + + if (parentSplitIdx == BASE_ASSET_PATH_IDX) { + Collections.addAll(assetPaths, mCachedBasePaths); + } else { + Collections.addAll(assetPaths, mCachedSplitPaths[parentSplitIdx]); + } + + assetPaths.add(mSplitPaths[splitIdx]); + addAllConfigSplits(mSplitNames[splitIdx], assetPaths); + mCachedSplitPaths[splitIdx] = assetPaths.toArray(new String[assetPaths.size()]); + mCachedAssetManagers[splitIdx] = createAssetManagerWithPaths(mCachedSplitPaths[splitIdx], + mFlags); + } + + @Override + public AssetManager getBaseAssetManager() throws PackageParser.PackageParserException { + loadDependenciesForSplit(BASE_ASSET_PATH_IDX); + return mCachedBaseAssetManager; + } + + @Override + public AssetManager getSplitAssetManager(int idx) throws PackageParser.PackageParserException { + loadDependenciesForSplit(idx); + return mCachedAssetManagers[idx]; + } + + @Override + public void close() throws Exception { + IoUtils.closeQuietly(mCachedBaseAssetManager); + for (AssetManager assets : mCachedAssetManagers) { + IoUtils.closeQuietly(assets); + } + } +} diff --git a/core/java/android/content/pm/split/SplitAssetLoader.java b/core/java/android/content/pm/split/SplitAssetLoader.java new file mode 100644 index 000000000000..108fb95a150d --- /dev/null +++ b/core/java/android/content/pm/split/SplitAssetLoader.java @@ -0,0 +1,30 @@ +/* + * 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 android.content.pm.split; + +import android.content.pm.PackageParser; +import android.content.res.AssetManager; + +/** + * Simple interface for loading base Assets and Splits. Used by PackageParser when parsing + * split APKs. + * + * @hide + */ +public interface SplitAssetLoader extends AutoCloseable { + AssetManager getBaseAssetManager() throws PackageParser.PackageParserException; + AssetManager getSplitAssetManager(int splitIdx) throws PackageParser.PackageParserException; +} diff --git a/core/java/android/content/pm/split/SplitDependencyLoaderHelper.java b/core/java/android/content/pm/split/SplitDependencyLoaderHelper.java new file mode 100644 index 000000000000..b49348000449 --- /dev/null +++ b/core/java/android/content/pm/split/SplitDependencyLoaderHelper.java @@ -0,0 +1,149 @@ +/* + * 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 android.content.pm.split; + +import android.annotation.Nullable; +import android.util.IntArray; +import android.util.SparseIntArray; + +/** + * A helper class that implements the dependency tree traversal for splits. Callbacks + * are implemented by subclasses to notify whether a split has already been constructed + * and is cached, and to actually create the split requested. + * + * This helper is meant to be subclassed so as to reduce the number of allocations + * needed to make use of it. + * + * All inputs and outputs are assumed to be indices into an array of splits. + * + * @hide + */ +public abstract class SplitDependencyLoaderHelper<E extends Exception> { + @Nullable private final SparseIntArray mDependencies; + + /** + * Construct a new SplitDependencyLoaderHelper. Meant to be called from the + * subclass constructor. + * @param dependencies The dependency tree of splits. Can be null, which leads to + * just the implicit dependency of all splits on the base. + */ + protected SplitDependencyLoaderHelper(@Nullable SparseIntArray dependencies) { + mDependencies = dependencies; + } + + /** + * Traverses the dependency tree and constructs any splits that are not already + * cached. This routine short-circuits and skips the creation of splits closer to the + * root if they are cached, as reported by the subclass implementation of + * {@link #isSplitCached(int)}. The construction of splits is delegated to the subclass + * implementation of {@link #constructSplit(int, int)}. + * @param splitIdx The index of the split to load. Can be -1, which represents the + * base Application. + */ + protected void loadDependenciesForSplit(int splitIdx) throws E { + // Quick check before any allocations are done. + if (isSplitCached(splitIdx)) { + return; + } + + final IntArray linearDependencies = new IntArray(); + linearDependencies.add(splitIdx); + + // Collect all the dependencies that need to be constructed. + // They will be listed from leaf to root. + while (splitIdx >= 0) { + splitIdx = mDependencies != null ? mDependencies.get(splitIdx, -1) : -1; + if (isSplitCached(splitIdx)) { + break; + } + linearDependencies.add(splitIdx); + } + + // Visit each index, from right to left (root to leaf). + int parentIdx = splitIdx; + for (int i = linearDependencies.size() - 1; i >= 0; i--) { + final int idx = linearDependencies.get(i); + constructSplit(idx, parentIdx); + parentIdx = idx; + } + } + + /** + * Subclass to report whether the split at `splitIdx` is cached and need not be constructed. + * It is assumed that if `splitIdx` is cached, any parent of `splitIdx` is also cached. + * @param splitIdx The index of the split to check for in the cache. + * @return true if the split is cached and does not need to be constructed. + */ + protected abstract boolean isSplitCached(int splitIdx); + + /** + * Subclass to construct a split at index `splitIdx` with parent split `parentSplitIdx`. + * The result is expected to be cached by the subclass in its own structures. + * @param splitIdx The index of the split to construct. Can be -1, which represents the + * base Application. + * @param parentSplitIdx The index of the parent split. Can be -1, which represents the + * base Application. + * @throws E + */ + protected abstract void constructSplit(int splitIdx, int parentSplitIdx) throws E; + + /** + * Returns true if `splitName` represents a Configuration split of `featureSplitName`. + * + * A Configuration split's name is prefixed with the associated Feature split's name + * or the empty string if the split is for the base Application APK. It is then followed by the + * dollar sign character "$" and some unique string that should represent the configurations + * the split contains. + * + * Example: + * <table> + * <tr> + * <th>Feature split name</th> + * <th>Configuration split name: xhdpi</th> + * <th>Configuration split name: fr-rFR</th> + * </tr> + * <tr> + * <td>(base APK)</td> + * <td><code>$xhdpi</code></td> + * <td><code>$fr-rFR</code></td> + * </tr> + * <tr> + * <td><code>Extras</code></td> + * <td><code>Extras$xhdpi</code></td> + * <td><code>Extras$fr-rFR</code></td> + * </tr> + * </table> + * + * @param splitName The name of the split to check. + * @param featureSplitName The name of the Feature split. May be null or "" if checking + * the base Application APK. + * @return true if the splitName represents a Configuration split of featureSplitName. + */ + protected static boolean isConfigurationSplitOf(String splitName, String featureSplitName) { + if (featureSplitName == null || featureSplitName.length() == 0) { + // We are looking for configuration splits of the base, which have some legacy support. + if (splitName.startsWith("config_")) { + return true; + } else if (splitName.startsWith("$")) { + return true; + } else { + return false; + } + } else { + return splitName.startsWith(featureSplitName + "$"); + } + } +} diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java index d6d5cb6493e0..1db685ab8dec 100644 --- a/core/java/android/os/Parcel.java +++ b/core/java/android/os/Parcel.java @@ -26,6 +26,7 @@ import android.util.Size; import android.util.SizeF; import android.util.SparseArray; import android.util.SparseBooleanArray; +import android.util.SparseIntArray; import libcore.util.SneakyThrow; @@ -891,6 +892,21 @@ public final class Parcel { } } + public final void writeSparseIntArray(SparseIntArray val) { + if (val == null) { + writeInt(-1); + return; + } + int N = val.size(); + writeInt(N); + int i=0; + while (i < N) { + writeInt(val.keyAt(i)); + writeInt(val.valueAt(i)); + i++; + } + } + public final void writeBooleanArray(boolean[] val) { if (val != null) { int N = val.length; @@ -2154,6 +2170,20 @@ public final class Parcel { } /** + * Read and return a new SparseIntArray object from the parcel at the current + * dataPosition(). Returns null if the previously written array object was null. + */ + public final SparseIntArray readSparseIntArray() { + int N = readInt(); + if (N < 0) { + return null; + } + SparseIntArray sa = new SparseIntArray(N); + readSparseIntArrayInternal(sa, N); + return sa; + } + + /** * Read and return a new ArrayList containing a particular object type from * the parcel that was written with {@link #writeTypedList} at the * current dataPosition(). Returns null if the @@ -2922,6 +2952,15 @@ public final class Parcel { } } + private void readSparseIntArrayInternal(SparseIntArray outVal, int N) { + while (N > 0) { + int key = readInt(); + int value = readInt(); + outVal.append(key, value); + N--; + } + } + /** * @hide For testing */ diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 113ace34ce52..0dde91bb57b0 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -1001,6 +1001,13 @@ <enum name="preferExternal" value="2" /> </attr> + <!-- If set to <code>true</code>, indicates to the platform that any split APKs + installed for this application should be loaded into their own Context + objects and not appear in the base application's Context. + + <p>The default value of this attribute is <code>false</code>. --> + <attr name="isolatedSplits" format="boolean" /> + <!-- Extra options for an activity's UI. Applies to either the {@code <activity>} or {@code <application>} tag. If specified on the {@code <application>} tag these will be considered defaults for all activities in the @@ -1266,6 +1273,7 @@ <attr name="sharedUserId" /> <attr name="sharedUserLabel" /> <attr name="installLocation" /> + <attr name="isolatedSplits" /> </declare-styleable> <!-- The <code>application</code> tag describes application-level components @@ -2462,4 +2470,8 @@ <attr name="hash" format="string" /> </declare-styleable> + <declare-styleable name="AndroidManifestUsesSplit" parent="AndroidManifest"> + <attr name="name" format="string" /> + </declare-styleable> + </resources> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index e387650ca2dd..1146871e488a 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2789,6 +2789,7 @@ <public name="certDigest" /> <public name="splitName" /> <public name="colorMode" /> + <public name="isolatedSplits" /> </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 d1aed3e3018a..067a13642781 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -894,7 +894,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, + final PackageLite pkg = new PackageLite(null, baseApk, null, null, splitPaths.toArray(new String[splitPaths.size()]), null); final boolean isForwardLocked = (params.installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0; diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 63a5d145fe62..9899cd410b54 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -10101,8 +10101,10 @@ public class PackageManagerService extends IPackageManager.Stub { a.info.packageName = pkg.applicationInfo.packageName; a.info.sourceDir = pkg.applicationInfo.sourceDir; a.info.publicSourceDir = pkg.applicationInfo.publicSourceDir; + a.info.splitNames = pkg.splitNames; a.info.splitSourceDirs = pkg.applicationInfo.splitSourceDirs; a.info.splitPublicSourceDirs = pkg.applicationInfo.splitPublicSourceDirs; + a.info.splitDependencies = pkg.applicationInfo.splitDependencies; a.info.dataDir = pkg.applicationInfo.dataDir; a.info.deviceProtectedDataDir = pkg.applicationInfo.deviceProtectedDataDir; a.info.credentialProtectedDataDir = pkg.applicationInfo.credentialProtectedDataDir; diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index 49b96b0b55fe..2f8d74925df1 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -162,7 +162,7 @@ class PackageManagerShellCommand extends ShellCommand { if (file.isFile()) { try { ApkLite baseApk = PackageParser.parseApkLite(file, 0); - PackageLite pkgLite = new PackageLite(null, baseApk, null, null, null); + PackageLite pkgLite = new PackageLite(null, baseApk, null, null, null, null); params.sessionParams.setSize(PackageHelper.calculateInstalledSize( pkgLite, false, params.sessionParams.abiOverride)); } catch (PackageParserException | IOException e) { diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-runner/src/android/test/mock/MockContext.java index b6e701eb5225..9ffd92d0a754 100644 --- a/test-runner/src/android/test/mock/MockContext.java +++ b/test-runner/src/android/test/mock/MockContext.java @@ -692,6 +692,13 @@ public class MockContext extends Context { return null; } + /** @hide */ + @Override + public Context createContextForSplit(String splitName) + throws PackageManager.NameNotFoundException { + throw new UnsupportedOperationException(); + } + /** {@hide} */ @Override public Context createPackageContextAsUser(String packageName, int flags, UserHandle user) diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java index 68680d5d8d9c..dff4f6905db5 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java @@ -1325,6 +1325,12 @@ public final class BridgeContext extends Context { } @Override + public Context createContextForSplit(String splitName) { + // pass + return null; + } + + @Override public String[] databaseList() { // pass return null; |