[ Android 调试技巧 ] 为什么 service list 打印出来的服务信息为空?

[ 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]

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值