From 4d0fd71700065e3db479867c6ec3190cdf9342c1 Mon Sep 17 00:00:00 2001 From: Jason Monk Date: Tue, 6 Jun 2017 18:01:31 -0400 Subject: [PATCH] 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 Bug: 62534409 Change-Id: I5fc2a7ec52bb0e232966704d16f94a192d033267 Merged-In: Iaa2215b225c3b97f4e9ac809dfe499f98c7ad5f1 --- core/java/android/app/ActivityThread.java | 64 +++++++++++++++++++----- core/java/android/app/Application.java | 82 ++++++++++++++++++++++++++++++- 2 files changed, 133 insertions(+), 13 deletions(-) diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index df550807dd30..8a8f8dd681fd 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -2708,8 +2708,14 @@ public final class ActivityThread { Activity activity = null; try { java.lang.ClassLoader cl = appContext.getClassLoader(); - activity = mInstrumentation.newActivity( - cl, component.getClassName(), r.intent); + 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 instantiate(ClassLoader cl, String className, Context c, + Instantiator 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 instantiate(ClassLoader cl, String className, Intent intent, Context c, + IntentInstantiator 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 instantiate(Application app, ClassLoader cl, String className) + throws ClassNotFoundException, IllegalAccessException, InstantiationException; + } + + private interface IntentInstantiator { + 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) { diff --git a/core/java/android/app/Application.java b/core/java/android/app/Application.java index 156df36a600c..7fb5e2e7e442 100644 --- a/core/java/android/app/Application.java +++ b/core/java/android/app/Application.java @@ -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,79 @@ 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. + * @hide + */ + 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. + * @hide + */ + 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. + * @hide + */ + 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. + * @hide + */ + 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. + * @hide + */ + 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. + * @hide + */ + public Instrumentation instantiateInstrumentation(ClassLoader cl, String className) { + return null; + } } \ No newline at end of file -- 2.11.0