blob: 48127a75b86e8a7ed0163c26b6c07f4231a6175d [file] [log] [blame]
Jorim Jaggicff0acb2014-03-31 16:35:15 +02001/*
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
17package com.android.systemui;
18
dooyoung.hwangb6479842016-08-31 14:15:22 +090019import android.app.ActivityThread;
Jorim Jaggicff0acb2014-03-31 16:35:15 +020020import android.app.Application;
Dan Sandlerdc5f16b2014-04-22 11:51:42 -040021import android.content.BroadcastReceiver;
22import android.content.Context;
23import android.content.Intent;
24import android.content.IntentFilter;
dooyoung.hwangb6479842016-08-31 14:15:22 +090025import android.content.pm.ApplicationInfo;
Jorim Jaggicff0acb2014-03-31 16:35:15 +020026import android.content.res.Configuration;
Beverly97b8ce12019-02-01 16:55:48 -050027import android.os.Handler;
28import android.os.Looper;
Winsonfe67aba2015-09-22 14:04:46 -070029import android.os.Process;
Dan Sandlerdc5f16b2014-04-22 11:51:42 -040030import android.os.SystemProperties;
Fyodor Kupolovf3591352017-06-23 18:20:40 -070031import android.os.Trace;
Winsonfe67aba2015-09-22 14:04:46 -070032import android.os.UserHandle;
Jason Monk421a9412017-02-06 09:15:21 -080033import android.util.ArraySet;
Jorim Jaggicff0acb2014-03-31 16:35:15 +020034import android.util.Log;
Beverly70dcd002018-03-29 17:09:16 -040035import android.util.TimingsTraceLog;
Jorim Jaggicff0acb2014-03-31 16:35:15 +020036
Jason Monk86bc3312016-08-16 13:17:56 -040037import com.android.systemui.plugins.OverlayPlugin;
38import com.android.systemui.plugins.PluginListener;
Tony Wickham023cb192018-09-13 15:23:04 -070039import com.android.systemui.shared.plugins.PluginManager;
Beverly1488f692019-06-07 09:32:46 -040040import com.android.systemui.statusbar.phone.DozeParameters;
Jason Monk2a6ea9c2017-01-26 11:14:51 -050041import com.android.systemui.statusbar.phone.StatusBar;
Lucas Dupin1a8588d2018-08-21 12:18:47 -070042import com.android.systemui.statusbar.phone.StatusBarWindowController;
Dan Sandler8e032e12017-01-25 13:41:38 -050043import com.android.systemui.util.NotificationChannels;
Jorim Jaggi61f39a72015-10-29 16:54:18 +010044
Jorim Jaggicff0acb2014-03-31 16:35:15 +020045import java.util.HashMap;
46import java.util.Map;
47
48/**
49 * Application class for SystemUI.
50 */
Jason Monk49fa0162017-01-11 09:21:56 -050051public class SystemUIApplication extends Application implements SysUiServiceProvider {
Jorim Jaggicff0acb2014-03-31 16:35:15 +020052
Dan Sandlerdc2ddd72018-01-24 16:09:53 -050053 public static final String TAG = "SystemUIService";
Jorim Jaggicff0acb2014-03-31 16:35:15 +020054 private static final boolean DEBUG = false;
55
56 /**
Jorim Jaggicff0acb2014-03-31 16:35:15 +020057 * Hold a reference on the stuff we start.
58 */
yoshiki iguchi61b37082017-11-29 16:46:32 +090059 private SystemUI[] mServices;
Jorim Jaggi3beffdf2014-04-03 17:37:37 +020060 private boolean mServicesStarted;
Dan Sandlerdc5f16b2014-04-22 11:51:42 -040061 private boolean mBootCompleted;
Winsonfe67aba2015-09-22 14:04:46 -070062 private final Map<Class<?>, Object> mComponents = new HashMap<>();
Dave Mankoff94b82ab2019-07-03 13:26:55 -040063 private ContextAvailableCallback mContextAvailableCallback;
Jorim Jaggicff0acb2014-03-31 16:35:15 +020064
Dave Mankoff037d9fc2019-08-09 11:08:52 -040065 public SystemUIApplication() {
66 super();
67 Log.v(TAG, "SystemUIApplication constructed.");
68 }
69
Adrian Roos070a0b62014-04-10 23:25:03 +020070 @Override
71 public void onCreate() {
72 super.onCreate();
Dave Mankoff037d9fc2019-08-09 11:08:52 -040073 Log.v(TAG, "SystemUIApplication created.");
Dave Mankoff94b82ab2019-07-03 13:26:55 -040074 // This line is used to setup Dagger's dependency injection and should be kept at the
75 // top of this method.
Dave Mankoff037d9fc2019-08-09 11:08:52 -040076 TimingsTraceLog log = new TimingsTraceLog("SystemUIBootTiming",
77 Trace.TRACE_TAG_APP);
78 log.traceBegin("DependencyInjection");
Dave Mankoff94b82ab2019-07-03 13:26:55 -040079 mContextAvailableCallback.onContextAvailable(this);
Dave Mankoff037d9fc2019-08-09 11:08:52 -040080 log.traceEnd();
Dave Mankoff94b82ab2019-07-03 13:26:55 -040081
Adrian Roos070a0b62014-04-10 23:25:03 +020082 // 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.
Lucas Dupine17ce522017-07-17 15:45:06 -070085 setTheme(R.style.Theme_SystemUI);
Dan Sandlerdc5f16b2014-04-22 11:51:42 -040086
Winsonfe67aba2015-09-22 14:04:46 -070087 if (Process.myUserHandle().equals(UserHandle.SYSTEM)) {
Beverly6ea063e2018-04-19 10:27:06 -040088 IntentFilter bootCompletedFilter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
89 bootCompletedFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
Winsonfe67aba2015-09-22 14:04:46 -070090 registerReceiver(new BroadcastReceiver() {
91 @Override
92 public void onReceive(Context context, Intent intent) {
93 if (mBootCompleted) return;
Dan Sandlerdc5f16b2014-04-22 11:51:42 -040094
Winsonfe67aba2015-09-22 14:04:46 -070095 if (DEBUG) Log.v(TAG, "BOOT_COMPLETED received");
96 unregisterReceiver(this);
97 mBootCompleted = true;
98 if (mServicesStarted) {
99 final int N = mServices.length;
100 for (int i = 0; i < N; i++) {
101 mServices[i].onBootCompleted();
102 }
Dan Sandlerdc5f16b2014-04-22 11:51:42 -0400103 }
Beverly70dcd002018-03-29 17:09:16 -0400104
Beverly6ea063e2018-04-19 10:27:06 -0400105
Dan Sandlerdc5f16b2014-04-22 11:51:42 -0400106 }
Beverly6ea063e2018-04-19 10:27:06 -0400107 }, bootCompletedFilter);
108
109 IntentFilter localeChangedFilter = new IntentFilter(Intent.ACTION_LOCALE_CHANGED);
110 registerReceiver(new BroadcastReceiver() {
111 @Override
112 public void onReceive(Context context, Intent intent) {
113 if (Intent.ACTION_LOCALE_CHANGED.equals(intent.getAction())) {
114 if (!mBootCompleted) return;
115 // Update names of SystemUi notification channels
116 NotificationChannels.createAll(context);
117 }
118 }
119 }, localeChangedFilter);
Winsonfe67aba2015-09-22 14:04:46 -0700120 } else {
dooyoung.hwangb6479842016-08-31 14:15:22 +0900121 // We don't need to startServices for sub-process that is doing some tasks.
122 // (screenshots, sweetsweetdesserts or tuner ..)
123 String processName = ActivityThread.currentProcessName();
124 ApplicationInfo info = getApplicationInfo();
125 if (processName != null && processName.startsWith(info.processName + ":")) {
126 return;
127 }
Winsonfe67aba2015-09-22 14:04:46 -0700128 // For a secondary user, boot-completed will never be called because it has already
129 // been broadcasted on startup for the primary SystemUI process. Instead, for
130 // components which require the SystemUI component to be initialized per-user, we
131 // start those components now for the current non-system user.
yoshiki iguchi61b37082017-11-29 16:46:32 +0900132 startSecondaryUserServicesIfNeeded();
Winsonfe67aba2015-09-22 14:04:46 -0700133 }
Adrian Roos070a0b62014-04-10 23:25:03 +0200134 }
135
Jorim Jaggi3beffdf2014-04-03 17:37:37 +0200136 /**
137 * Makes sure that all the SystemUI services are running. If they are already running, this is a
138 * no-op. This is needed to conditinally start all the services, as we only need to have it in
139 * the main process.
Jorim Jaggi3beffdf2014-04-03 17:37:37 +0200140 * <p>This method must only be called from the main thread.</p>
141 */
Jason Monkbe3235a2017-04-05 09:29:53 -0400142
Jorim Jaggi3beffdf2014-04-03 17:37:37 +0200143 public void startServicesIfNeeded() {
yoshiki iguchi61b37082017-11-29 16:46:32 +0900144 String[] names = getResources().getStringArray(R.array.config_systemUIServiceComponents);
145 startServicesIfNeeded(names);
Winsonfe67aba2015-09-22 14:04:46 -0700146 }
147
Winson3c2c34b2016-04-04 17:47:41 -0700148 /**
149 * Ensures that all the Secondary user SystemUI services are running. If they are already
Dave Mankoff037d9fc2019-08-09 11:08:52 -0400150 * running, this is a no-op. This is needed to conditionally start all the services, as we only
Winson3c2c34b2016-04-04 17:47:41 -0700151 * need to have it in the main process.
Winson3c2c34b2016-04-04 17:47:41 -0700152 * <p>This method must only be called from the main thread.</p>
153 */
154 void startSecondaryUserServicesIfNeeded() {
yoshiki iguchi61b37082017-11-29 16:46:32 +0900155 String[] names =
156 getResources().getStringArray(R.array.config_systemUIServiceComponentsPerUser);
157 startServicesIfNeeded(names);
Winson3c2c34b2016-04-04 17:47:41 -0700158 }
159
yoshiki iguchi61b37082017-11-29 16:46:32 +0900160 private void startServicesIfNeeded(String[] services) {
Jorim Jaggi3beffdf2014-04-03 17:37:37 +0200161 if (mServicesStarted) {
162 return;
163 }
yoshiki iguchi61b37082017-11-29 16:46:32 +0900164 mServices = new SystemUI[services.length];
Dan Sandlerdc5f16b2014-04-22 11:51:42 -0400165
166 if (!mBootCompleted) {
167 // check to see if maybe it was already completed long before we began
168 // see ActivityManagerService.finishBooting()
169 if ("1".equals(SystemProperties.get("sys.boot_completed"))) {
170 mBootCompleted = true;
Dave Mankoff037d9fc2019-08-09 11:08:52 -0400171 if (DEBUG) {
172 Log.v(TAG, "BOOT_COMPLETED was already sent");
173 }
Dan Sandlerdc5f16b2014-04-22 11:51:42 -0400174 }
175 }
176
Winsonfe67aba2015-09-22 14:04:46 -0700177 Log.v(TAG, "Starting SystemUI services for user " +
178 Process.myUserHandle().getIdentifier() + ".");
Fyodor Kupolov6e3461b2017-08-10 17:00:43 -0700179 TimingsTraceLog log = new TimingsTraceLog("SystemUIBootTiming",
Fyodor Kupolovf3591352017-06-23 18:20:40 -0700180 Trace.TRACE_TAG_APP);
181 log.traceBegin("StartServices");
Winsonfe67aba2015-09-22 14:04:46 -0700182 final int N = services.length;
Jason Monk421a9412017-02-06 09:15:21 -0800183 for (int i = 0; i < N; i++) {
yoshiki iguchi61b37082017-11-29 16:46:32 +0900184 String clsName = services[i];
185 if (DEBUG) Log.d(TAG, "loading: " + clsName);
186 log.traceBegin("StartServices" + clsName);
Fyodor Kupolovf3591352017-06-23 18:20:40 -0700187 long ti = System.currentTimeMillis();
yoshiki iguchi61b37082017-11-29 16:46:32 +0900188 Class cls;
Jorim Jaggicff0acb2014-03-31 16:35:15 +0200189 try {
yoshiki iguchi61b37082017-11-29 16:46:32 +0900190 cls = Class.forName(clsName);
Jason Monk27d01a622018-12-10 15:57:09 -0500191 Object o = cls.newInstance();
192 if (o instanceof SystemUI.Injector) {
193 o = ((SystemUI.Injector) o).apply(this);
194 }
195 mServices[i] = (SystemUI) o;
yoshiki iguchi61b37082017-11-29 16:46:32 +0900196 } catch(ClassNotFoundException ex){
197 throw new RuntimeException(ex);
Jorim Jaggicff0acb2014-03-31 16:35:15 +0200198 } catch (IllegalAccessException ex) {
199 throw new RuntimeException(ex);
200 } catch (InstantiationException ex) {
201 throw new RuntimeException(ex);
202 }
Muyuan Li94ce94e2016-02-24 16:20:54 -0800203
Jorim Jaggicff0acb2014-03-31 16:35:15 +0200204 mServices[i].mContext = this;
205 mServices[i].mComponents = mComponents;
206 if (DEBUG) Log.d(TAG, "running: " + mServices[i]);
207 mServices[i].start();
Fyodor Kupolovf3591352017-06-23 18:20:40 -0700208 log.traceEnd();
Dan Sandlerdc5f16b2014-04-22 11:51:42 -0400209
Fyodor Kupolovf3591352017-06-23 18:20:40 -0700210 // Warn if initialization of component takes too long
211 ti = System.currentTimeMillis() - ti;
212 if (ti > 1000) {
yoshiki iguchi61b37082017-11-29 16:46:32 +0900213 Log.w(TAG, "Initialization of " + cls.getName() + " took " + ti + " ms");
Fyodor Kupolovf3591352017-06-23 18:20:40 -0700214 }
Dan Sandlerdc5f16b2014-04-22 11:51:42 -0400215 if (mBootCompleted) {
216 mServices[i].onBootCompleted();
217 }
Jorim Jaggicff0acb2014-03-31 16:35:15 +0200218 }
Jason Monk297c04e2018-08-23 17:16:59 -0400219 Dependency.get(InitController.class).executePostInitTasks();
Fyodor Kupolovf3591352017-06-23 18:20:40 -0700220 log.traceEnd();
Beverly97b8ce12019-02-01 16:55:48 -0500221 final Handler mainHandler = new Handler(Looper.getMainLooper());
Jason Monk5bec68f2017-02-08 20:45:10 -0800222 Dependency.get(PluginManager.class).addPluginListener(
Jason Monk86bc3312016-08-16 13:17:56 -0400223 new PluginListener<OverlayPlugin>() {
beverlytf9dfd232019-05-06 12:14:22 -0700224 private ArraySet<OverlayPlugin> mOverlays = new ArraySet<>();
Jason Monk421a9412017-02-06 09:15:21 -0800225
226 @Override
227 public void onPluginConnected(OverlayPlugin plugin, Context pluginContext) {
Beverly97b8ce12019-02-01 16:55:48 -0500228 mainHandler.post(new Runnable() {
229 @Override
230 public void run() {
231 StatusBar statusBar = getComponent(StatusBar.class);
232 if (statusBar != null) {
233 plugin.setup(statusBar.getStatusBarWindow(),
Beverly1488f692019-06-07 09:32:46 -0400234 statusBar.getNavigationBarView(), new Callback(plugin),
235 DozeParameters.getInstance(getBaseContext()));
Beverly97b8ce12019-02-01 16:55:48 -0500236 }
237 }
238 });
Jason Monk421a9412017-02-06 09:15:21 -0800239 }
240
241 @Override
242 public void onPluginDisconnected(OverlayPlugin plugin) {
Beverly97b8ce12019-02-01 16:55:48 -0500243 mainHandler.post(new Runnable() {
244 @Override
245 public void run() {
246 mOverlays.remove(plugin);
247 Dependency.get(StatusBarWindowController.class).setForcePluginOpen(
248 mOverlays.size() != 0);
249 }
250 });
Jason Monk421a9412017-02-06 09:15:21 -0800251 }
beverlytf9dfd232019-05-06 12:14:22 -0700252
253 class Callback implements OverlayPlugin.Callback {
254 private final OverlayPlugin mPlugin;
255
256 Callback(OverlayPlugin plugin) {
257 mPlugin = plugin;
258 }
259
260 @Override
261 public void onHoldStatusBarOpenChange() {
262 if (mPlugin.holdStatusBarOpen()) {
263 mOverlays.add(mPlugin);
264 } else {
265 mOverlays.remove(mPlugin);
266 }
267 mainHandler.post(new Runnable() {
268 @Override
269 public void run() {
270 Dependency.get(StatusBarWindowController.class)
271 .setStateListener(b -> mOverlays.forEach(
272 o -> o.setCollapseDesired(b)));
273 Dependency.get(StatusBarWindowController.class)
274 .setForcePluginOpen(mOverlays.size() != 0);
275 }
276 });
277 }
278 }
Jason Monk5bec68f2017-02-08 20:45:10 -0800279 }, OverlayPlugin.class, true /* Allow multiple plugins */);
Jason Monk86bc3312016-08-16 13:17:56 -0400280
Jorim Jaggi3beffdf2014-04-03 17:37:37 +0200281 mServicesStarted = true;
Jorim Jaggicff0acb2014-03-31 16:35:15 +0200282 }
283
284 @Override
285 public void onConfigurationChanged(Configuration newConfig) {
Jorim Jaggi3beffdf2014-04-03 17:37:37 +0200286 if (mServicesStarted) {
Dave Mankoff037d9fc2019-08-09 11:08:52 -0400287 Dependency.staticOnConfigurationChanged(newConfig);
Jorim Jaggi3beffdf2014-04-03 17:37:37 +0200288 int len = mServices.length;
289 for (int i = 0; i < len; i++) {
Winson94a14852015-09-23 12:44:33 -0700290 if (mServices[i] != null) {
291 mServices[i].onConfigurationChanged(newConfig);
292 }
Jorim Jaggi3beffdf2014-04-03 17:37:37 +0200293 }
Jorim Jaggicff0acb2014-03-31 16:35:15 +0200294 }
295 }
296
297 @SuppressWarnings("unchecked")
298 public <T> T getComponent(Class<T> interfaceType) {
299 return (T) mComponents.get(interfaceType);
300 }
301
302 public SystemUI[] getServices() {
303 return mServices;
304 }
Dave Mankoff94b82ab2019-07-03 13:26:55 -0400305
306 void setContextAvailableCallback(ContextAvailableCallback callback) {
307 mContextAvailableCallback = callback;
308 }
309
310 interface ContextAvailableCallback {
311 void onContextAvailable(Context context);
312 }
Jorim Jaggicff0acb2014-03-31 16:35:15 +0200313}