Android4.0以上版本增加调试白名单功能,意在提高系统的安全性,但是对我们这些搞自动化测试的人来说,这个东西还是有些麻烦。特别是事先并不知道哪个PC会来调试的情况,就比较难办了。
怎么解决呢?看看源代码是如何实现这个功能的吧。
源码位置在android_source/system/core/adb/adb.c,当看到第一行就我就大喜:
property_get("ro.adb.secure",value, "0");
auth_enabled = !strcmp(value,"1");
从上面代码可以看出,只要ro.adb.secure不为1,那么这个功能就关闭了。于是尝试修改这个配置项,它存在于系统根目录default.prop。发现这个文件是只读的,怎么修改也不行,后面进一步了解,他是固化在boot.img中的,要修改的话要修改boot.img中的ramdisk 然后重新刷到机器中,感觉这个方法不是很方便。
下面只有想办法通过其他途径了,那就来看看是adb鉴权的流程吧,如果需要鉴权则:
if (auth_enabled)
adb_auth_init();
其中adb_auth_init()中有一句:
fd = android_get_control_socket("adbd");
上面是一个启动一个PF_UNIX本地socket服务端,这个地方暂时说到这里,后面会再说这个服务的作用。
PC端adb连接手机端adbd成功后会发送一个连接CNXN请求,adbd收到这个请求发现需要鉴权会生成一个随机令牌发送鉴权请求AUTH(ADB_AUTH_TOKEN)给PC客户端。
case A_CNXN:
if (HOST || !auth_enabled) {
handle_online(t);
if(!HOST) send_connect(t);
} else{
send_auth_request(t);// 请求客户来鉴权ADB_AUTH_TOKEN
}
case A_AUTH:
if (p->msg.arg0 == ADB_AUTH_TOKEN){
t->key = adb_auth_nextkey(t->key);
if(t->key) {
// 发送ADB_AUTH_SIGNATURE,将已签名私钥发送adbd
send_auth_response(p->data,p->msg.data_length, t);
} elseif (p->msg.arg0 == ADB_AUTH_SIGNATURE) {
if(adb_auth_verify(t->token, p->data, p->msg.data_length)) {
adb_auth_verified(t); // 鉴权成功
t->failed_auth_attempts = 0;
} else{
if(t->failed_auth_attempts++> 10)
adb_sleep_ms(1000);
send_auth_request(t);// 鉴权失败,继续请求客户端来鉴权
}
此时客户端再来进入ADB_AUTH_TOKEN:
if (p->msg.arg0 == ADB_AUTH_TOKEN) {
t->key = adb_auth_nextkey(t->key);
if(t->key) {
send_auth_response(p->data, p->msg.data_length, t);
} else{
/* Nomore private keys to try, send the public key */
// 当没有私可发,就会把公钥发送手机端请求授权ADB_AUTH_RSAPUBLICKEY
send_auth_publickey(t);
}
此时手机端abdb进入:
else if (p->msg.arg0 == ADB_AUTH_RSAPUBLICKEY) {
adb_auth_confirm_key(p->data, p->msg.data_length, t);// 授权确认
}
进入adb_auth_confirm_key看看:
void adb_auth_confirm_key(unsigned char *key, size_t len, atransport *t)
{
char msg[MAX_PAYLOAD];
int ret;
if (framework_fd < 0) { // 这里的framework_fd就是上层framework与前面的说的本地服务adbd建立的连接
D("Client not connected\n");
return;
}
if (key[len - 1] != '\0') {
D("Key must be a null-terminated string\n");
return;
}
ret = snprintf(msg, sizeof(msg), "PK%s", key);// 简单包装一个消息
if (ret >= (signed)sizeof(msg)) {
D("Key too long. ret=%d", ret);
return;
}
D("Sending '%s'\n", msg);
ret = unix_write(framework_fd,msg, ret); // 发给上层去处理
if (ret < 0) {
D("Failed to write PK, errno=%d\n", errno);
return;
}
再到上层frameworks中找到frameworks/base/services/java/com/android/server/usb/UsbDebuggingManager.java这个类,这个类中会启动一个线程与本地的名为adbd的服务连接,等待发来的RSA鉴权公钥,然后弹出提示框让手机用户确认是否同意。如果同意则会将公钥写入adb_keys,然后返回OK,否则就返回NO。
那么如何让系统自动确认而不弹框呢?你可能已经有解决办法了,就是自己写一个socket client端,连接名为adbd的本地socket server。这样adbd就会把RSA公钥发给client,client再把公钥直接写入到/data/misc/adb/adb_keys中,再返回成功的标识OK给adbd,这时adbd就会重新加载adb_keys文件,鉴权就通过了。而且不会有任何提示框。
不过这个client不能在framework上实现,在连接的时候会报权限不足。因为这里面限制只有system权限才可以连接这个名为adbd的PF_UNIX的本地通信服务。所以先要把手机root一下,然后写一个native程序,这样就OK了。