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