1 /*
2  * 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 
17 package com.android.systemui;
18 
19 import android.app.Activity;
20 import android.app.Application;
21 import android.app.Service;
22 import android.content.BroadcastReceiver;
23 import android.content.ContentProvider;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.util.Log;
27 
28 import androidx.annotation.NonNull;
29 import androidx.annotation.Nullable;
30 import androidx.core.app.AppComponentFactory;
31 
32 import com.android.systemui.dagger.ContextComponentHelper;
33 import com.android.systemui.dagger.SysUIComponent;
34 
35 import java.lang.reflect.InvocationTargetException;
36 import java.lang.reflect.Method;
37 
38 import javax.inject.Inject;
39 
40 /**
41  * Implementation of AppComponentFactory that injects into constructors.
42  *
43  * This class sets up dependency injection when creating our application.
44  *
45  * Services support dependency injection into their constructors.
46  *
47  * ContentProviders support injection into member variables - _not_ constructors.
48  */
49 public class SystemUIAppComponentFactory extends AppComponentFactory {
50 
51     private static final String TAG = "AppComponentFactory";
52     @Inject
53     public ContextComponentHelper mComponentHelper;
54 
SystemUIAppComponentFactory()55     public SystemUIAppComponentFactory() {
56         super();
57     }
58 
59     @NonNull
60     @Override
instantiateApplicationCompat( @onNull ClassLoader cl, @NonNull String className)61     public Application instantiateApplicationCompat(
62             @NonNull ClassLoader cl, @NonNull String className)
63             throws InstantiationException, IllegalAccessException, ClassNotFoundException {
64         Application app = super.instantiateApplicationCompat(cl, className);
65         if (app instanceof ContextInitializer) {
66             ((ContextInitializer) app).setContextAvailableCallback(
67                     context -> {
68                         SystemUIFactory.createFromConfig(context);
69                         SystemUIFactory.getInstance().getSysUIComponent().inject(
70                                 SystemUIAppComponentFactory.this);
71                     }
72             );
73         }
74 
75         return app;
76     }
77 
78     @NonNull
79     @Override
instantiateProviderCompat( @onNull ClassLoader cl, @NonNull String className)80     public ContentProvider instantiateProviderCompat(
81             @NonNull ClassLoader cl, @NonNull String className)
82             throws InstantiationException, IllegalAccessException, ClassNotFoundException {
83 
84         ContentProvider contentProvider = super.instantiateProviderCompat(cl, className);
85         if (contentProvider instanceof ContextInitializer) {
86             ((ContextInitializer) contentProvider).setContextAvailableCallback(
87                     context -> {
88                         SystemUIFactory.createFromConfig(context);
89                         SysUIComponent rootComponent =
90                                 SystemUIFactory.getInstance().getSysUIComponent();
91                         try {
92                             Method injectMethod = rootComponent.getClass()
93                                     .getMethod("inject", contentProvider.getClass());
94                             injectMethod.invoke(rootComponent, contentProvider);
95                         } catch (NoSuchMethodException
96                                 | IllegalAccessException
97                                 | InvocationTargetException e) {
98                             Log.w(TAG, "No injector for class: " + contentProvider.getClass(), e);
99                         }
100                     }
101             );
102         }
103 
104         return contentProvider;
105     }
106 
107     @NonNull
108     @Override
instantiateActivityCompat(@onNull ClassLoader cl, @NonNull String className, @Nullable Intent intent)109     public Activity instantiateActivityCompat(@NonNull ClassLoader cl, @NonNull String className,
110             @Nullable Intent intent)
111             throws InstantiationException, IllegalAccessException, ClassNotFoundException {
112         if (mComponentHelper == null) {
113             // This shouldn't happen, but is seen on occasion.
114             // Bug filed against framework to take a look: http://b/141008541
115             SystemUIFactory.getInstance().getSysUIComponent().inject(
116                     SystemUIAppComponentFactory.this);
117         }
118         Activity activity = mComponentHelper.resolveActivity(className);
119         if (activity != null) {
120             return activity;
121         }
122         return super.instantiateActivityCompat(cl, className, intent);
123     }
124 
125     @NonNull
126     @Override
instantiateServiceCompat( @onNull ClassLoader cl, @NonNull String className, Intent intent)127     public Service instantiateServiceCompat(
128             @NonNull ClassLoader cl, @NonNull String className, Intent intent)
129             throws InstantiationException, IllegalAccessException, ClassNotFoundException {
130         if (mComponentHelper == null) {
131             // This shouldn't happen, but does when a device is freshly formatted.
132             // Bug filed against framework to take a look: http://b/141008541
133             SystemUIFactory.getInstance().getSysUIComponent().inject(
134                     SystemUIAppComponentFactory.this);
135         }
136         Service service = mComponentHelper.resolveService(className);
137         if (service != null) {
138             return service;
139         }
140         return super.instantiateServiceCompat(cl, className, intent);
141     }
142 
143     @NonNull
144     @Override
instantiateReceiverCompat(@onNull ClassLoader cl, @NonNull String className, @Nullable Intent intent)145     public BroadcastReceiver instantiateReceiverCompat(@NonNull ClassLoader cl,
146             @NonNull String className, @Nullable Intent intent)
147             throws InstantiationException, IllegalAccessException, ClassNotFoundException {
148         if (mComponentHelper == null) {
149             // This shouldn't happen, but does when a device is freshly formatted.
150             // Bug filed against framework to take a look: http://b/141008541
151             SystemUIFactory.getInstance().getSysUIComponent().inject(
152                     SystemUIAppComponentFactory.this);
153         }
154         BroadcastReceiver receiver = mComponentHelper.resolveBroadcastReceiver(className);
155         if (receiver != null) {
156             return receiver;
157         }
158 
159         return super.instantiateReceiverCompat(cl, className, intent);
160     }
161 
162     /**
163      * A callback that receives a Context when one is ready.
164      */
165     public interface ContextAvailableCallback {
onContextAvailable(Context context)166         void onContextAvailable(Context context);
167     }
168 
169     /**
170      * Implemented in classes that get started by the system before a context is available.
171      */
172     public interface ContextInitializer {
setContextAvailableCallback(ContextAvailableCallback callback)173         void setContextAvailableCallback(ContextAvailableCallback callback);
174     }
175 }
176