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

Commit 80acbab4 authored by Caitlin Shkuratov's avatar Caitlin Shkuratov
Browse files

[Pod] Split ContextInitializer into 2 separate interfaces.

ContextInitializer is used to provide Context to parts of
SysUI that are created before the Context is available. The
two parts of SysUI that implement ContextInitializers are:
 - The main application (SystemUIApplication)
 - Content providers

This CL splits ContextInitializer into
ApplicationContextInitializer and
ContentProviderContextInitializer.

The advantage is that ContentProviderContextInitializer
doesn't need access to SystemUIInitializer, so we can remove
that from the ContentProviderContextInitializer. This will
let us split out SysUI's Dagger graph construction out from
the rest of the SysUI monolith.

Bug: 307607958
Flag: EXEMPT refactor
Test: mp sysuig -> verify SystemUIApplication and all
content providers are still started via logs
Test: atest SystemUIApplicationTest CustomizationProviderTest

Change-Id: I9f1216a2f65b2991fc0696359b5aa5f0e6ac61fc
parent 7f0e1c36
Loading
Loading
Loading
Loading
+30 −57
Original line number Original line Diff line number Diff line
@@ -24,6 +24,8 @@ import android.content.Context
import android.content.Intent
import android.content.Intent
import android.util.Log
import android.util.Log
import androidx.core.app.AppComponentFactory
import androidx.core.app.AppComponentFactory
import com.android.systemui.application.ApplicationContextInitializer
import com.android.systemui.application.ContentProviderContextInitializer
import com.android.systemui.dagger.ContextComponentHelper
import com.android.systemui.dagger.ContextComponentHelper
import com.android.systemui.dagger.SysUIComponent
import com.android.systemui.dagger.SysUIComponent
import com.android.tools.r8.keepanno.annotations.KeepTarget
import com.android.tools.r8.keepanno.annotations.KeepTarget
@@ -37,8 +39,8 @@ import javax.inject.Inject
 *
 *
 * This class sets up dependency injection when creating our application.
 * This class sets up dependency injection when creating our application.
 *
 *
 * Activities, Services, and BroadcastReceivers support dependency injection into
 * Activities, Services, and BroadcastReceivers support dependency injection into their
 * their constructors.
 * constructors.
 *
 *
 * ContentProviders support injection into member variables - _not_ constructors.
 * ContentProviders support injection into member variables - _not_ constructors.
 */
 */
@@ -49,8 +51,7 @@ abstract class SystemUIAppComponentFactoryBase : AppComponentFactory() {
        var systemUIInitializer: SystemUIInitializer? = null
        var systemUIInitializer: SystemUIInitializer? = null
    }
    }


    @set:Inject
    @set:Inject lateinit var componentHelper: ContextComponentHelper
    lateinit var componentHelper: ContextComponentHelper


    /**
    /**
     * Returns a new [SystemUIInitializer].
     * Returns a new [SystemUIInitializer].
@@ -60,7 +61,8 @@ abstract class SystemUIAppComponentFactoryBase : AppComponentFactory() {
    protected abstract fun createSystemUIInitializer(context: Context): SystemUIInitializer
    protected abstract fun createSystemUIInitializer(context: Context): SystemUIInitializer


    private fun createSystemUIInitializerInternal(context: Context): SystemUIInitializer {
    private fun createSystemUIInitializerInternal(context: Context): SystemUIInitializer {
        return systemUIInitializer ?: run {
        return systemUIInitializer
            ?: run {
                val initializer = createSystemUIInitializer(context.applicationContext)
                val initializer = createSystemUIInitializer(context.applicationContext)
                try {
                try {
                    initializer.init(false)
                    initializer.init(false)
@@ -69,9 +71,7 @@ abstract class SystemUIAppComponentFactoryBase : AppComponentFactory() {
                } catch (exception: InterruptedException) {
                } catch (exception: InterruptedException) {
                    throw RuntimeException("Failed to initialize SysUI", exception)
                    throw RuntimeException("Failed to initialize SysUI", exception)
                }
                }
            initializer.sysUIComponent.inject(
                initializer.sysUIComponent.inject(this@SystemUIAppComponentFactoryBase)
                this@SystemUIAppComponentFactoryBase
            )


                systemUIInitializer = initializer
                systemUIInitializer = initializer
                return initializer
                return initializer
@@ -80,27 +80,28 @@ abstract class SystemUIAppComponentFactoryBase : AppComponentFactory() {


    override fun instantiateApplicationCompat(cl: ClassLoader, className: String): Application {
    override fun instantiateApplicationCompat(cl: ClassLoader, className: String): Application {
        val app = super.instantiateApplicationCompat(cl, className)
        val app = super.instantiateApplicationCompat(cl, className)
        if (app !is ContextInitializer) {
        if (app !is ApplicationContextInitializer) {
            throw RuntimeException("App must implement ContextInitializer")
            throw RuntimeException("App must implement ApplicationContextInitializer")
        } else {
        } else {
            app.setContextAvailableCallback { context ->
            app.setContextAvailableCallback { context ->
                createSystemUIInitializerInternal(context)
                createSystemUIInitializerInternal(context)
            }
            }
        }
        }

        return app
        return app
    }
    }


    @UsesReflection(KeepTarget(instanceOfClassConstant = SysUIComponent::class, methodName = "inject"))
    @UsesReflection(
        KeepTarget(instanceOfClassConstant = SysUIComponent::class, methodName = "inject")
    )
    override fun instantiateProviderCompat(cl: ClassLoader, className: String): ContentProvider {
    override fun instantiateProviderCompat(cl: ClassLoader, className: String): ContentProvider {
        val contentProvider = super.instantiateProviderCompat(cl, className)
        val contentProvider = super.instantiateProviderCompat(cl, className)
        if (contentProvider is ContextInitializer) {
        if (contentProvider is ContentProviderContextInitializer) {
            contentProvider.setContextAvailableCallback { context ->
            contentProvider.setContextAvailableCallback { context ->
                val initializer = createSystemUIInitializerInternal(context)
                val initializer = createSystemUIInitializerInternal(context)
                val rootComponent = initializer.sysUIComponent
                val rootComponent = initializer.sysUIComponent
                try {
                try {
                    val injectMethod = rootComponent.javaClass
                    val injectMethod =
                        .getMethod("inject", contentProvider.javaClass)
                        rootComponent.javaClass.getMethod("inject", contentProvider.javaClass)
                    injectMethod.invoke(rootComponent, contentProvider)
                    injectMethod.invoke(rootComponent, contentProvider)
                } catch (e: NoSuchMethodException) {
                } catch (e: NoSuchMethodException) {
                    Log.w(TAG, "No injector for class: " + contentProvider.javaClass, e)
                    Log.w(TAG, "No injector for class: " + contentProvider.javaClass, e)
@@ -118,7 +119,7 @@ abstract class SystemUIAppComponentFactoryBase : AppComponentFactory() {
    override fun instantiateActivityCompat(
    override fun instantiateActivityCompat(
        cl: ClassLoader,
        cl: ClassLoader,
        className: String,
        className: String,
        intent: Intent?
        intent: Intent?,
    ): Activity {
    ): Activity {
        if (!this::componentHelper.isInitialized) {
        if (!this::componentHelper.isInitialized) {
            // This shouldn't happen, but is seen on occasion.
            // This shouldn't happen, but is seen on occasion.
@@ -132,7 +133,7 @@ abstract class SystemUIAppComponentFactoryBase : AppComponentFactory() {
    override fun instantiateServiceCompat(
    override fun instantiateServiceCompat(
        cl: ClassLoader,
        cl: ClassLoader,
        className: String,
        className: String,
        intent: Intent?
        intent: Intent?,
    ): Service {
    ): Service {
        if (!this::componentHelper.isInitialized) {
        if (!this::componentHelper.isInitialized) {
            // This shouldn't happen, but does when a device is freshly formatted.
            // This shouldn't happen, but does when a device is freshly formatted.
@@ -146,7 +147,7 @@ abstract class SystemUIAppComponentFactoryBase : AppComponentFactory() {
    override fun instantiateReceiverCompat(
    override fun instantiateReceiverCompat(
        cl: ClassLoader,
        cl: ClassLoader,
        className: String,
        className: String,
        intent: Intent?
        intent: Intent?,
    ): BroadcastReceiver {
    ): BroadcastReceiver {
        if (!this::componentHelper.isInitialized) {
        if (!this::componentHelper.isInitialized) {
            // This shouldn't happen, but does when a device is freshly formatted.
            // This shouldn't happen, but does when a device is freshly formatted.
@@ -156,32 +157,4 @@ abstract class SystemUIAppComponentFactoryBase : AppComponentFactory() {
        return componentHelper.resolveBroadcastReceiver(className)
        return componentHelper.resolveBroadcastReceiver(className)
            ?: super.instantiateReceiverCompat(cl, className, intent)
            ?: 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)
    }
}
}
+5 −3
Original line number Original line Diff line number Diff line
@@ -42,6 +42,8 @@ import androidx.annotation.VisibleForTesting;
import com.airbnb.lottie.Lottie;
import com.airbnb.lottie.Lottie;
import com.airbnb.lottie.LottieConfig;
import com.airbnb.lottie.LottieConfig;
import com.android.internal.protolog.ProtoLog;
import com.android.internal.protolog.ProtoLog;
import com.android.systemui.application.ApplicationContextAvailableCallback;
import com.android.systemui.application.ApplicationContextInitializer;
import com.android.systemui.dagger.GlobalRootComponent;
import com.android.systemui.dagger.GlobalRootComponent;
import com.android.systemui.dagger.SysUIComponent;
import com.android.systemui.dagger.SysUIComponent;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.dump.DumpManager;
@@ -67,7 +69,7 @@ import javax.inject.Provider;
 * Application class for SystemUI.
 * Application class for SystemUI.
 */
 */
public class SystemUIApplication extends Application implements
public class SystemUIApplication extends Application implements
        SystemUIAppComponentFactoryBase.ContextInitializer, HasWMComponent {
        ApplicationContextInitializer, HasWMComponent {


    public static final String TAG = "SystemUIService";
    public static final String TAG = "SystemUIService";
    private static final boolean DEBUG = false;
    private static final boolean DEBUG = false;
@@ -79,7 +81,7 @@ public class SystemUIApplication extends Application implements
     */
     */
    private CoreStartable[] mServices;
    private CoreStartable[] mServices;
    private boolean mServicesStarted;
    private boolean mServicesStarted;
    private SystemUIAppComponentFactoryBase.ContextAvailableCallback mContextAvailableCallback;
    private ApplicationContextAvailableCallback mContextAvailableCallback;
    private SysUIComponent mSysUIComponent;
    private SysUIComponent mSysUIComponent;
    private SystemUIInitializer mInitializer;
    private SystemUIInitializer mInitializer;
    private ProcessWrapper mProcessWrapper;
    private ProcessWrapper mProcessWrapper;
@@ -492,7 +494,7 @@ public class SystemUIApplication extends Application implements


    @Override
    @Override
    public void setContextAvailableCallback(
    public void setContextAvailableCallback(
            @NonNull SystemUIAppComponentFactoryBase.ContextAvailableCallback callback) {
            @NonNull ApplicationContextAvailableCallback callback) {
        mContextAvailableCallback = callback;
        mContextAvailableCallback = callback;
    }
    }


+31 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2025 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.application

import android.content.Context
import com.android.systemui.SystemUIInitializer

/**
 * An Interface for [Application] classes that can be notified when an Application Context becomes
 * available.
 *
 * An instance of this will be passed to implementers of [ApplicationContextInitializer].
 */
fun interface ApplicationContextAvailableCallback {
    /** Notifies when the Application Context is available. */
    fun onContextAvailable(context: Context): SystemUIInitializer
}
+34 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2025 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.application

/**
 * Interface for classes that can be constructed by the system before a context is available.
 *
 * This is intended for [Application] implementations that may not have a Context until some point
 * after construction or are themselves a [Context].
 *
 * Implementers will be passed a [ApplicationContextAvailableCallback] that they should call as soon
 * as an Application Context is ready.
 */
interface ApplicationContextInitializer {
    /**
     * Called to supply the [ApplicationContextAvailableCallback] that should be called when an
     * Application [Context] is available.
     */
    fun setContextAvailableCallback(callback: ApplicationContextAvailableCallback)
}
+30 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2025 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.application

import android.content.Context

/**
 * An Interface for [ContentProvider] classes that can be notified when an Application Context
 * becomes available.
 *
 * An instance of this will be passed to implementers of [ContentProviderContextInitializer].
 */
fun interface ContentProviderContextAvailableCallback {
    /** Notifies when the Application Context is available. */
    fun onContextAvailable(context: Context)
}
Loading