之前遇到一个问题,就是cts挂测1-2天就会出现cts异常中断,后来找到原因是由于sn号改变,导致cts在手机重启后不能连上手机。
一、通过adb devices代码 得知pc获取手机sn号的方式
正常情况下,我们可以使用adb devices来查看手机的序列号,我们可以从adb 这块入手,去看pc是如何获取手机序列号的。
adb devices是pc侧的命令,我们先从adb的Commandline入手:
if (!strcmp(argv[0], "devices")) {
const char *listopt;
if (argc < 2) {
listopt = "";
} else if (argc == 2 && !strcmp(argv[1], "-l")) {
listopt = argv[1];
} else {
fprintf(stderr, "Usage: adb devices [-l]\n");
return 1;
}
std::string query = android::base::StringPrintf("host:%s%s", argv[0], listopt);
printf("List of devices attached\n");
return adb_query_command(query);
}
在处理adb devices的时候最后调用了adb_query_command
static int adb_query_command(const std::string& command) {
std::string result;
std::string error;
if (!adb_query(command, &result, &error)) {
fprintf(stderr, "error: %s\n", error.c_str());
return 1;
}
printf("%s\n", result.c_str());
return 0;
}
adb_query_command又调用了adb_query,在这个函数中又调用了adb_connect应该先和pc侧的server通信。
bool adb_query(const std::string& service, std::string* result, std::string* error) {
D("adb_query: %s\n", service.c_str());
int fd = adb_connect(service, error);
if (fd < 0) {
fprintf(stderr,"error: %s\n", error->c_str());
return 0;
}
result->clear();
if (!ReadProtocolString(fd, result, error)) {
adb_close(fd);
return false;
}
return true;
}
我们在看adb_main.cpp中有这么段代码,是在ADB_HOST这个宏中,说明是pc上的代码
#if ADB_HOST
HOST = 1;
#ifdef WORKAROUND_BUG6558362
if(is_daemon) adb_set_affinity();
#endif
usb_init();//这个函数比较关键
local_init(DEFAULT_ADB_LOCAL_TRANSPORT_PORT);
adb_auth_init();
std::string local_name = android::base::StringPrintf("tcp:%d", server_port);
if (install_listener(local_name, "*smartsocket*", NULL, 0)) {
exit(1);
}
usb_init函数,这就到了Usb_linux.cpp中了
void usb_init()
{
adb_thread_t tid;
struct sigaction actions;
LOG("%s: usb_linux usb_init\n", __FUNCTION__);
memset(&actions, 0, sizeof(actions));
sigemptyset(&actions.sa_mask);
actions.sa_flags = 0;
actions.sa_handler = sigalrm_handler;
sigaction(SIGALRM,& actions, NULL);
if(adb_thread_create(&tid, device_poll_thread, NULL)){
fatal_errno("cannot create input thread");
}
}
我们简单看下,usb_init是如何到Usb_linux.cpp中的,因为有好几个usb_init函数。
这个就需要看Android.mk文件了:
LOCAL_MODULE := adbd
LOCAL_FORCE_STATIC_EXECUTABLE := true
LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT_SBIN)
LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_SBIN_UNSTRIPPED)
LOCAL_C_INCLUDES += system/extras/ext4_utils
LOCAL_STATIC_LIBRARIES := \
libadbd \
libbase \
libfs_mgr \
liblog \
libcutils \
libc \
libmincrypt \
libselinux \
libext4_utils_static \
include $(BUILD_EXECUTABLE)
我们再来看libadbd这个静态库
LOCAL_MODULE := libadbd
LOCAL_CFLAGS := $(LIBADB_CFLAGS) -DADB_HOST=0
LOCAL_SRC_FILES := \
$(LIBADB_SRC_FILES) \
adb_auth_client.cpp \
fdevent.cpp \
jdwp_service.cpp \
qemu_tracing.cpp \
usb_linux_client.cpp \
LOCAL_SHARED_LIBRARIES := libbase
include $(BUILD_STATIC_LIBRARY)
这个静态库是包括usb_linux_client.cpp这个文件,说明adbd中参与编译的是usb_linux_client.cpp,而我们这边是usb_linux.cpp是host也就是pc侧的
如果是pc侧的代码,会定义类似下面这段代码,定义ADB_HOST这个宏
LOCAL_CFLAGS := -DADB_HOST=0
我们再回过头来看usb_init这个函数:
void usb_init()
{
adb_thread_t tid;
struct sigaction actions;
LOG("%s: usb_linux usb_init\n", __FUNCTION__);
memset(&actions, 0, sizeof(actions));
sigemptyset(&actions.sa_mask);
actions.sa_flags = 0;
actions.sa_handler = sigalrm_handler;
sigaction(SIGALRM,& actions, NULL);
if(adb_thread_create(&tid, device_poll_thread, NULL)){
fatal_errno("cannot create input thread");
}
}
调用了device_poll_thread函数:
static void* device_poll_thread(void* unused) {
D("Created device thread\n");
while (true) {
// TODO: Use inotify.
find_usb_device("/dev/bus/usb", register_device);
kick_disconnected_devices();
sleep(1);
}
return nullptr;
}
这个函数中又调用了register_device函数:
std::string serial_path = android::base::StringPrintf(
"/sys/bus/usb/devices/%s/serial", dev_path + 4);
std::string serial;
if (!android::base::ReadFileToString(serial_path, &serial)) {
D("[ usb read %s failed: %s ]\n", serial_path.c_str(), strerror(errno));
// We don't actually want to treat an unknown serial as an error because
// devices aren't able to communicate a serial number in early bringup.
// http://b/20883914
serial = "";
}
在register_device函数中最后是去读取sys/bus/usb/devices/%s/serial这个节点的。
说明adb devices读取sn号是通过pc侧读取的,而不是通过adb 到手机侧获取sn号,再传输过来。就是说明当pc和手机连接的时候,手机的usb驱动会和pc的usb驱动同步,从而将手机侧的sn号同步到pc侧。
二、设置sn号与连接usb
通过上面adb devices的分析,我们大体知道pc侧是如何获取到手机的sn号的。而我们现在就需要分析,为何手机多次重启后,sn号会改变,变成默认的sn号。
由此我想到了,我们的代码设置sn号,是在set_usb_serial_action函数中,而我们之前猜测,pc和手机是在usb连接的时候同步sn号的,而我们还有一个函数是get_amt_mode
是根据开机原因设置sys.lc.amtmode这个属性
void get_amt_mode()
{
char *amt_file = "/sys/bootinfo/get_boot_mode";
char amt_mode[15];
int fd, n;
fd = open(amt_file, O_RDONLY);
if (fd < 0) goto error;
n = read(fd, amt_mode, 14);
close(fd);
if (n < 0) goto error;
if(!strncmp(amt_mode, "amt1", 4)) {
property_set("sys.lc.amtmode", "1");
}else if(!strncmp(amt_mode, "amt3", 4)) {
property_set("sys.lc.amtmode", "3");
}else if(!strncmp(amt_mode, "normal", 6)) {
property_set("sys.lc.amtmode", "0");
}
return ;
error:
property_set("sys.lc.amtmode", "0");
}
而在init.rc中会监听这个属性,监听这个动作就有连接usb这块
on property:sys.lc.amtmode=3
write /sys/class/android_usb/android0/enable 0
write /sys/class/android_usb/android0/idVendor 18D1
write /sys/class/android_usb/android0/idProduct 181B
write /sys/class/android_usb/android0/functions serial,adb
write /sys/class/android_usb/android0/f_serial/serial_port_num 5
write /sys/class/android_usb/android0/enable 1
setprop sys.usb.state serial,adb
setprop sys.usb.config serial,adb
start adbd
start emsd
start debuggerd
start akmd09911
start memsicd
start AcdApiDaemon
start fmt
class_start amt3_services
on property:sys.lc.amtmode=1
write /sys/class/android_usb/android0/enable 0
write /sys/class/android_usb/android0/idVendor 18D1
write /sys/class/android_usb/android0/idProduct 181B
write /sys/class/android_usb/android0/functions serial,adb
write /sys/class/android_usb/android0/f_serial/serial_port_num 5
write /sys/class/android_usb/android0/enable 1
setprop sys.usb.state serial,adb
setprop sys.usb.config serial,adb
start adbd
start lc-oms-sa
start emsd_amt
start lc-oms-amt-1
start lc-oms-mla1-u
start lc-oms-mla2-u
start lc-oms-mla3-u
start lc-oms-mla4-u
start lc-oms-mla5-u
而我们之前两个函数是get_amt_mode在前,set_usb_serial_action在后,这样在get_amt_mode执行完就会设置sys.lc.amtmode这个属性,这个属性的监听在init进程中,而后才去在set_usb_serial_action中设置sn号,这样两个动作就是异步的,就有可能会有先后问题。最后解决方法很简单,就是把这两个函数的先后顺序换一换。