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

Commit b319ccfd authored by David Brazdil's avatar David Brazdil
Browse files

Create AppComponentFactory.instantiateClassLoader API

Adds a hook to AppComponentFactory to allow control over the
instantiation of the main app class loader. LoadedApk creates
the default class loader and uses it to load the base APK. If
AppComponentFactory is declared in the manifest, its new method
instantiateClassLoader is called and its result becomes the
class loader used by LoadedApk to instantiate other classes
declared in the manifest. By default this is simply the class
loader created by LoadedApk.

Second method provides AppComponentFactory with a copy of
ApplicationInfo. The factory otherwise cannot locate any of the
app's resources, including its APK or the data folder.

Bug: 111342996
Test: atest CtsClassLoaderFactoryPathClassLoaderTestCases
Test: atest CtsClassLoaderFactoryInMemoryDexClassLoaderTestCases
Merged-In: Id21d9afaf00b9cb64a107bc9893b952407cff0b5
Change-Id: Id21d9afaf00b9cb64a107bc9893b952407cff0b5
(cherry picked from commit fd6dcc21)
parent e1a7e507
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -4200,8 +4200,10 @@ package android.app {
  public class AppComponentFactory {
    ctor public AppComponentFactory();
    method public android.content.pm.ApplicationInfo getApplicationInfo();
    method public android.app.Activity instantiateActivity(java.lang.ClassLoader, java.lang.String, android.content.Intent) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
    method public android.app.Application instantiateApplication(java.lang.ClassLoader, java.lang.String) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
    method public java.lang.ClassLoader instantiateClassLoader(java.lang.ClassLoader);
    method public android.content.ContentProvider instantiateProvider(java.lang.ClassLoader, java.lang.String) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
    method public android.content.BroadcastReceiver instantiateReceiver(java.lang.ClassLoader, java.lang.String, android.content.Intent) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
    method public android.app.Service instantiateService(java.lang.ClassLoader, java.lang.String, android.content.Intent) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+25 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import android.annotation.Nullable;
import android.content.BroadcastReceiver;
import android.content.ContentProvider;
import android.content.Intent;
import android.content.pm.ApplicationInfo;

/**
 * Interface used to control the instantiation of manifest elements.
@@ -32,6 +33,17 @@ import android.content.Intent;
 */
public class AppComponentFactory {

    /**
     * Allows application to override the creation of the default class loader.
     * This can be used to perform things such as dependency injection or setting up
     * a custom class loader hierarchy.
     *
     * @param cl        The default classloader instantiated by platform.
     */
    public @NonNull ClassLoader instantiateClassLoader(@NonNull ClassLoader cl) {
        return cl;
    }

    /**
     * Allows application to override the creation of the application object. This can be used to
     * perform things such as dependency injection or class loader changes to these
@@ -121,6 +133,19 @@ public class AppComponentFactory {
        return (ContentProvider) cl.loadClass(className).newInstance();
    }

    private ApplicationInfo mApplicationInfo = null;

    void setApplicationInfo(ApplicationInfo info) {
        mApplicationInfo = info;
    }

    /**
     * Returns the ApplicationInfo associated with this package.
     */
    public ApplicationInfo getApplicationInfo() {
        return mApplicationInfo;
    }

    /**
     * @hide
     */
+38 −18
Original line number Diff line number Diff line
@@ -117,6 +117,7 @@ public final class LoadedApk {
    private File mCredentialProtectedDataDirFile;
    @UnsupportedAppUsage
    private final ClassLoader mBaseClassLoader;
    private ClassLoader mDefaultClassLoader;
    private final boolean mSecurityViolation;
    private final boolean mIncludeCode;
    private final boolean mRegisterPackage;
@@ -224,9 +225,10 @@ public final class LoadedApk {
        mSecurityViolation = false;
        mIncludeCode = true;
        mRegisterPackage = false;
        mClassLoader = ClassLoader.getSystemClassLoader();
        mResources = Resources.getSystem();
        mAppComponentFactory = createAppFactory(mApplicationInfo, mClassLoader);
        mDefaultClassLoader = ClassLoader.getSystemClassLoader();
        mAppComponentFactory = createAppFactory(mApplicationInfo, mDefaultClassLoader);
        mClassLoader = mAppComponentFactory.instantiateClassLoader(mDefaultClassLoader);
    }

    /**
@@ -235,15 +237,21 @@ public final class LoadedApk {
    void installSystemApplicationInfo(ApplicationInfo info, ClassLoader classLoader) {
        assert info.packageName.equals("android");
        mApplicationInfo = info;
        mClassLoader = classLoader;
        mAppComponentFactory = createAppFactory(info, classLoader);
        mDefaultClassLoader = classLoader;
        mAppComponentFactory = createAppFactory(info, mDefaultClassLoader);
        mClassLoader = mAppComponentFactory.instantiateClassLoader(mDefaultClassLoader);
    }

    private AppComponentFactory createAppFactory(ApplicationInfo appInfo, ClassLoader cl) {
        if (appInfo.appComponentFactory != null && cl != null) {
            try {
                return (AppComponentFactory) cl.loadClass(appInfo.appComponentFactory)
                        .newInstance();
                AppComponentFactory factory = (AppComponentFactory) cl.loadClass(
                        appInfo.appComponentFactory).newInstance();
                // Pass a copy of ApplicationInfo to the factory. Copying protects the framework
                // from apps which would override the factory and change ApplicationInfo contents.
                // ApplicationInfo is used to set up the default class loader.
                factory.setApplicationInfo(new ApplicationInfo(appInfo));
                return factory;
            } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
                Slog.e(TAG, "Unable to instantiate appComponentFactory", e);
            }
@@ -357,7 +365,7 @@ public final class LoadedApk {
                        getClassLoader());
            }
        }
        mAppComponentFactory = createAppFactory(aInfo, mClassLoader);
        mAppComponentFactory = createAppFactory(aInfo, mDefaultClassLoader);
    }

    private void setApplicationInfo(ApplicationInfo aInfo) {
@@ -633,11 +641,12 @@ public final class LoadedApk {
            }

            if (mBaseClassLoader != null) {
                mClassLoader = mBaseClassLoader;
                mDefaultClassLoader = mBaseClassLoader;
            } else {
                mClassLoader = ClassLoader.getSystemClassLoader();
                mDefaultClassLoader = ClassLoader.getSystemClassLoader();
            }
            mAppComponentFactory = createAppFactory(mApplicationInfo, mClassLoader);
            mAppComponentFactory = createAppFactory(mApplicationInfo, mDefaultClassLoader);
            mClassLoader = mAppComponentFactory.instantiateClassLoader(mDefaultClassLoader);

            return;
        }
@@ -715,9 +724,9 @@ public final class LoadedApk {
        // call System.loadLibrary() on a classloader from a LoadedApk with
        // mIncludeCode == false).
        if (!mIncludeCode) {
            if (mClassLoader == null) {
            if (mDefaultClassLoader == null) {
                StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
                mClassLoader = ApplicationLoaders.getDefault().getClassLoader(
                mDefaultClassLoader = ApplicationLoaders.getDefault().getClassLoader(
                        "" /* codePath */, mApplicationInfo.targetSdkVersion, isBundledApp,
                        librarySearchPath, libraryPermittedPath, mBaseClassLoader,
                        null /* classLoaderName */);
@@ -725,6 +734,10 @@ public final class LoadedApk {
                mAppComponentFactory = AppComponentFactory.DEFAULT;
            }

            if (mClassLoader == null) {
                mClassLoader = mAppComponentFactory.instantiateClassLoader(mDefaultClassLoader);
            }

            return;
        }

@@ -741,16 +754,16 @@ public final class LoadedApk {
                    ", JNI path: " + librarySearchPath);

        boolean needToSetupJitProfiles = false;
        if (mClassLoader == null) {
        if (mDefaultClassLoader == null) {
            // Temporarily disable logging of disk reads on the Looper thread
            // as this is early and necessary.
            StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();

            mClassLoader = ApplicationLoaders.getDefault().getClassLoader(zip,
            mDefaultClassLoader = ApplicationLoaders.getDefault().getClassLoader(zip,
                    mApplicationInfo.targetSdkVersion, isBundledApp, librarySearchPath,
                    libraryPermittedPath, mBaseClassLoader,
                    mApplicationInfo.classLoaderName);
            mAppComponentFactory = createAppFactory(mApplicationInfo, mClassLoader);
            mAppComponentFactory = createAppFactory(mApplicationInfo, mDefaultClassLoader);

            StrictMode.setThreadPolicy(oldPolicy);
            // Setup the class loader paths for profiling.
@@ -761,7 +774,7 @@ public final class LoadedApk {
            // Temporarily disable logging of disk reads on the Looper thread as this is necessary
            StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
            try {
                ApplicationLoaders.getDefault().addNative(mClassLoader, libPaths);
                ApplicationLoaders.getDefault().addNative(mDefaultClassLoader, libPaths);
            } finally {
                StrictMode.setThreadPolicy(oldPolicy);
            }
@@ -799,7 +812,7 @@ public final class LoadedApk {
        if (!extraLibPaths.isEmpty()) {
            StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
            try {
                ApplicationLoaders.getDefault().addNative(mClassLoader, extraLibPaths);
                ApplicationLoaders.getDefault().addNative(mDefaultClassLoader, extraLibPaths);
            } finally {
                StrictMode.setThreadPolicy(oldPolicy);
            }
@@ -807,7 +820,7 @@ public final class LoadedApk {

        if (addedPaths != null && addedPaths.size() > 0) {
            final String add = TextUtils.join(File.pathSeparator, addedPaths);
            ApplicationLoaders.getDefault().addPath(mClassLoader, add);
            ApplicationLoaders.getDefault().addPath(mDefaultClassLoader, add);
            // Setup the new code paths for profiling.
            needToSetupJitProfiles = true;
        }
@@ -824,6 +837,13 @@ public final class LoadedApk {
        if (needToSetupJitProfiles && !ActivityThread.isSystem()) {
            setupJitProfileSupport();
        }

        // Call AppComponentFactory to select/create the main class loader of this app.
        // Since this may call code in the app, mDefaultClassLoader must be fully set up
        // before invoking the factory.
        if (mClassLoader == null) {
            mClassLoader = mAppComponentFactory.instantiateClassLoader(mDefaultClassLoader);
        }
    }

    @UnsupportedAppUsage