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.app.Notification; 22 import android.content.BroadcastReceiver; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.content.IntentFilter; 26 import android.content.pm.ApplicationInfo; 27 import android.content.res.Configuration; 28 import android.os.Bundle; 29 import android.os.Looper; 30 import android.os.Process; 31 import android.os.SystemProperties; 32 import android.os.Trace; 33 import android.os.UserHandle; 34 import android.util.Log; 35 import android.util.TimingsTraceLog; 36 import android.view.SurfaceControl; 37 import android.view.ThreadedRenderer; 38 import android.view.View; 39 40 import com.android.internal.protolog.common.ProtoLog; 41 import com.android.systemui.dagger.GlobalRootComponent; 42 import com.android.systemui.dagger.SysUIComponent; 43 import com.android.systemui.dump.DumpManager; 44 import com.android.systemui.statusbar.policy.ConfigurationController; 45 import com.android.systemui.util.NotificationChannels; 46 47 import java.util.Comparator; 48 import java.util.Map; 49 import java.util.TreeMap; 50 51 import javax.inject.Provider; 52 53 /** 54 * Application class for SystemUI. 55 */ 56 public class SystemUIApplication extends Application implements 57 SystemUIAppComponentFactoryBase.ContextInitializer { 58 59 public static final String TAG = "SystemUIService"; 60 private static final boolean DEBUG = false; 61 62 private BootCompleteCacheImpl mBootCompleteCache; 63 64 /** 65 * Hold a reference on the stuff we start. 66 */ 67 private CoreStartable[] mServices; 68 private boolean mServicesStarted; 69 private SystemUIAppComponentFactoryBase.ContextAvailableCallback mContextAvailableCallback; 70 private SysUIComponent mSysUIComponent; 71 private SystemUIInitializer mInitializer; 72 SystemUIApplication()73 public SystemUIApplication() { 74 super(); 75 Log.v(TAG, "SystemUIApplication constructed."); 76 // SysUI may be building without protolog preprocessing in some cases 77 ProtoLog.REQUIRE_PROTOLOGTOOL = false; 78 } 79 getRootComponent()80 protected GlobalRootComponent getRootComponent() { 81 return mInitializer.getRootComponent(); 82 } 83 84 @Override onCreate()85 public void onCreate() { 86 super.onCreate(); 87 Log.v(TAG, "SystemUIApplication created."); 88 // This line is used to setup Dagger's dependency injection and should be kept at the 89 // top of this method. 90 TimingsTraceLog log = new TimingsTraceLog("SystemUIBootTiming", 91 Trace.TRACE_TAG_APP); 92 log.traceBegin("DependencyInjection"); 93 mInitializer = mContextAvailableCallback.onContextAvailable(this); 94 mSysUIComponent = mInitializer.getSysUIComponent(); 95 mBootCompleteCache = mSysUIComponent.provideBootCacheImpl(); 96 log.traceEnd(); 97 98 // Enable Looper trace points. 99 // This allows us to see Handler callbacks on traces. 100 Looper.getMainLooper().setTraceTag(Trace.TRACE_TAG_APP); 101 102 // Set the application theme that is inherited by all services. Note that setting the 103 // application theme in the manifest does only work for activities. Keep this in sync with 104 // the theme set there. 105 setTheme(R.style.Theme_SystemUI); 106 107 View.setTraceLayoutSteps( 108 SystemProperties.getBoolean("persist.debug.trace_layouts", false)); 109 View.setTracedRequestLayoutClassClass( 110 SystemProperties.get("persist.debug.trace_request_layout_class", null)); 111 112 if (Process.myUserHandle().equals(UserHandle.SYSTEM)) { 113 IntentFilter bootCompletedFilter = new 114 IntentFilter(Intent.ACTION_LOCKED_BOOT_COMPLETED); 115 bootCompletedFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); 116 117 // If SF GPU context priority is set to realtime, then SysUI should run at high. 118 // The priority is defaulted at medium. 119 int sfPriority = SurfaceControl.getGPUContextPriority(); 120 Log.i(TAG, "Found SurfaceFlinger's GPU Priority: " + sfPriority); 121 if (sfPriority == ThreadedRenderer.EGL_CONTEXT_PRIORITY_REALTIME_NV) { 122 Log.i(TAG, "Setting SysUI's GPU Context priority to: " 123 + ThreadedRenderer.EGL_CONTEXT_PRIORITY_HIGH_IMG); 124 ThreadedRenderer.setContextPriority( 125 ThreadedRenderer.EGL_CONTEXT_PRIORITY_HIGH_IMG); 126 } 127 128 registerReceiver(new BroadcastReceiver() { 129 @Override 130 public void onReceive(Context context, Intent intent) { 131 if (mBootCompleteCache.isBootComplete()) return; 132 133 if (DEBUG) Log.v(TAG, "BOOT_COMPLETED received"); 134 unregisterReceiver(this); 135 mBootCompleteCache.setBootComplete(); 136 if (mServicesStarted) { 137 final int N = mServices.length; 138 for (int i = 0; i < N; i++) { 139 notifyBootCompleted(mServices[i]); 140 } 141 } 142 } 143 }, bootCompletedFilter); 144 145 IntentFilter localeChangedFilter = new IntentFilter(Intent.ACTION_LOCALE_CHANGED); 146 registerReceiver(new BroadcastReceiver() { 147 @Override 148 public void onReceive(Context context, Intent intent) { 149 if (Intent.ACTION_LOCALE_CHANGED.equals(intent.getAction())) { 150 if (!mBootCompleteCache.isBootComplete()) return; 151 // Update names of SystemUi notification channels 152 NotificationChannels.createAll(context); 153 } 154 } 155 }, localeChangedFilter); 156 } else { 157 // We don't need to startServices for sub-process that is doing some tasks. 158 // (screenshots, sweetsweetdesserts or tuner ..) 159 String processName = ActivityThread.currentProcessName(); 160 ApplicationInfo info = getApplicationInfo(); 161 if (processName != null && processName.startsWith(info.processName + ":")) { 162 return; 163 } 164 // For a secondary user, boot-completed will never be called because it has already 165 // been broadcasted on startup for the primary SystemUI process. Instead, for 166 // components which require the SystemUI component to be initialized per-user, we 167 // start those components now for the current non-system user. 168 startSecondaryUserServicesIfNeeded(); 169 } 170 } 171 172 /** 173 * Makes sure that all the SystemUI services are running. If they are already running, this is a 174 * no-op. This is needed to conditinally start all the services, as we only need to have it in 175 * the main process. 176 * <p>This method must only be called from the main thread.</p> 177 */ 178 startServicesIfNeeded()179 public void startServicesIfNeeded() { 180 final String vendorComponent = mInitializer.getVendorComponent(getResources()); 181 182 // Sort the startables so that we get a deterministic ordering. 183 // TODO: make #start idempotent and require users of CoreStartable to call it. 184 Map<Class<?>, Provider<CoreStartable>> sortedStartables = new TreeMap<>( 185 Comparator.comparing(Class::getName)); 186 sortedStartables.putAll(mSysUIComponent.getStartables()); 187 sortedStartables.putAll(mSysUIComponent.getPerUserStartables()); 188 startServicesIfNeeded( 189 sortedStartables, "StartServices", vendorComponent); 190 } 191 192 /** 193 * Ensures that all the Secondary user SystemUI services are running. If they are already 194 * running, this is a no-op. This is needed to conditionally start all the services, as we only 195 * need to have it in the main process. 196 * <p>This method must only be called from the main thread.</p> 197 */ startSecondaryUserServicesIfNeeded()198 void startSecondaryUserServicesIfNeeded() { 199 // Sort the startables so that we get a deterministic ordering. 200 Map<Class<?>, Provider<CoreStartable>> sortedStartables = new TreeMap<>( 201 Comparator.comparing(Class::getName)); 202 sortedStartables.putAll(mSysUIComponent.getPerUserStartables()); 203 startServicesIfNeeded( 204 sortedStartables, "StartSecondaryServices", null); 205 } 206 startServicesIfNeeded( Map<Class<?>, Provider<CoreStartable>> startables, String metricsPrefix, String vendorComponent)207 private void startServicesIfNeeded( 208 Map<Class<?>, Provider<CoreStartable>> startables, 209 String metricsPrefix, 210 String vendorComponent) { 211 if (mServicesStarted) { 212 return; 213 } 214 mServices = new CoreStartable[startables.size() + (vendorComponent == null ? 0 : 1)]; 215 216 if (!mBootCompleteCache.isBootComplete()) { 217 // check to see if maybe it was already completed long before we began 218 // see ActivityManagerService.finishBooting() 219 if ("1".equals(SystemProperties.get("sys.boot_completed"))) { 220 mBootCompleteCache.setBootComplete(); 221 if (DEBUG) { 222 Log.v(TAG, "BOOT_COMPLETED was already sent"); 223 } 224 } 225 } 226 227 DumpManager dumpManager = mSysUIComponent.createDumpManager(); 228 229 Log.v(TAG, "Starting SystemUI services for user " + 230 Process.myUserHandle().getIdentifier() + "."); 231 TimingsTraceLog log = new TimingsTraceLog("SystemUIBootTiming", 232 Trace.TRACE_TAG_APP); 233 log.traceBegin(metricsPrefix); 234 235 int i = 0; 236 for (Map.Entry<Class<?>, Provider<CoreStartable>> entry : startables.entrySet()) { 237 String clsName = entry.getKey().getName(); 238 int j = i; // Copied to make lambda happy. 239 timeInitialization( 240 clsName, 241 () -> mServices[j] = startStartable(clsName, entry.getValue()), 242 log, 243 metricsPrefix); 244 i++; 245 } 246 247 if (vendorComponent != null) { 248 timeInitialization( 249 vendorComponent, 250 () -> mServices[mServices.length - 1] = 251 startAdditionalStartable(vendorComponent), 252 log, 253 metricsPrefix); 254 } 255 256 for (i = 0; i < mServices.length; i++) { 257 CoreStartable service = mServices[i]; 258 if (mBootCompleteCache.isBootComplete()) { 259 notifyBootCompleted(service); 260 } 261 262 if (service.isDumpCritical()) { 263 dumpManager.registerCriticalDumpable(service.getClass().getName(), service); 264 } else { 265 dumpManager.registerNormalDumpable(service.getClass().getName(), service); 266 } 267 } 268 mSysUIComponent.getInitController().executePostInitTasks(); 269 log.traceEnd(); 270 271 mServicesStarted = true; 272 } 273 notifyBootCompleted(CoreStartable coreStartable)274 private static void notifyBootCompleted(CoreStartable coreStartable) { 275 if (Trace.isEnabled()) { 276 Trace.traceBegin( 277 Trace.TRACE_TAG_APP, 278 coreStartable.getClass().getSimpleName() + ".onBootCompleted()"); 279 } 280 coreStartable.onBootCompleted(); 281 Trace.endSection(); 282 } 283 timeInitialization(String clsName, Runnable init, TimingsTraceLog log, String metricsPrefix)284 private static void timeInitialization(String clsName, Runnable init, TimingsTraceLog log, 285 String metricsPrefix) { 286 long ti = System.currentTimeMillis(); 287 log.traceBegin(metricsPrefix + " " + clsName); 288 init.run(); 289 log.traceEnd(); 290 291 // Warn if initialization of component takes too long 292 ti = System.currentTimeMillis() - ti; 293 if (ti > 1000) { 294 Log.w(TAG, "Initialization of " + clsName + " took " + ti + " ms"); 295 } 296 } 297 startAdditionalStartable(String clsName)298 private static CoreStartable startAdditionalStartable(String clsName) { 299 CoreStartable startable; 300 if (DEBUG) Log.d(TAG, "loading: " + clsName); 301 if (Trace.isEnabled()) { 302 Trace.traceBegin( 303 Trace.TRACE_TAG_APP, clsName + ".newInstance()"); 304 } 305 try { 306 startable = (CoreStartable) Class.forName(clsName).newInstance(); 307 } catch (ClassNotFoundException 308 | IllegalAccessException 309 | InstantiationException ex) { 310 throw new RuntimeException(ex); 311 } finally { 312 Trace.endSection(); 313 } 314 315 return startStartable(startable); 316 } 317 startStartable(String clsName, Provider<CoreStartable> provider)318 private static CoreStartable startStartable(String clsName, Provider<CoreStartable> provider) { 319 if (DEBUG) Log.d(TAG, "loading: " + clsName); 320 if (Trace.isEnabled()) { 321 Trace.traceBegin( 322 Trace.TRACE_TAG_APP, "Provider<" + clsName + ">.get()"); 323 } 324 CoreStartable startable = provider.get(); 325 Trace.endSection(); 326 return startStartable(startable); 327 } 328 startStartable(CoreStartable startable)329 private static CoreStartable startStartable(CoreStartable startable) { 330 if (DEBUG) Log.d(TAG, "running: " + startable); 331 if (Trace.isEnabled()) { 332 Trace.traceBegin( 333 Trace.TRACE_TAG_APP, startable.getClass().getSimpleName() + ".start()"); 334 } 335 startable.start(); 336 Trace.endSection(); 337 338 return startable; 339 } 340 341 @Override onConfigurationChanged(Configuration newConfig)342 public void onConfigurationChanged(Configuration newConfig) { 343 if (mServicesStarted) { 344 ConfigurationController configController = mSysUIComponent.getConfigurationController(); 345 if (Trace.isEnabled()) { 346 Trace.traceBegin( 347 Trace.TRACE_TAG_APP, 348 configController.getClass().getSimpleName() + ".onConfigurationChanged()"); 349 } 350 configController.onConfigurationChanged(newConfig); 351 Trace.endSection(); 352 int len = mServices.length; 353 for (int i = 0; i < len; i++) { 354 if (mServices[i] != null) { 355 if (Trace.isEnabled()) { 356 Trace.traceBegin( 357 Trace.TRACE_TAG_APP, 358 mServices[i].getClass().getSimpleName() 359 + ".onConfigurationChanged()"); 360 } 361 mServices[i].onConfigurationChanged(newConfig); 362 Trace.endSection(); 363 } 364 } 365 } 366 } 367 getServices()368 public CoreStartable[] getServices() { 369 return mServices; 370 } 371 372 @Override setContextAvailableCallback( SystemUIAppComponentFactoryBase.ContextAvailableCallback callback)373 public void setContextAvailableCallback( 374 SystemUIAppComponentFactoryBase.ContextAvailableCallback callback) { 375 mContextAvailableCallback = callback; 376 } 377 378 /** Update a notifications application name. */ overrideNotificationAppName(Context context, Notification.Builder n, boolean system)379 public static void overrideNotificationAppName(Context context, Notification.Builder n, 380 boolean system) { 381 final Bundle extras = new Bundle(); 382 String appName = system 383 ? context.getString(com.android.internal.R.string.notification_app_name_system) 384 : context.getString(com.android.internal.R.string.notification_app_name_settings); 385 extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, appName); 386 387 n.addExtras(extras); 388 } 389 } 390