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

Commit a80bfb5d authored by Jason Monk's avatar Jason Monk
Browse files

Add AppComponentFactory API

API to allow some classloader control over instantiation of
items from application's manifest. Unlike the first attempt
this does not encourage developers to extend Application, it simply
provides a small surface to allow control over the creation of objects
like Activities, Services, Receivers, and Providers.

Test: manual
Bug: 70623879
Change-Id: Idcab7c60f54ce3f4575ac29dcdcae321cf458bf3
parent 3fe9d790
Loading
Loading
Loading
Loading
+11 −0
Original line number Original line Diff line number Diff line
@@ -292,6 +292,7 @@ package android {
    field public static final int apduServiceBanner = 16843757; // 0x10103ed
    field public static final int apduServiceBanner = 16843757; // 0x10103ed
    field public static final int apiKey = 16843281; // 0x1010211
    field public static final int apiKey = 16843281; // 0x1010211
    field public static final int appCategory = 16844101; // 0x1010545
    field public static final int appCategory = 16844101; // 0x1010545
    field public static final int appComponentFactory = 16844154; // 0x101057a
    field public static final int author = 16843444; // 0x10102b4
    field public static final int author = 16843444; // 0x10102b4
    field public static final int authorities = 16842776; // 0x1010018
    field public static final int authorities = 16842776; // 0x1010018
    field public static final int autoAdvanceViewId = 16843535; // 0x101030f
    field public static final int autoAdvanceViewId = 16843535; // 0x101030f
@@ -4174,6 +4175,15 @@ package android.app {
    ctor public AliasActivity();
    ctor public AliasActivity();
  }
  }
  public class AppComponentFactory {
    ctor public AppComponentFactory();
    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 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;
  }
  public class AppOpsManager {
  public class AppOpsManager {
    method public int checkOp(java.lang.String, int, java.lang.String);
    method public int checkOp(java.lang.String, int, java.lang.String);
    method public int checkOpNoThrow(java.lang.String, int, java.lang.String);
    method public int checkOpNoThrow(java.lang.String, int, java.lang.String);
@@ -10409,6 +10419,7 @@ package android.content.pm {
    field public static final int FLAG_UPDATED_SYSTEM_APP = 128; // 0x80
    field public static final int FLAG_UPDATED_SYSTEM_APP = 128; // 0x80
    field public static final int FLAG_USES_CLEARTEXT_TRAFFIC = 134217728; // 0x8000000
    field public static final int FLAG_USES_CLEARTEXT_TRAFFIC = 134217728; // 0x8000000
    field public static final int FLAG_VM_SAFE_MODE = 16384; // 0x4000
    field public static final int FLAG_VM_SAFE_MODE = 16384; // 0x4000
    field public java.lang.String appComponentFactory;
    field public java.lang.String backupAgentName;
    field public java.lang.String backupAgentName;
    field public int category;
    field public int category;
    field public java.lang.String className;
    field public java.lang.String className;
+13 −4
Original line number Original line Diff line number Diff line
@@ -3152,7 +3152,8 @@ public final class ActivityThread extends ClientTransactionHandler {
            data.intent.setExtrasClassLoader(cl);
            data.intent.setExtrasClassLoader(cl);
            data.intent.prepareToEnterProcess();
            data.intent.prepareToEnterProcess();
            data.setExtrasClassLoader(cl);
            data.setExtrasClassLoader(cl);
            receiver = (BroadcastReceiver)cl.loadClass(component).newInstance();
            receiver = packageInfo.getAppFactory()
                    .instantiateReceiver(cl, data.info.name, data.intent);
        } catch (Exception e) {
        } catch (Exception e) {
            if (DEBUG_BROADCAST) Slog.i(TAG,
            if (DEBUG_BROADCAST) Slog.i(TAG,
                    "Finishing failed broadcast to " + data.intent.getComponent());
                    "Finishing failed broadcast to " + data.intent.getComponent());
@@ -3307,7 +3308,8 @@ public final class ActivityThread extends ClientTransactionHandler {
        Service service = null;
        Service service = null;
        try {
        try {
            java.lang.ClassLoader cl = packageInfo.getClassLoader();
            java.lang.ClassLoader cl = packageInfo.getClassLoader();
            service = (Service) cl.loadClass(data.info.name).newInstance();
            service = packageInfo.getAppFactory()
                    .instantiateService(cl, data.info.name, data.intent);
        } catch (Exception e) {
        } catch (Exception e) {
            if (!mInstrumentation.onException(service, e)) {
            if (!mInstrumentation.onException(service, e)) {
                throw new RuntimeException(
                throw new RuntimeException(
@@ -5644,6 +5646,7 @@ public final class ActivityThread extends ClientTransactionHandler {
            }
            }
        } else {
        } else {
            mInstrumentation = new Instrumentation();
            mInstrumentation = new Instrumentation();
            mInstrumentation.basicInit(this);
        }
        }


        if ((data.appInfo.flags&ApplicationInfo.FLAG_LARGE_HEAP) != 0) {
        if ((data.appInfo.flags&ApplicationInfo.FLAG_LARGE_HEAP) != 0) {
@@ -6175,8 +6178,13 @@ public final class ActivityThread extends ClientTransactionHandler {


            try {
            try {
                final java.lang.ClassLoader cl = c.getClassLoader();
                final java.lang.ClassLoader cl = c.getClassLoader();
                localProvider = (ContentProvider)cl.
                LoadedApk packageInfo = peekPackageInfo(ai.packageName, true);
                    loadClass(info.name).newInstance();
                if (packageInfo == null) {
                    // System startup case.
                    packageInfo = getSystemContext().mPackageInfo;
                }
                localProvider = packageInfo.getAppFactory()
                        .instantiateProvider(cl, info.name);
                provider = localProvider.getIContentProvider();
                provider = localProvider.getIContentProvider();
                if (provider == null) {
                if (provider == null) {
                    Slog.e(TAG, "Failed to instantiate class " +
                    Slog.e(TAG, "Failed to instantiate class " +
@@ -6322,6 +6330,7 @@ public final class ActivityThread extends ClientTransactionHandler {
                    UserHandle.myUserId());
                    UserHandle.myUserId());
            try {
            try {
                mInstrumentation = new Instrumentation();
                mInstrumentation = new Instrumentation();
                mInstrumentation.basicInit(this);
                ContextImpl context = ContextImpl.createAppContext(
                ContextImpl context = ContextImpl.createAppContext(
                        this, getSystemContext().mPackageInfo);
                        this, getSystemContext().mPackageInfo);
                mInitialApplication = context.mPackageInfo.makeApplication(true, null);
                mInitialApplication = context.mPackageInfo.makeApplication(true, null);
+112 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2017 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.app;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.BroadcastReceiver;
import android.content.ContentProvider;
import android.content.Intent;

/**
 * Interface used to control the instantiation of manifest elements.
 *
 * @see #instantiateApplication
 * @see #instantiateActivity
 * @see #instantiateService
 * @see #instantiateReceiver
 * @see #instantiateProvider
 */
public class AppComponentFactory {

    /**
     * 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
     * classes.
     *
     * @param cl        The default classloader to use for instantiation.
     * @param className The class to be instantiated.
     */
    public @NonNull Application instantiateApplication(@NonNull ClassLoader cl,
            @NonNull String className)
            throws InstantiationException, IllegalAccessException, ClassNotFoundException {
        return (Application) cl.loadClass(className).newInstance();
    }

    /**
     * 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.
     *
     * @param cl        The default classloader to use for instantiation.
     * @param className The class to be instantiated.
     * @param intent    Intent creating the class.
     */
    public @NonNull Activity instantiateActivity(@NonNull ClassLoader cl, @NonNull String className,
            @Nullable Intent intent)
            throws InstantiationException, IllegalAccessException, ClassNotFoundException {
        return (Activity) cl.loadClass(className).newInstance();
    }

    /**
     * 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.
     *
     * @param cl        The default classloader to use for instantiation.
     * @param className The class to be instantiated.
     * @param intent    Intent creating the class.
     */
    public @NonNull BroadcastReceiver instantiateReceiver(@NonNull ClassLoader cl,
            @NonNull String className, @Nullable Intent intent)
            throws InstantiationException, IllegalAccessException, ClassNotFoundException {
        return (BroadcastReceiver) cl.loadClass(className).newInstance();
    }

    /**
     * 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.
     *
     * @param cl        The default classloader to use for instantiation.
     * @param className The class to be instantiated.
     * @param intent    Intent creating the class.
     */
    public @NonNull Service instantiateService(@NonNull ClassLoader cl,
            @NonNull String className, @Nullable Intent intent)
            throws InstantiationException, IllegalAccessException, ClassNotFoundException {
        return (Service) cl.loadClass(className).newInstance();
    }

    /**
     * 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.
     *
     * @param cl        The default classloader to use for instantiation.
     * @param className The class to be instantiated.
     */
    public @NonNull ContentProvider instantiateProvider(@NonNull ClassLoader cl,
            @NonNull String className)
            throws InstantiationException, IllegalAccessException, ClassNotFoundException {
        return (ContentProvider) cl.loadClass(className).newInstance();
    }

    /**
     * @hide
     */
    public static AppComponentFactory DEFAULT = new AppComponentFactory();
}
+21 −2
Original line number Original line Diff line number Diff line
@@ -1114,7 +1114,10 @@ public class Instrumentation {
    public Application newApplication(ClassLoader cl, String className, Context context)
    public Application newApplication(ClassLoader cl, String className, Context context)
            throws InstantiationException, IllegalAccessException, 
            throws InstantiationException, IllegalAccessException, 
            ClassNotFoundException {
            ClassNotFoundException {
        return newApplication(cl.loadClass(className), context);
        Application app = getFactory(context.getPackageName())
                .instantiateApplication(cl, className);
        app.attach(context);
        return app;
    }
    }
    
    
    /**
    /**
@@ -1201,7 +1204,15 @@ public class Instrumentation {
            Intent intent)
            Intent intent)
            throws InstantiationException, IllegalAccessException,
            throws InstantiationException, IllegalAccessException,
            ClassNotFoundException {
            ClassNotFoundException {
        return (Activity)cl.loadClass(className).newInstance();
        String pkg = intent.getComponent().getPackageName();
        return getFactory(pkg).instantiateActivity(cl, className, intent);
    }

    private AppComponentFactory getFactory(String pkg) {
        LoadedApk apk = mThread.peekPackageInfo(pkg, true);
        // This is in the case of starting up "android".
        if (apk == null) apk = mThread.getSystemContext().mPackageInfo;
        return apk.getAppFactory();
    }
    }


    private void prePerformCreate(Activity activity) {
    private void prePerformCreate(Activity activity) {
@@ -1950,6 +1961,14 @@ public class Instrumentation {
        mUiAutomationConnection = uiAutomationConnection;
        mUiAutomationConnection = uiAutomationConnection;
    }
    }


    /**
     * Only sets the ActivityThread up, keeps everything else null because app is not being
     * instrumented.
     */
    final void basicInit(ActivityThread thread) {
        mThread = thread;
    }

    /** @hide */
    /** @hide */
    public static void checkStartActivityResult(int res, Object intent) {
    public static void checkStartActivityResult(int res, Object intent) {
        if (!ActivityManager.isStartResultFatalError(res)) {
        if (!ActivityManager.isStartResultFatalError(res)) {
+21 −0
Original line number Original line Diff line number Diff line
@@ -125,6 +125,7 @@ public final class LoadedApk {
        = new ArrayMap<>();
        = new ArrayMap<>();
    private final ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>> mUnboundServices
    private final ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>> mUnboundServices
        = new ArrayMap<>();
        = new ArrayMap<>();
    private AppComponentFactory mAppComponentFactory;


    Application getApplication() {
    Application getApplication() {
        return mApplication;
        return mApplication;
@@ -148,6 +149,7 @@ public final class LoadedApk {
        mIncludeCode = includeCode;
        mIncludeCode = includeCode;
        mRegisterPackage = registerPackage;
        mRegisterPackage = registerPackage;
        mDisplayAdjustments.setCompatibilityInfo(compatInfo);
        mDisplayAdjustments.setCompatibilityInfo(compatInfo);
        mAppComponentFactory = createAppFactory(mApplicationInfo, mBaseClassLoader);
    }
    }


    private static ApplicationInfo adjustNativeLibraryPaths(ApplicationInfo info) {
    private static ApplicationInfo adjustNativeLibraryPaths(ApplicationInfo info) {
@@ -203,6 +205,7 @@ public final class LoadedApk {
        mRegisterPackage = false;
        mRegisterPackage = false;
        mClassLoader = ClassLoader.getSystemClassLoader();
        mClassLoader = ClassLoader.getSystemClassLoader();
        mResources = Resources.getSystem();
        mResources = Resources.getSystem();
        mAppComponentFactory = createAppFactory(mApplicationInfo, mClassLoader);
    }
    }


    /**
    /**
@@ -212,6 +215,23 @@ public final class LoadedApk {
        assert info.packageName.equals("android");
        assert info.packageName.equals("android");
        mApplicationInfo = info;
        mApplicationInfo = info;
        mClassLoader = classLoader;
        mClassLoader = classLoader;
        mAppComponentFactory = createAppFactory(info, classLoader);
    }

    private AppComponentFactory createAppFactory(ApplicationInfo appInfo, ClassLoader cl) {
        if (appInfo.appComponentFactory != null) {
            try {
                return (AppComponentFactory) cl.loadClass(appInfo.appComponentFactory)
                        .newInstance();
            } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
                Slog.e(TAG, "Unable to instantiate appComponentFactory", e);
            }
        }
        return AppComponentFactory.DEFAULT;
    }

    public AppComponentFactory getAppFactory() {
        return mAppComponentFactory;
    }
    }


    public String getPackageName() {
    public String getPackageName() {
@@ -313,6 +333,7 @@ public final class LoadedApk {
                        getClassLoader());
                        getClassLoader());
            }
            }
        }
        }
        mAppComponentFactory = createAppFactory(aInfo, mClassLoader);
    }
    }


    private void setApplicationInfo(ApplicationInfo aInfo) {
    private void setApplicationInfo(ApplicationInfo aInfo) {
Loading