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

Commit 6945710e authored by Jason Monk's avatar Jason Monk
Browse files

Add API to allow apps more control over instantiation

Let them control construction of standard manifest elements (like
activities) through Application. This will allow for things like
dependency injection and class loader magic.

Test: manual
Change-Id: Iaa2215b225c3b97f4e9ac809dfe499f98c7ad5f1
parent d05b6252
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -4204,6 +4204,12 @@ package android.app {
  public class Application extends android.content.ContextWrapper implements android.content.ComponentCallbacks2 {
    ctor public Application();
    method public android.app.Activity instantiateActivity(java.lang.ClassLoader, java.lang.String, android.content.Intent);
    method public android.app.backup.BackupAgent instantiateBackupAgent(java.lang.ClassLoader, java.lang.String);
    method public android.app.Instrumentation instantiateInstrumentation(java.lang.ClassLoader, java.lang.String);
    method public android.content.ContentProvider instantiateProvider(java.lang.ClassLoader, java.lang.String);
    method public android.content.BroadcastReceiver instantiateReceiver(java.lang.ClassLoader, java.lang.String, android.content.Intent);
    method public android.app.Service instantiateService(java.lang.ClassLoader, java.lang.String, android.content.Intent);
    method public void onConfigurationChanged(android.content.res.Configuration);
    method public void onCreate();
    method public void onLowMemory();
+6 −0
Original line number Diff line number Diff line
@@ -4359,6 +4359,12 @@ package android.app {
  public class Application extends android.content.ContextWrapper implements android.content.ComponentCallbacks2 {
    ctor public Application();
    method public android.app.Activity instantiateActivity(java.lang.ClassLoader, java.lang.String, android.content.Intent);
    method public android.app.backup.BackupAgent instantiateBackupAgent(java.lang.ClassLoader, java.lang.String);
    method public android.app.Instrumentation instantiateInstrumentation(java.lang.ClassLoader, java.lang.String);
    method public android.content.ContentProvider instantiateProvider(java.lang.ClassLoader, java.lang.String);
    method public android.content.BroadcastReceiver instantiateReceiver(java.lang.ClassLoader, java.lang.String, android.content.Intent);
    method public android.app.Service instantiateService(java.lang.ClassLoader, java.lang.String, android.content.Intent);
    method public void onConfigurationChanged(android.content.res.Configuration);
    method public void onCreate();
    method public void onLowMemory();
+6 −0
Original line number Diff line number Diff line
@@ -4217,6 +4217,12 @@ package android.app {
  public class Application extends android.content.ContextWrapper implements android.content.ComponentCallbacks2 {
    ctor public Application();
    method public android.app.Activity instantiateActivity(java.lang.ClassLoader, java.lang.String, android.content.Intent);
    method public android.app.backup.BackupAgent instantiateBackupAgent(java.lang.ClassLoader, java.lang.String);
    method public android.app.Instrumentation instantiateInstrumentation(java.lang.ClassLoader, java.lang.String);
    method public android.content.ContentProvider instantiateProvider(java.lang.ClassLoader, java.lang.String);
    method public android.content.BroadcastReceiver instantiateReceiver(java.lang.ClassLoader, java.lang.String, android.content.Intent);
    method public android.app.Service instantiateService(java.lang.ClassLoader, java.lang.String, android.content.Intent);
    method public void onConfigurationChanged(android.content.res.Configuration);
    method public void onCreate();
    method public void onLowMemory();
+53 −11
Original line number Diff line number Diff line
@@ -2708,8 +2708,14 @@ public final class ActivityThread {
        Activity activity = null;
        try {
            java.lang.ClassLoader cl = appContext.getClassLoader();
            if (appContext.getApplicationContext() instanceof Application) {
                activity = ((Application) appContext.getApplicationContext())
                        .instantiateActivity(cl, component.getClassName(), r.intent);
            }
            if (activity == null) {
                activity = mInstrumentation.newActivity(
                        cl, component.getClassName(), r.intent);
            }
            StrictMode.incrementExpectedActivityCount(activity.getClass());
            r.intent.setExtrasClassLoader(cl);
            r.intent.prepareToEnterProcess();
@@ -3234,7 +3240,8 @@ public final class ActivityThread {
            data.intent.setExtrasClassLoader(cl);
            data.intent.prepareToEnterProcess();
            data.setExtrasClassLoader(cl);
            receiver = (BroadcastReceiver)cl.loadClass(component).newInstance();
            receiver = instantiate(cl, component, data.intent, app,
                    Application::instantiateReceiver);
        } catch (Exception e) {
            if (DEBUG_BROADCAST) Slog.i(TAG,
                    "Finishing failed broadcast to " + data.intent.getComponent());
@@ -3322,12 +3329,13 @@ public final class ActivityThread {
            } else {
                try {
                    if (DEBUG_BACKUP) Slog.v(TAG, "Initializing agent class " + classname);
                    ContextImpl context = ContextImpl.createAppContext(this, packageInfo);

                    java.lang.ClassLoader cl = packageInfo.getClassLoader();
                    agent = (BackupAgent) cl.loadClass(classname).newInstance();
                    agent = instantiate(cl, classname, context,
                            Application::instantiateBackupAgent);

                    // set up the agent's context
                    ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
                    context.setOuterContext(agent);
                    agent.attach(context);

@@ -3387,9 +3395,12 @@ public final class ActivityThread {
        LoadedApk packageInfo = getPackageInfoNoCheck(
                data.info.applicationInfo, data.compatInfo);
        Service service = null;
        Application app = null;
        try {
            app = packageInfo.makeApplication(false, mInstrumentation);
            java.lang.ClassLoader cl = packageInfo.getClassLoader();
            service = (Service) cl.loadClass(data.info.name).newInstance();
            service = instantiate(cl, data.info.name, data.intent, app,
                    Application::instantiateService);
        } catch (Exception e) {
            if (!mInstrumentation.onException(service, e)) {
                throw new RuntimeException(
@@ -3404,7 +3415,6 @@ public final class ActivityThread {
            ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
            context.setOuterContext(service);

            Application app = packageInfo.makeApplication(false, mInstrumentation);
            service.attach(context, this, data.info.name, data.token, app,
                    ActivityManager.getService());
            service.onCreate();
@@ -5721,8 +5731,8 @@ public final class ActivityThread {

            try {
                final ClassLoader cl = instrContext.getClassLoader();
                mInstrumentation = (Instrumentation)
                    cl.loadClass(data.instrumentationName.getClassName()).newInstance();
                mInstrumentation = instantiate(cl, data.instrumentationName.getClassName(),
                        instrContext, Application::instantiateInstrumentation);
            } catch (Exception e) {
                throw new RuntimeException(
                    "Unable to instantiate instrumentation "
@@ -6267,8 +6277,8 @@ public final class ActivityThread {

            try {
                final java.lang.ClassLoader cl = c.getClassLoader();
                localProvider = (ContentProvider)cl.
                    loadClass(info.name).newInstance();
                localProvider = instantiate(cl, info.name, context,
                        Application::instantiateProvider);
                provider = localProvider.getIContentProvider();
                if (provider == null) {
                    Slog.e(TAG, "Failed to instantiate class " +
@@ -6467,6 +6477,38 @@ public final class ActivityThread {
        }
    }

    private <T> T instantiate(ClassLoader cl, String className, Context c,
            Instantiator<T> instantiator)
            throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        if (c.getApplicationContext() instanceof Application) {
            T a = instantiator.instantiate((Application) c.getApplicationContext(),
                    cl, className);
            if (a != null) return a;
        }
        return (T) cl.loadClass(className).newInstance();
    }

    private <T> T instantiate(ClassLoader cl, String className, Intent intent, Context c,
            IntentInstantiator<T> instantiator)
            throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        if (c.getApplicationContext() instanceof Application) {
            T a = instantiator.instantiate((Application) c.getApplicationContext(),
                    cl, className, intent);
            if (a != null) return a;
        }
        return (T) cl.loadClass(className).newInstance();
    }

    private interface Instantiator<T> {
        T instantiate(Application app, ClassLoader cl, String className)
                throws ClassNotFoundException, IllegalAccessException, InstantiationException;
    }

    private interface IntentInstantiator<T> {
        T instantiate(Application app, ClassLoader cl, String className, Intent intent)
                throws ClassNotFoundException, IllegalAccessException, InstantiationException;
    }

    private static class EventLoggingReporter implements EventLogger.Reporter {
        @Override
        public void report (int code, Object... list) {
+74 −2
Original line number Diff line number Diff line
@@ -16,17 +16,20 @@

package android.app;

import java.util.ArrayList;

import android.annotation.CallSuper;
import android.app.backup.BackupAgent;
import android.content.BroadcastReceiver;
import android.content.ComponentCallbacks;
import android.content.ComponentCallbacks2;
import android.content.ContentProvider;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Bundle;

import java.util.ArrayList;

/**
 * Base class for maintaining global application state. You can provide your own
 * implementation by creating a subclass and specifying the fully-qualified name
@@ -289,4 +292,73 @@ public class Application extends ContextWrapper implements ComponentCallbacks2 {
            }
        }
    }

    /**
     * Allows application to override the creation of activities. This can be used to
     * perform things such as dependency injection or class loader changes to these
     * classes. Return null to use the default creation flow.
     * @param cl The default classloader to use for instantiation.
     * @param className The class to be instantiated.
     * @param intent Intent creating the class.
     */
    public Activity instantiateActivity(ClassLoader cl, String className, Intent intent) {
        return null;
    }

    /**
     * Allows application to override the creation of receivers. This can be used to
     * perform things such as dependency injection or class loader changes to these
     * classes. Return null to use the default creation flow.
     * @param cl The default classloader to use for instantiation.
     * @param className The class to be instantiated.
     * @param intent Intent creating the class.
     */
    public BroadcastReceiver instantiateReceiver(ClassLoader cl, String className, Intent intent) {
        return null;
    }

    /**
     * Allows application to override the creation of services. This can be used to
     * perform things such as dependency injection or class loader changes to these
     * classes. Return null to use the default creation flow.
     * @param cl The default classloader to use for instantiation.
     * @param className The class to be instantiated.
     * @param intent Intent creating the class.
     */
    public Service instantiateService(ClassLoader cl, String className, Intent intent) {
        return null;
    }

    /**
     * Allows application to override the creation of providers. This can be used to
     * perform things such as dependency injection or class loader changes to these
     * classes. Return null to use the default creation flow.
     * @param cl The default classloader to use for instantiation.
     * @param className The class to be instantiated.
     */
    public ContentProvider instantiateProvider(ClassLoader cl, String className) {
        return null;
    }

    /**
     * Allows application to override the creation of backup agents. This can be used to
     * perform things such as dependency injection or class loader changes to these
     * classes. Return null to use the default creation flow.
     * @param cl The default classloader to use for instantiation.
     * @param className The class to be instantiated.
     */
    public BackupAgent instantiateBackupAgent(ClassLoader cl, String className) {
        return null;
    }

    /**
     * Allows application to override the creation of instrumentation. This can be used to
     * perform things such as dependency injection or class loader changes to these
     * classes. Return null to use the default creation flow.
     * @param cl The default classloader to use for instantiation.
     * @param className The class to be instantiated.
     */
    public Instrumentation instantiateInstrumentation(ClassLoader cl, String className) {
        return null;
    }
}
 No newline at end of file