一起看看StatusBarManagerService(一)

写在前面

工作需要涉及到这部分代码,但是我对此了解很少;边学边总结,把这部分逻辑和涉及到的知识点弄明白。该系列不确定几篇,随缘。
本篇主要介绍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构造函数

StatusBarManagerService继承自IStatusBarService.Stub,IStatusBarService.aidl文件中声明的方法(截了一部分):

IStatusBarService

前面的方法为调用SBM常用方法,其中disable方法可以通过设置不同的flag来实现不同操作,详见:
StatusBarManager中的相关标志位

(2) StatusBarManagerService与systemui关联

systemui中有一个类,CommandQueue.java,继承IStatusBar.Stub;IStatusBar中声明的方法与前面IStatusBarService调到systemui的一致。

IStatusBar

systemui中各个模块想要接收SBM的回调都通过CommandQueue,向其注册Callback;callbak中方法声明和IStatusBar一致,只不过方法都是default空,可以根据需要选择实现。

CommandQueue.Callback

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

通过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构造函数

CommandQueue有一个成员变量CommandRegistry:

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.

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值