blob: 61a0f72315ea734b1509a6159421b31c26df8a9e [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;
Jason Monk2a6ea9c2017-01-26 11:14:51 -050040import com.android.systemui.statusbar.phone.StatusBar;
Lucas Dupin1a8588d2018-08-21 12:18:47 -070041import com.android.systemui.statusbar.phone.StatusBarWindowController;
Dan Sandler8e032e12017-01-25 13:41:38 -050042import com.android.systemui.util.NotificationChannels;
Jorim Jaggi61f39a72015-10-29 16:54:18 +010043
Jorim Jaggicff0acb2014-03-31 16:35:15 +020044import java.util.HashMap;
45import java.util.Map;
46
47/**
48 * Application class for SystemUI.
49 */
Jason Monk49fa0162017-01-11 09:21:56 -050050public class SystemUIApplication extends Application implements SysUiServiceProvider {
Jorim Jaggicff0acb2014-03-31 16:35:15 +020051
Dan Sandlerdc2ddd72018-01-24 16:09:53 -050052 public static final String TAG = "SystemUIService";
Jorim Jaggicff0acb2014-03-31 16:35:15 +020053 private static final boolean DEBUG = false;
54
55 /**
Jorim Jaggicff0acb2014-03-31 16:35:15 +020056 * Hold a reference on the stuff we start.
57 */
yoshiki iguchi61b37082017-11-29 16:46:32 +090058 private SystemUI[] mServices;
Jorim Jaggi3beffdf2014-04-03 17:37:37 +020059 private boolean mServicesStarted;
Dan Sandlerdc5f16b2014-04-22 11:51:42 -040060 private boolean mBootCompleted;
Winsonfe67aba2015-09-22 14:04:46 -070061 private final Map<Class<?>, Object> mComponents = new HashMap<>();
Jorim Jaggicff0acb2014-03-31 16:35:15 +020062
Adrian Roos070a0b62014-04-10 23:25:03 +020063 @Override
64 public void onCreate() {
65 super.onCreate();
66 // Set the application theme that is inherited by all services. Note that setting the
67 // application theme in the manifest does only work for activities. Keep this in sync with
68 // the theme set there.
Lucas Dupine17ce522017-07-17 15:45:06 -070069 setTheme(R.style.Theme_SystemUI);
Dan Sandlerdc5f16b2014-04-22 11:51:42 -040070
Xiyuan Xia1b30f792016-01-06 08:50:30 -080071 SystemUIFactory.createFromConfig(this);
72
Winsonfe67aba2015-09-22 14:04:46 -070073 if (Process.myUserHandle().equals(UserHandle.SYSTEM)) {
Beverly6ea063e2018-04-19 10:27:06 -040074 IntentFilter bootCompletedFilter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
75 bootCompletedFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
Winsonfe67aba2015-09-22 14:04:46 -070076 registerReceiver(new BroadcastReceiver() {
77 @Override
78 public void onReceive(Context context, Intent intent) {
79 if (mBootCompleted) return;
Dan Sandlerdc5f16b2014-04-22 11:51:42 -040080
Winsonfe67aba2015-09-22 14:04:46 -070081 if (DEBUG) Log.v(TAG, "BOOT_COMPLETED received");
82 unregisterReceiver(this);
83 mBootCompleted = true;
84 if (mServicesStarted) {
85 final int N = mServices.length;
86 for (int i = 0; i < N; i++) {
87 mServices[i].onBootCompleted();
88 }
Dan Sandlerdc5f16b2014-04-22 11:51:42 -040089 }
Beverly70dcd002018-03-29 17:09:16 -040090
Beverly6ea063e2018-04-19 10:27:06 -040091
Dan Sandlerdc5f16b2014-04-22 11:51:42 -040092 }
Beverly6ea063e2018-04-19 10:27:06 -040093 }, bootCompletedFilter);
94
95 IntentFilter localeChangedFilter = new IntentFilter(Intent.ACTION_LOCALE_CHANGED);
96 registerReceiver(new BroadcastReceiver() {
97 @Override
98 public void onReceive(Context context, Intent intent) {
99 if (Intent.ACTION_LOCALE_CHANGED.equals(intent.getAction())) {
100 if (!mBootCompleted) return;
101 // Update names of SystemUi notification channels
102 NotificationChannels.createAll(context);
103 }
104 }
105 }, localeChangedFilter);
Winsonfe67aba2015-09-22 14:04:46 -0700106 } else {
dooyoung.hwangb6479842016-08-31 14:15:22 +0900107 // We don't need to startServices for sub-process that is doing some tasks.
108 // (screenshots, sweetsweetdesserts or tuner ..)
109 String processName = ActivityThread.currentProcessName();
110 ApplicationInfo info = getApplicationInfo();
111 if (processName != null && processName.startsWith(info.processName + ":")) {
112 return;
113 }
Winsonfe67aba2015-09-22 14:04:46 -0700114 // For a secondary user, boot-completed will never be called because it has already
115 // been broadcasted on startup for the primary SystemUI process. Instead, for
116 // components which require the SystemUI component to be initialized per-user, we
117 // start those components now for the current non-system user.
yoshiki iguchi61b37082017-11-29 16:46:32 +0900118 startSecondaryUserServicesIfNeeded();
Winsonfe67aba2015-09-22 14:04:46 -0700119 }
Adrian Roos070a0b62014-04-10 23:25:03 +0200120 }
121
Jorim Jaggi3beffdf2014-04-03 17:37:37 +0200122 /**
123 * Makes sure that all the SystemUI services are running. If they are already running, this is a
124 * no-op. This is needed to conditinally start all the services, as we only need to have it in
125 * the main process.
Jorim Jaggi3beffdf2014-04-03 17:37:37 +0200126 * <p>This method must only be called from the main thread.</p>
127 */
Jason Monkbe3235a2017-04-05 09:29:53 -0400128
Jorim Jaggi3beffdf2014-04-03 17:37:37 +0200129 public void startServicesIfNeeded() {
yoshiki iguchi61b37082017-11-29 16:46:32 +0900130 String[] names = getResources().getStringArray(R.array.config_systemUIServiceComponents);
131 startServicesIfNeeded(names);
Winsonfe67aba2015-09-22 14:04:46 -0700132 }
133
Winson3c2c34b2016-04-04 17:47:41 -0700134 /**
135 * Ensures that all the Secondary user SystemUI services are running. If they are already
136 * running, this is a no-op. This is needed to conditinally start all the services, as we only
137 * need to have it in the main process.
Winson3c2c34b2016-04-04 17:47:41 -0700138 * <p>This method must only be called from the main thread.</p>
139 */
140 void startSecondaryUserServicesIfNeeded() {
yoshiki iguchi61b37082017-11-29 16:46:32 +0900141 String[] names =
142 getResources().getStringArray(R.array.config_systemUIServiceComponentsPerUser);
143 startServicesIfNeeded(names);
Winson3c2c34b2016-04-04 17:47:41 -0700144 }
145
yoshiki iguchi61b37082017-11-29 16:46:32 +0900146 private void startServicesIfNeeded(String[] services) {
Jorim Jaggi3beffdf2014-04-03 17:37:37 +0200147 if (mServicesStarted) {
148 return;
149 }
yoshiki iguchi61b37082017-11-29 16:46:32 +0900150 mServices = new SystemUI[services.length];
Dan Sandlerdc5f16b2014-04-22 11:51:42 -0400151
152 if (!mBootCompleted) {
153 // check to see if maybe it was already completed long before we began
154 // see ActivityManagerService.finishBooting()
155 if ("1".equals(SystemProperties.get("sys.boot_completed"))) {
156 mBootCompleted = true;
157 if (DEBUG) Log.v(TAG, "BOOT_COMPLETED was already sent");
158 }
159 }
160
Winsonfe67aba2015-09-22 14:04:46 -0700161 Log.v(TAG, "Starting SystemUI services for user " +
162 Process.myUserHandle().getIdentifier() + ".");
Fyodor Kupolov6e3461b2017-08-10 17:00:43 -0700163 TimingsTraceLog log = new TimingsTraceLog("SystemUIBootTiming",
Fyodor Kupolovf3591352017-06-23 18:20:40 -0700164 Trace.TRACE_TAG_APP);
165 log.traceBegin("StartServices");
Winsonfe67aba2015-09-22 14:04:46 -0700166 final int N = services.length;
Jason Monk421a9412017-02-06 09:15:21 -0800167 for (int i = 0; i < N; i++) {
yoshiki iguchi61b37082017-11-29 16:46:32 +0900168 String clsName = services[i];
169 if (DEBUG) Log.d(TAG, "loading: " + clsName);
170 log.traceBegin("StartServices" + clsName);
Fyodor Kupolovf3591352017-06-23 18:20:40 -0700171 long ti = System.currentTimeMillis();
yoshiki iguchi61b37082017-11-29 16:46:32 +0900172 Class cls;
Jorim Jaggicff0acb2014-03-31 16:35:15 +0200173 try {
yoshiki iguchi61b37082017-11-29 16:46:32 +0900174 cls = Class.forName(clsName);
Jason Monk27d01a622018-12-10 15:57:09 -0500175 Object o = cls.newInstance();
176 if (o instanceof SystemUI.Injector) {
177 o = ((SystemUI.Injector) o).apply(this);
178 }
179 mServices[i] = (SystemUI) o;
yoshiki iguchi61b37082017-11-29 16:46:32 +0900180 } catch(ClassNotFoundException ex){
181 throw new RuntimeException(ex);
Jorim Jaggicff0acb2014-03-31 16:35:15 +0200182 } catch (IllegalAccessException ex) {
183 throw new RuntimeException(ex);
184 } catch (InstantiationException ex) {
185 throw new RuntimeException(ex);
186 }
Muyuan Li94ce94e2016-02-24 16:20:54 -0800187
Jorim Jaggicff0acb2014-03-31 16:35:15 +0200188 mServices[i].mContext = this;
189 mServices[i].mComponents = mComponents;
190 if (DEBUG) Log.d(TAG, "running: " + mServices[i]);
191 mServices[i].start();
Fyodor Kupolovf3591352017-06-23 18:20:40 -0700192 log.traceEnd();
Dan Sandlerdc5f16b2014-04-22 11:51:42 -0400193
Fyodor Kupolovf3591352017-06-23 18:20:40 -0700194 // Warn if initialization of component takes too long
195 ti = System.currentTimeMillis() - ti;
196 if (ti > 1000) {
yoshiki iguchi61b37082017-11-29 16:46:32 +0900197 Log.w(TAG, "Initialization of " + cls.getName() + " took " + ti + " ms");
Fyodor Kupolovf3591352017-06-23 18:20:40 -0700198 }
Dan Sandlerdc5f16b2014-04-22 11:51:42 -0400199 if (mBootCompleted) {
200 mServices[i].onBootCompleted();
201 }
Jorim Jaggicff0acb2014-03-31 16:35:15 +0200202 }
Jason Monk297c04e2018-08-23 17:16:59 -0400203 Dependency.get(InitController.class).executePostInitTasks();
Fyodor Kupolovf3591352017-06-23 18:20:40 -0700204 log.traceEnd();
Beverly97b8ce12019-02-01 16:55:48 -0500205 final Handler mainHandler = new Handler(Looper.getMainLooper());
Jason Monk5bec68f2017-02-08 20:45:10 -0800206 Dependency.get(PluginManager.class).addPluginListener(
Jason Monk86bc3312016-08-16 13:17:56 -0400207 new PluginListener<OverlayPlugin>() {
beverlytf9dfd232019-05-06 12:14:22 -0700208 private ArraySet<OverlayPlugin> mOverlays = new ArraySet<>();
Jason Monk421a9412017-02-06 09:15:21 -0800209
210 @Override
211 public void onPluginConnected(OverlayPlugin plugin, Context pluginContext) {
Beverly97b8ce12019-02-01 16:55:48 -0500212 mainHandler.post(new Runnable() {
213 @Override
214 public void run() {
215 StatusBar statusBar = getComponent(StatusBar.class);
216 if (statusBar != null) {
217 plugin.setup(statusBar.getStatusBarWindow(),
beverlytf9dfd232019-05-06 12:14:22 -0700218 statusBar.getNavigationBarView(), new Callback(plugin));
Beverly97b8ce12019-02-01 16:55:48 -0500219 }
220 }
221 });
Jason Monk421a9412017-02-06 09:15:21 -0800222 }
223
224 @Override
225 public void onPluginDisconnected(OverlayPlugin plugin) {
Beverly97b8ce12019-02-01 16:55:48 -0500226 mainHandler.post(new Runnable() {
227 @Override
228 public void run() {
229 mOverlays.remove(plugin);
230 Dependency.get(StatusBarWindowController.class).setForcePluginOpen(
231 mOverlays.size() != 0);
232 }
233 });
Jason Monk421a9412017-02-06 09:15:21 -0800234 }
beverlytf9dfd232019-05-06 12:14:22 -0700235
236 class Callback implements OverlayPlugin.Callback {
237 private final OverlayPlugin mPlugin;
238
239 Callback(OverlayPlugin plugin) {
240 mPlugin = plugin;
241 }
242
243 @Override
244 public void onHoldStatusBarOpenChange() {
245 if (mPlugin.holdStatusBarOpen()) {
246 mOverlays.add(mPlugin);
247 } else {
248 mOverlays.remove(mPlugin);
249 }
250 mainHandler.post(new Runnable() {
251 @Override
252 public void run() {
253 Dependency.get(StatusBarWindowController.class)
254 .setStateListener(b -> mOverlays.forEach(
255 o -> o.setCollapseDesired(b)));
256 Dependency.get(StatusBarWindowController.class)
257 .setForcePluginOpen(mOverlays.size() != 0);
258 }
259 });
260 }
261 }
Jason Monk5bec68f2017-02-08 20:45:10 -0800262 }, OverlayPlugin.class, true /* Allow multiple plugins */);
Jason Monk86bc3312016-08-16 13:17:56 -0400263
Jorim Jaggi3beffdf2014-04-03 17:37:37 +0200264 mServicesStarted = true;
Jorim Jaggicff0acb2014-03-31 16:35:15 +0200265 }
266
267 @Override
268 public void onConfigurationChanged(Configuration newConfig) {
Jorim Jaggi3beffdf2014-04-03 17:37:37 +0200269 if (mServicesStarted) {
270 int len = mServices.length;
271 for (int i = 0; i < len; i++) {
Winson94a14852015-09-23 12:44:33 -0700272 if (mServices[i] != null) {
273 mServices[i].onConfigurationChanged(newConfig);
274 }
Jorim Jaggi3beffdf2014-04-03 17:37:37 +0200275 }
Jorim Jaggicff0acb2014-03-31 16:35:15 +0200276 }
277 }
278
279 @SuppressWarnings("unchecked")
280 public <T> T getComponent(Class<T> interfaceType) {
281 return (T) mComponents.get(interfaceType);
282 }
283
284 public SystemUI[] getServices() {
285 return mServices;
286 }
Jorim Jaggicff0acb2014-03-31 16:35:15 +0200287}