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.Process; 28 import android.os.SystemProperties; 29 import android.os.Trace; 30 import android.os.UserHandle; 31 import android.util.ArraySet; 32 import android.util.TimingsTraceLog; 33 import android.util.Log; 34 35 import com.android.systemui.globalactions.GlobalActionsComponent; 36 import com.android.systemui.keyboard.KeyboardUI; 37 import com.android.systemui.keyguard.KeyguardViewMediator; 38 import com.android.systemui.media.RingtonePlayer; 39 import com.android.systemui.pip.PipUI; 40 import com.android.systemui.plugins.GlobalActions; 41 import com.android.systemui.plugins.OverlayPlugin; 42 import com.android.systemui.plugins.Plugin; 43 import com.android.systemui.plugins.PluginListener; 44 import com.android.systemui.plugins.PluginManager; 45 import com.android.systemui.power.PowerUI; 46 import com.android.systemui.recents.Recents; 47 import com.android.systemui.shortcut.ShortcutKeyDispatcher; 48 import com.android.systemui.stackdivider.Divider; 49 import com.android.systemui.statusbar.CommandQueue; 50 import com.android.systemui.statusbar.phone.StatusBar; 51 import com.android.systemui.statusbar.phone.StatusBarWindowManager; 52 import com.android.systemui.usb.StorageNotification; 53 import com.android.systemui.util.NotificationChannels; 54 import com.android.systemui.util.leak.GarbageMonitor; 55 import com.android.systemui.volume.VolumeUI; 56 57 import java.util.HashMap; 58 import java.util.Map; 59 60 /** 61 * Application class for SystemUI. 62 */ 63 public class SystemUIApplication extends Application implements SysUiServiceProvider { 64 65 private static final String TAG = "SystemUIService"; 66 private static final boolean DEBUG = false; 67 68 /** 69 * The classes of the stuff to start. 70 */ 71 private final Class<?>[] SERVICES = new Class[] { 72 Dependency.class, 73 NotificationChannels.class, 74 CommandQueue.CommandQueueStart.class, 75 KeyguardViewMediator.class, 76 Recents.class, 77 VolumeUI.class, 78 Divider.class, 79 SystemBars.class, 80 StorageNotification.class, 81 PowerUI.class, 82 RingtonePlayer.class, 83 KeyboardUI.class, 84 PipUI.class, 85 ShortcutKeyDispatcher.class, 86 VendorServices.class, 87 GarbageMonitor.Service.class, 88 LatencyTester.class, 89 GlobalActionsComponent.class, 90 RoundedCorners.class, 91 }; 92 93 /** 94 * The classes of the stuff to start for each user. This is a subset of the services listed 95 * above. 96 */ 97 private final Class<?>[] SERVICES_PER_USER = new Class[] { 98 Dependency.class, 99 NotificationChannels.class, 100 Recents.class 101 }; 102 103 /** 104 * Hold a reference on the stuff we start. 105 */ 106 private final SystemUI[] mServices = new SystemUI[SERVICES.length]; 107 private boolean mServicesStarted; 108 private boolean mBootCompleted; 109 private final Map<Class<?>, Object> mComponents = new HashMap<>(); 110 111 @Override onCreate()112 public void onCreate() { 113 super.onCreate(); 114 // Set the application theme that is inherited by all services. Note that setting the 115 // application theme in the manifest does only work for activities. Keep this in sync with 116 // the theme set there. 117 setTheme(R.style.Theme_SystemUI); 118 119 SystemUIFactory.createFromConfig(this); 120 121 if (Process.myUserHandle().equals(UserHandle.SYSTEM)) { 122 IntentFilter filter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED); 123 filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); 124 registerReceiver(new BroadcastReceiver() { 125 @Override 126 public void onReceive(Context context, Intent intent) { 127 if (mBootCompleted) return; 128 129 if (DEBUG) Log.v(TAG, "BOOT_COMPLETED received"); 130 unregisterReceiver(this); 131 mBootCompleted = true; 132 if (mServicesStarted) { 133 final int N = mServices.length; 134 for (int i = 0; i < N; i++) { 135 mServices[i].onBootCompleted(); 136 } 137 } 138 } 139 }, filter); 140 } else { 141 // We don't need to startServices for sub-process that is doing some tasks. 142 // (screenshots, sweetsweetdesserts or tuner ..) 143 String processName = ActivityThread.currentProcessName(); 144 ApplicationInfo info = getApplicationInfo(); 145 if (processName != null && processName.startsWith(info.processName + ":")) { 146 return; 147 } 148 // For a secondary user, boot-completed will never be called because it has already 149 // been broadcasted on startup for the primary SystemUI process. Instead, for 150 // components which require the SystemUI component to be initialized per-user, we 151 // start those components now for the current non-system user. 152 startServicesIfNeeded(SERVICES_PER_USER); 153 } 154 } 155 156 /** 157 * Makes sure that all the SystemUI services are running. If they are already running, this is a 158 * no-op. This is needed to conditinally start all the services, as we only need to have it in 159 * the main process. 160 * <p>This method must only be called from the main thread.</p> 161 */ 162 startServicesIfNeeded()163 public void startServicesIfNeeded() { 164 startServicesIfNeeded(SERVICES); 165 } 166 167 /** 168 * Ensures that all the Secondary user SystemUI services are running. If they are already 169 * running, this is a no-op. This is needed to conditinally start all the services, as we only 170 * need to have it in the main process. 171 * <p>This method must only be called from the main thread.</p> 172 */ startSecondaryUserServicesIfNeeded()173 void startSecondaryUserServicesIfNeeded() { 174 startServicesIfNeeded(SERVICES_PER_USER); 175 } 176 startServicesIfNeeded(Class<?>[] services)177 private void startServicesIfNeeded(Class<?>[] services) { 178 if (mServicesStarted) { 179 return; 180 } 181 182 if (!mBootCompleted) { 183 // check to see if maybe it was already completed long before we began 184 // see ActivityManagerService.finishBooting() 185 if ("1".equals(SystemProperties.get("sys.boot_completed"))) { 186 mBootCompleted = true; 187 if (DEBUG) Log.v(TAG, "BOOT_COMPLETED was already sent"); 188 } 189 } 190 191 Log.v(TAG, "Starting SystemUI services for user " + 192 Process.myUserHandle().getIdentifier() + "."); 193 TimingsTraceLog log = new TimingsTraceLog("SystemUIBootTiming", 194 Trace.TRACE_TAG_APP); 195 log.traceBegin("StartServices"); 196 final int N = services.length; 197 for (int i = 0; i < N; i++) { 198 Class<?> cl = services[i]; 199 if (DEBUG) Log.d(TAG, "loading: " + cl); 200 log.traceBegin("StartServices" + cl.getSimpleName()); 201 long ti = System.currentTimeMillis(); 202 try { 203 204 Object newService = SystemUIFactory.getInstance().createInstance(cl); 205 mServices[i] = (SystemUI) ((newService == null) ? cl.newInstance() : newService); 206 } catch (IllegalAccessException ex) { 207 throw new RuntimeException(ex); 208 } catch (InstantiationException ex) { 209 throw new RuntimeException(ex); 210 } 211 212 mServices[i].mContext = this; 213 mServices[i].mComponents = mComponents; 214 if (DEBUG) Log.d(TAG, "running: " + mServices[i]); 215 mServices[i].start(); 216 log.traceEnd(); 217 218 // Warn if initialization of component takes too long 219 ti = System.currentTimeMillis() - ti; 220 if (ti > 1000) { 221 Log.w(TAG, "Initialization of " + cl.getName() + " took " + ti + " ms"); 222 } 223 if (mBootCompleted) { 224 mServices[i].onBootCompleted(); 225 } 226 } 227 log.traceEnd(); 228 Dependency.get(PluginManager.class).addPluginListener( 229 new PluginListener<OverlayPlugin>() { 230 private ArraySet<OverlayPlugin> mOverlays; 231 232 @Override 233 public void onPluginConnected(OverlayPlugin plugin, Context pluginContext) { 234 StatusBar statusBar = getComponent(StatusBar.class); 235 if (statusBar != null) { 236 plugin.setup(statusBar.getStatusBarWindow(), 237 statusBar.getNavigationBarView()); 238 } 239 // Lazy init. 240 if (mOverlays == null) mOverlays = new ArraySet<>(); 241 if (plugin.holdStatusBarOpen()) { 242 mOverlays.add(plugin); 243 Dependency.get(StatusBarWindowManager.class).setStateListener(b -> 244 mOverlays.forEach(o -> o.setCollapseDesired(b))); 245 Dependency.get(StatusBarWindowManager.class).setForcePluginOpen( 246 mOverlays.size() != 0); 247 248 } 249 } 250 251 @Override 252 public void onPluginDisconnected(OverlayPlugin plugin) { 253 mOverlays.remove(plugin); 254 Dependency.get(StatusBarWindowManager.class).setForcePluginOpen( 255 mOverlays.size() != 0); 256 } 257 }, OverlayPlugin.class, true /* Allow multiple plugins */); 258 259 mServicesStarted = true; 260 } 261 262 @Override onConfigurationChanged(Configuration newConfig)263 public void onConfigurationChanged(Configuration newConfig) { 264 if (mServicesStarted) { 265 int len = mServices.length; 266 for (int i = 0; i < len; i++) { 267 if (mServices[i] != null) { 268 mServices[i].onConfigurationChanged(newConfig); 269 } 270 } 271 } 272 } 273 274 @SuppressWarnings("unchecked") getComponent(Class<T> interfaceType)275 public <T> T getComponent(Class<T> interfaceType) { 276 return (T) mComponents.get(interfaceType); 277 } 278 getServices()279 public SystemUI[] getServices() { 280 return mServices; 281 } 282 } 283