From 2983d174f0b197173766b0cab65256446ee083a6 Mon Sep 17 00:00:00 2001 From: Dave Mankoff Date: Wed, 15 Jun 2022 21:03:10 +0000 Subject: [PATCH] Change SysemUIFactory to SystemUIInitializer. This change allows variants of SystemUI to remove the reflection traditionally used to startup systemui and instead directly stand up the code that they need. Specifically, it introduces two primary pieces: - SystemUIInitializer A rename of the SystemUIFactory along with some modest refactoring. It is now an abstract class that new SystemUI build targets should implement themselves. Implementations use this class to specify a root Dagger component used to stand up the rest of SystemUI. A default SystemUIInitializerImpl is provided that starts up AOSP. - SystemUIAppComponentFactoryBase An abstract implementation of AppComponentFactory that is used to decide what SystemUIInitializer should be loaded. Implementers should return a SystemUIInitializer instance of their choice in #createSystemUIInitializer. The implementation of SystemUIAppComponentFactoryBase should in turn be specified in AndroidManifest.xml's Application.android:appComponentFactory attribute. The default SystemUIAppComponentFactory loads the SystemUIInitializer implementation reflectively. This should be considered deprecated behavior and may be removed in a future release. Please define your own SystemUIAppComponentFactoryBase implementation and return a specific SystemUIInitializer implementation from it. Currently, AOSP, TV, and Pixel specific builds still rely on the reflective loader. Other internal builds have been moved off of the reflective loader. Bug: 235624311 Test: manual Change-Id: I7029fb2960f54fb29e08b96ee49170f150a1d0b6 --- packages/SystemUI/proguard.flags | 4 +- .../SystemUI/res/values-television/config.xml | 2 +- packages/SystemUI/res/values/config.xml | 2 +- .../systemui/SystemUIAppComponentFactory.java | 158 +-------------- .../SystemUIAppComponentFactoryBase.kt | 183 ++++++++++++++++++ .../android/systemui/SystemUIApplication.java | 23 ++- ...IFactory.java => SystemUIInitializer.java} | 103 +++------- .../systemui/SystemUIInitializerFactory.kt | 72 +++++++ .../systemui/SystemUIInitializerImpl.kt | 30 +++ .../systemui/dagger/SysUIComponent.java | 4 +- .../systemui/dagger/SystemUIModule.java | 6 - .../android/systemui/dagger/WMComponent.java | 4 +- .../systemui/people/PeopleProvider.java | 9 +- ...actory.java => TvSystemUIInitializer.java} | 12 +- .../com/android/systemui/DependencyTest.java | 10 +- .../systemui/SysuiBaseFragmentTest.java | 12 +- .../com/android/systemui/SysuiTestCase.java | 7 +- .../keyguard/KeyguardSliceProviderTest.java | 3 +- .../CollapsedStatusBarFragmentTest.java | 2 + 19 files changed, 377 insertions(+), 269 deletions(-) create mode 100644 packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactoryBase.kt rename packages/SystemUI/src/com/android/systemui/{SystemUIFactory.java => SystemUIInitializer.java} (76%) create mode 100644 packages/SystemUI/src/com/android/systemui/SystemUIInitializerFactory.kt create mode 100644 packages/SystemUI/src/com/android/systemui/SystemUIInitializerImpl.kt rename packages/SystemUI/src/com/android/systemui/tv/{TvSystemUIFactory.java => TvSystemUIInitializer.java} (73%) diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags index 6ffd661636539..ed01494a911ba 100644 --- a/packages/SystemUI/proguard.flags +++ b/packages/SystemUI/proguard.flags @@ -9,8 +9,8 @@ -keep class com.android.systemui.statusbar.phone.CentralSurfaces -keep class com.android.systemui.statusbar.tv.TvStatusBar -keep class com.android.systemui.car.CarSystemUIFactory --keep class com.android.systemui.SystemUIFactory --keep class com.android.systemui.tv.TvSystemUIFactory +-keep class com.android.systemui.SystemUIInitializer +-keep class com.android.systemui.tv.TvSystemUIInitializer -keep class * extends com.android.systemui.CoreStartable -keep class * implements com.android.systemui.CoreStartable$Injector diff --git a/packages/SystemUI/res/values-television/config.xml b/packages/SystemUI/res/values-television/config.xml index 94f6c3952e06d..375bd394bb4ce 100644 --- a/packages/SystemUI/res/values-television/config.xml +++ b/packages/SystemUI/res/values-television/config.xml @@ -22,7 +22,7 @@ - com.android.systemui.tv.TvSystemUIFactory + com.android.systemui.tv.TvSystemUIInitializer diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 771973c36053e..82a3b58a51555 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -286,7 +286,7 @@ false - com.android.systemui.SystemUIFactory + com.android.systemui.SystemUIInitializerImpl -1dp diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java index 714d267bb07de..527ce127820e8 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java @@ -16,160 +16,22 @@ package com.android.systemui; -import android.app.Activity; -import android.app.Application; -import android.app.Service; -import android.content.BroadcastReceiver; -import android.content.ContentProvider; import android.content.Context; -import android.content.Intent; -import android.util.Log; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.core.app.AppComponentFactory; - -import com.android.systemui.dagger.ContextComponentHelper; -import com.android.systemui.dagger.SysUIComponent; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - -import javax.inject.Inject; /** - * Implementation of AppComponentFactory that injects into constructors. + * Starts up SystemUI using the AOSP {@link SystemUIInitializerImpl}. * - * This class sets up dependency injection when creating our application. + * This initializer relies on reflection to start everything up and should be considered deprecated. + * Instead, create your own {@link SystemUIAppComponentFactoryBase}, specify it in your + * AndroidManifest.xml and construct your own {@link SystemUIInitializer} directly. * - * Services support dependency injection into their constructors. - * - * ContentProviders support injection into member variables - _not_ constructors. + * @deprecated Define your own SystemUIAppComponentFactoryBase implementation and use that. This + * implementation may be changed or removed in future releases. */ -public class SystemUIAppComponentFactory extends AppComponentFactory { - - private static final String TAG = "AppComponentFactory"; - @Inject - public ContextComponentHelper mComponentHelper; - - public SystemUIAppComponentFactory() { - super(); - } - - @NonNull +@Deprecated +public class SystemUIAppComponentFactory extends SystemUIAppComponentFactoryBase { @Override - public Application instantiateApplicationCompat( - @NonNull ClassLoader cl, @NonNull String className) - throws InstantiationException, IllegalAccessException, ClassNotFoundException { - Application app = super.instantiateApplicationCompat(cl, className); - if (app instanceof ContextInitializer) { - ((ContextInitializer) app).setContextAvailableCallback( - context -> { - SystemUIFactory.createFromConfig(context); - SystemUIFactory.getInstance().getSysUIComponent().inject( - SystemUIAppComponentFactory.this); - } - ); - } - - return app; - } - - @NonNull - @Override - public ContentProvider instantiateProviderCompat( - @NonNull ClassLoader cl, @NonNull String className) - throws InstantiationException, IllegalAccessException, ClassNotFoundException { - - ContentProvider contentProvider = super.instantiateProviderCompat(cl, className); - if (contentProvider instanceof ContextInitializer) { - ((ContextInitializer) contentProvider).setContextAvailableCallback( - context -> { - SystemUIFactory.createFromConfig(context); - SysUIComponent rootComponent = - SystemUIFactory.getInstance().getSysUIComponent(); - try { - Method injectMethod = rootComponent.getClass() - .getMethod("inject", contentProvider.getClass()); - injectMethod.invoke(rootComponent, contentProvider); - } catch (NoSuchMethodException - | IllegalAccessException - | InvocationTargetException e) { - Log.w(TAG, "No injector for class: " + contentProvider.getClass(), e); - } - } - ); - } - - return contentProvider; - } - - @NonNull - @Override - public Activity instantiateActivityCompat(@NonNull ClassLoader cl, @NonNull String className, - @Nullable Intent intent) - throws InstantiationException, IllegalAccessException, ClassNotFoundException { - if (mComponentHelper == null) { - // This shouldn't happen, but is seen on occasion. - // Bug filed against framework to take a look: http://b/141008541 - SystemUIFactory.getInstance().getSysUIComponent().inject( - SystemUIAppComponentFactory.this); - } - Activity activity = mComponentHelper.resolveActivity(className); - if (activity != null) { - return activity; - } - return super.instantiateActivityCompat(cl, className, intent); - } - - @NonNull - @Override - public Service instantiateServiceCompat( - @NonNull ClassLoader cl, @NonNull String className, Intent intent) - throws InstantiationException, IllegalAccessException, ClassNotFoundException { - if (mComponentHelper == null) { - // This shouldn't happen, but does when a device is freshly formatted. - // Bug filed against framework to take a look: http://b/141008541 - SystemUIFactory.getInstance().getSysUIComponent().inject( - SystemUIAppComponentFactory.this); - } - Service service = mComponentHelper.resolveService(className); - if (service != null) { - return service; - } - return super.instantiateServiceCompat(cl, className, intent); - } - - @NonNull - @Override - public BroadcastReceiver instantiateReceiverCompat(@NonNull ClassLoader cl, - @NonNull String className, @Nullable Intent intent) - throws InstantiationException, IllegalAccessException, ClassNotFoundException { - if (mComponentHelper == null) { - // This shouldn't happen, but does when a device is freshly formatted. - // Bug filed against framework to take a look: http://b/141008541 - SystemUIFactory.getInstance().getSysUIComponent().inject( - SystemUIAppComponentFactory.this); - } - BroadcastReceiver receiver = mComponentHelper.resolveBroadcastReceiver(className); - if (receiver != null) { - return receiver; - } - - return super.instantiateReceiverCompat(cl, className, intent); - } - - /** - * A callback that receives a Context when one is ready. - */ - public interface ContextAvailableCallback { - void onContextAvailable(Context context); - } - - /** - * Implemented in classes that get started by the system before a context is available. - */ - public interface ContextInitializer { - void setContextAvailableCallback(ContextAvailableCallback callback); + protected SystemUIInitializer createSystemUIInitializer(Context context) { + return SystemUIInitializerFactory.createWithContext(context); } } diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactoryBase.kt b/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactoryBase.kt new file mode 100644 index 0000000000000..12108b01ab28e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactoryBase.kt @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2019 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 com.android.systemui + +import android.app.Activity +import android.app.Application +import android.app.Service +import android.content.BroadcastReceiver +import android.content.ContentProvider +import android.content.Context +import android.content.Intent +import android.util.Log +import androidx.core.app.AppComponentFactory +import com.android.systemui.dagger.ContextComponentHelper +import java.lang.reflect.InvocationTargetException +import java.util.concurrent.ExecutionException +import javax.inject.Inject + +/** + * Implementation of AppComponentFactory that injects into constructors. + * + * This class sets up dependency injection when creating our application. + * + * Activities, Services, and BroadcastReceivers support dependency injection into + * their constructors. + * + * ContentProviders support injection into member variables - _not_ constructors. + */ +abstract class SystemUIAppComponentFactoryBase : AppComponentFactory() { + companion object { + private const val TAG = "AppComponentFactory" + // Must be static due to http://b/141008541. + var systemUIInitializer: SystemUIInitializer? = null + } + + @set:Inject + lateinit var componentHelper: ContextComponentHelper + + /** + * Returns a new [SystemUIInitializer]. + * + * The returned implementation should be specific to your build. + */ + protected abstract fun createSystemUIInitializer(context: Context): SystemUIInitializer + + private fun createSystemUIInitializerInternal(context: Context): SystemUIInitializer { + return systemUIInitializer ?: run { + val initializer = createSystemUIInitializer(context.applicationContext) + try { + initializer.init(false) + } catch (exception: ExecutionException) { + throw RuntimeException("Failed to initialize SysUI", exception) + } catch (exception: InterruptedException) { + throw RuntimeException("Failed to initialize SysUI", exception) + } + initializer.sysUIComponent.inject( + this@SystemUIAppComponentFactoryBase + ) + + systemUIInitializer = initializer + return initializer + } + } + + override fun instantiateApplicationCompat(cl: ClassLoader, className: String): Application { + val app = super.instantiateApplicationCompat(cl, className) + if (app !is ContextInitializer) { + throw RuntimeException("App must implement ContextInitializer") + } else { + app.setContextAvailableCallback { context -> + createSystemUIInitializerInternal(context) + } + } + + return app + } + + override fun instantiateProviderCompat(cl: ClassLoader, className: String): ContentProvider { + val contentProvider = super.instantiateProviderCompat(cl, className) + if (contentProvider is ContextInitializer) { + contentProvider.setContextAvailableCallback { context -> + val initializer = createSystemUIInitializerInternal(context) + val rootComponent = initializer.sysUIComponent + try { + val injectMethod = rootComponent.javaClass + .getMethod("inject", contentProvider.javaClass) + injectMethod.invoke(rootComponent, contentProvider) + } catch (e: NoSuchMethodException) { + Log.w(TAG, "No injector for class: " + contentProvider.javaClass, e) + } catch (e: IllegalAccessException) { + Log.w(TAG, "No injector for class: " + contentProvider.javaClass, e) + } catch (e: InvocationTargetException) { + Log.w(TAG, "No injector for class: " + contentProvider.javaClass, e) + } + initializer + } + } + return contentProvider + } + + override fun instantiateActivityCompat( + cl: ClassLoader, + className: String, + intent: Intent? + ): Activity { + if (!this::componentHelper.isInitialized) { + // This shouldn't happen, but is seen on occasion. + // Bug filed against framework to take a look: http://b/141008541 + systemUIInitializer?.sysUIComponent?.inject(this@SystemUIAppComponentFactoryBase) + } + return componentHelper.resolveActivity(className) + ?: super.instantiateActivityCompat(cl, className, intent) + } + + override fun instantiateServiceCompat( + cl: ClassLoader, + className: String, + intent: Intent? + ): Service { + if (!this::componentHelper.isInitialized) { + // This shouldn't happen, but does when a device is freshly formatted. + // Bug filed against framework to take a look: http://b/141008541 + systemUIInitializer?.sysUIComponent?.inject(this@SystemUIAppComponentFactoryBase) + } + return componentHelper.resolveService(className) + ?: super.instantiateServiceCompat(cl, className, intent) + } + + override fun instantiateReceiverCompat( + cl: ClassLoader, + className: String, + intent: Intent? + ): BroadcastReceiver { + if (!this::componentHelper.isInitialized) { + // This shouldn't happen, but does when a device is freshly formatted. + // Bug filed against framework to take a look: http://b/141008541 + systemUIInitializer?.sysUIComponent?.inject(this@SystemUIAppComponentFactoryBase) + } + return componentHelper.resolveBroadcastReceiver(className) + ?: super.instantiateReceiverCompat(cl, className, intent) + } + + /** + * An Interface for classes that can be notified when an Application Context becomes available. + * + * An instance of this will be passed to implementers of [ContextInitializer]. + */ + fun interface ContextAvailableCallback { + /** Notifies when the Application Context is available. */ + fun onContextAvailable(context: Context): SystemUIInitializer + } + + /** + * Interface for classes that can be constructed by the system before a context is available. + * + * This is intended for [Application] and [ContentProvider] implementations that + * either may not have a Context until some point after construction or are themselves + * a [Context]. + * + * Implementers will be passed a [ContextAvailableCallback] that they should call as soon + * as an Application Context is ready. + */ + interface ContextInitializer { + /** + * Called to supply the [ContextAvailableCallback] that should be called when an + * Application [Context] is available. + */ + fun setContextAvailableCallback(callback: ContextAvailableCallback) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java index 6d3fd503dff6e..9138b2346ab81 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java @@ -42,7 +42,6 @@ import android.view.SurfaceControl; import android.view.ThreadedRenderer; import com.android.internal.protolog.common.ProtoLog; -import com.android.systemui.dagger.ContextComponentHelper; import com.android.systemui.dagger.GlobalRootComponent; import com.android.systemui.dagger.SysUIComponent; import com.android.systemui.dump.DumpManager; @@ -65,7 +64,6 @@ public class SystemUIApplication extends Application implements public static final String TAG = "SystemUIService"; private static final boolean DEBUG = false; - private ContextComponentHelper mComponentHelper; private BootCompleteCacheImpl mBootCompleteCache; private DumpManager mDumpManager; @@ -80,8 +78,8 @@ public class SystemUIApplication extends Application implements private CoreStartable[] mServices; private boolean mServicesStarted; private SystemUIAppComponentFactory.ContextAvailableCallback mContextAvailableCallback; - private GlobalRootComponent mRootComponent; private SysUIComponent mSysUIComponent; + private SystemUIInitializer mInitializer; public SystemUIApplication() { super(); @@ -90,6 +88,10 @@ public class SystemUIApplication extends Application implements ProtoLog.REQUIRE_PROTOLOGTOOL = false; } + protected GlobalRootComponent getRootComponent() { + return mInitializer.getRootComponent(); + } + @Override public void onCreate() { super.onCreate(); @@ -99,10 +101,8 @@ public class SystemUIApplication extends Application implements TimingsTraceLog log = new TimingsTraceLog("SystemUIBootTiming", Trace.TRACE_TAG_APP); log.traceBegin("DependencyInjection"); - mContextAvailableCallback.onContextAvailable(this); - mRootComponent = SystemUIFactory.getInstance().getRootComponent(); - mSysUIComponent = SystemUIFactory.getInstance().getSysUIComponent(); - mComponentHelper = mSysUIComponent.getContextComponentHelper(); + mInitializer = mContextAvailableCallback.onContextAvailable(this); + mSysUIComponent = mInitializer.getSysUIComponent(); mBootCompleteCache = mSysUIComponent.provideBootCacheImpl(); log.traceEnd(); @@ -189,15 +189,14 @@ public class SystemUIApplication extends Application implements */ public void startServicesIfNeeded() { - final String vendorComponent = SystemUIFactory.getInstance() - .getVendorComponent(getResources()); + final String vendorComponent = mInitializer.getVendorComponent(getResources()); // Sort the startables so that we get a deterministic ordering. // TODO: make #start idempotent and require users of CoreStartable to call it. Map, Provider> sortedStartables = new TreeMap<>( Comparator.comparing(Class::getName)); - sortedStartables.putAll(SystemUIFactory.getInstance().getStartableComponents()); - sortedStartables.putAll(SystemUIFactory.getInstance().getStartableComponentsPerUser()); + sortedStartables.putAll(mSysUIComponent.getStartables()); + sortedStartables.putAll(mSysUIComponent.getPerUserStartables()); startServicesIfNeeded( sortedStartables, "StartServices", vendorComponent); } @@ -212,7 +211,7 @@ public class SystemUIApplication extends Application implements // Sort the startables so that we get a deterministic ordering. Map, Provider> sortedStartables = new TreeMap<>( Comparator.comparing(Class::getName)); - sortedStartables.putAll(SystemUIFactory.getInstance().getStartableComponentsPerUser()); + sortedStartables.putAll(mSysUIComponent.getPerUserStartables()); startServicesIfNeeded( sortedStartables, "StartSecondaryServices", null); } diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java similarity index 76% rename from packages/SystemUI/src/com/android/systemui/SystemUIFactory.java rename to packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java index ca94b8cd05b74..5100c09912c8d 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2022 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. @@ -11,7 +11,7 @@ * 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 + * limitations under the License. */ package com.android.systemui; @@ -22,8 +22,6 @@ import android.os.Handler; import android.os.HandlerThread; import android.util.Log; -import com.android.internal.annotations.VisibleForTesting; -import com.android.systemui.dagger.DaggerGlobalRootComponent; import com.android.systemui.dagger.GlobalRootComponent; import com.android.systemui.dagger.SysUIComponent; import com.android.systemui.dagger.WMComponent; @@ -31,66 +29,47 @@ import com.android.systemui.util.InitializationChecker; import com.android.wm.shell.dagger.WMShellConcurrencyModule; import com.android.wm.shell.transition.ShellTransitions; -import java.util.Map; import java.util.Optional; import java.util.concurrent.ExecutionException; -import javax.inject.Provider; - /** - * Class factory to provide customizable SystemUI components. + * Initializer that stands up SystemUI. + * + * Implementations should override {@link #getGlobalRootComponentBuilder()} to fill in their own + * Dagger root component. */ -public class SystemUIFactory { +public abstract class SystemUIInitializer { private static final String TAG = "SystemUIFactory"; - static SystemUIFactory mFactory; + private final Context mContext; + private GlobalRootComponent mRootComponent; private WMComponent mWMComponent; private SysUIComponent mSysUIComponent; private InitializationChecker mInitializationChecker; - public static T getInstance() { - return (T) mFactory; + public SystemUIInitializer(Context context) { + mContext = context; } - public static void createFromConfig(Context context) { - createFromConfig(context, false); + protected abstract GlobalRootComponent.Builder getGlobalRootComponentBuilder(); + + /** + * Prepares the SysUIComponent builder before it is built. + * @param sysUIBuilder the builder provided by the root component's getSysUIComponent() method + * @param wm the built WMComponent from the root component's getWMComponent() method + */ + protected SysUIComponent.Builder prepareSysUIComponentBuilder( + SysUIComponent.Builder sysUIBuilder, WMComponent wm) { + return sysUIBuilder; } - @VisibleForTesting - public static void createFromConfig(Context context, boolean fromTest) { - if (mFactory != null) { - return; - } - - final String clsName = context.getString(R.string.config_systemUIFactoryComponent); - if (clsName == null || clsName.length() == 0) { - throw new RuntimeException("No SystemUIFactory component configured"); - } - - try { - Class cls = null; - cls = context.getClassLoader().loadClass(clsName); - mFactory = (SystemUIFactory) cls.newInstance(); - mFactory.init(context, fromTest); - } catch (Throwable t) { - Log.w(TAG, "Error creating SystemUIFactory component: " + clsName, t); - throw new RuntimeException(t); - } - } - - @VisibleForTesting - static void cleanup() { - mFactory = null; - } - - public SystemUIFactory() {} - - @VisibleForTesting - public void init(Context context, boolean fromTest) - throws ExecutionException, InterruptedException { + /** + * Starts the initialization process. This stands up the Dagger graph. + */ + public void init(boolean fromTest) throws ExecutionException, InterruptedException { mRootComponent = getGlobalRootComponentBuilder() - .context(context) + .context(mContext) .instrumentationTest(fromTest) .build(); @@ -98,7 +77,7 @@ public class SystemUIFactory { boolean initializeComponents = mInitializationChecker.initializeComponents(); // Stand up WMComponent - setupWmComponent(context); + setupWmComponent(mContext); if (initializeComponents) { // Only initialize when not starting from tests since this currently initializes some // components that shouldn't be run in the test environment @@ -188,20 +167,6 @@ public class SystemUIFactory { } } - /** - * Prepares the SysUIComponent builder before it is built. - * @param sysUIBuilder the builder provided by the root component's getSysUIComponent() method - * @param wm the built WMComponent from the root component's getWMComponent() method - */ - protected SysUIComponent.Builder prepareSysUIComponentBuilder( - SysUIComponent.Builder sysUIBuilder, WMComponent wm) { - return sysUIBuilder; - } - - protected GlobalRootComponent.Builder getGlobalRootComponentBuilder() { - return DaggerGlobalRootComponent.builder(); - } - public GlobalRootComponent getRootComponent() { return mRootComponent; } @@ -214,24 +179,10 @@ public class SystemUIFactory { return mSysUIComponent; } - /** - * Returns the list of {@link CoreStartable} components that should be started at startup. - */ - public Map, Provider> getStartableComponents() { - return mSysUIComponent.getStartables(); - } - /** * Returns the list of additional system UI components that should be started. */ public String getVendorComponent(Resources resources) { return resources.getString(R.string.config_systemUIVendorServiceComponent); } - - /** - * Returns the list of {@link CoreStartable} components that should be started per user. - */ - public Map, Provider> getStartableComponentsPerUser() { - return mSysUIComponent.getPerUserStartables(); - } } diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIInitializerFactory.kt b/packages/SystemUI/src/com/android/systemui/SystemUIInitializerFactory.kt new file mode 100644 index 0000000000000..b9454e8c3be86 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/SystemUIInitializerFactory.kt @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2022 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 com.android.systemui + +import android.annotation.SuppressLint +import android.content.Context +import android.util.Log +import com.android.internal.annotations.VisibleForTesting +import com.android.systemui.util.Assert + +/** + * Factory to reflectively lookup a [SystemUIInitializer] to start SystemUI with. + */ +@Deprecated("Provide your own {@link SystemUIAppComponentFactoryBase} that doesn't need this.") +object SystemUIInitializerFactory { + private const val TAG = "SysUIInitializerFactory" + @SuppressLint("StaticFieldLeak") + private var initializer: SystemUIInitializer? = null + + /** + * Instantiate a [SystemUIInitializer] reflectively. + */ + @JvmStatic + fun createWithContext(context: Context): SystemUIInitializer { + return createFromConfig(context) + } + + /** + * Instantiate a [SystemUIInitializer] reflectively. + */ + @JvmStatic + private fun createFromConfig(context: Context): SystemUIInitializer { + Assert.isMainThread() + + return createFromConfigNoAssert(context) + } + + @JvmStatic + @VisibleForTesting + fun createFromConfigNoAssert(context: Context): SystemUIInitializer { + + return initializer ?: run { + val className = context.getString(R.string.config_systemUIFactoryComponent) + if (className.isEmpty()) { + throw RuntimeException("No SystemUIFactory component configured") + } + try { + val cls = context.classLoader.loadClass(className) + val constructor = cls.getConstructor(Context::class.java) + (constructor.newInstance(context) as SystemUIInitializer).apply { + initializer = this + } + } catch (t: Throwable) { + Log.w(TAG, "Error creating SystemUIInitializer component: $className", t) + throw t + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIInitializerImpl.kt b/packages/SystemUI/src/com/android/systemui/SystemUIInitializerImpl.kt new file mode 100644 index 0000000000000..8920c928da09a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/SystemUIInitializerImpl.kt @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2022 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 com.android.systemui + +import android.content.Context +import com.android.systemui.dagger.DaggerGlobalRootComponent +import com.android.systemui.dagger.GlobalRootComponent + +/** + * {@link SystemUIInitializer} that stands up AOSP SystemUI. + */ +class SystemUIInitializerImpl(context: Context) : SystemUIInitializer(context) { + override fun getGlobalRootComponentBuilder(): GlobalRootComponent.Builder { + return DaggerGlobalRootComponent.builder() + } +} diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java index 3a1b12955647c..550af7cafc751 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java @@ -21,7 +21,7 @@ import com.android.systemui.BootCompleteCacheImpl; import com.android.systemui.CoreStartable; import com.android.systemui.Dependency; import com.android.systemui.InitController; -import com.android.systemui.SystemUIAppComponentFactory; +import com.android.systemui.SystemUIAppComponentFactoryBase; import com.android.systemui.dagger.qualifiers.PerUser; import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.KeyguardSliceProvider; @@ -241,7 +241,7 @@ public interface SysUIComponent { /** * Member injection into the supplied argument. */ - void inject(SystemUIAppComponentFactory factory); + void inject(SystemUIAppComponentFactoryBase factory); /** * Member injection into the supplied argument. diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index 137e288887281..8cc952df47ed8 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -26,7 +26,6 @@ import com.android.keyguard.clock.ClockModule; import com.android.keyguard.dagger.KeyguardBouncerComponent; import com.android.systemui.BootCompleteCache; import com.android.systemui.BootCompleteCacheImpl; -import com.android.systemui.SystemUIFactory; import com.android.systemui.appops.dagger.AppOpsModule; import com.android.systemui.assist.AssistModule; import com.android.systemui.biometrics.AlternateUdfpsTouchProvider; @@ -198,11 +197,6 @@ public abstract class SystemUIModule { @Binds abstract SystemClock bindSystemClock(SystemClockImpl systemClock); - @Provides - static SystemUIFactory getSystemUIFactory() { - return SystemUIFactory.getInstance(); - } - // TODO: This should provided by the WM component /** Provides Optional of BubbleManager */ @SysUISingleton diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java index 1570a7ebc0c4f..f2f1798c94f66 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java @@ -21,7 +21,7 @@ import android.os.HandlerThread; import androidx.annotation.Nullable; -import com.android.systemui.SystemUIFactory; +import com.android.systemui.SystemUIInitializerFactory; import com.android.systemui.tv.TvWMComponent; import com.android.wm.shell.ShellCommandHandler; import com.android.wm.shell.ShellInit; @@ -52,7 +52,7 @@ import dagger.Subcomponent; /** * Dagger Subcomponent for WindowManager. This class explicitly describes the interfaces exported * from the WM component into the SysUI component (in - * {@link SystemUIFactory#init(Context, boolean)}), and references the specific dependencies + * {@link SystemUIInitializerFactory#init(Context, boolean)}), and references the specific dependencies * provided by its particular device/form-factor SystemUI implementation. * * ie. {@link WMComponent} includes {@link WMShellModule} diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleProvider.java b/packages/SystemUI/src/com/android/systemui/people/PeopleProvider.java index b55d86e8fb1c2..0ba077eb09127 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleProvider.java +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleProvider.java @@ -29,7 +29,8 @@ import android.os.UserHandle; import android.util.Log; import android.widget.RemoteViews; -import com.android.systemui.SystemUIAppComponentFactory; +import com.android.systemui.SystemUIAppComponentFactoryBase.ContextAvailableCallback; +import com.android.systemui.SystemUIAppComponentFactoryBase.ContextInitializer; import com.android.systemui.people.widget.PeopleSpaceWidgetManager; import com.android.systemui.shared.system.PeopleProviderUtils; @@ -37,11 +38,11 @@ import javax.inject.Inject; /** API that returns a People Tile preview. */ public class PeopleProvider extends ContentProvider implements - SystemUIAppComponentFactory.ContextInitializer { + ContextInitializer { private static final String TAG = "PeopleProvider"; private static final boolean DEBUG = PeopleSpaceUtils.DEBUG; private static final String EMPTY_STRING = ""; - private SystemUIAppComponentFactory.ContextAvailableCallback mCallback; + private ContextAvailableCallback mCallback; @Inject PeopleSpaceWidgetManager mPeopleSpaceWidgetManager; @@ -144,7 +145,7 @@ public class PeopleProvider extends ContentProvider implements @Override public void setContextAvailableCallback( - SystemUIAppComponentFactory.ContextAvailableCallback callback) { + ContextAvailableCallback callback) { mCallback = callback; } } diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIInitializer.java similarity index 73% rename from packages/SystemUI/src/com/android/systemui/tv/TvSystemUIFactory.java rename to packages/SystemUI/src/com/android/systemui/tv/TvSystemUIInitializer.java index 0b5594b14d0ec..fabbb2c1f44d4 100644 --- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIFactory.java +++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIInitializer.java @@ -16,14 +16,20 @@ package com.android.systemui.tv; -import com.android.systemui.SystemUIFactory; +import android.content.Context; + +import com.android.systemui.SystemUIInitializer; import com.android.systemui.dagger.GlobalRootComponent; /** - * TV variant {@link SystemUIFactory}, that substitutes default {@link GlobalRootComponent} for + * TV variant {@link SystemUIInitializer}, that substitutes default {@link GlobalRootComponent} for * {@link TvGlobalRootComponent} */ -public class TvSystemUIFactory extends SystemUIFactory { +public class TvSystemUIInitializer extends SystemUIInitializer { + public TvSystemUIInitializer(Context context) { + super(context); + } + @Override protected GlobalRootComponent.Builder getGlobalRootComponentBuilder() { return DaggerTvGlobalRootComponent.builder(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java b/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java index 5d8e4351cfd9b..a0fdc8f1555e3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java @@ -26,6 +26,8 @@ import com.android.systemui.statusbar.policy.FlashlightController; import org.junit.Assert; import org.junit.Test; +import java.util.concurrent.ExecutionException; + @SmallTest public class DependencyTest extends SysuiTestCase { @@ -44,10 +46,12 @@ public class DependencyTest extends SysuiTestCase { } @Test - public void testInitDependency() { + public void testInitDependency() throws ExecutionException, InterruptedException { Dependency.clearDependencies(); - Dependency dependency = - SystemUIFactory.getInstance().getSysUIComponent().createDependency(); + SystemUIInitializer initializer = + SystemUIInitializerFactory.createFromConfigNoAssert(mContext); + initializer.init(true); + Dependency dependency = initializer.getSysUIComponent().createDependency(); dependency.start(); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java index 8c20b248d02c0..9179efc9f39f5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java @@ -34,6 +34,8 @@ import org.junit.Before; import org.junit.Rule; import org.mockito.Mockito; +import java.util.concurrent.ExecutionException; + public abstract class SysuiBaseFragmentTest extends BaseFragmentTest { public static final Class[] ALL_SUPPORTED_CLASSES = LeakCheckedTest.ALL_SUPPORTED_CLASSES; @@ -54,10 +56,11 @@ public abstract class SysuiBaseFragmentTest extends BaseFragmentTest { } @Before - public void SysuiSetup() { - SystemUIFactory.createFromConfig(mContext, true); - mDependency = new TestableDependency( - SystemUIFactory.getInstance().getSysUIComponent().createDependency()); + public void sysuiSetup() throws ExecutionException, InterruptedException { + SystemUIInitializer initializer = + SystemUIInitializerFactory.createFromConfigNoAssert(mContext); + initializer.init(true); + mDependency = new TestableDependency(initializer.getSysUIComponent().createDependency()); Dependency.setInstance(mDependency); // TODO: Figure out another way to give reference to a SysuiTestableContext. @@ -77,7 +80,6 @@ public abstract class SysuiBaseFragmentTest extends BaseFragmentTest { public void SysuiTeardown() { InstrumentationRegistry.registerInstance(mRealInstrumentation, InstrumentationRegistry.getArguments()); - SystemUIFactory.cleanup(); } @AfterClass diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java index 8c7927782d2d7..c52ea60f0bfc6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java +++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java @@ -76,9 +76,10 @@ public abstract class SysuiTestCase { @Before public void SysuiSetup() throws Exception { - SystemUIFactory.createFromConfig(mContext, true); - mDependency = new TestableDependency( - SystemUIFactory.getInstance().getSysUIComponent().createDependency()); + SystemUIInitializer initializer = + SystemUIInitializerFactory.createFromConfigNoAssert(mContext); + initializer.init(true); + mDependency = new TestableDependency(initializer.getSysUIComponent().createDependency()); Dependency.setInstance(mDependency); mFakeBroadcastDispatcher = new FakeBroadcastDispatcher(mContext, mock(Looper.class), mock(Executor.class), mock(DumpManager.class), diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java index 51c2580554653..23516c94d8519 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java @@ -45,6 +45,7 @@ import androidx.slice.core.SliceQuery; import androidx.test.filters.SmallTest; import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.systemui.SystemUIInitializerImpl; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.NotificationMediaManager; @@ -100,7 +101,7 @@ public class KeyguardSliceProviderTest extends SysuiTestCase { MockitoAnnotations.initMocks(this); mIsZenMode = false; mProvider = new TestableKeyguardSliceProvider(); - mProvider.setContextAvailableCallback(context -> { }); + mProvider.setContextAvailableCallback(context -> new SystemUIInitializerImpl(mContext)); mProvider.attachInfo(getContext(), null); reset(mContentResolver); SliceProvider.setSpecs(new HashSet<>(Arrays.asList(SliceSpecs.LIST))); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java index 3d03c4750869d..6f9e60fc5d69a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java @@ -44,6 +44,7 @@ import com.android.systemui.SysuiBaseFragmentTest; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.log.LogBuffer; import com.android.systemui.log.LogcatEchoTracker; +import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.DisableFlagsLogger; @@ -112,6 +113,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @Before public void setup() { injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES); + mDependency.injectMockDependency(DarkIconDispatcher.class); } @Test