文章目录
  1. 1. 作用
    1. 1.1. list
    2. 1.2. debug
  2. 2. 源码分析
    1. 2.1. ListCommand
    2. 2.2. DebugCommand
    3. 2.3. HelpCommand
  3. 3. 总结

android 自带了很多 cmds 的小工具,都挺用有的,也有些比较有意思。这里介绍的是 lshal 这个工具。说之前先把相关源码位置说下(10.0 的):

1
2
3
4
5
# lshal 源码:
frameworks/native/cmds/lshal/
# auto vhal 源码:
hardware/interfaces/automotive/vehicle/2.0/default/

作用

之前的文章介绍的:Android 一些有意思的命令小工具 —— dumpsysAndroid 一些有意思的命令小工具 —— service 。service 的作用在于列出 ServiceManager 中已经注册了的服务,并且可以调用服务的接口来进行调试。而 dumpsys 则是调用指定服务的 dump 接口打印服务中的一些状态,用于调试。

上面这些工具都是针对系统的 Binder 服务的。从 Android 8.0 开始,Android 在 HAL 层引入了 HIDL 的设计。将 hal 层模块变成类似 Binder 的结构,通过统一的接口,来解耦 hal 层(详细信息可以去看官网的介绍:HIDL)。这个就相当于是在 hal 层也跑一些系统服务,只不过为了安全考虑,Android 把 hal 层的服务和 java 层的 SystemService 区分开了,hal 层使用的 binder 设备是 /dev/hwbinder(java 层的是 /dev/binder),hal 层的是 hwservicemanager(java 层的是 ServiceManager,不过这2个进程代码是一样的,就是启动的时候传入参数不一样,使用的 binder 设备节点不一样而已)。

lshal 就是针对 hidl 的 service 和 dumpsys 的集合。当然它的功能不止这些,看官方介绍,还能生成兼容性矩阵描述信息之类的(官方的介绍:LSHAL)。但是我们这里主要是介绍类似 service 和 dumpsys 的功能:

list

list 命令会列出所有的 HIDL 服务(不加 list 也行,默认空参数就是 list 命令):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
| All binderized services (registered with hwservicemanager)
VINTF R Interface Thread Use Server Clients
FM Y android.frameworks.cameraservice.service@2.0::ICameraService/default 0/3 1896 1709
FM Y android.frameworks.displayservice@1.0::IDisplayService/default 0/1 1862 1709
DC,FM Y android.frameworks.schedulerservice@1.0::ISchedulingPolicyService/default 0/5 2032 1709
FM Y android.frameworks.sensorservice@1.0::ISensorManager/default 0/5 2032 1709
FM Y android.frameworks.stats@1.0::IStats/default 0/1 1908 1709
DM,FC Y android.hardware.audio.effect@5.0::IEffectsFactory/default 0/4 1831 1846 1709
DM,FC Y android.hardware.audio@5.0::IDevicesFactory/default 0/4 1831 1846 1709
DM N android.hardware.automotive.evs@1.0::IEvsEnumerator/EvsEnumeratorHw 0/1 1832 1709
DM,FC N android.hardware.automotive.vehicle@2.0::IVehicle/default 0/3 12682 1709
DM,FC Y android.hardware.camera.provider@2.4::ICameraProvider/legacy/0 0/4 2112 1896 1709
...
| All interfaces that getService() has ever returned as a passthrough interface;
| PIDs / processes shown below might be inaccurate because the process
| might have relinquished the interface or might have died.
| The Server / Server CMD column can be ignored.
| The Clients / Clients CMD column shows all process that have ever dlopen'ed
| the library and successfully fetched the passthrough implementation.
VINTF R Interface Thread Use Server Clients
FC ? android.hardware.audio.effect@5.0::IEffectsFactory/default N/A 1831 1831
FC ? android.hardware.audio@5.0::IDevicesFactory/default N/A 1831 1831
FC ? android.hardware.camera.provider@2.4::ICameraProvider/legacy/0 N/A 2112 2112
X ? android.hardware.gnss@1.0::IGnss/default N/A 1836 1836
FC ? android.hardware.graphics.allocator@2.0::IAllocator/default N/A 1837 1837
FC ? android.hardware.graphics.composer@2.1::IComposer/default N/A 1838 1838
...
| All available passthrough implementations (all -impl.so files).
| These may return subclasses through their respective HIDL_FETCH_I* functions.
VINTF R Interface Thread Use Server Clients
FC ? android.hardware.audio.effect@5.0::I*/* (/vendor/lib/hw/) N/A N/A 1831
FC ? android.hardware.audio@5.0::I*/* (/vendor/lib/hw/) N/A N/A 1831
FC ? android.hardware.camera.provider@2.4::I*/* (/vendor/lib/hw/) N/A N/A 2112
FC ? android.hardware.drm@1.0::I*/* (/vendor/lib/hw/) N/A N/A
X ? android.hardware.gnss@1.0::I*/* (/vendor/lib/hw/) N/A N/A 1836
FC ? android.hardware.graphics.allocator@2.0::I*/* (/vendor/lib/hw/) N/A N/A 1837
FC ? android.hardware.graphics.composer@2.1::I*/* (/vendor/lib/hw/) N/A N/A 1838
...

这里分了几个类别把所有的 hidl 都列了出来,从源码里面可以看得到所有类别,对应类别没有的就没列:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
mServicesTable.setDescription(
"| All binderized services (registered with hwservicemanager)");
mPassthroughRefTable.setDescription(
"| All interfaces that getService() has ever returned as a passthrough interface;\n"
"| PIDs / processes shown below might be inaccurate because the process\n"
"| might have relinquished the interface or might have died.\n"
"| The Server / Server CMD column can be ignored.\n"
"| The Clients / Clients CMD column shows all process that have ever dlopen'ed \n"
"| the library and successfully fetched the passthrough implementation.");
mImplementationsTable.setDescription(
"| All available passthrough implementations (all -impl.so files).\n"
"| These may return subclasses through their respective HIDL_FETCH_I* functions.");
mManifestHalsTable.setDescription(
"| All HALs that are in VINTF manifest.");
mLazyHalsTable.setDescription(
"| All HALs that are declared in VINTF manifest:\n"
"| - as hwbinder HALs but are not registered to hwservicemanager, and\n"
"| - as hwbinder/passthrough HALs with no implementation.");

    (1). 绑定式HAL: 使用 hwbinder IPC 通讯实现的 hal。mServicesTable 属于这一类。这也是 8.0 之后的新 hal 架构,大多数的 hal 是这种类型(上面我没贴完整所有的 hidl)。
    (2). 直通式HAL: 又分2种:一种是以 HIDL 分装的传统的 hal,这个是 mImplementationsTable 类别;另一种就是完全传统的 hal,这个是 mPassthroughRefTable 类别。这2类的比较少,应该是要慢慢淘汰掉的。

还有剩下2个类别:mManifestHalsTable 好像是单纯的 VINTF manifest 文件,mLazyHalsTable 好像是使用了 HIDL 框架的但是没在 hwservicemanager 中注册的和没有真正实现的。这2类目前都没有,先暂时不管先。详细的 hal 分类可以看官网说明:HAL 类型

下面列出来每一列的含义:
    (1). VINTF: 这个叫供应商接口(Vendor Interface),主要是用于描述 hal 的接口信息的,现在 hal interface 也分版本了,就涉及到兼容性。具体的可以看官网说明:供应商接口对象 。列出的一些 FM、DC、FC 我暂时还没去搞明白是啥意思,以后再说。
    (2). R: 这个 R 我也暂时还没去搞明白是啥意思,以后再说。
    (3). Interface: 这个就是 hidl 向 hwservicemanager 注册服务的接口名字,client 需要通过名字来获取对应的服务对象。这个和 java 的 SystemService 是类似的。例如说 ActivityManager 的接口名字叫 activity,WindowManager 的叫 window,只不过 hidl 的名字普遍比较长(包名)还带版本。
    (4). Thread: 应该是 hidl 中运行的服务线程数量,但是有2个,我猜测应该第一个是正在响应服务调用的,后面那个应该是线程池的数量。具体我还没去核对源码,后面再说。
    (5). Use: 空的,好像没啥用
    (6). Server: 服务运行的进程 pid
    (7). Clients: 连接的 client 的进程的 pid

lshal 列出的信息比 service 多很多。虽然有些含义目前还没明白是什么意思,但是用来确认 hal 服务有没有在运行已经足够了。

debug

lshal debug xx :
(待补充)

源码分析

lshal 的源码目录结构如下:

1
2
3
4
Android.bp DebugCommand.h libprocpartition Lshal.cpp NullableOStream.h PipeRelay.h test.cpp Timeout.h
Command.h HelpCommand.cpp ListCommand.cpp Lshal.h OWNERS TableEntry.cpp TextTable.cpp utils.cpp
DebugCommand.cpp HelpCommand.h ListCommand.h main.cpp PipeRelay.cpp TableEntry.h TextTable.h utils.h

lshal 是一个 bin 文件,程序入口在 Lshal.cpp 的 mian 函数里面。命令处理的方式采用了面向对象的设计思想:可以看到源码目录下有下面几个文件:

1
2
3
4
5
Command.h
HelpCommand.h
ListCommand.h
DebugCommand.h

Command 是基类,定义了命令的基本接口,Help、List、Debug 分别实现了 “help”, “list”, “debug” 命令。现在连小工具都采用 c++ 面向对象了,am 命令也是这样了,以前还都是 c ,面向结构的。在 Lshal.cpp 里面有讲这些命令的对象加入到命令列表里面:

1
2
3
4
5
6
7
8
9
10
11
12
13
Lshal::Lshal(std::ostream &out, std::ostream &err,
sp<hidl::manager::V1_0::IServiceManager> serviceManager,
sp<hidl::manager::V1_0::IServiceManager> passthroughManager)
: mOut(out), mErr(err),
mServiceManager(serviceManager),
mPassthroughManager(passthroughManager) {
// 加入3个支持的命令,后面如果要支持其他命令只要扩展实现了 Command,直接添加就行了,不用改程序流程结构
mRegisteredCommands.push_back({std::make_unique<ListCommand>(*this)});
mRegisteredCommands.push_back({std::make_unique<DebugCommand>(*this)});
mRegisteredCommands.push_back({std::make_unique<HelpCommand>(*this)});
}

通过 Command 的名字来匹配要执行的命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
Command* Lshal::selectCommand(const std::string& command) const {
// 空参数就是 list 命令
if (command.empty()) {
return selectCommand(ListCommand::GetName());
}
// 根据参数字符串匹配命令池中匹配的命令
for (const auto& e : mRegisteredCommands) {
if (e->getName() == command) {
return e.get();
}
}
return nullptr;
}
Status Lshal::main(const Arg &arg) {
// Allow SIGINT to terminate all threads.
signal(SIGINT, signalHandler);
Status status = parseArgs(arg);
if (status != OK) {
usage();
return status;
}
auto c = selectCommand(mCommand);
if (c == nullptr) {
// unknown command, print global usage
usage();
return USAGE;
}
status = c->main(arg);
...
}

ListCommand

这个源码比较复杂(获取的信息很多,所以列出的信息也多),暂时没分析。

DebugCommand

debug 命令的调用流程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
// DebugCommand.cpp:
// -------------------------------
Status DebugCommand::main(const Arg &arg) {
Status status = parseArgs(arg);
if (status != OK) {
return status;
}
auto pair = splitFirst(mInterfaceName, '/');
FQName fqName;
if (!FQName::parse(pair.first, &fqName) || fqName.isIdentifier() || !fqName.isFullyQualified()) {
mLshal.err() << "Invalid fully-qualified name '" << pair.first << "'\n\n";
return USAGE;
}
return mLshal.emitDebugInfo(
pair.first, pair.second.empty() ? "default" : pair.second, mOptions,
mExcludesParentInstances,
mLshal.out().buf(),
mLshal.err());
}
// lshal.cpp:
// -------------------------------
Status Lshal::emitDebugInfo(
const std::string &interfaceName,
const std::string &instanceName,
const std::vector<std::string> &options,
bool excludesParentInstances,
std::ostream &out,
NullableOStream<std::ostream> err) const {
using android::hidl::base::V1_0::IBase;
using android::hardware::details::getDescriptor;
// 通过接口名字获取 hidl Client对象,接口名字list命令可以列出来
hardware::Return<sp<IBase>> retBase = serviceManager()->get(interfaceName, instanceName);
if (!retBase.isOk()) {
std::string msg = "Cannot get " + interfaceName + "/" + instanceName + ": "
+ retBase.description();
err << msg << std::endl;
LOG(ERROR) << msg;
return TRANSACTION_ERROR;
}
sp<IBase> base = retBase;
if (base == nullptr) {
std::string msg = interfaceName + "/" + instanceName + " does not exist, or "
+ "no permission to connect.";
err << msg << std::endl;
LOG(ERROR) << msg;
return NO_INTERFACE;
}
if (excludesParentInstances) {
const std::string descriptor = getDescriptor(base.get());
if (descriptor.empty()) {
std::string msg = interfaceName + "/" + instanceName + " getDescriptor failed";
err << msg << std::endl;
LOG(ERROR) << msg;
}
if (descriptor != interfaceName) {
return OK;
}
}
PipeRelay relay(out);
if (relay.initCheck() != OK) {
std::string msg = "PipeRelay::initCheck() FAILED w/ " + std::to_string(relay.initCheck());
err << msg << std::endl;
LOG(ERROR) << msg;
return IO_ERROR;
}
deleted_unique_ptr<native_handle_t> fdHandle(
native_handle_create(1 /* numFds */, 0 /* numInts */),
native_handle_delete);
// 这个 fd 是传给 hidl 用于打印的 fd,可以用 dprintf 打印在 shell 里面
fdHandle->data[0] = relay.fd();
// 调用 hidl 的 debug 接口,需要 hidl 自行实现
hardware::Return<void> ret = base->debug(fdHandle.get(), convert(options));
if (!ret.isOk()) {
std::string msg = "debug() FAILED on " + interfaceName + "/" + instanceName + ": "
+ ret.description();
err << msg << std::endl;
LOG(ERROR) << msg;
return TRANSACTION_ERROR;
}
return OK;
}

debug 命令最后是调用到了指定 hidl 的 debug 接口,debug 这个接口是 hidl 基类里面定义的,可以实现,也可以不实现:

1
2
virtual ::android::hardware::Return<void> debug(const ::android::hardware::hidl_handle& fd, const ::android::hardware::hidl_vec<::android::hardware::hidl_string>& optio

这个是由 hidl 的接口定义文件 .hal 自动生成的,生成的文件在: out/.intermediates/hardware/interfaces/xx/xx_genc++_headers/gen/xx/xx.h。和 aidl 类似,都是由一个工具在编译的时候自动生成源码(aidl 可以参看以前的分析文章:Android Binder 分析——懒人的工具(AIDL))。

例如说我们可以在 hidl 的 debug 接口里面实现打印一些关键变量信息(就变成了 dumpsys),它还可以接收参数,我们也可以用来做一些调试接口(dumpsys 也可以接受参数,但是 lshal 没法自由的直接调用所有接口,和 service 的 call 命令还是有点区别)。

HelpCommand

这个很简单了,就是简单的打印帮助信息,不分析了。

总结

我在接触 hidl 的时候,就在想有没有类似 SS 的 dumpsys 和 service 命令,然后看官方介绍,还真的有。这个工具在调试 hidl 还是挺好用的。

文章目录
  1. 1. 作用
    1. 1.1. list
    2. 1.2. debug
  2. 2. 源码分析
    1. 2.1. ListCommand
    2. 2.2. DebugCommand
    3. 2.3. HelpCommand
  3. 3. 总结