写在前面
工作需要涉及到这部分代码,但是我对此了解很少;边学边总结,把这部分逻辑和涉及到的知识点弄明白。该系列不确定几篇,随缘。
本篇主要介绍StatusBarManagerService与systemui之间的关联。
了解StatusBarManagerService
1. StatusBarManagerService是干什么用的?
Android系统中,三方app/系统应用/底层模块,想要与systemui交互,绝大多数要通过StatusBarManagerService去进行。这个服务名称为状态栏服务,但通过这个服务可以管理systemui的大部分组件,如状态栏、导航栏、最近任务、通控中心等。
因为在systemui源码中,谷歌在这一部分没有进行拆分,调用代码都放在StatusBar.java中去中转,导致StatusBar这个类非常臃肿。从Android U开始,谷歌对systemui状态栏部分进行MVVM改造,StatusBar更名为CentralSurfaces进行了接口化拆分,在此不多赘述感兴趣可以看下源码。
2. StatusBarManagerService是怎么生效的?
(1) StatusBarManagerService的创建
同其他ManagerService一样,StatusBarManagerService服务也是在SystemServer中被创建
private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
...
StatusBarManagerService statusBar = null;
...
t.traceBegin("StartStatusBarManagerService");
try {
statusBar = new StatusBarManagerService(context);
if (!isWatch) {
statusBar.publishGlobalActionsProvider();
}
ServiceManager.addService(Context.STATUS_BAR_SERVICE, statusBar, false,
DUMP_FLAG_PRIORITY_NORMAL | DUMP_FLAG_PROTO);
} catch (Throwable e) {
reportWtf("starting StatusBarManagerService", e);
}
t.traceEnd();
...
}
StatusBarManagerService继承自IStatusBarService.Stub,IStatusBarService.aidl文件中声明的方法(截了一部分):
前面的方法为调用SBM常用方法,其中disable方法可以通过设置不同的flag来实现不同操作,详见:
StatusBarManager中的相关标志位
(2) StatusBarManagerService与systemui关联
systemui中有一个类,CommandQueue.java,继承IStatusBar.Stub;IStatusBar中声明的方法与前面IStatusBarService调到systemui的一致。
systemui中各个模块想要接收SBM的回调都通过CommandQueue,向其注册Callback;callbak中方法声明和IStatusBar一致,只不过方法都是default空,可以根据需要选择实现。
systemui在CentralSurfaces中将commandqueue注册给了StatusBarManagerService:
protected IStatusBarService mBarService;
...
@Override
public void start() {
...
mBarService = IStatusBarService.Stub.asInterface(ServiceManager.getService(Context.STATUS_BAR_SERVICE));
...
RegisterStatusBarResult result = null;
try {
result = mBarService.registerStatusBar(mCommandQueue);
} catch (RemoteException ex) {
ex.rethrowFromSystemServer();
}
...
}
通过registerStatusBar方法,SBMService中mBar保存了IStatusBar,即CommandQueue。
StatusBarShellCommand和CommandRegistry
我们知道可以通过adb去dump systemui的状态,例如:
adb shell dumpsys statusbar
adb shell dumpsys activity service SystemUIService
因为systemui中很多类都实现了dumpable,在dump中将关键信息打印出,以用作debug。
通过SBMService对systemui的调用需要自己编写测试app才能实现,这样操作很麻烦,能否通过adb去测试一些内容呢?
CommandQueue有一个成员变量CommandRegistry:
CommandRegistry的registerCommand方法可以让systemui中的控件注册adb cmd命令来执行:
commandRegistry.registerCommand("tile-service-add") { TileServiceRequestCommand() }
commandQueue.addCallback(commandQueueCallback)
inner class TileServiceRequestCommand : Command {
override fun execute(pw: PrintWriter, args: List<String>) {
val componentName: ComponentName = ComponentName.unflattenFromString(args[0])
?: run {
Log.w(TAG, "Malformed componentName ${args[0]}")
return
}
requestTileAdd(componentName, args[1], args[2], null) {
Log.d(TAG, "Response: $it")
}
}
override fun help(pw: PrintWriter) {
pw.println("Usage: adb shell cmd statusbar tile-service-add " +
"<componentName> <appName> <label>")
}
}
而原生StatusBarShellCommand中预制了很多命令,以该类Android U上的完整代码来结束这篇文章吧:
/*
* Copyright (C) 2016 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.server.statusbar;
import static android.app.StatusBarManager.DEFAULT_SETUP_DISABLE2_FLAGS;
import static android.app.StatusBarManager.DEFAULT_SETUP_DISABLE_FLAGS;
import static android.app.StatusBarManager.DISABLE2_NONE;
import static android.app.StatusBarManager.DISABLE_NONE;
import android.app.StatusBarManager.DisableInfo;
import android.content.ComponentName;
import android.content.Context;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ShellCommand;
import android.service.quicksettings.TileService;
import android.util.Pair;
import java.io.PrintWriter;
public class StatusBarShellCommand extends ShellCommand {
private static final IBinder sToken = new StatusBarShellCommandToken();
private final StatusBarManagerService mInterface;
private final Context mContext;
public StatusBarShellCommand(StatusBarManagerService service, Context context) {
mInterface = service;
mContext = context;
}
@Override
public int onCommand(String cmd) {
if (cmd == null) {
onHelp();
return 1;
}
try {
switch (cmd) {
case "expand-notifications":
return runExpandNotifications();
case "expand-settings":
return runExpandSettings();
case "collapse":
return runCollapse();
case "add-tile":
return runAddTile();
case "remove-tile":
return runRemoveTile();
case "click-tile":
return runClickTile();
case "check-support":
final PrintWriter pw = getOutPrintWriter();
pw.println(String.valueOf(TileService.isQuickSettingsSupported()));
return 0;
case "get-status-icons":
return runGetStatusIcons();
case "disable-for-setup":
return runDisableForSetup();
case "send-disable-flag":
return runSendDisableFlag();
case "tracing":
return runTracing();
case "run-gc":
return runGc();
// Handle everything that would be handled in `handleDefaultCommand()` explicitly,
// so the default can be to pass all args to StatusBar
case "-h":
case "help":
onHelp();
return 0;
case "dump":
return super.handleDefaultCommands(cmd);
default:
return runPassArgsToStatusBar();
}
} catch (RemoteException e) {
final PrintWriter pw = getOutPrintWriter();
pw.println("Remote exception: " + e);
}
return -1;
}
private int runAddTile() throws RemoteException {
mInterface.addTile(ComponentName.unflattenFromString(getNextArgRequired()));
return 0;
}
private int runRemoveTile() throws RemoteException {
mInterface.remTile(ComponentName.unflattenFromString(getNextArgRequired()));
return 0;
}
private int runClickTile() throws RemoteException {
mInterface.clickTile(ComponentName.unflattenFromString(getNextArgRequired()));
return 0;
}
private int runCollapse() throws RemoteException {
mInterface.collapsePanels();
return 0;
}
private int runExpandSettings() throws RemoteException {
mInterface.expandSettingsPanel(null);
return 0;
}
private int runExpandNotifications() throws RemoteException {
mInterface.expandNotificationsPanel();
return 0;
}
private int runGetStatusIcons() {
final PrintWriter pw = getOutPrintWriter();
for (String icon : mInterface.getStatusBarIcons()) {
pw.println(icon);
}
return 0;
}
private int runDisableForSetup() {
String arg = getNextArgRequired();
String pkg = mContext.getPackageName();
boolean disable = Boolean.parseBoolean(arg);
if (disable) {
mInterface.disable(DEFAULT_SETUP_DISABLE_FLAGS, sToken, pkg);
mInterface.disable2(DEFAULT_SETUP_DISABLE2_FLAGS, sToken, pkg);
} else {
mInterface.disable(DISABLE_NONE, sToken, pkg);
mInterface.disable2(DISABLE2_NONE, sToken, pkg);
}
return 0;
}
private int runSendDisableFlag() {
String pkg = mContext.getPackageName();
int disable1 = DISABLE_NONE;
int disable2 = DISABLE2_NONE;
DisableInfo info = new DisableInfo();
String arg = getNextArg();
while (arg != null) {
switch (arg) {
case "search":
info.setSearchDisabled(true);
break;
case "home":
info.setNagivationHomeDisabled(true);
break;
case "recents":
info.setRecentsDisabled(true);
break;
case "notification-alerts":
info.setNotificationPeekingDisabled(true);
break;
case "statusbar-expansion":
info.setStatusBarExpansionDisabled(true);
break;
case "system-icons":
info.setSystemIconsDisabled(true);
break;
case "clock":
info.setClockDisabled(true);
break;
case "notification-icons":
info.setNotificationIconsDisabled(true);
break;
default:
break;
}
arg = getNextArg();
}
Pair<Integer, Integer> flagPair = info.toFlags();
mInterface.disable(flagPair.first, sToken, pkg);
mInterface.disable2(flagPair.second, sToken, pkg);
return 0;
}
private int runPassArgsToStatusBar() {
mInterface.passThroughShellCommand(getAllArgs(), getOutFileDescriptor());
return 0;
}
private int runTracing() {
switch (getNextArg()) {
case "start":
mInterface.startTracing();
break;
case "stop":
mInterface.stopTracing();
break;
}
return 0;
}
private int runGc() {
mInterface.runGcForTest();
return 0;
}
@Override
public void onHelp() {
final PrintWriter pw = getOutPrintWriter();
pw.println("Status bar commands:");
pw.println(" help");
pw.println(" Print this help text.");
pw.println("");
pw.println(" expand-notifications");
pw.println(" Open the notifications panel.");
pw.println("");
pw.println(" expand-settings");
pw.println(" Open the notifications panel and expand quick settings if present.");
pw.println("");
pw.println(" collapse");
pw.println(" Collapse the notifications and settings panel.");
pw.println("");
pw.println(" add-tile COMPONENT");
pw.println(" Add a TileService of the specified component");
pw.println("");
pw.println(" remove-tile COMPONENT");
pw.println(" Remove a TileService of the specified component");
pw.println("");
pw.println(" click-tile COMPONENT");
pw.println(" Click on a TileService of the specified component");
pw.println("");
pw.println(" check-support");
pw.println(" Check if this device supports QS + APIs");
pw.println("");
pw.println(" get-status-icons");
pw.println(" Print the list of status bar icons and the order they appear in");
pw.println("");
pw.println(" disable-for-setup DISABLE");
pw.println(" If true, disable status bar components unsuitable for device setup");
pw.println("");
pw.println(" send-disable-flag FLAG...");
pw.println(" Send zero or more disable flags (parsed individually) to StatusBarManager");
pw.println(" Valid options:");
pw.println(" <blank> - equivalent to \"none\"");
pw.println(" none - re-enables all components");
pw.println(" search - disable search");
pw.println(" home - disable naviagation home");
pw.println(" recents - disable recents/overview");
pw.println(" notification-peek - disable notification peeking");
pw.println(" statusbar-expansion - disable status bar expansion");
pw.println(" system-icons - disable system icons appearing in status bar");
pw.println(" clock - disable clock appearing in status bar");
pw.println(" notification-icons - disable notification icons from status bar");
pw.println("");
pw.println(" tracing (start | stop)");
pw.println(" Start or stop SystemUI tracing");
pw.println("");
pw.println(" NOTE: any command not listed here will be passed through to IStatusBar");
pw.println("");
pw.println(" Commands implemented in SystemUI:");
pw.flush();
// Sending null args to systemui will print help
mInterface.passThroughShellCommand(new String[] {}, getOutFileDescriptor());
}
/**
* Token to send to StatusBarManagerService for disable* commands
*/
private static final class StatusBarShellCommandToken extends Binder {
}
}
写在后面
如果文章中有错误的地方,希望各位大佬们批评指正~
If you like this article, it is written by Johnny Deng.
If not, I don’t know who wrote it.