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.app.Notification;
22 import android.content.BroadcastReceiver;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.IntentFilter;
26 import android.content.pm.ApplicationInfo;
27 import android.content.res.Configuration;
28 import android.os.Bundle;
29 import android.os.Looper;
30 import android.os.Process;
31 import android.os.SystemProperties;
32 import android.os.Trace;
33 import android.os.UserHandle;
34 import android.util.Log;
35 import android.util.TimingsTraceLog;
36 import android.view.SurfaceControl;
37 import android.view.ThreadedRenderer;
38 import android.view.View;
39 
40 import com.android.internal.protolog.common.ProtoLog;
41 import com.android.systemui.dagger.GlobalRootComponent;
42 import com.android.systemui.dagger.SysUIComponent;
43 import com.android.systemui.dump.DumpManager;
44 import com.android.systemui.statusbar.policy.ConfigurationController;
45 import com.android.systemui.util.NotificationChannels;
46 
47 import java.util.Comparator;
48 import java.util.Map;
49 import java.util.TreeMap;
50 
51 import javax.inject.Provider;
52 
53 /**
54  * Application class for SystemUI.
55  */
56 public class SystemUIApplication extends Application implements
57         SystemUIAppComponentFactoryBase.ContextInitializer {
58 
59     public static final String TAG = "SystemUIService";
60     private static final boolean DEBUG = false;
61 
62     private BootCompleteCacheImpl mBootCompleteCache;
63 
64     /**
65      * Hold a reference on the stuff we start.
66      */
67     private CoreStartable[] mServices;
68     private boolean mServicesStarted;
69     private SystemUIAppComponentFactoryBase.ContextAvailableCallback mContextAvailableCallback;
70     private SysUIComponent mSysUIComponent;
71     private SystemUIInitializer mInitializer;
72 
SystemUIApplication()73     public SystemUIApplication() {
74         super();
75         Log.v(TAG, "SystemUIApplication constructed.");
76         // SysUI may be building without protolog preprocessing in some cases
77         ProtoLog.REQUIRE_PROTOLOGTOOL = false;
78     }
79 
getRootComponent()80     protected GlobalRootComponent getRootComponent() {
81         return mInitializer.getRootComponent();
82     }
83 
84     @Override
onCreate()85     public void onCreate() {
86         super.onCreate();
87         Log.v(TAG, "SystemUIApplication created.");
88         // This line is used to setup Dagger's dependency injection and should be kept at the
89         // top of this method.
90         TimingsTraceLog log = new TimingsTraceLog("SystemUIBootTiming",
91                 Trace.TRACE_TAG_APP);
92         log.traceBegin("DependencyInjection");
93         mInitializer = mContextAvailableCallback.onContextAvailable(this);
94         mSysUIComponent = mInitializer.getSysUIComponent();
95         mBootCompleteCache = mSysUIComponent.provideBootCacheImpl();
96         log.traceEnd();
97 
98         // Enable Looper trace points.
99         // This allows us to see Handler callbacks on traces.
100         Looper.getMainLooper().setTraceTag(Trace.TRACE_TAG_APP);
101 
102         // Set the application theme that is inherited by all services. Note that setting the
103         // application theme in the manifest does only work for activities. Keep this in sync with
104         // the theme set there.
105         setTheme(R.style.Theme_SystemUI);
106 
107         View.setTraceLayoutSteps(
108                 SystemProperties.getBoolean("persist.debug.trace_layouts", false));
109         View.setTracedRequestLayoutClassClass(
110                 SystemProperties.get("persist.debug.trace_request_layout_class", null));
111 
112         if (Process.myUserHandle().equals(UserHandle.SYSTEM)) {
113             IntentFilter bootCompletedFilter = new
114                     IntentFilter(Intent.ACTION_LOCKED_BOOT_COMPLETED);
115             bootCompletedFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
116 
117             // If SF GPU context priority is set to realtime, then SysUI should run at high.
118             // The priority is defaulted at medium.
119             int sfPriority = SurfaceControl.getGPUContextPriority();
120             Log.i(TAG, "Found SurfaceFlinger's GPU Priority: " + sfPriority);
121             if (sfPriority == ThreadedRenderer.EGL_CONTEXT_PRIORITY_REALTIME_NV) {
122                 Log.i(TAG, "Setting SysUI's GPU Context priority to: "
123                         + ThreadedRenderer.EGL_CONTEXT_PRIORITY_HIGH_IMG);
124                 ThreadedRenderer.setContextPriority(
125                         ThreadedRenderer.EGL_CONTEXT_PRIORITY_HIGH_IMG);
126             }
127 
128             registerReceiver(new BroadcastReceiver() {
129                 @Override
130                 public void onReceive(Context context, Intent intent) {
131                     if (mBootCompleteCache.isBootComplete()) return;
132 
133                     if (DEBUG) Log.v(TAG, "BOOT_COMPLETED received");
134                     unregisterReceiver(this);
135                     mBootCompleteCache.setBootComplete();
136                     if (mServicesStarted) {
137                         final int N = mServices.length;
138                         for (int i = 0; i < N; i++) {
139                             notifyBootCompleted(mServices[i]);
140                         }
141                     }
142                 }
143             }, bootCompletedFilter);
144 
145             IntentFilter localeChangedFilter = new IntentFilter(Intent.ACTION_LOCALE_CHANGED);
146             registerReceiver(new BroadcastReceiver() {
147                 @Override
148                 public void onReceive(Context context, Intent intent) {
149                     if (Intent.ACTION_LOCALE_CHANGED.equals(intent.getAction())) {
150                         if (!mBootCompleteCache.isBootComplete()) return;
151                         // Update names of SystemUi notification channels
152                         NotificationChannels.createAll(context);
153                     }
154                 }
155             }, localeChangedFilter);
156         } else {
157             // We don't need to startServices for sub-process that is doing some tasks.
158             // (screenshots, sweetsweetdesserts or tuner ..)
159             String processName = ActivityThread.currentProcessName();
160             ApplicationInfo info = getApplicationInfo();
161             if (processName != null && processName.startsWith(info.processName + ":")) {
162                 return;
163             }
164             // For a secondary user, boot-completed will never be called because it has already
165             // been broadcasted on startup for the primary SystemUI process.  Instead, for
166             // components which require the SystemUI component to be initialized per-user, we
167             // start those components now for the current non-system user.
168             startSecondaryUserServicesIfNeeded();
169         }
170     }
171 
172     /**
173      * Makes sure that all the SystemUI services are running. If they are already running, this is a
174      * no-op. This is needed to conditinally start all the services, as we only need to have it in
175      * the main process.
176      * <p>This method must only be called from the main thread.</p>
177      */
178 
startServicesIfNeeded()179     public void startServicesIfNeeded() {
180         final String vendorComponent = mInitializer.getVendorComponent(getResources());
181 
182         // Sort the startables so that we get a deterministic ordering.
183         // TODO: make #start idempotent and require users of CoreStartable to call it.
184         Map<Class<?>, Provider<CoreStartable>> sortedStartables = new TreeMap<>(
185                 Comparator.comparing(Class::getName));
186         sortedStartables.putAll(mSysUIComponent.getStartables());
187         sortedStartables.putAll(mSysUIComponent.getPerUserStartables());
188         startServicesIfNeeded(
189                 sortedStartables, "StartServices", vendorComponent);
190     }
191 
192     /**
193      * Ensures that all the Secondary user SystemUI services are running. If they are already
194      * running, this is a no-op. This is needed to conditionally start all the services, as we only
195      * need to have it in the main process.
196      * <p>This method must only be called from the main thread.</p>
197      */
startSecondaryUserServicesIfNeeded()198     void startSecondaryUserServicesIfNeeded() {
199         // Sort the startables so that we get a deterministic ordering.
200         Map<Class<?>, Provider<CoreStartable>> sortedStartables = new TreeMap<>(
201                 Comparator.comparing(Class::getName));
202         sortedStartables.putAll(mSysUIComponent.getPerUserStartables());
203         startServicesIfNeeded(
204                 sortedStartables, "StartSecondaryServices", null);
205     }
206 
startServicesIfNeeded( Map<Class<?>, Provider<CoreStartable>> startables, String metricsPrefix, String vendorComponent)207     private void startServicesIfNeeded(
208             Map<Class<?>, Provider<CoreStartable>> startables,
209             String metricsPrefix,
210             String vendorComponent) {
211         if (mServicesStarted) {
212             return;
213         }
214         mServices = new CoreStartable[startables.size() + (vendorComponent == null ? 0 : 1)];
215 
216         if (!mBootCompleteCache.isBootComplete()) {
217             // check to see if maybe it was already completed long before we began
218             // see ActivityManagerService.finishBooting()
219             if ("1".equals(SystemProperties.get("sys.boot_completed"))) {
220                 mBootCompleteCache.setBootComplete();
221                 if (DEBUG) {
222                     Log.v(TAG, "BOOT_COMPLETED was already sent");
223                 }
224             }
225         }
226 
227         DumpManager dumpManager = mSysUIComponent.createDumpManager();
228 
229         Log.v(TAG, "Starting SystemUI services for user " +
230                 Process.myUserHandle().getIdentifier() + ".");
231         TimingsTraceLog log = new TimingsTraceLog("SystemUIBootTiming",
232                 Trace.TRACE_TAG_APP);
233         log.traceBegin(metricsPrefix);
234 
235         int i = 0;
236         for (Map.Entry<Class<?>, Provider<CoreStartable>> entry : startables.entrySet()) {
237             String clsName = entry.getKey().getName();
238             int j = i;  // Copied to make lambda happy.
239             timeInitialization(
240                     clsName,
241                     () -> mServices[j] = startStartable(clsName, entry.getValue()),
242                     log,
243                     metricsPrefix);
244             i++;
245         }
246 
247         if (vendorComponent != null) {
248             timeInitialization(
249                     vendorComponent,
250                     () -> mServices[mServices.length - 1] =
251                             startAdditionalStartable(vendorComponent),
252                     log,
253                     metricsPrefix);
254         }
255 
256         for (i = 0; i < mServices.length; i++) {
257             CoreStartable service = mServices[i];
258             if (mBootCompleteCache.isBootComplete()) {
259                 notifyBootCompleted(service);
260             }
261 
262             if (service.isDumpCritical()) {
263                 dumpManager.registerCriticalDumpable(service.getClass().getName(), service);
264             } else {
265                 dumpManager.registerNormalDumpable(service.getClass().getName(), service);
266             }
267         }
268         mSysUIComponent.getInitController().executePostInitTasks();
269         log.traceEnd();
270 
271         mServicesStarted = true;
272     }
273 
notifyBootCompleted(CoreStartable coreStartable)274     private static void notifyBootCompleted(CoreStartable coreStartable) {
275         if (Trace.isEnabled()) {
276             Trace.traceBegin(
277                     Trace.TRACE_TAG_APP,
278                     coreStartable.getClass().getSimpleName() + ".onBootCompleted()");
279         }
280         coreStartable.onBootCompleted();
281         Trace.endSection();
282     }
283 
timeInitialization(String clsName, Runnable init, TimingsTraceLog log, String metricsPrefix)284     private static void timeInitialization(String clsName, Runnable init, TimingsTraceLog log,
285             String metricsPrefix) {
286         long ti = System.currentTimeMillis();
287         log.traceBegin(metricsPrefix + " " + clsName);
288         init.run();
289         log.traceEnd();
290 
291         // Warn if initialization of component takes too long
292         ti = System.currentTimeMillis() - ti;
293         if (ti > 1000) {
294             Log.w(TAG, "Initialization of " + clsName + " took " + ti + " ms");
295         }
296     }
297 
startAdditionalStartable(String clsName)298     private static CoreStartable startAdditionalStartable(String clsName) {
299         CoreStartable startable;
300         if (DEBUG) Log.d(TAG, "loading: " + clsName);
301         if (Trace.isEnabled()) {
302             Trace.traceBegin(
303                     Trace.TRACE_TAG_APP, clsName + ".newInstance()");
304         }
305         try {
306             startable = (CoreStartable) Class.forName(clsName).newInstance();
307         } catch (ClassNotFoundException
308                 | IllegalAccessException
309                 | InstantiationException ex) {
310             throw new RuntimeException(ex);
311         } finally {
312             Trace.endSection();
313         }
314 
315         return startStartable(startable);
316     }
317 
startStartable(String clsName, Provider<CoreStartable> provider)318     private static CoreStartable startStartable(String clsName, Provider<CoreStartable> provider) {
319         if (DEBUG) Log.d(TAG, "loading: " + clsName);
320         if (Trace.isEnabled()) {
321             Trace.traceBegin(
322                     Trace.TRACE_TAG_APP, "Provider<" + clsName + ">.get()");
323         }
324         CoreStartable startable = provider.get();
325         Trace.endSection();
326         return startStartable(startable);
327     }
328 
startStartable(CoreStartable startable)329     private static CoreStartable startStartable(CoreStartable startable) {
330         if (DEBUG) Log.d(TAG, "running: " + startable);
331         if (Trace.isEnabled()) {
332             Trace.traceBegin(
333                     Trace.TRACE_TAG_APP, startable.getClass().getSimpleName() + ".start()");
334         }
335         startable.start();
336         Trace.endSection();
337 
338         return startable;
339     }
340 
341     @Override
onConfigurationChanged(Configuration newConfig)342     public void onConfigurationChanged(Configuration newConfig) {
343         if (mServicesStarted) {
344             ConfigurationController configController = mSysUIComponent.getConfigurationController();
345             if (Trace.isEnabled()) {
346                 Trace.traceBegin(
347                         Trace.TRACE_TAG_APP,
348                         configController.getClass().getSimpleName() + ".onConfigurationChanged()");
349             }
350             configController.onConfigurationChanged(newConfig);
351             Trace.endSection();
352             int len = mServices.length;
353             for (int i = 0; i < len; i++) {
354                 if (mServices[i] != null) {
355                     if (Trace.isEnabled()) {
356                         Trace.traceBegin(
357                                 Trace.TRACE_TAG_APP,
358                                 mServices[i].getClass().getSimpleName()
359                                         + ".onConfigurationChanged()");
360                     }
361                     mServices[i].onConfigurationChanged(newConfig);
362                     Trace.endSection();
363                 }
364             }
365         }
366     }
367 
getServices()368     public CoreStartable[] getServices() {
369         return mServices;
370     }
371 
372     @Override
setContextAvailableCallback( SystemUIAppComponentFactoryBase.ContextAvailableCallback callback)373     public void setContextAvailableCallback(
374             SystemUIAppComponentFactoryBase.ContextAvailableCallback callback) {
375         mContextAvailableCallback = callback;
376     }
377 
378     /** Update a notifications application name. */
overrideNotificationAppName(Context context, Notification.Builder n, boolean system)379     public static void overrideNotificationAppName(Context context, Notification.Builder n,
380             boolean system) {
381         final Bundle extras = new Bundle();
382         String appName = system
383                 ? context.getString(com.android.internal.R.string.notification_app_name_system)
384                 : context.getString(com.android.internal.R.string.notification_app_name_settings);
385         extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, appName);
386 
387         n.addExtras(extras);
388     }
389 }
390