blob: 0b1fe6994c3decbafb75dc73fb5a63da01cad349 [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 Mankoff2ea5a832019-07-03 13:26:55 -040063 private ContextAvailableCallback mContextAvailableCallback;
Jorim Jaggicff0acb2014-03-31 16:35:15 +020064
Adrian Roos070a0b62014-04-10 23:25:03 +020065 @Override
66 public void onCreate() {
67 super.onCreate();
Dave Mankoff2ea5a832019-07-03 13:26:55 -040068 // This line is used to setup Dagger's dependency injection and should be kept at the
69 // top of this method.
70 mContextAvailableCallback.onContextAvailable(this);
71
Adrian Roos070a0b62014-04-10 23:25:03 +020072 // Set the application theme that is inherited by all services. Note that setting the
73 // application theme in the manifest does only work for activities. Keep this in sync with
74 // the theme set there.
Lucas Dupine17ce522017-07-17 15:45:06 -070075 setTheme(R.style.Theme_SystemUI);
Dan Sandlerdc5f16b2014-04-22 11:51:42 -040076
Winsonfe67aba2015-09-22 14:04:46 -070077 if (Process.myUserHandle().equals(UserHandle.SYSTEM)) {
Beverly6ea063e2018-04-19 10:27:06 -040078 IntentFilter bootCompletedFilter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
79 bootCompletedFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
Winsonfe67aba2015-09-22 14:04:46 -070080 registerReceiver(new BroadcastReceiver() {
81 @Override
82 public void onReceive(Context context, Intent intent) {
83 if (mBootCompleted) return;
Dan Sandlerdc5f16b2014-04-22 11:51:42 -040084
Winsonfe67aba2015-09-22 14:04:46 -070085 if (DEBUG) Log.v(TAG, "BOOT_COMPLETED received");
86 unregisterReceiver(this);
87 mBootCompleted = true;
88 if (mServicesStarted) {
89 final int N = mServices.length;
90 for (int i = 0; i < N; i++) {
91 mServices[i].onBootCompleted();
92 }
Dan Sandlerdc5f16b2014-04-22 11:51:42 -040093 }
Beverly70dcd002018-03-29 17:09:16 -040094
Beverly6ea063e2018-04-19 10:27:06 -040095
Dan Sandlerdc5f16b2014-04-22 11:51:42 -040096 }
Beverly6ea063e2018-04-19 10:27:06 -040097 }, bootCompletedFilter);
98
99 IntentFilter localeChangedFilter = new IntentFilter(Intent.ACTION_LOCALE_CHANGED);
100 registerReceiver(new BroadcastReceiver() {
101 @Override
102 public void onReceive(Context context, Intent intent) {
103 if (Intent.ACTION_LOCALE_CHANGED.equals(intent.getAction())) {
104 if (!mBootCompleted) return;
105 // Update names of SystemUi notification channels
106 NotificationChannels.createAll(context);
107 }
108 }
109 }, localeChangedFilter);
Winsonfe67aba2015-09-22 14:04:46 -0700110 } else {
dooyoung.hwangb6479842016-08-31 14:15:22 +0900111 // We don't need to startServices for sub-process that is doing some tasks.
112 // (screenshots, sweetsweetdesserts or tuner ..)
113 String processName = ActivityThread.currentProcessName();
114 ApplicationInfo info = getApplicationInfo();
115 if (processName != null && processName.startsWith(info.processName + ":")) {
116 return;
117 }
Winsonfe67aba2015-09-22 14:04:46 -0700118 // For a secondary user, boot-completed will never be called because it has already
119 // been broadcasted on startup for the primary SystemUI process. Instead, for
120 // components which require the SystemUI component to be initialized per-user, we
121 // start those components now for the current non-system user.
yoshiki iguchi61b37082017-11-29 16:46:32 +0900122 startSecondaryUserServicesIfNeeded();
Winsonfe67aba2015-09-22 14:04:46 -0700123 }
Adrian Roos070a0b62014-04-10 23:25:03 +0200124 }
125
Jorim Jaggi3beffdf2014-04-03 17:37:37 +0200126 /**
127 * Makes sure that all the SystemUI services are running. If they are already running, this is a
128 * no-op. This is needed to conditinally start all the services, as we only need to have it in
129 * the main process.
Jorim Jaggi3beffdf2014-04-03 17:37:37 +0200130 * <p>This method must only be called from the main thread.</p>
131 */
Jason Monkbe3235a2017-04-05 09:29:53 -0400132
Jorim Jaggi3beffdf2014-04-03 17:37:37 +0200133 public void startServicesIfNeeded() {
yoshiki iguchi61b37082017-11-29 16:46:32 +0900134 String[] names = getResources().getStringArray(R.array.config_systemUIServiceComponents);
135 startServicesIfNeeded(names);
Winsonfe67aba2015-09-22 14:04:46 -0700136 }
137
Winson3c2c34b2016-04-04 17:47:41 -0700138 /**
139 * Ensures that all the Secondary user SystemUI services are running. If they are already
140 * running, this is a no-op. This is needed to conditinally start all the services, as we only
141 * need to have it in the main process.
Winson3c2c34b2016-04-04 17:47:41 -0700142 * <p>This method must only be called from the main thread.</p>
143 */
144 void startSecondaryUserServicesIfNeeded() {
yoshiki iguchi61b37082017-11-29 16:46:32 +0900145 String[] names =
146 getResources().getStringArray(R.array.config_systemUIServiceComponentsPerUser);
147 startServicesIfNeeded(names);
Winson3c2c34b2016-04-04 17:47:41 -0700148 }
149
yoshiki iguchi61b37082017-11-29 16:46:32 +0900150 private void startServicesIfNeeded(String[] services) {
Jorim Jaggi3beffdf2014-04-03 17:37:37 +0200151 if (mServicesStarted) {
152 return;
153 }
yoshiki iguchi61b37082017-11-29 16:46:32 +0900154 mServices = new SystemUI[services.length];
Dan Sandlerdc5f16b2014-04-22 11:51:42 -0400155
156 if (!mBootCompleted) {
157 // check to see if maybe it was already completed long before we began
158 // see ActivityManagerService.finishBooting()
159 if ("1".equals(SystemProperties.get("sys.boot_completed"))) {
160 mBootCompleted = true;
161 if (DEBUG) Log.v(TAG, "BOOT_COMPLETED was already sent");
162 }
163 }
164
Winsonfe67aba2015-09-22 14:04:46 -0700165 Log.v(TAG, "Starting SystemUI services for user " +
166 Process.myUserHandle().getIdentifier() + ".");
Fyodor Kupolov6e3461b2017-08-10 17:00:43 -0700167 TimingsTraceLog log = new TimingsTraceLog("SystemUIBootTiming",
Fyodor Kupolovf3591352017-06-23 18:20:40 -0700168 Trace.TRACE_TAG_APP);
169 log.traceBegin("StartServices");
Winsonfe67aba2015-09-22 14:04:46 -0700170 final int N = services.length;
Jason Monk421a9412017-02-06 09:15:21 -0800171 for (int i = 0; i < N; i++) {
yoshiki iguchi61b37082017-11-29 16:46:32 +0900172 String clsName = services[i];
173 if (DEBUG) Log.d(TAG, "loading: " + clsName);
174 log.traceBegin("StartServices" + clsName);
Fyodor Kupolovf3591352017-06-23 18:20:40 -0700175 long ti = System.currentTimeMillis();
yoshiki iguchi61b37082017-11-29 16:46:32 +0900176 Class cls;
Jorim Jaggicff0acb2014-03-31 16:35:15 +0200177 try {
yoshiki iguchi61b37082017-11-29 16:46:32 +0900178 cls = Class.forName(clsName);
Jason Monk27d01a622018-12-10 15:57:09 -0500179 Object o = cls.newInstance();
180 if (o instanceof SystemUI.Injector) {
181 o = ((SystemUI.Injector) o).apply(this);
182 }
183 mServices[i] = (SystemUI) o;
yoshiki iguchi61b37082017-11-29 16:46:32 +0900184 } catch(ClassNotFoundException ex){
185 throw new RuntimeException(ex);
Jorim Jaggicff0acb2014-03-31 16:35:15 +0200186 } catch (IllegalAccessException ex) {
187 throw new RuntimeException(ex);
188 } catch (InstantiationException ex) {
189 throw new RuntimeException(ex);
190 }
Muyuan Li94ce94e2016-02-24 16:20:54 -0800191
Jorim Jaggicff0acb2014-03-31 16:35:15 +0200192 mServices[i].mContext = this;
193 mServices[i].mComponents = mComponents;
194 if (DEBUG) Log.d(TAG, "running: " + mServices[i]);
195 mServices[i].start();
Fyodor Kupolovf3591352017-06-23 18:20:40 -0700196 log.traceEnd();
Dan Sandlerdc5f16b2014-04-22 11:51:42 -0400197
Fyodor Kupolovf3591352017-06-23 18:20:40 -0700198 // Warn if initialization of component takes too long
199 ti = System.currentTimeMillis() - ti;
200 if (ti > 1000) {
yoshiki iguchi61b37082017-11-29 16:46:32 +0900201 Log.w(TAG, "Initialization of " + cls.getName() + " took " + ti + " ms");
Fyodor Kupolovf3591352017-06-23 18:20:40 -0700202 }
Dan Sandlerdc5f16b2014-04-22 11:51:42 -0400203 if (mBootCompleted) {
204 mServices[i].onBootCompleted();
205 }
Jorim Jaggicff0acb2014-03-31 16:35:15 +0200206 }
Jason Monk297c04e2018-08-23 17:16:59 -0400207 Dependency.get(InitController.class).executePostInitTasks();
Fyodor Kupolovf3591352017-06-23 18:20:40 -0700208 log.traceEnd();
Beverly97b8ce12019-02-01 16:55:48 -0500209 final Handler mainHandler = new Handler(Looper.getMainLooper());
Jason Monk5bec68f2017-02-08 20:45:10 -0800210 Dependency.get(PluginManager.class).addPluginListener(
Jason Monk86bc3312016-08-16 13:17:56 -0400211 new PluginListener<OverlayPlugin>() {
beverlytf9dfd232019-05-06 12:14:22 -0700212 private ArraySet<OverlayPlugin> mOverlays = new ArraySet<>();
Jason Monk421a9412017-02-06 09:15:21 -0800213
214 @Override
215 public void onPluginConnected(OverlayPlugin plugin, Context pluginContext) {
Beverly97b8ce12019-02-01 16:55:48 -0500216 mainHandler.post(new Runnable() {
217 @Override
218 public void run() {
219 StatusBar statusBar = getComponent(StatusBar.class);
220 if (statusBar != null) {
221 plugin.setup(statusBar.getStatusBarWindow(),
Beverly1488f692019-06-07 09:32:46 -0400222 statusBar.getNavigationBarView(), new Callback(plugin),
223 DozeParameters.getInstance(getBaseContext()));
Beverly97b8ce12019-02-01 16:55:48 -0500224 }
225 }
226 });
Jason Monk421a9412017-02-06 09:15:21 -0800227 }
228
229 @Override
230 public void onPluginDisconnected(OverlayPlugin plugin) {
Beverly97b8ce12019-02-01 16:55:48 -0500231 mainHandler.post(new Runnable() {
232 @Override
233 public void run() {
234 mOverlays.remove(plugin);
235 Dependency.get(StatusBarWindowController.class).setForcePluginOpen(
236 mOverlays.size() != 0);
237 }
238 });
Jason Monk421a9412017-02-06 09:15:21 -0800239 }
beverlytf9dfd232019-05-06 12:14:22 -0700240
241 class Callback implements OverlayPlugin.Callback {
242 private final OverlayPlugin mPlugin;
243
244 Callback(OverlayPlugin plugin) {
245 mPlugin = plugin;
246 }
247
248 @Override
249 public void onHoldStatusBarOpenChange() {
250 if (mPlugin.holdStatusBarOpen()) {
251 mOverlays.add(mPlugin);
252 } else {
253 mOverlays.remove(mPlugin);
254 }
255 mainHandler.post(new Runnable() {
256 @Override
257 public void run() {
258 Dependency.get(StatusBarWindowController.class)
259 .setStateListener(b -> mOverlays.forEach(
260 o -> o.setCollapseDesired(b)));
261 Dependency.get(StatusBarWindowController.class)
262 .setForcePluginOpen(mOverlays.size() != 0);
263 }
264 });
265 }
266 }
Jason Monk5bec68f2017-02-08 20:45:10 -0800267 }, OverlayPlugin.class, true /* Allow multiple plugins */);
Jason Monk86bc3312016-08-16 13:17:56 -0400268
Jorim Jaggi3beffdf2014-04-03 17:37:37 +0200269 mServicesStarted = true;
Jorim Jaggicff0acb2014-03-31 16:35:15 +0200270 }
271
272 @Override
273 public void onConfigurationChanged(Configuration newConfig) {
Jorim Jaggi3beffdf2014-04-03 17:37:37 +0200274 if (mServicesStarted) {
275 int len = mServices.length;
276 for (int i = 0; i < len; i++) {
Winson94a14852015-09-23 12:44:33 -0700277 if (mServices[i] != null) {
278 mServices[i].onConfigurationChanged(newConfig);
279 }
Jorim Jaggi3beffdf2014-04-03 17:37:37 +0200280 }
Jorim Jaggicff0acb2014-03-31 16:35:15 +0200281 }
282 }
283
284 @SuppressWarnings("unchecked")
285 public <T> T getComponent(Class<T> interfaceType) {
286 return (T) mComponents.get(interfaceType);
287 }
288
289 public SystemUI[] getServices() {
290 return mServices;
291 }
Dave Mankoff2ea5a832019-07-03 13:26:55 -0400292
293 void setContextAvailableCallback(ContextAvailableCallback callback) {
294 mContextAvailableCallback = callback;
295 }
296
297 interface ContextAvailableCallback {
298 void onContextAvailable(Context context);
299 }
Jorim Jaggicff0acb2014-03-31 16:35:15 +0200300}