[ Android 调试技巧 ] 为什么 service list 打印出来的服务信息为空?
尊重原创,转载请注明出处!
创作不易,如有帮助请点赞支持~
关于 service list 的分析
之前在开发调试以及定位问题的时候,经常会用到 adb shell service list
这个指令,用来查询系统服务,确认系统服务是否正常运行。
但是执行命令过程中,经常会发现部分服务打印出来的信息为空。随便截取了命令执行结果的部分:
XXX:/ # service list
Found 151 services:
90 pinner: []
96 vibrator: [android.os.IVibratorService]
97 overlay: [android.content.om.IOverlayManager]
98 content: [android.content.IContentService]
99 settings: []
100 account: [android.accounts.IAccountManager]
101 telephony.registry: [com.android.internal.telephony.ITelephonyRegistry]
106 battery: []
可以看到,vibrator、overlay 等服务后面是会打印对应的接口信息,但是 settings、battery 后面打印的信息为空!
之前一直以为没有打印出后面信息的,是表示服务挂掉了。但是后面测试发现并不一定,有些打印信息为空的服务实际上还是可以正常调用。
最近查一个问题的时候,通过 service list
查询 SettingsService,又遇到了查询信息为空的情况,即上面所示的 settings。
信息为空到底表示什么情况?要搞清楚这个问题,还是得看看源码。
service 命令的源码路径:/frameworks/native/cmds/service/service.cpp
在 main 方法中接收参数,处理 service list
的命令:
int main(int argc, char* const argv[])
{
......
else if (strcmp(argv[optind], "list") == 0) {
// 首先通过 listServices 列举出所有的系统服务
Vector<String16> services = sm->listServices();
aout << "Found " << services.size() << " services:" << endl;
for (unsigned i = 0; i < services.size(); i++) {
String16 name = services[i];
// 通过 checkService 检查服务是否存在
sp<IBinder> service = sm->checkService(name);
aout << i
<< "\t" << good_old_string(name)
// 关键是这里,通过 get_interface_name 获取到服务名称后面打印的[]内的信息
<< ": [" << good_old_string(get_interface_name(service)) << "]"
<< endl;
}
}
......
}
从上面代码可以发现,[] 中打印的内容,是通过 get_interface_name
函数得到的,我们再来看看 它的实现:
// get the name of the generic interface we hold a reference to
static String16 get_interface_name(sp<IBinder> service)
{
// 注意,这里当service不为空才会调到服务端。也就是当service挂掉时,一样返回空信息
if (service != NULL) {
Parcel data, reply;
// 客户端调用接口获取返回的字符串,code为INTERFACE_TRANSACTION
status_t err = service->transact(IBinder::INTERFACE_TRANSACTION, data, &reply);
if (err == NO_ERROR) {
return reply.readString16();
}
}
return String16();
}
protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply,
int flags) throws RemoteException {
......
if (code == INTERFACE_TRANSACTION) {
// 服务端将getInterfaceDescriptor的结果返回给客户端
reply.writeString(getInterfaceDescriptor());
return true;
}
......
}
getInterfaceDescriptor
即返回接口名称,如下:
/**
* Default implementation returns an empty interface name.
*/
public @Nullable String getInterfaceDescriptor() {
return mDescriptor;
}
从上面代码可以知道,service list
打印出来的是服务名称 + 接口名称。当服务没有对 descriptor 进行赋值时,service list
打印出来的信息自然也为空。
因此,service list
打印出来的服务信息为空,并不代表服务已经挂掉了!
PS:一般情况下,通过 aidl 声明接口,编译生成后的 java 代码会自动生成对应包名+类名的 DESCRIPTOR,因此继承自 Stub 的服务都能正常打印出接口名称:
如果是自己写的服务,继承 Binder,实现 IInterface 接口,则默认情况下无法打印接口名称,即 [] 的情况
当然,这里只说了 java 层的情况,关于 native 的服务,都是一样的道理,童鞋们可以自行研究。
通过 service check 查询服务
那么除了通过代码,还有什么指令可以确认服务是否挂掉吗?答案是 service check
命令。
XXX:/ # service list | grep xxtest
0 xxtest: []
XXX:/ # service check xxtest
Service xxtest: found
XXX:/ # 这时把进程 kill 掉,服务同时也会挂掉。再执行相同的指令
XXX:/ # service list | grep xxtest
0 xxtest: []
XXX:/ # service check xxtest
Service xxtest: not found
以上是自己测试的情况,应用在启动时添加 “xxtest” 的系统服务。
由于是直接继承 Binder,没有给 descriptor 赋值,因此 service list
打印信息为空;但是通过 service check
命令可以确认服务存在。
把应用进程 kill 掉后,service list
仍然可以列举出对应的服务;但是通过 service check
命令可以知道无法找到服务,即服务已经挂掉了。
总结
一、查询系统注册了哪些服务可以通过以下指令:adb shell service list
二、service list
打印 [ ] 时,可能是以下两种情况:
1、服务本身挂掉了
2、服务依然健在,但它的 descriptor 为空,因此不返回接口名称
三、查询服务是否挂掉可以通过以下指令:adb shell service check [service_name]