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.Handler; 28 import android.os.Looper; 29 import android.os.Process; 30 import android.os.SystemProperties; 31 import android.os.Trace; 32 import android.os.UserHandle; 33 import android.util.ArraySet; 34 import android.util.Log; 35 import android.util.TimingsTraceLog; 36 37 import com.android.systemui.plugins.OverlayPlugin; 38 import com.android.systemui.plugins.PluginListener; 39 import com.android.systemui.shared.plugins.PluginManager; 40 import com.android.systemui.statusbar.phone.StatusBar; 41 import com.android.systemui.statusbar.phone.StatusBarWindowController; 42 import com.android.systemui.util.NotificationChannels; 43 44 import java.util.HashMap; 45 import java.util.Map; 46 47 /** 48 * Application class for SystemUI. 49 */ 50 public class SystemUIApplication extends Application implements SysUiServiceProvider { 51 52 public static final String TAG = "SystemUIService"; 53 private static final boolean DEBUG = false; 54 55 /** 56 * Hold a reference on the stuff we start. 57 */ 58 private SystemUI[] mServices; 59 private boolean mServicesStarted; 60 private boolean mBootCompleted; 61 private final Map<Class<?>, Object> mComponents = new HashMap<>(); 62 63 @Override onCreate()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. 69 setTheme(R.style.Theme_SystemUI); 70 71 SystemUIFactory.createFromConfig(this); 72 73 if (Process.myUserHandle().equals(UserHandle.SYSTEM)) { 74 IntentFilter bootCompletedFilter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED); 75 bootCompletedFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); 76 registerReceiver(new BroadcastReceiver() { 77 @Override 78 public void onReceive(Context context, Intent intent) { 79 if (mBootCompleted) return; 80 81 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 } 89 } 90 91 92 } 93 }, 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); 106 } else { 107 // 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 } 114 // 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. 118 startSecondaryUserServicesIfNeeded(); 119 } 120 } 121 122 /** 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. 126 * <p>This method must only be called from the main thread.</p> 127 */ 128 startServicesIfNeeded()129 public void startServicesIfNeeded() { 130 String[] names = getResources().getStringArray(R.array.config_systemUIServiceComponents); 131 startServicesIfNeeded(names); 132 } 133 134 /** 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. 138 * <p>This method must only be called from the main thread.</p> 139 */ startSecondaryUserServicesIfNeeded()140 void startSecondaryUserServicesIfNeeded() { 141 String[] names = 142 getResources().getStringArray(R.array.config_systemUIServiceComponentsPerUser); 143 startServicesIfNeeded(names); 144 } 145 startServicesIfNeeded(String[] services)146 private void startServicesIfNeeded(String[] services) { 147 if (mServicesStarted) { 148 return; 149 } 150 mServices = new SystemUI[services.length]; 151 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 161 Log.v(TAG, "Starting SystemUI services for user " + 162 Process.myUserHandle().getIdentifier() + "."); 163 TimingsTraceLog log = new TimingsTraceLog("SystemUIBootTiming", 164 Trace.TRACE_TAG_APP); 165 log.traceBegin("StartServices"); 166 final int N = services.length; 167 for (int i = 0; i < N; i++) { 168 String clsName = services[i]; 169 if (DEBUG) Log.d(TAG, "loading: " + clsName); 170 log.traceBegin("StartServices" + clsName); 171 long ti = System.currentTimeMillis(); 172 Class cls; 173 try { 174 cls = Class.forName(clsName); 175 Object o = cls.newInstance(); 176 if (o instanceof SystemUI.Injector) { 177 o = ((SystemUI.Injector) o).apply(this); 178 } 179 mServices[i] = (SystemUI) o; 180 } catch(ClassNotFoundException ex){ 181 throw new RuntimeException(ex); 182 } catch (IllegalAccessException ex) { 183 throw new RuntimeException(ex); 184 } catch (InstantiationException ex) { 185 throw new RuntimeException(ex); 186 } 187 188 mServices[i].mContext = this; 189 mServices[i].mComponents = mComponents; 190 if (DEBUG) Log.d(TAG, "running: " + mServices[i]); 191 mServices[i].start(); 192 log.traceEnd(); 193 194 // Warn if initialization of component takes too long 195 ti = System.currentTimeMillis() - ti; 196 if (ti > 1000) { 197 Log.w(TAG, "Initialization of " + cls.getName() + " took " + ti + " ms"); 198 } 199 if (mBootCompleted) { 200 mServices[i].onBootCompleted(); 201 } 202 } 203 Dependency.get(InitController.class).executePostInitTasks(); 204 log.traceEnd(); 205 final Handler mainHandler = new Handler(Looper.getMainLooper()); 206 Dependency.get(PluginManager.class).addPluginListener( 207 new PluginListener<OverlayPlugin>() { 208 private ArraySet<OverlayPlugin> mOverlays = new ArraySet<>(); 209 210 @Override 211 public void onPluginConnected(OverlayPlugin plugin, Context pluginContext) { 212 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(), 218 statusBar.getNavigationBarView(), new Callback(plugin)); 219 } 220 } 221 }); 222 } 223 224 @Override 225 public void onPluginDisconnected(OverlayPlugin plugin) { 226 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 }); 234 } 235 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 } 262 }, OverlayPlugin.class, true /* Allow multiple plugins */); 263 264 mServicesStarted = true; 265 } 266 267 @Override onConfigurationChanged(Configuration newConfig)268 public void onConfigurationChanged(Configuration newConfig) { 269 if (mServicesStarted) { 270 int len = mServices.length; 271 for (int i = 0; i < len; i++) { 272 if (mServices[i] != null) { 273 mServices[i].onConfigurationChanged(newConfig); 274 } 275 } 276 } 277 } 278 279 @SuppressWarnings("unchecked") getComponent(Class<T> interfaceType)280 public <T> T getComponent(Class<T> interfaceType) { 281 return (T) mComponents.get(interfaceType); 282 } 283 getServices()284 public SystemUI[] getServices() { 285 return mServices; 286 } 287 } 288