之前遇到一个问题,就是cts挂测1-2天就会出现cts异常中断,后来找到原因是由于sn号改变,导致cts在手机重启后不能连上手机。
正常情况下,我们可以使用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侧。
通过上面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号,这样两个动作就是异步的,就有可能会有先后问题。最后解决方法很简单,就是把这两个函数的先后顺序换一换。