Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4e862815 authored by Adam Lesinski's avatar Adam Lesinski
Browse files

Add support for Split APK dependcies

Apps can now declare in their base APK AndroidManifest.xml
that they want to have their split APKs loaded in isolated
Contexts. This means code and resources from the split
get loaded into their own ClassLoader and AssetManager.

<manifest xmlns:android="..."
          ...
          android:isolatedSplits="true"
          ...

In order to make this more useful, splits can declare dependencies
on other splits, which will all get pulled in to the Context
and run as expected at runtime.

A split declares its dependency on another split by using the
tag <uses-split> in its AndroidManifest.xml:

<manifest xmlns:android="...">
    ...
    <uses-split android:name="feature_split_1" />
    ...

A split can have a single parent on which it depends on. This is
due to the limitation of having a single ClassLoader parent.
All splits depend on the base APK implicitly.

PackageManager verifies that no cycles exist and that each dependency
is present before allowing an installation to succeed.

The runtime will then load splits based on the dependencies.

Given the following APKs:

base <-- split A <-- split C
  ^----- split B

If an Activity defined in split C is launched, then the base,
split A, and split C will be loaded into the ClassLoader defined
for the Activity's Context. The AssetManager will similarly be loaded
with the resources of the splits.

A split can be manually loaded by creating a Context for that split, defined
by its name:

Context.createContextForSplit("my_feature_split_1");

All installed Activities, Services, Receivers, and Providers are accessible
to other apps via Intent resolution. When they are instantiated, they are
given the appropriate Context that satisfies any dependencies the split they
were defined in stipulated.

Test: WIP (CTS tests to come)
Change-Id: I8989712b241b7bc84381f2919d88455fcad62161
parent 896c7f89
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -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;
+8 −0
Original line number Diff line number Diff line
@@ -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);
+8 −0
Original line number Diff line number Diff line
@@ -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;
+1 −1
Original line number Diff line number Diff line
@@ -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));
+25 −13
Original line number Diff line number Diff line
@@ -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.
Loading