android6.0 adbd深い分析(四)adbd usb線を抜いて再接続する過程

24474 ワード

このブログでは、adbdがusb線で抜いてから接続するコードの流れを主に分析します.

一、log印刷プロセス


まず、自分でデバッグしたコード印刷を見てみましょう.
表示の問題でlogの時間を除いてpidとtidだけを表示しました
// usb 
185   188 I adbd    : output_thread:(null): remote read failed for transport
185   188 I adbd    : output_thread:(null) SYNC offline for transport
185   185 I adbd    : handle_packet: A_SYNC
185   185 I adbd    : handle_packet: A_SYNC CS_OFFLINE
185   187 I adbd    : input_thread:(null): transport SYNC offline
185   187 I adbd    : input_thread:(null): transport input thread is exiting, fd 13
185   186 I usb_adb_open_thread: adbd usb_thread - opening device
185   186 I usb_adb_open_thread: adbd opening device succeeded
185   185 I adbd    : handle_packet: A_SYNC
185   185 I adbd    : handle_packet: A_SYNC send_packet
185  2164 I adbd    : input_thread:(null): transport SYNC online



// usb 
185  2165 I adbd    : output_thread: read_from_remote after
185   185 I adbd    : handle_packet: A_CNXN
185   185 I adbd    : handle_packet: A_CNXN handle_online
185   185 I adbd    : handle_packet: A_CNXN send_connect
185  2165 I adbd    : output_thread: read_from_remote after
185   185 I adbd    : adb command: 'shell:dumpsys iphonesubinfo'
185  2165 I adbd    : output_thread: read_from_remote after
185  2165 I adbd    : output_thread: read_from_remote after
185   185 I adbd    : adb command: 'shell:dumpsys battery'
185  2165 I adbd    : output_thread: read_from_remote after
185  2165 I adbd    : output_thread: read_from_remote after
185  2165 I adbd    : output_thread: read_from_remote after
185   185 I adbd    : handle_packet: A_CNXN
185   185 I adbd    : handle_packet: A_CNXN handle_offline
185   185 I adbd    : handle_packet: A_CNXN handle_online
185   185 I adbd    : handle_packet: A_CNXN send_connect

二、usb線を抜く流れ


2.1 output_thread adb駆動データ読み出しエラー


私たちはlogに従って、まずoutput_です.thread adb駆動データの読み出しエラー
static void *output_thread(void *_t)
{
    atransport *t = reinterpret_cast<atransport*>(_t);
    apacket *p;

    D("%s: starting transport output thread on fd %d, SYNC online (%d)
", t->serial, t->fd, t->sync_token + 1); p = get_apacket(); p->msg.command = A_SYNC; p->msg.arg0 = 1; p->msg.arg1 = ++(t->sync_token); p->msg.magic = A_SYNC ^ 0xffffffff; if(write_packet(t->fd, t->serial, &p)) { put_apacket(p); D("%s: failed to write SYNC packet
", t->serial); goto oops; } D("%s: data pump started
", t->serial); for(;;) { p = get_apacket(); if(t->read_from_remote(p, t) == 0){ D("%s: received remote packet, sending to transport
", t->serial); if(write_packet(t->fd, t->serial, &p)){ put_apacket(p); D("%s: failed to write apacket to transport
", t->serial); LOG("%s:%s: failed to write apacket to transport
", __FUNCTION__, t->serial); goto oops; } } else { D("%s: remote read failed for transport
", t->serial); LOG("%s:%s: remote read failed for transport
", __FUNCTION__, t->serial);// adb put_apacket(p); break; } } D("%s: SYNC offline for transport
", t->serial); LOG("%s:%s SYNC offline for transport
", __FUNCTION__, t->serial);// p = get_apacket(); p->msg.command = A_SYNC; p->msg.arg0 = 0; p->msg.arg1 = 0; p->msg.magic = A_SYNC ^ 0xffffffff; if(write_packet(t->fd, t->serial, &p)) {// sockpair put_apacket(p); D("%s: failed to write SYNC apacket to transport", t->serial); }
output_thread adb駆動データの読み出しエラーが発生し、スレッドを終了しsockpairの反対側のデータを送信
反対側のsocketpair関連関数はtransport_socket_events関数
        fdevent_install(&(t->transport_fde),
                        t->transport_socket,
                        transport_socket_events,
                        t);

transport_socket_events関数、handleを呼び出しました.packet関数
static void transport_socket_events(int fd, unsigned events, void *_t)
{
    atransport *t = reinterpret_cast<atransport*>(_t);
    D("transport_socket_events(fd=%d, events=%04x,...)
", fd, events); if(events & FDE_READ){ apacket *p = 0; if(read_packet(fd, t->serial, &p)){ D("%s: failed to read packet from transport socket on fd %d
", t->serial, fd); } else { handle_packet(p, (atransport *) _t); } } }

2.2 handle_义齿


handle_packet関数受信A_SYNCコマンド、offline処理
void handle_packet(apacket *p, atransport *t)
{
    asocket *s;

    D("handle_packet() %c%c%c%c
", ((char*) (&(p->msg.command)))[0], ((char*) (&(p->msg.command)))[1], ((char*) (&(p->msg.command)))[2], ((char*) (&(p->msg.command)))[3]); print_packet("recv", p); switch(p->msg.command){ case A_SYNC: LOG("%s: A_SYNC
", __FUNCTION__);// sync if(p->msg.arg0){ send_packet(p, t); LOG("%s: A_SYNC send_packet
", __FUNCTION__); if(HOST) send_connect(t); } else { t->connection_state = CS_OFFLINE; LOG("%s: A_SYNC CS_OFFLINE
", __FUNCTION__);//offline handle_offline(t); send_packet(p, t); } return;

hand_packetはsyncメッセージを処理します.まずhandle_を見てみましょう.offline関数は、実は状態を処理します
void handle_offline(atransport *t)
{
    D("adb: offline
"); //Close the associated usb t->online = 0; run_transport_disconnects(t); }

2.3 Input_thread処理offline


offlineを処理する私たちはsend_を見ますpacketもsocketpairの反対側にデータを書き、inputThreadがデータ受信を行う
static void *input_thread(void *_t)
{
    atransport *t = reinterpret_cast<atransport*>(_t);
    apacket *p;
    int active = 0;

    D("%s: starting transport input thread, reading from fd %d
", t->serial, t->fd); for(;;){ if(read_packet(t->fd, t->serial, &p)) { D("%s: failed to read apacket from transport on fd %d
", t->serial, t->fd ); LOG("%s:%s: failed to read apacket from transport on fd %d
", __FUNCTION__, t->serial, t->fd ); break; } if(p->msg.command == A_SYNC){ if(p->msg.arg0 == 0) { D("%s: transport SYNC offline
", t->serial); put_apacket(p); LOG("%s:%s: transport SYNC offline
", __FUNCTION__, t->serial);// handle_packet offline , break; } else { if(p->msg.arg1 == t->sync_token) { LOG("%s:%s: transport SYNC online
", __FUNCTION__, t->serial); active = 1; } else { D("%s: transport ignoring SYNC %d != %d
", t->serial, p->msg.arg1, t->sync_token); } } } else { if(active) { D("%s: transport got packet, sending to remote
", t->serial); t->write_to_remote(p, t); } else { D("%s: transport ignoring packet while offline
", t->serial); } } put_apacket(p); } // this is necessary to avoid a race condition that occured when a transport closes // while a client socket is still active. close_all_sockets(t); D("%s: transport input thread is exiting, fd %d
", t->serial, t->fd); LOG("%s:%s: transport input thread is exiting, fd %d
", __FUNCTION__, t->serial, t->fd);// kick_transport(t);// kick_transport transport_unref(t); return 0;

Input_thread受信handle_packetからofflineメッセージが送られてきて、スレッドを直接終了し、kick_を呼び出しました.transport関数
void kick_transport(atransport* t)
{
    if (t && !t->kicked)
    {
        int  kicked;

        adb_mutex_lock(&transport_lock);
        kicked = t->kicked;
        if (!kicked)
            t->kicked = 1;
        adb_mutex_unlock(&transport_lock);

        if (!kicked)
            t->kick(t);
    }
}

ここでkickを呼び出したのはremote_kick関数
void init_usb_transport(atransport *t, usb_handle *h, int state)
{
    D("transport: usb
"); t->close = remote_close; t->kick = remote_kick; t->read_from_remote = remote_read; t->write_to_remote = remote_write; t->sync_token = 1; t->connection_state = state; t->type = kTransportUsb; t->usb = h; #if ADB_HOST HOST = 1; #else HOST = 0; #endif }

remote_kickはusbを呼び出したkick関数
static void remote_kick(atransport *t)
{
    usb_kick(t->usb);
}
でusbが呼び出されましたhandleのkick関数
void usb_kick(usb_handle *h)
{
    h->kick(h);
}

USb_adb_Init初期化時kickはusb_adb_kick
static void usb_adb_init()
{
    usb_handle* h = reinterpret_cast<usb_handle*>(calloc(1, sizeof(usb_handle)));
    if (h == nullptr) fatal("couldn't allocate usb_handle");

    h->write = usb_adb_write;
    h->read = usb_adb_read;
    h->kick = usb_adb_kick;

この関数を見てみましょう.最終的にsignalを送信しました.
static void usb_adb_kick(usb_handle *h)
{
    D("usb_kick
"); adb_mutex_lock(&h->lock); adb_close(h->fd); h->fd = -1; // notify usb_adb_open_thread that we are disconnected adb_cond_signal(&h->notify); adb_mutex_unlock(&h->lock); }

このsignalはusb_adb_open_thread待機状態が破られ、adbドライバが再開され、input_が再開されます.thread output_thread
static void *usb_adb_open_thread(void *x)
{
    struct usb_handle *usb = (struct usb_handle *)x;
    int fd;

    while (true) {
        // wait until the USB device needs opening
        adb_mutex_lock(&usb->lock);
        while (usb->fd != -1)
            adb_cond_wait(&usb->notify, &usb->lock);//  
        adb_mutex_unlock(&usb->lock);

        D("[ usb_thread - opening device ]
");// adb __android_log_print(ANDROID_LOG_INFO, __FUNCTION__, "adbd usb_thread - opening device
"); do { /* XXX use inotify? */ fd = unix_open("/dev/android_adb", O_RDWR); if (fd < 0) { // to support older kernels fd = unix_open("/dev/android", O_RDWR); } if (fd < 0) { adb_sleep_ms(1000); } } while (fd < 0); D("[ opening device succeeded ]
"); __android_log_print(ANDROID_LOG_INFO, __FUNCTION__, "adbd opening device succeeded
"); close_on_exec(fd); usb->fd = fd; D("[ usb_thread - registering device ]
"); register_usb_transport(usb, 0, 0, 1);// usb , input_thread output_thread } // never gets here return 0; }

2.3再開オープンアウトプット_thread input_threadスレッド


そしてoutput_threadが開いたらhandle_に向かいますpacketはA_を送信しますSYNCコマンド
static void *output_thread(void *_t)
{
    atransport *t = reinterpret_cast<atransport*>(_t);
    apacket *p;

    D("%s: starting transport output thread on fd %d, SYNC online (%d)
", t->serial, t->fd, t->sync_token + 1); p = get_apacket(); p->msg.command = A_SYNC; p->msg.arg0 = 1; p->msg.arg1 = ++(t->sync_token); p->msg.magic = A_SYNC ^ 0xffffffff; if(write_packet(t->fd, t->serial, &p)) { put_apacket(p); D("%s: failed to write SYNC packet
", t->serial); goto oops; }

そしてhandle_へpacketはこのsyncコマンドをどのように処理しますか?
void handle_packet(apacket *p, atransport *t)
{
    asocket *s;

    D("handle_packet() %c%c%c%c
", ((char*) (&(p->msg.command)))[0], ((char*) (&(p->msg.command)))[1], ((char*) (&(p->msg.command)))[2], ((char*) (&(p->msg.command)))[3]); print_packet("recv", p); switch(p->msg.command){ case A_SYNC: LOG("%s: A_SYNC
", __FUNCTION__); if(p->msg.arg0){// 1 send_packet(p, t);// input_thread LOG("%s: A_SYNC send_packet
", __FUNCTION__); if(HOST) send_connect(t); } else { t->connection_state = CS_OFFLINE; LOG("%s: A_SYNC CS_OFFLINE
", __FUNCTION__); handle_offline(t); send_packet(p, t); } return;

そしてinput_threadこのsyncコマンドを受信した後の処理は以下の通りです.
static void *input_thread(void *_t)
{
    atransport *t = reinterpret_cast<atransport*>(_t);
    apacket *p;
    int active = 0;

    D("%s: starting transport input thread, reading from fd %d
", t->serial, t->fd); for(;;){ if(read_packet(t->fd, t->serial, &p)) { D("%s: failed to read apacket from transport on fd %d
", t->serial, t->fd ); LOG("%s:%s: failed to read apacket from transport on fd %d
", __FUNCTION__, t->serial, t->fd ); break; } if(p->msg.command == A_SYNC){ if(p->msg.arg0 == 0) { D("%s: transport SYNC offline
", t->serial); put_apacket(p); LOG("%s:%s: transport SYNC offline
", __FUNCTION__, t->serial); break; } else { if(p->msg.arg1 == t->sync_token) {// sync , active 1, adb D("%s: transport SYNC online
", t->serial); LOG("%s:%s: transport SYNC online
", __FUNCTION__, t->serial); active = 1; } else { D("%s: transport ignoring SYNC %d != %d
", t->serial, p->msg.arg1, t->sync_token); LOG("%s:%s: transport ignoring SYNC
", __FUNCTION__, t->serial); } } } else { if(active) { D("%s: transport got packet, sending to remote
", t->serial); t->write_to_remote(p, t); } else { D("%s: transport ignoring packet while offline
", t->serial); } } put_apacket(p); }

input_threadはsyncコマンドを受信してactiveを1にし、次回adbドライバにデータを書くことができます.
しかし、疑問があるのではないでしょうか.この時usbが先に抜けていて、挿入されていないので、なぜこの時input_threadはadb駆動でデータを書くことができますか?答えはここです.
static void *output_thread(void *_t)
{
    atransport *t = reinterpret_cast<atransport*>(_t);
    apacket *p;

    D("%s: starting transport output thread on fd %d, SYNC online (%d)
", t->serial, t->fd, t->sync_token + 1); p = get_apacket(); p->msg.command = A_SYNC; p->msg.arg0 = 1; p->msg.arg1 = ++(t->sync_token); p->msg.magic = A_SYNC ^ 0xffffffff; if(write_packet(t->fd, t->serial, &p)) { put_apacket(p); D("%s: failed to write SYNC packet
", t->serial); goto oops; } D("%s: data pump started
", t->serial); for(;;) { p = get_apacket(); // , result , if , , log, int result = t->read_from_remote(p, t);// usb , LOG("%s: read_from_remote after
", __FUNCTION__); if(result == 0){ D("%s: received remote packet, sending to transport
", t->serial); if(write_packet(t->fd, t->serial, &p)){ put_apacket(p); D("%s: failed to write apacket to transport
", t->serial); LOG("%s:%s: failed to write apacket to transport
", __FUNCTION__, t->serial); goto oops; } } else { D("%s: remote read failed for transport
", t->serial); LOG("%s:%s: remote read failed for transport
", __FUNCTION__, t->serial); put_apacket(p); break; } }
この時私たちはoutputでthreadでコードを少し修正してlogを増やして、usb線が差し込まれていないことに気づいたとき、このときoutput_threadはadb駆動データを読み出す際にブロックされ、input_にデータが伝達されないthreadはadbドライバに書きましたが、もちろん問題はありません.

三、usb線を差し込む流れ


次に、usb線を細かく挿入するコードフローを分析します.
まずlogを見て、usb線を抜いてread_を印刷しました.from_remote afterというロゴは、この時点でoutput_threadはadb駆動からデータを読み出してブロックしなくなり、handle_にデータを送信しました.packet.
handle_packetはこの時adb駆動のA_を直接受け取りましたCNXNコマンド:
    case A_CNXN: /* CONNECT(version, maxdata, "system-id-string") */
            /* XXX verify version, etc */
	LOG("%s: A_CNXN
", __FUNCTION__); if(t->connection_state != CS_OFFLINE) { t->connection_state = CS_OFFLINE; handle_offline(t); LOG("%s: A_CNXN handle_offline
", __FUNCTION__); } parse_banner(reinterpret_cast<const char*>(p->data), t); if (HOST || !auth_required) { handle_online(t); LOG("%s: A_CNXN handle_online
", __FUNCTION__); if (!HOST) { send_connect(t); LOG("%s: A_CNXN send_connect
", __FUNCTION__); } } else { send_auth_request(t); LOG("%s: A_CNXN send_auth_request
", __FUNCTION__); } break;

最初からtransportのconnection_state = CS_OFFLINEなので、先にhandle_を処理しましたonline、send_を呼び出しました接続関数:
void handle_online(atransport *t)
{
    D("adb: online
"); t->online = 1; }

send_接続はinput_へthreadは接続情報を送信し、最後にadb駆動に送信し、pcはadbdと接続されていることを知っています.
void send_connect(atransport *t)
{
    D("Calling send_connect 
"); apacket *cp = get_apacket(); cp->msg.command = A_CNXN; cp->msg.arg0 = A_VERSION; cp->msg.arg1 = MAX_PAYLOAD; cp->msg.data_length = fill_connect_data((char *)cp->data, sizeof(cp->data)); send_packet(cp, t); }

そしてparse_bannerという関数でconnection_stateが処理しました
void parse_banner(const char* banner, atransport* t) {
    D("parse_banner: %s
", banner); // The format is something like: // "device::ro.product.name=x;ro.product.model=y;ro.product.device=z;". std::vector<std::string> pieces = android::base::Split(banner, ":"); if (pieces.size() > 2) { const std::string& props = pieces[2]; for (auto& prop : android::base::Split(props, ";")) { // The list of properties was traditionally ;-terminated rather than ;-separated. if (prop.empty()) continue; std::vector<std::string> key_value = android::base::Split(prop, "="); if (key_value.size() != 2) continue; const std::string& key = key_value[0]; const std::string& value = key_value[1]; if (key == "ro.product.name") { qual_overwrite(&t->product, value); } else if (key == "ro.product.model") { qual_overwrite(&t->model, value); } else if (key == "ro.product.device") { qual_overwrite(&t->device, value); } } } const std::string& type = pieces[0]; if (type == "bootloader") { D("setting connection_state to CS_BOOTLOADER
"); t->connection_state = CS_BOOTLOADER; update_transports(); } else if (type == "device") { D("setting connection_state to CS_DEVICE
"); t->connection_state = CS_DEVICE; update_transports(); } else if (type == "recovery") { D("setting connection_state to CS_RECOVERY
"); t->connection_state = CS_RECOVERY; update_transports(); } else if (type == "sideload") { D("setting connection_state to CS_SIDELOAD
"); t->connection_state = CS_SIDELOAD; update_transports(); } else { D("setting connection_state to CS_HOST
"); t->connection_state = CS_HOST; } }

だから私はA_を処理していますCNXNの時、parse_bannerは前後にロゴを打った
   case A_CNXN: /* CONNECT(version, maxdata, "system-id-string") */
            /* XXX verify version, etc */
		LOG("%s: A_CNXN
", __FUNCTION__); if(t->connection_state != CS_OFFLINE) { t->connection_state = CS_OFFLINE; handle_offline(t); LOG("%s: A_CNXN handle_offline
", __FUNCTION__); } LOG("%s: A_CNXN parse_banner before: connection_state:%d
", __FUNCTION__, t->connection_state); parse_banner(reinterpret_cast<const char*>(p->data), t); LOG("%s: A_CNXN parse_banner after: connection_state:%d
", __FUNCTION__, t->connection_state);

最後にusb線が差し込まれたときにconnection_が始まることに気づきましたstate:0つまりCS_OFFLINE、後は3つまりCS_になりましたHOST
185   185 I adbd    : handle_packet: A_CNXN
185   185 I adbd    : handle_packet: A_CNXN parse_banner before: connection_state:0
185   185 I adbd    : handle_packet: A_CNXN parse_banner after: connection_state:3
185   185 I adbd    : handle_packet: A_CNXN handle_online
185   185 I adbd    : handle_packet: A_CNXN send_connect

後ろのロゴについては、shell:dumpsys iphonesubinfo'shell:dumpsys battery'は別のコマンドです.
// usb 
185   185 I adbd    : handle_packet: A_CNXN
185   185 I adbd    : handle_packet: A_CNXN parse_banner before: connection_state:0
185   185 I adbd    : handle_packet: A_CNXN parse_banner after: connection_state:3
185   185 I adbd    : handle_packet: A_CNXN handle_online
185   185 I adbd    : handle_packet: A_CNXN send_connect
185   185 I adbd    : adb command: 'shell:dumpsys iphonesubinfo'
185   185 I adbd    : adb command: 'shell:dumpsys battery'
185   185 I adbd    : handle_packet: A_CNXN
185   185 I adbd    : handle_packet: A_CNXN handle_offline
185   185 I adbd    : handle_packet: A_CNXN parse_banner before: connection_state:0
185   185 I adbd    : handle_packet: A_CNXN parse_banner after: connection_state:3
185   185 I adbd    : handle_packet: A_CNXN handle_online
185   185 I adbd    : handle_packet: A_CNXN send_connect


最後にもう一つのコマンドがhandleを先に処理します.offline、状態はまず0で、それから3に変更します
    case A_CNXN: /* CONNECT(version, maxdata, "system-id-string") */
            /* XXX verify version, etc */
		LOG("%s: A_CNXN
", __FUNCTION__); if(t->connection_state != CS_OFFLINE) { t->connection_state = CS_OFFLINE;// handleoffline connection_state = CS_OFFLINE 0 handle_offline(t); LOG("%s: A_CNXN handle_offline
", __FUNCTION__); } LOG("%s: A_CNXN parse_banner before: connection_state:%d
", __FUNCTION__, t->connection_state); parse_banner(reinterpret_cast<const char*>(p->data), t); LOG("%s: A_CNXN parse_banner after: connection_state:%d
", __FUNCTION__, t->connection_state); ......

最後になぜ2回接続したのか、よくわかりませんが、これは協議で規定されているはずです.