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