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.DozeParameters; 41 import com.android.systemui.statusbar.phone.StatusBar; 42 import com.android.systemui.statusbar.phone.StatusBarWindowController; 43 import com.android.systemui.util.NotificationChannels; 44 45 import java.util.HashMap; 46 import java.util.Map; 47 48 /** 49 * Application class for SystemUI. 50 */ 51 public class SystemUIApplication extends Application implements SysUiServiceProvider { 52 53 public static final String TAG = "SystemUIService"; 54 private static final boolean DEBUG = false; 55 56 /** 57 * Hold a reference on the stuff we start. 58 */ 59 private SystemUI[] mServices; 60 private boolean mServicesStarted; 61 private boolean mBootCompleted; 62 private final Map<Class<?>, Object> mComponents = new HashMap<>(); 63 private ContextAvailableCallback mContextAvailableCallback; 64 SystemUIApplication()65 public SystemUIApplication() { 66 super(); 67 Log.v(TAG, "SystemUIApplication constructed."); 68 } 69 70 @Override onCreate()71 public void onCreate() { 72 super.onCreate(); 73 Log.v(TAG, "SystemUIApplication created."); 74 // This line is used to setup Dagger's dependency injection and should be kept at the 75 // top of this method. 76 TimingsTraceLog log = new TimingsTraceLog("SystemUIBootTiming", 77 Trace.TRACE_TAG_APP); 78 log.traceBegin("DependencyInjection"); 79 mContextAvailableCallback.onContextAvailable(this); 80 log.traceEnd(); 81 82 // 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. 85 setTheme(R.style.Theme_SystemUI); 86 87 if (Process.myUserHandle().equals(UserHandle.SYSTEM)) { 88 IntentFilter bootCompletedFilter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED); 89 bootCompletedFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); 90 registerReceiver(new BroadcastReceiver() { 91 @Override 92 public void onReceive(Context context, Intent intent) { 93 if (mBootCompleted) return; 94 95 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 } 103 } 104 105 106 } 107 }, 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); 120 } else { 121 // 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 } 128 // 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. 132 startSecondaryUserServicesIfNeeded(); 133 } 134 } 135 136 /** 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. 140 * <p>This method must only be called from the main thread.</p> 141 */ 142 startServicesIfNeeded()143 public void startServicesIfNeeded() { 144 String[] names = getResources().getStringArray(R.array.config_systemUIServiceComponents); 145 startServicesIfNeeded(/* metricsPrefix= */ "StartServices", names); 146 } 147 148 /** 149 * Ensures that all the Secondary user SystemUI services are running. If they are already 150 * running, this is a no-op. This is needed to conditionally start all the services, as we only 151 * need to have it in the main process. 152 * <p>This method must only be called from the main thread.</p> 153 */ startSecondaryUserServicesIfNeeded()154 void startSecondaryUserServicesIfNeeded() { 155 String[] names = 156 getResources().getStringArray(R.array.config_systemUIServiceComponentsPerUser); 157 startServicesIfNeeded(/* metricsPrefix= */ "StartSecondaryServices", names); 158 } 159 startServicesIfNeeded(String metricsPrefix, String[] services)160 private void startServicesIfNeeded(String metricsPrefix, String[] services) { 161 if (mServicesStarted) { 162 return; 163 } 164 mServices = new SystemUI[services.length]; 165 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; 171 if (DEBUG) { 172 Log.v(TAG, "BOOT_COMPLETED was already sent"); 173 } 174 } 175 } 176 177 Log.v(TAG, "Starting SystemUI services for user " + 178 Process.myUserHandle().getIdentifier() + "."); 179 180 TimingsTraceLog log = new TimingsTraceLog("SystemUIBootTiming", 181 Trace.TRACE_TAG_APP); 182 log.traceBegin(metricsPrefix); 183 184 final int N = services.length; 185 for (int i = 0; i < N; i++) { 186 String clsName = services[i]; 187 if (DEBUG) Log.d(TAG, "loading: " + clsName); 188 log.traceBegin(metricsPrefix + clsName); 189 long ti = System.currentTimeMillis(); 190 Class cls; 191 try { 192 cls = Class.forName(clsName); 193 Object o = cls.newInstance(); 194 if (o instanceof SystemUI.Injector) { 195 o = ((SystemUI.Injector) o).apply(this); 196 } 197 mServices[i] = (SystemUI) o; 198 } catch(ClassNotFoundException ex){ 199 throw new RuntimeException(ex); 200 } catch (IllegalAccessException ex) { 201 throw new RuntimeException(ex); 202 } catch (InstantiationException ex) { 203 throw new RuntimeException(ex); 204 } 205 206 mServices[i].mContext = this; 207 mServices[i].mComponents = mComponents; 208 if (DEBUG) Log.d(TAG, "running: " + mServices[i]); 209 mServices[i].start(); 210 log.traceEnd(); 211 212 // Warn if initialization of component takes too long 213 ti = System.currentTimeMillis() - ti; 214 if (ti > 1000) { 215 Log.w(TAG, "Initialization of " + cls.getName() + " took " + ti + " ms"); 216 } 217 if (mBootCompleted) { 218 mServices[i].onBootCompleted(); 219 } 220 } 221 Dependency.get(InitController.class).executePostInitTasks(); 222 log.traceEnd(); 223 final Handler mainHandler = new Handler(Looper.getMainLooper()); 224 Dependency.get(PluginManager.class).addPluginListener( 225 new PluginListener<OverlayPlugin>() { 226 private ArraySet<OverlayPlugin> mOverlays = new ArraySet<>(); 227 228 @Override 229 public void onPluginConnected(OverlayPlugin plugin, Context pluginContext) { 230 mainHandler.post(new Runnable() { 231 @Override 232 public void run() { 233 StatusBar statusBar = getComponent(StatusBar.class); 234 if (statusBar != null) { 235 plugin.setup(statusBar.getStatusBarWindow(), 236 statusBar.getNavigationBarView(), new Callback(plugin), 237 DozeParameters.getInstance(getBaseContext())); 238 } 239 } 240 }); 241 } 242 243 @Override 244 public void onPluginDisconnected(OverlayPlugin plugin) { 245 mainHandler.post(new Runnable() { 246 @Override 247 public void run() { 248 mOverlays.remove(plugin); 249 Dependency.get(StatusBarWindowController.class).setForcePluginOpen( 250 mOverlays.size() != 0); 251 } 252 }); 253 } 254 255 class Callback implements OverlayPlugin.Callback { 256 private final OverlayPlugin mPlugin; 257 258 Callback(OverlayPlugin plugin) { 259 mPlugin = plugin; 260 } 261 262 @Override 263 public void onHoldStatusBarOpenChange() { 264 if (mPlugin.holdStatusBarOpen()) { 265 mOverlays.add(mPlugin); 266 } else { 267 mOverlays.remove(mPlugin); 268 } 269 mainHandler.post(new Runnable() { 270 @Override 271 public void run() { 272 Dependency.get(StatusBarWindowController.class) 273 .setStateListener(b -> mOverlays.forEach( 274 o -> o.setCollapseDesired(b))); 275 Dependency.get(StatusBarWindowController.class) 276 .setForcePluginOpen(mOverlays.size() != 0); 277 } 278 }); 279 } 280 } 281 }, OverlayPlugin.class, true /* Allow multiple plugins */); 282 283 mServicesStarted = true; 284 } 285 286 @Override onConfigurationChanged(Configuration newConfig)287 public void onConfigurationChanged(Configuration newConfig) { 288 if (mServicesStarted) { 289 SystemUIFactory 290 .getInstance() 291 .getRootComponent() 292 .getConfigurationController() 293 .onConfigurationChanged(newConfig); 294 int len = mServices.length; 295 for (int i = 0; i < len; i++) { 296 if (mServices[i] != null) { 297 mServices[i].onConfigurationChanged(newConfig); 298 } 299 } 300 } 301 } 302 303 @SuppressWarnings("unchecked") getComponent(Class<T> interfaceType)304 public <T> T getComponent(Class<T> interfaceType) { 305 return (T) mComponents.get(interfaceType); 306 } 307 getServices()308 public SystemUI[] getServices() { 309 return mServices; 310 } 311 setContextAvailableCallback(ContextAvailableCallback callback)312 void setContextAvailableCallback(ContextAvailableCallback callback) { 313 mContextAvailableCallback = callback; 314 } 315 316 interface ContextAvailableCallback { onContextAvailable(Context context)317 void onContextAvailable(Context context); 318 } 319 } 320