1 /*
<lambda>null2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.systemui
17 
18 import android.app.Activity
19 import android.app.Application
20 import android.app.Service
21 import android.content.BroadcastReceiver
22 import android.content.ContentProvider
23 import android.content.Context
24 import android.content.Intent
25 import android.util.Log
26 import androidx.core.app.AppComponentFactory
27 import com.android.systemui.dagger.ContextComponentHelper
28 import com.android.systemui.dagger.SysUIComponent
29 import com.android.tools.r8.keepanno.annotations.KeepTarget
30 import com.android.tools.r8.keepanno.annotations.UsesReflection
31 import java.lang.reflect.InvocationTargetException
32 import java.util.concurrent.ExecutionException
33 import javax.inject.Inject
34 
35 /**
36  * Implementation of AppComponentFactory that injects into constructors.
37  *
38  * This class sets up dependency injection when creating our application.
39  *
40  * Activities, Services, and BroadcastReceivers support dependency injection into
41  * their constructors.
42  *
43  * ContentProviders support injection into member variables - _not_ constructors.
44  */
45 abstract class SystemUIAppComponentFactoryBase : AppComponentFactory() {
46     companion object {
47         private const val TAG = "AppComponentFactory"
48         // Must be static due to http://b/141008541.
49         var systemUIInitializer: SystemUIInitializer? = null
50     }
51 
52     @set:Inject
53     lateinit var componentHelper: ContextComponentHelper
54 
55     /**
56      * Returns a new [SystemUIInitializer].
57      *
58      * The returned implementation should be specific to your build.
59      */
60     protected abstract fun createSystemUIInitializer(context: Context): SystemUIInitializer
61 
62     private fun createSystemUIInitializerInternal(context: Context): SystemUIInitializer {
63         return systemUIInitializer ?: run {
64             val initializer = createSystemUIInitializer(context.applicationContext)
65             try {
66                 initializer.init(false)
67             } catch (exception: ExecutionException) {
68                 throw RuntimeException("Failed to initialize SysUI", exception)
69             } catch (exception: InterruptedException) {
70                 throw RuntimeException("Failed to initialize SysUI", exception)
71             }
72             initializer.sysUIComponent.inject(
73                 this@SystemUIAppComponentFactoryBase
74             )
75 
76             systemUIInitializer = initializer
77             return initializer
78         }
79     }
80 
81     override fun instantiateApplicationCompat(cl: ClassLoader, className: String): Application {
82         val app = super.instantiateApplicationCompat(cl, className)
83         if (app !is ContextInitializer) {
84             throw RuntimeException("App must implement ContextInitializer")
85         } else {
86             app.setContextAvailableCallback { context ->
87                 createSystemUIInitializerInternal(context)
88             }
89         }
90 
91         return app
92     }
93 
94     @UsesReflection(KeepTarget(instanceOfClassConstant = SysUIComponent::class, methodName = "inject"))
95     override fun instantiateProviderCompat(cl: ClassLoader, className: String): ContentProvider {
96         val contentProvider = super.instantiateProviderCompat(cl, className)
97         if (contentProvider is ContextInitializer) {
98             contentProvider.setContextAvailableCallback { context ->
99                 val initializer = createSystemUIInitializerInternal(context)
100                 val rootComponent = initializer.sysUIComponent
101                 try {
102                     val injectMethod = rootComponent.javaClass
103                         .getMethod("inject", contentProvider.javaClass)
104                     injectMethod.invoke(rootComponent, contentProvider)
105                 } catch (e: NoSuchMethodException) {
106                     Log.w(TAG, "No injector for class: " + contentProvider.javaClass, e)
107                 } catch (e: IllegalAccessException) {
108                     Log.w(TAG, "No injector for class: " + contentProvider.javaClass, e)
109                 } catch (e: InvocationTargetException) {
110                     Log.w(TAG, "No injector for class: " + contentProvider.javaClass, e)
111                 }
112                 initializer
113             }
114         }
115         return contentProvider
116     }
117 
118     override fun instantiateActivityCompat(
119         cl: ClassLoader,
120         className: String,
121         intent: Intent?
122     ): Activity {
123         if (!this::componentHelper.isInitialized) {
124             // This shouldn't happen, but is seen on occasion.
125             // Bug filed against framework to take a look: http://b/141008541
126             systemUIInitializer?.sysUIComponent?.inject(this@SystemUIAppComponentFactoryBase)
127         }
128         return componentHelper.resolveActivity(className)
129             ?: super.instantiateActivityCompat(cl, className, intent)
130     }
131 
132     override fun instantiateServiceCompat(
133         cl: ClassLoader,
134         className: String,
135         intent: Intent?
136     ): Service {
137         if (!this::componentHelper.isInitialized) {
138             // This shouldn't happen, but does when a device is freshly formatted.
139             // Bug filed against framework to take a look: http://b/141008541
140             systemUIInitializer?.sysUIComponent?.inject(this@SystemUIAppComponentFactoryBase)
141         }
142         return componentHelper.resolveService(className)
143             ?: super.instantiateServiceCompat(cl, className, intent)
144     }
145 
146     override fun instantiateReceiverCompat(
147         cl: ClassLoader,
148         className: String,
149         intent: Intent?
150     ): BroadcastReceiver {
151         if (!this::componentHelper.isInitialized) {
152             // This shouldn't happen, but does when a device is freshly formatted.
153             // Bug filed against framework to take a look: http://b/141008541
154             systemUIInitializer?.sysUIComponent?.inject(this@SystemUIAppComponentFactoryBase)
155         }
156         return componentHelper.resolveBroadcastReceiver(className)
157             ?: super.instantiateReceiverCompat(cl, className, intent)
158     }
159 
160     /**
161      * An Interface for classes that can be notified when an Application Context becomes available.
162      *
163      * An instance of this will be passed to implementers of [ContextInitializer].
164      */
165     fun interface ContextAvailableCallback {
166         /** Notifies when the Application Context is available.  */
167         fun onContextAvailable(context: Context): SystemUIInitializer
168     }
169 
170     /**
171      * Interface for classes that can be constructed by the system before a context is available.
172      *
173      * This is intended for [Application] and [ContentProvider] implementations that
174      * either may not have a Context until some point after construction or are themselves
175      * a [Context].
176      *
177      * Implementers will be passed a [ContextAvailableCallback] that they should call as soon
178      * as an Application Context is ready.
179      */
180     interface ContextInitializer {
181         /**
182          * Called to supply the [ContextAvailableCallback] that should be called when an
183          * Application [Context] is available.
184          */
185         fun setContextAvailableCallback(callback: ContextAvailableCallback)
186     }
187 }
188