// TODO: BUG: The Peak on Windows rarely doesn't close the input thread. // This causes the UI thread to hang on exit. void *input_thread(void *_t, struct dll_io_bridge * _io_bridge) { i_bridge = _io_bridge; atransport *t = (atransport *)_t; apacket *p; int active = 0; D("%s: starting transport input thread, reading from fd %d\n", t->serial, t->fd); for(;;){ if(read_packet(t->fd, t->serial, &p)) { D("%s: failed to read apacket from transport on fd %d\n", t->serial, t->fd ); break; } if(p->msg.command == A_SYNC){ if(p->msg.arg0 == 0) { D("%s: transport SYNC offline\n", t->serial); put_apacket(p); break; } else { if(p->msg.arg1 == t->sync_token) { D("%s: transport SYNC online\n", t->serial); active = 1; } else { D("%s: transport ignoring SYNC %d != %d\n", t->serial, p->msg.arg1, t->sync_token); } } } else { if(active) { D("%s: transport got packet %d, sending to remote\n", t->serial, p->msg.command); t->write_to_remote(p, t); } else { D("%s: transport ignoring packet while offline\n", 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. D("Pre-close sockets input-thread\n"); close_all_sockets(t); D("%s: transport input thread is exiting, fd %d\n", t->serial, t->fd); #ifdef WIN32 kick_transport(t, i_bridge->AdbCloseHandle); #else kick_transport(t, NULL); #endif D("Post-kick transport input-thread\n"); transport_unref(t); D("Post-unref transport input-thread\n"); return 0; }
static void handle_output_oops(atransport * t, bool(*close_handle_func)(ADBAPIHANDLE)) { D("%s: transport output thread is exiting\n", t->serial); kick_transport(t, close_handle_func); D("After kick, before unref\n"); transport_unref(t); D("After unref\n"); }
static void *output_thread(void *_t) { atransport *t = reinterpret_cast<atransport*>(_t); apacket *p; ADB_LOGD(ADB_TSPT, "%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); ADB_LOGE(ADB_TSPT, "%s: failed to write SYNC packet", t->serial); goto oops; } ADB_LOGD(ADB_TSPT, "%s: data pump started", t->serial); for (;;) { p = get_apacket(); if (t->read_from_remote(p, t) == 0) { ADB_LOGD(ADB_TSPT, "%s: received remote packet, sending to transport", t->serial); if (write_packet(t->fd, t->serial, &p)) { put_apacket(p); ADB_LOGE(ADB_TSPT, "%s: failed to write apacket to transport", t->serial); goto oops; } } else { ADB_LOGE(ADB_TSPT, "%s: remote read failed for transport", t->serial); put_apacket(p); break; } } ADB_LOGD(ADB_TSPT, "%s: SYNC offline for transport", 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)) { put_apacket(p); ADB_LOGW(ADB_TSPT, "%s: failed to write SYNC apacket to transport", t->serial); } oops: ADB_LOGD(ADB_TSPT, "%s: transport output thread is exiting", t->serial); kick_transport(t); transport_unref(t); return 0; }
static void *input_thread(void *_t) { atransport *t = reinterpret_cast<atransport*>(_t); apacket *p; int active = 0; ADB_LOGD(ADB_TSPT, "%s: starting transport input thread, reading from fd %d", t->serial, t->fd); for (;;) { if (read_packet(t->fd, t->serial, &p)) { ADB_LOGE(ADB_TSPT, "%s: failed to read apacket from transport on fd %d", t->serial, t->fd); break; } if (p->msg.command == A_SYNC) { if (p->msg.arg0 == 0) { ADB_LOGE(ADB_TSPT, "%s: transport SYNC offline", t->serial); put_apacket(p); break; } else { if (p->msg.arg1 == t->sync_token) { ADB_LOGD(ADB_TSPT, "%s: transport SYNC online", t->serial); active = 1; } else { ADB_LOGD(ADB_TSPT, "%s: transport ignoring SYNC %d != %d", t->serial, p->msg.arg1, t->sync_token); } } } else { if (active) { ADB_LOGD(ADB_TSPT, "%s: transport got packet, sending to remote", t->serial); t->write_to_remote(p, t); } else { ADB_LOGD(ADB_TSPT, "%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); ADB_LOGD(ADB_TSPT, "%s: transport input thread is exiting, fd %d", t->serial, t->fd); kick_transport(t); transport_unref(t); return 0; }
// The transport is opened by transport_register_func before // the read_transport and write_transport threads are started. // // The read_transport thread issues a SYNC(1, token) message to let // the write_transport thread know to start things up. In the event // of transport IO failure, the read_transport thread will post a // SYNC(0,0) message to ensure shutdown. // // The transport will not actually be closed until both threads exit, but the threads // will kick the transport on their way out to disconnect the underlying device. // // read_transport thread reads data from a transport (representing a usb/tcp connection), // and makes the main thread call handle_packet(). static void *read_transport_thread(void *_t) { atransport *t = reinterpret_cast<atransport*>(_t); apacket *p; adb_thread_setname(android::base::StringPrintf("<-%s", (t->serial != nullptr ? t->serial : "transport"))); D("%s: starting read_transport 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); goto oops; } } else { D("%s: remote read failed for transport", t->serial); put_apacket(p); break; } } D("%s: SYNC offline for transport", 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)) { put_apacket(p); D("%s: failed to write SYNC apacket to transport", t->serial); } oops: D("%s: read_transport thread is exiting", t->serial); kick_transport(t); transport_unref(t); return 0; }
void unregister_transport(atransport *t) { adb_mutex_lock(&transport_lock); t->next->prev = t->prev; t->prev->next = t->next; adb_mutex_unlock(&transport_lock); kick_transport(t); transport_unref(t); }
static void *output_thread(void *_t) { atransport *t = _t; apacket *p; D("from_remote: starting thread for transport %p, on fd %d\n", t, t->fd ); D("from_remote: transport %p SYNC online (%d)\n", t, 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, &p)) { put_apacket(p); D("from_remote: failed to write SYNC apacket to transport %p", t); goto oops; } D("from_remote: data pump for transport %p\n", t); for(;;) { p = get_apacket(); if(t->read_from_remote(p, t) == 0){ D("from_remote: received remote packet, sending to transport %p\n", t); if(write_packet(t->fd, &p)){ put_apacket(p); D("from_remote: failed to write apacket to transport %p", t); goto oops; } } else { D("from_remote: remote read failed for transport %p\n", p); put_apacket(p); break; } } D("from_remote: SYNC offline for transport %p\n", t); 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, &p)) { put_apacket(p); D("from_remote: failed to write SYNC apacket to transport %p", t); } oops: D("from_remote: thread is exiting for transport %p\n", t); kick_transport(t); transport_unref(t); return 0; }
TEST(transport, kick_transport_already_kicked) { // Ensure that the transport is not modified if the transport has already been // kicked. TestTransport t; t.kicked = 1; t.kick = [](atransport*) { FAIL() << "Kick should not have been called"; }; TestTransport expected; expected.kicked = 1; expected.kick = t.kick; kick_transport(&t); ASSERT_EQ(expected, t); }
static void *input_thread(void *_t) { atransport *t = _t; apacket *p; int active = 0; D("to_remote: starting input_thread for %p, reading from fd %d\n", t, t->fd); for(;;){ if(read_packet(t->fd, &p)) { D("to_remote: failed to read apacket from transport %p on fd %d\n", t, t->fd ); break; } if(p->msg.command == A_SYNC){ if(p->msg.arg0 == 0) { D("to_remote: transport %p SYNC offline\n", t); put_apacket(p); break; } else { if(p->msg.arg1 == t->sync_token) { D("to_remote: transport %p SYNC online\n", t); active = 1; } else { D("to_remote: trandport %p ignoring SYNC %d != %d\n", t, p->msg.arg1, t->sync_token); } } } else { if(active) { D("to_remote: transport %p got packet, sending to remote\n", t); t->write_to_remote(p, t); } else { D("to_remote: transport %p ignoring packet while offline\n", t); } } 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("to_remote: thread is exiting for transport %p, fd %d\n", t, t->fd); kick_transport(t); transport_unref(t); return 0; }
// write_transport thread gets packets sent by the main thread (through send_packet()), // and writes to a transport (representing a usb/tcp connection). static void *write_transport_thread(void *_t) { atransport *t = reinterpret_cast<atransport*>(_t); apacket *p; int active = 0; adb_thread_setname(android::base::StringPrintf("->%s", (t->serial != nullptr ? t->serial : "transport"))); D("%s: starting write_transport 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 ); break; } if(p->msg.command == A_SYNC){ if(p->msg.arg0 == 0) { D("%s: transport SYNC offline", t->serial); put_apacket(p); break; } else { if(p->msg.arg1 == t->sync_token) { D("%s: transport SYNC online", 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); } D("%s: write_transport thread is exiting, fd %d", t->serial, t->fd); kick_transport(t); transport_unref(t); return 0; }
TEST(transport, kick_transport) { TestTransport t; // Mutate some member so we can test that the function is run. t.kick = [](atransport* trans) { trans->fd = 42; }; TestTransport expected; expected.kick = t.kick; expected.fd = 42; expected.kicked = 1; kick_transport(&t); ASSERT_EQ(42, t.fd); ASSERT_EQ(1, t.kicked); ASSERT_EQ(expected, t); }
static void reconnect_service(int fd, void* arg) { WriteFdExactly(fd, "done"); adb_close(fd); atransport* t = static_cast<atransport*>(arg); kick_transport(t); }
int handle_host_request(const char* service, TransportType type, const char* serial, int reply_fd, asocket* s) { if (strcmp(service, "kill") == 0) { fprintf(stderr, "adb server killed by remote request\n"); fflush(stdout); SendOkay(reply_fd); // On Windows, if the process exits with open sockets that // shutdown(SD_SEND) has not been called on, TCP RST segments will be // sent to the peers which will cause their next recv() to error-out // with WSAECONNRESET. In the case of this code, that means the client // may not read the OKAY sent above. adb_shutdown(reply_fd); exit(0); } #if ADB_HOST // "transport:" is used for switching transport with a specified serial number // "transport-usb:" is used for switching transport to the only USB transport // "transport-local:" is used for switching transport to the only local transport // "transport-any:" is used for switching transport to the only transport if (!strncmp(service, "transport", strlen("transport"))) { TransportType type = kTransportAny; if (!strncmp(service, "transport-usb", strlen("transport-usb"))) { type = kTransportUsb; } else if (!strncmp(service, "transport-local", strlen("transport-local"))) { type = kTransportLocal; } else if (!strncmp(service, "transport-any", strlen("transport-any"))) { type = kTransportAny; } else if (!strncmp(service, "transport:", strlen("transport:"))) { service += strlen("transport:"); serial = service; } std::string error; atransport* t = acquire_one_transport(type, serial, nullptr, &error); if (t != nullptr) { s->transport = t; SendOkay(reply_fd); } else { SendFail(reply_fd, error); } return 1; } // return a list of all connected devices if (!strncmp(service, "devices", 7)) { bool long_listing = (strcmp(service+7, "-l") == 0); if (long_listing || service[7] == 0) { D("Getting device list..."); std::string device_list = list_transports(long_listing); D("Sending device list..."); return SendOkay(reply_fd, device_list); } return 1; } if (!strcmp(service, "features")) { std::string error; atransport* t = acquire_one_transport(type, serial, nullptr, &error); if (t != nullptr) { SendOkay(reply_fd, FeatureSetToString(t->features())); } else { SendFail(reply_fd, error); } return 0; } // remove TCP transport if (!strncmp(service, "disconnect:", 11)) { const std::string address(service + 11); if (address.empty()) { kick_all_tcp_devices(); return SendOkay(reply_fd, "disconnected everything"); } std::string serial; std::string host; int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT; std::string error; if (!android::base::ParseNetAddress(address, &host, &port, &serial, &error)) { return SendFail(reply_fd, android::base::StringPrintf("couldn't parse '%s': %s", address.c_str(), error.c_str())); } atransport* t = find_transport(serial.c_str()); if (t == nullptr) { return SendFail(reply_fd, android::base::StringPrintf("no such device '%s'", serial.c_str())); } kick_transport(t); return SendOkay(reply_fd, android::base::StringPrintf("disconnected %s", address.c_str())); } // Returns our value for ADB_SERVER_VERSION. if (!strcmp(service, "version")) { return SendOkay(reply_fd, android::base::StringPrintf("%04x", ADB_SERVER_VERSION)); } // These always report "unknown" rather than the actual error, for scripts. if (!strcmp(service, "get-serialno")) { std::string error; atransport* t = acquire_one_transport(type, serial, nullptr, &error); if (t) { return SendOkay(reply_fd, t->serial ? t->serial : "unknown"); } else { return SendFail(reply_fd, error); } } if (!strcmp(service, "get-devpath")) { std::string error; atransport* t = acquire_one_transport(type, serial, nullptr, &error); if (t) { return SendOkay(reply_fd, t->devpath ? t->devpath : "unknown"); } else { return SendFail(reply_fd, error); } } if (!strcmp(service, "get-state")) { std::string error; atransport* t = acquire_one_transport(type, serial, nullptr, &error); if (t) { return SendOkay(reply_fd, t->connection_state_name()); } else { return SendFail(reply_fd, error); } } // Indicates a new emulator instance has started. if (!strncmp(service, "emulator:", 9)) { int port = atoi(service+9); local_connect(port); /* we don't even need to send a reply */ return 0; } if (!strcmp(service, "reconnect")) { if (s->transport != nullptr) { kick_transport(s->transport); } return SendOkay(reply_fd, "done"); } #endif // ADB_HOST int ret = handle_forward_request(service, type, serial, reply_fd); if (ret >= 0) return ret - 1; return -1; }
int handle_host_request(const char* service, TransportType type, const char* serial, int reply_fd, asocket* s) { if (strcmp(service, "kill") == 0) { fprintf(stderr, "adb server killed by remote request\n"); fflush(stdout); SendOkay(reply_fd); // At least on Windows, if we exit() without shutdown(SD_SEND) or // closesocket(), the client's next recv() will error-out with // WSAECONNRESET and they'll never read the OKAY. adb_shutdown(reply_fd); exit(0); } #if ADB_HOST // "transport:" is used for switching transport with a specified serial number // "transport-usb:" is used for switching transport to the only USB transport // "transport-local:" is used for switching transport to the only local transport // "transport-any:" is used for switching transport to the only transport if (!strncmp(service, "transport", strlen("transport"))) { TransportType type = kTransportAny; if (!strncmp(service, "transport-usb", strlen("transport-usb"))) { type = kTransportUsb; } else if (!strncmp(service, "transport-local", strlen("transport-local"))) { type = kTransportLocal; } else if (!strncmp(service, "transport-any", strlen("transport-any"))) { type = kTransportAny; } else if (!strncmp(service, "transport:", strlen("transport:"))) { service += strlen("transport:"); serial = service; } std::string error_msg; atransport* t = acquire_one_transport(kCsAny, type, serial, &error_msg); if (t != nullptr) { s->transport = t; SendOkay(reply_fd); } else { SendFail(reply_fd, error_msg); } return 1; } // return a list of all connected devices if (!strncmp(service, "devices", 7)) { bool long_listing = (strcmp(service+7, "-l") == 0); if (long_listing || service[7] == 0) { D("Getting device list..."); std::string device_list = list_transports(long_listing); D("Sending device list..."); return SendOkay(reply_fd, device_list); } return 1; } if (!strcmp(service, "features")) { std::string error_msg; atransport* t = acquire_one_transport(kCsAny, type, serial, &error_msg); if (t != nullptr) { SendOkay(reply_fd, android::base::Join(t->features(), '\n')); } else { SendFail(reply_fd, error_msg); } return 0; } if (!strncmp(service, "check-feature:", strlen("check-feature:"))) { std::string error_msg; atransport* t = acquire_one_transport(kCsAny, type, serial, &error_msg); if (t && t->CanUseFeature(service + strlen("check-feature:"))) { // We could potentially extend this to reply with the feature // version if that becomes necessary. SendOkay(reply_fd, "1"); } else { // Empty response means unsupported feature. SendOkay(reply_fd, ""); } return 0; } // remove TCP transport if (!strncmp(service, "disconnect:", 11)) { const std::string address(service + 11); if (address.empty()) { kick_all_tcp_devices(); return SendOkay(reply_fd, "disconnected everything"); } std::string serial; std::string host; int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT; std::string error; if (!parse_host_and_port(address, &serial, &host, &port, &error)) { return SendFail(reply_fd, android::base::StringPrintf("couldn't parse '%s': %s", address.c_str(), error.c_str())); } atransport* t = find_transport(serial.c_str()); if (t == nullptr) { return SendFail(reply_fd, android::base::StringPrintf("no such device '%s'", serial.c_str())); } kick_transport(t); return SendOkay(reply_fd, android::base::StringPrintf("disconnected %s", address.c_str())); } // returns our value for ADB_SERVER_VERSION if (!strcmp(service, "version")) { return SendOkay(reply_fd, android::base::StringPrintf("%04x", ADB_SERVER_VERSION)); } // These always report "unknown" rather than the actual error, for scripts. if (!strcmp(service, "get-serialno")) { std::string ignored; atransport* t = acquire_one_transport(kCsAny, type, serial, &ignored); return SendOkay(reply_fd, (t && t->serial) ? t->serial : "unknown"); } if (!strcmp(service, "get-devpath")) { std::string ignored; atransport* t = acquire_one_transport(kCsAny, type, serial, &ignored); return SendOkay(reply_fd, (t && t->devpath) ? t->devpath : "unknown"); } if (!strcmp(service, "get-state")) { std::string ignored; atransport* t = acquire_one_transport(kCsAny, type, serial, &ignored); return SendOkay(reply_fd, t ? t->connection_state_name() : "unknown"); } // indicates a new emulator instance has started if (!strncmp(service, "emulator:", 9)) { int port = atoi(service+9); local_connect(port); /* we don't even need to send a reply */ return 0; } #endif // ADB_HOST int ret = handle_forward_request(service, type, serial, reply_fd); if (ret >= 0) return ret - 1; return -1; }