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