1 /*
2  * Copyright (C) 2014 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.ActivityThread;
20 import android.app.Application;
21 import android.content.BroadcastReceiver;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.IntentFilter;
25 import android.content.pm.ApplicationInfo;
26 import android.content.res.Configuration;
27 import android.os.Process;
28 import android.os.SystemProperties;
29 import android.os.Trace;
30 import android.os.UserHandle;
31 import android.util.Log;
32 import android.util.TimingsTraceLog;
33 
34 import com.android.systemui.dagger.ContextComponentHelper;
35 import com.android.systemui.dagger.SystemUIRootComponent;
36 import com.android.systemui.dump.DumpManager;
37 import com.android.systemui.util.NotificationChannels;
38 
39 import java.lang.reflect.Constructor;
40 import java.lang.reflect.InvocationTargetException;
41 
42 /**
43  * Application class for SystemUI.
44  */
45 public class SystemUIApplication extends Application implements
46         SystemUIAppComponentFactory.ContextInitializer {
47 
48     public static final String TAG = "SystemUIService";
49     private static final boolean DEBUG = false;
50 
51     private ContextComponentHelper mComponentHelper;
52     private BootCompleteCacheImpl mBootCompleteCache;
53 
54     /**
55      * Hold a reference on the stuff we start.
56      */
57     private SystemUI[] mServices;
58     private boolean mServicesStarted;
59     private SystemUIAppComponentFactory.ContextAvailableCallback mContextAvailableCallback;
60     private SystemUIRootComponent mRootComponent;
61 
SystemUIApplication()62     public SystemUIApplication() {
63         super();
64         Log.v(TAG, "SystemUIApplication constructed.");
65     }
66 
67     @Override
onCreate()68     public void onCreate() {
69         super.onCreate();
70         Log.v(TAG, "SystemUIApplication created.");
71         // This line is used to setup Dagger's dependency injection and should be kept at the
72         // top of this method.
73         TimingsTraceLog log = new TimingsTraceLog("SystemUIBootTiming",
74                 Trace.TRACE_TAG_APP);
75         log.traceBegin("DependencyInjection");
76         mContextAvailableCallback.onContextAvailable(this);
77         mRootComponent = SystemUIFactory.getInstance().getRootComponent();
78         mComponentHelper = mRootComponent.getContextComponentHelper();
79         mBootCompleteCache = mRootComponent.provideBootCacheImpl();
80         log.traceEnd();
81 
82         // Set the application theme that is inherited by all services. Note that setting the
83         // application theme in the manifest does only work for activities. Keep this in sync with
84         // the theme set there.
85         setTheme(R.style.Theme_SystemUI);
86 
87         if (Process.myUserHandle().equals(UserHandle.SYSTEM)) {
88             IntentFilter bootCompletedFilter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
89             bootCompletedFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
90             registerReceiver(new BroadcastReceiver() {
91                 @Override
92                 public void onReceive(Context context, Intent intent) {
93                     if (mBootCompleteCache.isBootComplete()) return;
94 
95                     if (DEBUG) Log.v(TAG, "BOOT_COMPLETED received");
96                     unregisterReceiver(this);
97                     mBootCompleteCache.setBootComplete();
98                     if (mServicesStarted) {
99                         final int N = mServices.length;
100                         for (int i = 0; i < N; i++) {
101                             mServices[i].onBootCompleted();
102                         }
103                     }
104                 }
105             }, bootCompletedFilter);
106 
107             IntentFilter localeChangedFilter = new IntentFilter(Intent.ACTION_LOCALE_CHANGED);
108             registerReceiver(new BroadcastReceiver() {
109                 @Override
110                 public void onReceive(Context context, Intent intent) {
111                     if (Intent.ACTION_LOCALE_CHANGED.equals(intent.getAction())) {
112                         if (!mBootCompleteCache.isBootComplete()) return;
113                         // Update names of SystemUi notification channels
114                         NotificationChannels.createAll(context);
115                     }
116                 }
117             }, localeChangedFilter);
118         } else {
119             // We don't need to startServices for sub-process that is doing some tasks.
120             // (screenshots, sweetsweetdesserts or tuner ..)
121             String processName = ActivityThread.currentProcessName();
122             ApplicationInfo info = getApplicationInfo();
123             if (processName != null && processName.startsWith(info.processName + ":")) {
124                 return;
125             }
126             // For a secondary user, boot-completed will never be called because it has already
127             // been broadcasted on startup for the primary SystemUI process.  Instead, for
128             // components which require the SystemUI component to be initialized per-user, we
129             // start those components now for the current non-system user.
130             startSecondaryUserServicesIfNeeded();
131         }
132     }
133 
134     /**
135      * Makes sure that all the SystemUI services are running. If they are already running, this is a
136      * no-op. This is needed to conditinally start all the services, as we only need to have it in
137      * the main process.
138      * <p>This method must only be called from the main thread.</p>
139      */
140 
startServicesIfNeeded()141     public void startServicesIfNeeded() {
142         String[] names = SystemUIFactory.getInstance().getSystemUIServiceComponents(getResources());
143         startServicesIfNeeded(/* metricsPrefix= */ "StartServices", names);
144     }
145 
146     /**
147      * Ensures that all the Secondary user SystemUI services are running. If they are already
148      * running, this is a no-op. This is needed to conditionally start all the services, as we only
149      * need to have it in the main process.
150      * <p>This method must only be called from the main thread.</p>
151      */
startSecondaryUserServicesIfNeeded()152     void startSecondaryUserServicesIfNeeded() {
153         String[] names = SystemUIFactory.getInstance().getSystemUIServiceComponentsPerUser(
154                 getResources());
155         startServicesIfNeeded(/* metricsPrefix= */ "StartSecondaryServices", names);
156     }
157 
startServicesIfNeeded(String metricsPrefix, String[] services)158     private void startServicesIfNeeded(String metricsPrefix, String[] services) {
159         if (mServicesStarted) {
160             return;
161         }
162         mServices = new SystemUI[services.length];
163 
164         if (!mBootCompleteCache.isBootComplete()) {
165             // check to see if maybe it was already completed long before we began
166             // see ActivityManagerService.finishBooting()
167             if ("1".equals(SystemProperties.get("sys.boot_completed"))) {
168                 mBootCompleteCache.setBootComplete();
169                 if (DEBUG) {
170                     Log.v(TAG, "BOOT_COMPLETED was already sent");
171                 }
172             }
173         }
174 
175         final DumpManager dumpManager = mRootComponent.createDumpManager();
176 
177         Log.v(TAG, "Starting SystemUI services for user " +
178                 Process.myUserHandle().getIdentifier() + ".");
179         TimingsTraceLog log = new TimingsTraceLog("SystemUIBootTiming",
180                 Trace.TRACE_TAG_APP);
181         log.traceBegin(metricsPrefix);
182         final int N = services.length;
183         for (int i = 0; i < N; i++) {
184             String clsName = services[i];
185             if (DEBUG) Log.d(TAG, "loading: " + clsName);
186             log.traceBegin(metricsPrefix + clsName);
187             long ti = System.currentTimeMillis();
188             try {
189                 SystemUI obj = mComponentHelper.resolveSystemUI(clsName);
190                 if (obj == null) {
191                     Constructor constructor = Class.forName(clsName).getConstructor(Context.class);
192                     obj = (SystemUI) constructor.newInstance(this);
193                 }
194                 mServices[i] = obj;
195             } catch (ClassNotFoundException
196                     | NoSuchMethodException
197                     | IllegalAccessException
198                     | InstantiationException
199                     | InvocationTargetException ex) {
200                 throw new RuntimeException(ex);
201             }
202 
203             if (DEBUG) Log.d(TAG, "running: " + mServices[i]);
204             mServices[i].start();
205             log.traceEnd();
206 
207             // Warn if initialization of component takes too long
208             ti = System.currentTimeMillis() - ti;
209             if (ti > 1000) {
210                 Log.w(TAG, "Initialization of " + clsName + " took " + ti + " ms");
211             }
212             if (mBootCompleteCache.isBootComplete()) {
213                 mServices[i].onBootCompleted();
214             }
215 
216             dumpManager.registerDumpable(mServices[i].getClass().getName(), mServices[i]);
217         }
218         mRootComponent.getInitController().executePostInitTasks();
219         log.traceEnd();
220 
221         mServicesStarted = true;
222     }
223 
224     @Override
onConfigurationChanged(Configuration newConfig)225     public void onConfigurationChanged(Configuration newConfig) {
226         if (mServicesStarted) {
227             mRootComponent.getConfigurationController().onConfigurationChanged(newConfig);
228             int len = mServices.length;
229             for (int i = 0; i < len; i++) {
230                 if (mServices[i] != null) {
231                     mServices[i].onConfigurationChanged(newConfig);
232                 }
233             }
234         }
235     }
236 
getServices()237     public SystemUI[] getServices() {
238         return mServices;
239     }
240 
241     @Override
setContextAvailableCallback( SystemUIAppComponentFactory.ContextAvailableCallback callback)242     public void setContextAvailableCallback(
243             SystemUIAppComponentFactory.ContextAvailableCallback callback) {
244         mContextAvailableCallback = callback;
245     }
246 
247 }
248