This is a significant change that allows Services to have their constructed injected into. The DozeService is now labeled as injectable (thought it takes no arguments). At least one Service has to be marked as injectable for Dagger to be happy compiling this code. Bug: 138787339 Test: atest SystemUITests Change-Id: I0ec92e01dd236bcee3083c8f8744c53b7ccc92d3 Merged-In: Ib58f8763c996fbc2aea07ead56493d2d9e936f5b
314 lines
12 KiB
Java
314 lines
12 KiB
Java
/*
|
|
* Copyright (C) 2014 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License
|
|
*/
|
|
|
|
package com.android.systemui;
|
|
|
|
import android.app.ActivityThread;
|
|
import android.app.Application;
|
|
import android.content.BroadcastReceiver;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.content.IntentFilter;
|
|
import android.content.pm.ApplicationInfo;
|
|
import android.content.res.Configuration;
|
|
import android.os.Handler;
|
|
import android.os.Looper;
|
|
import android.os.Process;
|
|
import android.os.SystemProperties;
|
|
import android.os.Trace;
|
|
import android.os.UserHandle;
|
|
import android.util.ArraySet;
|
|
import android.util.Log;
|
|
import android.util.TimingsTraceLog;
|
|
|
|
import com.android.systemui.plugins.OverlayPlugin;
|
|
import com.android.systemui.plugins.PluginListener;
|
|
import com.android.systemui.shared.plugins.PluginManager;
|
|
import com.android.systemui.statusbar.phone.DozeParameters;
|
|
import com.android.systemui.statusbar.phone.StatusBar;
|
|
import com.android.systemui.statusbar.phone.StatusBarWindowController;
|
|
import com.android.systemui.util.NotificationChannels;
|
|
|
|
import java.util.HashMap;
|
|
import java.util.Map;
|
|
|
|
/**
|
|
* Application class for SystemUI.
|
|
*/
|
|
public class SystemUIApplication extends Application implements SysUiServiceProvider {
|
|
|
|
public static final String TAG = "SystemUIService";
|
|
private static final boolean DEBUG = false;
|
|
|
|
/**
|
|
* Hold a reference on the stuff we start.
|
|
*/
|
|
private SystemUI[] mServices;
|
|
private boolean mServicesStarted;
|
|
private boolean mBootCompleted;
|
|
private final Map<Class<?>, Object> mComponents = new HashMap<>();
|
|
private ContextAvailableCallback mContextAvailableCallback;
|
|
|
|
public SystemUIApplication() {
|
|
super();
|
|
Log.v(TAG, "SystemUIApplication constructed.");
|
|
}
|
|
|
|
@Override
|
|
public void onCreate() {
|
|
super.onCreate();
|
|
Log.v(TAG, "SystemUIApplication created.");
|
|
// This line is used to setup Dagger's dependency injection and should be kept at the
|
|
// top of this method.
|
|
TimingsTraceLog log = new TimingsTraceLog("SystemUIBootTiming",
|
|
Trace.TRACE_TAG_APP);
|
|
log.traceBegin("DependencyInjection");
|
|
mContextAvailableCallback.onContextAvailable(this);
|
|
log.traceEnd();
|
|
|
|
// Set the application theme that is inherited by all services. Note that setting the
|
|
// application theme in the manifest does only work for activities. Keep this in sync with
|
|
// the theme set there.
|
|
setTheme(R.style.Theme_SystemUI);
|
|
|
|
if (Process.myUserHandle().equals(UserHandle.SYSTEM)) {
|
|
IntentFilter bootCompletedFilter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
|
|
bootCompletedFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
|
|
registerReceiver(new BroadcastReceiver() {
|
|
@Override
|
|
public void onReceive(Context context, Intent intent) {
|
|
if (mBootCompleted) return;
|
|
|
|
if (DEBUG) Log.v(TAG, "BOOT_COMPLETED received");
|
|
unregisterReceiver(this);
|
|
mBootCompleted = true;
|
|
if (mServicesStarted) {
|
|
final int N = mServices.length;
|
|
for (int i = 0; i < N; i++) {
|
|
mServices[i].onBootCompleted();
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
}, bootCompletedFilter);
|
|
|
|
IntentFilter localeChangedFilter = new IntentFilter(Intent.ACTION_LOCALE_CHANGED);
|
|
registerReceiver(new BroadcastReceiver() {
|
|
@Override
|
|
public void onReceive(Context context, Intent intent) {
|
|
if (Intent.ACTION_LOCALE_CHANGED.equals(intent.getAction())) {
|
|
if (!mBootCompleted) return;
|
|
// Update names of SystemUi notification channels
|
|
NotificationChannels.createAll(context);
|
|
}
|
|
}
|
|
}, localeChangedFilter);
|
|
} else {
|
|
// We don't need to startServices for sub-process that is doing some tasks.
|
|
// (screenshots, sweetsweetdesserts or tuner ..)
|
|
String processName = ActivityThread.currentProcessName();
|
|
ApplicationInfo info = getApplicationInfo();
|
|
if (processName != null && processName.startsWith(info.processName + ":")) {
|
|
return;
|
|
}
|
|
// For a secondary user, boot-completed will never be called because it has already
|
|
// been broadcasted on startup for the primary SystemUI process. Instead, for
|
|
// components which require the SystemUI component to be initialized per-user, we
|
|
// start those components now for the current non-system user.
|
|
startSecondaryUserServicesIfNeeded();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Makes sure that all the SystemUI services are running. If they are already running, this is a
|
|
* no-op. This is needed to conditinally start all the services, as we only need to have it in
|
|
* the main process.
|
|
* <p>This method must only be called from the main thread.</p>
|
|
*/
|
|
|
|
public void startServicesIfNeeded() {
|
|
String[] names = getResources().getStringArray(R.array.config_systemUIServiceComponents);
|
|
startServicesIfNeeded(names);
|
|
}
|
|
|
|
/**
|
|
* Ensures that all the Secondary user SystemUI services are running. If they are already
|
|
* running, this is a no-op. This is needed to conditionally start all the services, as we only
|
|
* need to have it in the main process.
|
|
* <p>This method must only be called from the main thread.</p>
|
|
*/
|
|
void startSecondaryUserServicesIfNeeded() {
|
|
String[] names =
|
|
getResources().getStringArray(R.array.config_systemUIServiceComponentsPerUser);
|
|
startServicesIfNeeded(names);
|
|
}
|
|
|
|
private void startServicesIfNeeded(String[] services) {
|
|
if (mServicesStarted) {
|
|
return;
|
|
}
|
|
mServices = new SystemUI[services.length];
|
|
|
|
if (!mBootCompleted) {
|
|
// check to see if maybe it was already completed long before we began
|
|
// see ActivityManagerService.finishBooting()
|
|
if ("1".equals(SystemProperties.get("sys.boot_completed"))) {
|
|
mBootCompleted = true;
|
|
if (DEBUG) {
|
|
Log.v(TAG, "BOOT_COMPLETED was already sent");
|
|
}
|
|
}
|
|
}
|
|
|
|
Log.v(TAG, "Starting SystemUI services for user " +
|
|
Process.myUserHandle().getIdentifier() + ".");
|
|
TimingsTraceLog log = new TimingsTraceLog("SystemUIBootTiming",
|
|
Trace.TRACE_TAG_APP);
|
|
log.traceBegin("StartServices");
|
|
final int N = services.length;
|
|
for (int i = 0; i < N; i++) {
|
|
String clsName = services[i];
|
|
if (DEBUG) Log.d(TAG, "loading: " + clsName);
|
|
log.traceBegin("StartServices" + clsName);
|
|
long ti = System.currentTimeMillis();
|
|
Class cls;
|
|
try {
|
|
cls = Class.forName(clsName);
|
|
Object o = cls.newInstance();
|
|
if (o instanceof SystemUI.Injector) {
|
|
o = ((SystemUI.Injector) o).apply(this);
|
|
}
|
|
mServices[i] = (SystemUI) o;
|
|
} catch(ClassNotFoundException ex){
|
|
throw new RuntimeException(ex);
|
|
} catch (IllegalAccessException ex) {
|
|
throw new RuntimeException(ex);
|
|
} catch (InstantiationException ex) {
|
|
throw new RuntimeException(ex);
|
|
}
|
|
|
|
mServices[i].mContext = this;
|
|
mServices[i].mComponents = mComponents;
|
|
if (DEBUG) Log.d(TAG, "running: " + mServices[i]);
|
|
mServices[i].start();
|
|
log.traceEnd();
|
|
|
|
// Warn if initialization of component takes too long
|
|
ti = System.currentTimeMillis() - ti;
|
|
if (ti > 1000) {
|
|
Log.w(TAG, "Initialization of " + cls.getName() + " took " + ti + " ms");
|
|
}
|
|
if (mBootCompleted) {
|
|
mServices[i].onBootCompleted();
|
|
}
|
|
}
|
|
Dependency.get(InitController.class).executePostInitTasks();
|
|
log.traceEnd();
|
|
final Handler mainHandler = new Handler(Looper.getMainLooper());
|
|
Dependency.get(PluginManager.class).addPluginListener(
|
|
new PluginListener<OverlayPlugin>() {
|
|
private ArraySet<OverlayPlugin> mOverlays = new ArraySet<>();
|
|
|
|
@Override
|
|
public void onPluginConnected(OverlayPlugin plugin, Context pluginContext) {
|
|
mainHandler.post(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
StatusBar statusBar = getComponent(StatusBar.class);
|
|
if (statusBar != null) {
|
|
plugin.setup(statusBar.getStatusBarWindow(),
|
|
statusBar.getNavigationBarView(), new Callback(plugin),
|
|
DozeParameters.getInstance(getBaseContext()));
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
@Override
|
|
public void onPluginDisconnected(OverlayPlugin plugin) {
|
|
mainHandler.post(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
mOverlays.remove(plugin);
|
|
Dependency.get(StatusBarWindowController.class).setForcePluginOpen(
|
|
mOverlays.size() != 0);
|
|
}
|
|
});
|
|
}
|
|
|
|
class Callback implements OverlayPlugin.Callback {
|
|
private final OverlayPlugin mPlugin;
|
|
|
|
Callback(OverlayPlugin plugin) {
|
|
mPlugin = plugin;
|
|
}
|
|
|
|
@Override
|
|
public void onHoldStatusBarOpenChange() {
|
|
if (mPlugin.holdStatusBarOpen()) {
|
|
mOverlays.add(mPlugin);
|
|
} else {
|
|
mOverlays.remove(mPlugin);
|
|
}
|
|
mainHandler.post(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
Dependency.get(StatusBarWindowController.class)
|
|
.setStateListener(b -> mOverlays.forEach(
|
|
o -> o.setCollapseDesired(b)));
|
|
Dependency.get(StatusBarWindowController.class)
|
|
.setForcePluginOpen(mOverlays.size() != 0);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}, OverlayPlugin.class, true /* Allow multiple plugins */);
|
|
|
|
mServicesStarted = true;
|
|
}
|
|
|
|
@Override
|
|
public void onConfigurationChanged(Configuration newConfig) {
|
|
if (mServicesStarted) {
|
|
Dependency.staticOnConfigurationChanged(newConfig);
|
|
int len = mServices.length;
|
|
for (int i = 0; i < len; i++) {
|
|
if (mServices[i] != null) {
|
|
mServices[i].onConfigurationChanged(newConfig);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
public <T> T getComponent(Class<T> interfaceType) {
|
|
return (T) mComponents.get(interfaceType);
|
|
}
|
|
|
|
public SystemUI[] getServices() {
|
|
return mServices;
|
|
}
|
|
|
|
void setContextAvailableCallback(ContextAvailableCallback callback) {
|
|
mContextAvailableCallback = callback;
|
|
}
|
|
|
|
interface ContextAvailableCallback {
|
|
void onContextAvailable(Context context);
|
|
}
|
|
}
|