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