コード例 #1
0
ファイル: services.cpp プロジェクト: darkLord19/system-core
static bool reboot_service_impl(int fd, const char* arg) {
    const char* reboot_arg = arg;
    bool auto_reboot = false;

    if (strcmp(reboot_arg, "sideload-auto-reboot") == 0) {
        auto_reboot = true;
        reboot_arg = "sideload";
    }

    // It reboots into sideload mode by setting "--sideload" or "--sideload_auto_reboot"
    // in the command file.
    if (strcmp(reboot_arg, "sideload") == 0) {
        if (getuid() != 0) {
            WriteFdExactly(fd, "'adb root' is required for 'adb reboot sideload'.\n");
            return false;
        }

        const std::vector<std::string> options = {
            auto_reboot ? "--sideload_auto_reboot" : "--sideload"
        };
        std::string err;
        if (!write_bootloader_message(options, &err)) {
            D("Failed to set bootloader message: %s", err.c_str());
            return false;
        }

        reboot_arg = "recovery";
    }

    sync();

    std::string reboot_string = android::base::StringPrintf("reboot,%s", reboot_arg);
    if (!android::base::SetProperty(ANDROID_RB_PROPERTY, reboot_string)) {
        WriteFdFmt(fd, "reboot (%s) failed\n", reboot_string.c_str());
        return false;
    }

    return true;
}
コード例 #2
0
static int write_data_link(int fd, const char *path, syncsendbuf *sbuf)
{
    int len, ret;

    len = readlink(path, sbuf->data, SYNC_DATA_MAX-1);
    if(len < 0) {
        fprintf(stderr, "error reading link '%s': %s\n", path, strerror(errno));
        return -1;
    }
    sbuf->data[len] = '\0';

    sbuf->size = htoll(len + 1);
    sbuf->id = ID_DATA;

    ret = !WriteFdExactly(fd, sbuf, sizeof(unsigned) * 2 + len + 1);
    if(ret)
        return -1;

    total_bytes += len + 1;

    return 0;
}
コード例 #3
0
TEST_F(LocalSocketTest, smoke) {
    const size_t PIPE_COUNT = 100;
    const size_t MESSAGE_LOOP_COUNT = 100;
    const std::string MESSAGE = "socket_test";
    int fd_pair1[2];
    int fd_pair2[2];
    ASSERT_EQ(0, adb_socketpair(fd_pair1));
    ASSERT_EQ(0, adb_socketpair(fd_pair2));
    pthread_t thread;
    ThreadArg thread_arg;
    thread_arg.first_read_fd = fd_pair1[0];
    thread_arg.last_write_fd = fd_pair2[1];
    thread_arg.middle_pipe_count = PIPE_COUNT;
    int writer = fd_pair1[1];
    int reader = fd_pair2[0];

    ASSERT_EQ(0, pthread_create(&thread, nullptr,
                                reinterpret_cast<void* (*)(void*)>(FdEventThreadFunc),
                                &thread_arg));

    usleep(1000);
    for (size_t i = 0; i < MESSAGE_LOOP_COUNT; ++i) {
        std::string read_buffer = MESSAGE;
        std::string write_buffer(MESSAGE.size(), 'a');
        ASSERT_TRUE(WriteFdExactly(writer, read_buffer.c_str(), read_buffer.size()));
        ASSERT_TRUE(ReadFdExactly(reader, &write_buffer[0], write_buffer.size()));
        ASSERT_EQ(read_buffer, write_buffer);
    }
    ASSERT_EQ(0, adb_close(writer));
    ASSERT_EQ(0, adb_close(reader));
    // Wait until the local sockets are closed.
    sleep(1);

    ASSERT_EQ(0, pthread_kill(thread, SIGUSR1));
    ASSERT_EQ(0, pthread_join(thread, nullptr));
}
コード例 #4
0
void restart_usb_service(int fd, void *cookie) {
    android::base::SetProperty("service.adb.tcp.port", "0");
    WriteFdExactly(fd, "restarting in USB mode\n");
    adb_close(fd);
}
/* A worker thread that monitors host connections, and registers a transport for
 * every new host connection. This thread replaces server_socket_thread on
 * condition that adbd daemon runs inside the emulator, and emulator uses QEMUD
 * pipe to communicate with adbd daemon inside the guest. This is done in order
 * to provide more robust communication channel between ADB host and guest. The
 * main issue with server_socket_thread approach is that it runs on top of TCP,
 * and thus is sensitive to network disruptions. For instance, the
 * ConnectionManager may decide to reset all network connections, in which case
 * the connection between ADB host and guest will be lost. To make ADB traffic
 * independent from the network, we use here 'adb' QEMUD service to transfer data
 * between the host, and the guest. See external/qemu/android/adb-*.* that
 * implements the emulator's side of the protocol. Another advantage of using
 * QEMUD approach is that ADB will be up much sooner, since it doesn't depend
 * anymore on network being set up.
 * The guest side of the protocol contains the following phases:
 * - Connect with adb QEMUD service. In this phase a handle to 'adb' QEMUD service
 *   is opened, and it becomes clear whether or not emulator supports that
 *   protocol.
 * - Wait for the ADB host to create connection with the guest. This is done by
 *   sending an 'accept' request to the adb QEMUD service, and waiting on
 *   response.
 * - When new ADB host connection is accepted, the connection with adb QEMUD
 *   service is registered as the transport, and a 'start' request is sent to the
 *   adb QEMUD service, indicating that the guest is ready to receive messages.
 *   Note that the guest will ignore messages sent down from the emulator before
 *   the transport registration is completed. That's why we need to send the
 *   'start' request after the transport is registered.
 */
static void qemu_socket_thread(void* arg) {
    /* 'accept' request to the adb QEMUD service. */
    static const char _accept_req[] = "accept";
    /* 'start' request to the adb QEMUD service. */
    static const char _start_req[] = "start";
    /* 'ok' reply from the adb QEMUD service. */
    static const char _ok_resp[] = "ok";

    const int port = (int) (uintptr_t) arg;
    int fd;
    char tmp[256];
    char con_name[32];

    adb_thread_setname("qemu socket");
    D("transport: qemu_socket_thread() starting");

    /* adb QEMUD service connection request. */
    snprintf(con_name, sizeof(con_name), "qemud:adb:%d", port);

    /* Connect to the adb QEMUD service. */
    fd = qemu_pipe_open(con_name);
    if (fd < 0) {
        /* This could be an older version of the emulator, that doesn't
         * implement adb QEMUD service. Fall back to the old TCP way. */
        D("adb service is not available. Falling back to TCP socket.");
        adb_thread_create(server_socket_thread, arg);
        return;
    }

    for(;;) {
        /*
         * Wait till the host creates a new connection.
         */

        /* Send the 'accept' request. */
        if (WriteFdExactly(fd, _accept_req, strlen(_accept_req))) {
            /* Wait for the response. In the response we expect 'ok' on success,
             * or 'ko' on failure. */
            if (!ReadFdExactly(fd, tmp, 2) || memcmp(tmp, _ok_resp, 2)) {
                D("Accepting ADB host connection has failed.");
                adb_close(fd);
            } else {
                /* Host is connected. Register the transport, and start the
                 * exchange. */
                std::string serial = android::base::StringPrintf("host-%d", fd);
                register_socket_transport(fd, serial.c_str(), port, 1);
                if (!WriteFdExactly(fd, _start_req, strlen(_start_req))) {
                    adb_close(fd);
                }
            }

            /* Prepare for accepting of the next ADB host connection. */
            fd = qemu_pipe_open(con_name);
            if (fd < 0) {
                D("adb service become unavailable.");
                return;
            }
        } else {
            D("Unable to send the '%s' request to ADB service.", _accept_req);
            return;
        }
    }
    D("transport: qemu_socket_thread() exiting");
    return;
}
コード例 #6
0
bool SendFail(int fd, const std::string& reason) {
    return WriteFdExactly(fd, "FAIL", 4) && SendProtocolString(fd, reason);
}
コード例 #7
0
bool SendOkay(int fd) {
    return WriteFdExactly(fd, "OKAY", 4);
}
コード例 #8
0
bool WriteFdExactly(int fd, const std::string& str) {
    return WriteFdExactly(fd, str.c_str(), str.size());
}
コード例 #9
0
void framebuffer_service(unique_fd fd) {
    struct fbinfo fbinfo;
    unsigned int i, bsize;
    char buf[640];
    int fd_screencap;
    int w, h, f, c;
    int fds[2];
    pid_t pid;

    if (pipe2(fds, O_CLOEXEC) < 0) return;

    pid = fork();
    if (pid < 0) goto done;

    if (pid == 0) {
        dup2(fds[1], STDOUT_FILENO);
        adb_close(fds[0]);
        adb_close(fds[1]);
        const char* command = "screencap";
        const char *args[2] = {command, nullptr};
        execvp(command, (char**)args);
        perror_exit("exec screencap failed");
    }

    adb_close(fds[1]);
    fd_screencap = fds[0];

    /* read w, h, format & color space */
    if(!ReadFdExactly(fd_screencap, &w, 4)) goto done;
    if(!ReadFdExactly(fd_screencap, &h, 4)) goto done;
    if(!ReadFdExactly(fd_screencap, &f, 4)) goto done;
    if(!ReadFdExactly(fd_screencap, &c, 4)) goto done;

    fbinfo.version = DDMS_RAWIMAGE_VERSION;
    fbinfo.colorSpace = c;
    /* see hardware/hardware.h */
    switch (f) {
        case 1: /* RGBA_8888 */
            fbinfo.bpp = 32;
            fbinfo.size = w * h * 4;
            fbinfo.width = w;
            fbinfo.height = h;
            fbinfo.red_offset = 0;
            fbinfo.red_length = 8;
            fbinfo.green_offset = 8;
            fbinfo.green_length = 8;
            fbinfo.blue_offset = 16;
            fbinfo.blue_length = 8;
            fbinfo.alpha_offset = 24;
            fbinfo.alpha_length = 8;
            break;
        case 2: /* RGBX_8888 */
            fbinfo.bpp = 32;
            fbinfo.size = w * h * 4;
            fbinfo.width = w;
            fbinfo.height = h;
            fbinfo.red_offset = 0;
            fbinfo.red_length = 8;
            fbinfo.green_offset = 8;
            fbinfo.green_length = 8;
            fbinfo.blue_offset = 16;
            fbinfo.blue_length = 8;
            fbinfo.alpha_offset = 24;
            fbinfo.alpha_length = 0;
            break;
        case 3: /* RGB_888 */
            fbinfo.bpp = 24;
            fbinfo.size = w * h * 3;
            fbinfo.width = w;
            fbinfo.height = h;
            fbinfo.red_offset = 0;
            fbinfo.red_length = 8;
            fbinfo.green_offset = 8;
            fbinfo.green_length = 8;
            fbinfo.blue_offset = 16;
            fbinfo.blue_length = 8;
            fbinfo.alpha_offset = 24;
            fbinfo.alpha_length = 0;
            break;
        case 4: /* RGB_565 */
            fbinfo.bpp = 16;
            fbinfo.size = w * h * 2;
            fbinfo.width = w;
            fbinfo.height = h;
            fbinfo.red_offset = 11;
            fbinfo.red_length = 5;
            fbinfo.green_offset = 5;
            fbinfo.green_length = 6;
            fbinfo.blue_offset = 0;
            fbinfo.blue_length = 5;
            fbinfo.alpha_offset = 0;
            fbinfo.alpha_length = 0;
            break;
        case 5: /* BGRA_8888 */
            fbinfo.bpp = 32;
            fbinfo.size = w * h * 4;
            fbinfo.width = w;
            fbinfo.height = h;
            fbinfo.red_offset = 16;
            fbinfo.red_length = 8;
            fbinfo.green_offset = 8;
            fbinfo.green_length = 8;
            fbinfo.blue_offset = 0;
            fbinfo.blue_length = 8;
            fbinfo.alpha_offset = 24;
            fbinfo.alpha_length = 8;
           break;
        default:
            goto done;
    }

    /* write header */
    if (!WriteFdExactly(fd.get(), &fbinfo, sizeof(fbinfo))) goto done;

    /* write data */
    for(i = 0; i < fbinfo.size; i += bsize) {
      bsize = sizeof(buf);
      if (i + bsize > fbinfo.size)
        bsize = fbinfo.size - i;
      if(!ReadFdExactly(fd_screencap, buf, bsize)) goto done;
      if (!WriteFdExactly(fd.get(), buf, bsize)) goto done;
    }

done:
    adb_close(fds[0]);

    TEMP_FAILURE_RETRY(waitpid(pid, nullptr, 0));
}
コード例 #10
0
static int sync_recv(int fd, const char* rpath, const char* lpath, bool show_progress) {
    syncmsg msg;
    int len;
    int lfd = -1;
    char *buffer = send_buffer.data;
    unsigned id;
    unsigned long long size = 0;

    len = strlen(rpath);
    if(len > 1024) return -1;

    if (show_progress) {
        // Determine remote file size.
        syncmsg stat_msg;
        stat_msg.req.id = ID_STAT;
        stat_msg.req.namelen = htoll(len);

        if (!WriteFdExactly(fd, &stat_msg.req, sizeof(stat_msg.req)) ||
            !WriteFdExactly(fd, rpath, len)) {
            return -1;
        }

        if (!ReadFdExactly(fd, &stat_msg.stat, sizeof(stat_msg.stat))) {
            return -1;
        }

        if (stat_msg.stat.id != ID_STAT) return -1;

        size = ltohl(stat_msg.stat.size);
    }

    msg.req.id = ID_RECV;
    msg.req.namelen = htoll(len);
    if(!WriteFdExactly(fd, &msg.req, sizeof(msg.req)) ||
       !WriteFdExactly(fd, rpath, len)) {
        return -1;
    }

    if(!ReadFdExactly(fd, &msg.data, sizeof(msg.data))) {
        return -1;
    }
    id = msg.data.id;

    if((id == ID_DATA) || (id == ID_DONE)) {
        adb_unlink(lpath);
        mkdirs(lpath);
        lfd = adb_creat(lpath, 0644);
        if(lfd < 0) {
            fprintf(stderr,"cannot create '%s': %s\n", lpath, strerror(errno));
            return -1;
        }
        goto handle_data;
    } else {
        goto remote_error;
    }

    for(;;) {
        if(!ReadFdExactly(fd, &msg.data, sizeof(msg.data))) {
            return -1;
        }
        id = msg.data.id;

    handle_data:
        len = ltohl(msg.data.size);
        if(id == ID_DONE) break;
        if(id != ID_DATA) goto remote_error;
        if(len > SYNC_DATA_MAX) {
            fprintf(stderr,"data overrun\n");
            adb_close(lfd);
            return -1;
        }

        if(!ReadFdExactly(fd, buffer, len)) {
            adb_close(lfd);
            return -1;
        }

        if(!WriteFdExactly(lfd, buffer, len)) {
            fprintf(stderr,"cannot write '%s': %s\n", rpath, strerror(errno));
            adb_close(lfd);
            return -1;
        }

        total_bytes += len;

        if (show_progress) {
            print_transfer_progress(total_bytes, size);
        }
    }

    adb_close(lfd);
    return 0;

remote_error:
    adb_close(lfd);
    adb_unlink(lpath);

    if(id == ID_FAIL) {
        len = ltohl(msg.data.size);
        if(len > 256) len = 256;
        if(!ReadFdExactly(fd, buffer, len)) {
            return -1;
        }
        buffer[len] = 0;
    } else {
        memcpy(buffer, &id, 4);
        buffer[4] = 0;
    }
    fprintf(stderr,"failed to copy '%s' to '%s': %s\n", rpath, lpath, buffer);
    return 0;
}
コード例 #11
0
static int sync_send(int fd, const char *lpath, const char *rpath,
                     unsigned mtime, mode_t mode, bool show_progress)
{
    syncmsg msg;
    int len, r;
    syncsendbuf *sbuf = &send_buffer;
    char* file_buffer = NULL;
    int size = 0;
    char tmp[64];

    len = strlen(rpath);
    if(len > 1024) goto fail;

    snprintf(tmp, sizeof(tmp), ",%d", mode);
    r = strlen(tmp);

    msg.req.id = ID_SEND;
    msg.req.namelen = htoll(len + r);

    if(!WriteFdExactly(fd, &msg.req, sizeof(msg.req)) ||
       !WriteFdExactly(fd, rpath, len) || !WriteFdExactly(fd, tmp, r)) {
        free(file_buffer);
        goto fail;
    }

    if (file_buffer) {
        write_data_buffer(fd, file_buffer, size, sbuf, show_progress);
        free(file_buffer);
    } else if (S_ISREG(mode))
        write_data_file(fd, lpath, sbuf, show_progress);
    else if (S_ISLNK(mode))
        write_data_link(fd, lpath, sbuf);
    else
        goto fail;

    msg.data.id = ID_DONE;
    msg.data.size = htoll(mtime);
    if(!WriteFdExactly(fd, &msg.data, sizeof(msg.data)))
        goto fail;

    if(!ReadFdExactly(fd, &msg.status, sizeof(msg.status)))
        return -1;

    if(msg.status.id != ID_OKAY) {
        if(msg.status.id == ID_FAIL) {
            len = ltohl(msg.status.msglen);
            if(len > 256) len = 256;
            if(!ReadFdExactly(fd, sbuf->data, len)) {
                return -1;
            }
            sbuf->data[len] = 0;
        } else
            strcpy(sbuf->data, "unknown reason");

        fprintf(stderr,"failed to copy '%s' to '%s': %s\n", lpath, rpath, sbuf->data);
        return -1;
    }

    return 0;

fail:
    fprintf(stderr,"protocol failure\n");
    adb_close(fd);
    return -1;
}
コード例 #12
0
static void close_adb(void* data) {
    adb_data* ad = reinterpret_cast<adb_data*>(data);
    WriteFdExactly(ad->sfd, "DONEDONE");
}
コード例 #13
0
static bool handle_send_file(int s, const char* path, uid_t uid,
                             gid_t gid, mode_t mode, std::vector<char>& buffer, bool do_unlink) {
    syncmsg msg;
    unsigned int timestamp = 0;

    int fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, mode);
    if (fd < 0 && errno == ENOENT) {
        if (!secure_mkdirs(path)) {
            SendSyncFailErrno(s, "secure_mkdirs failed");
            goto fail;
        }
        fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, mode);
    }
    if (fd < 0 && errno == EEXIST) {
        fd = adb_open_mode(path, O_WRONLY | O_CLOEXEC, mode);
    }
    if (fd < 0) {
        SendSyncFailErrno(s, "couldn't create file");
        goto fail;
    } else {
        if (fchown(fd, uid, gid) == -1) {
            SendSyncFailErrno(s, "fchown failed");
            goto fail;
        }

        // Not all filesystems support setting SELinux labels. http://b/23530370.
        selinux_android_restorecon(path, 0);

        // fchown clears the setuid bit - restore it if present.
        // Ignore the result of calling fchmod. It's not supported
        // by all filesystems. b/12441485
        fchmod(fd, mode);
    }

    while (true) {
        unsigned int len;

        if (!ReadFdExactly(s, &msg.data, sizeof(msg.data))) goto fail;

        if (msg.data.id != ID_DATA) {
            if (msg.data.id == ID_DONE) {
                timestamp = msg.data.size;
                break;
            }
            SendSyncFail(s, "invalid data message");
            goto fail;
        }
        len = msg.data.size;
        if (len > buffer.size()) { // TODO: resize buffer?
            SendSyncFail(s, "oversize data message");
            goto fail;
        }

        if (!ReadFdExactly(s, &buffer[0], len)) goto fail;

        if (!WriteFdExactly(fd, &buffer[0], len)) {
            SendSyncFailErrno(s, "write failed");
            goto fail;
        }
    }

    adb_close(fd);

    utimbuf u;
    u.actime = timestamp;
    u.modtime = timestamp;
    utime(path, &u);

    msg.status.id = ID_OKAY;
    msg.status.msglen = 0;
    return WriteFdExactly(s, &msg.status, sizeof(msg.status));

fail:
    if (fd >= 0) adb_close(fd);
    if (do_unlink) adb_unlink(path);
    return false;
}
コード例 #14
0
static bool handle_send_file(int s, const char* path, uid_t uid, gid_t gid, uint64_t capabilities,
                             mode_t mode, std::vector<char>& buffer, bool do_unlink) {
    syncmsg msg;
    unsigned int timestamp = 0;

    __android_log_security_bswrite(SEC_TAG_ADB_SEND_FILE, path);

    int fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, mode);
    if (fd < 0 && errno == ENOENT) {
        if (!secure_mkdirs(adb_dirname(path))) {
            SendSyncFailErrno(s, "secure_mkdirs failed");
            goto fail;
        }
        fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, mode);
    }
    if (fd < 0 && errno == EEXIST) {
        fd = adb_open_mode(path, O_WRONLY | O_CLOEXEC, mode);
    }
    if (fd < 0) {
        SendSyncFailErrno(s, "couldn't create file");
        goto fail;
    } else {
        if (fchown(fd, uid, gid) == -1) {
            SendSyncFailErrno(s, "fchown failed");
            goto fail;
        }

        // Not all filesystems support setting SELinux labels. http://b/23530370.
        selinux_android_restorecon(path, 0);

        // fchown clears the setuid bit - restore it if present.
        // Ignore the result of calling fchmod. It's not supported
        // by all filesystems, so we don't check for success. b/12441485
        fchmod(fd, mode);

        if (!update_capabilities(path, capabilities)) {
            SendSyncFailErrno(s, "update_capabilities failed");
            goto fail;
        }
    }

    while (true) {
        if (!ReadFdExactly(s, &msg.data, sizeof(msg.data))) goto fail;

        if (msg.data.id != ID_DATA) {
            if (msg.data.id == ID_DONE) {
                timestamp = msg.data.size;
                break;
            }
            SendSyncFail(s, "invalid data message");
            goto abort;
        }

        if (msg.data.size > buffer.size()) {  // TODO: resize buffer?
            SendSyncFail(s, "oversize data message");
            goto abort;
        }

        if (!ReadFdExactly(s, &buffer[0], msg.data.size)) goto abort;

        if (!WriteFdExactly(fd, &buffer[0], msg.data.size)) {
            SendSyncFailErrno(s, "write failed");
            goto fail;
        }
    }

    adb_close(fd);

    utimbuf u;
    u.actime = timestamp;
    u.modtime = timestamp;
    utime(path, &u);

    msg.status.id = ID_OKAY;
    msg.status.msglen = 0;
    return WriteFdExactly(s, &msg.status, sizeof(msg.status));

fail:
    // If there's a problem on the device, we'll send an ID_FAIL message and
    // close the socket. Unfortunately the kernel will sometimes throw that
    // data away if the other end keeps writing without reading (which is
    // the case with old versions of adb). To maintain compatibility, keep
    // reading and throwing away ID_DATA packets until the other side notices
    // that we've reported an error.
    while (true) {
        if (!ReadFdExactly(s, &msg.data, sizeof(msg.data))) goto fail;

        if (msg.data.id == ID_DONE) {
            goto abort;
        } else if (msg.data.id != ID_DATA) {
            char id[5];
            memcpy(id, &msg.data.id, sizeof(msg.data.id));
            id[4] = '\0';
            D("handle_send_fail received unexpected id '%s' during failure", id);
            goto abort;
        }

        if (msg.data.size > buffer.size()) {
            D("handle_send_fail received oversized packet of length '%u' during failure",
              msg.data.size);
            goto abort;
        }

        if (!ReadFdExactly(s, &buffer[0], msg.data.size)) goto abort;
    }

abort:
    if (fd >= 0) adb_close(fd);
    if (do_unlink) adb_unlink(path);
    return false;
}
コード例 #15
0
static void reconnect_service(int fd, void* arg) {
    WriteFdExactly(fd, "done");
    adb_close(fd);
    atransport* t = static_cast<atransport*>(arg);
    kick_transport(t);
}
コード例 #16
0
bool WriteFdExactly(int fd, const char* str) {
    return WriteFdExactly(fd, str, strlen(str));
}
コード例 #17
0
void restart_usb_service(int fd, void *cookie) {
    property_set("service.adb.tcp.port", "0");
    WriteFdExactly(fd, "restarting in USB mode\n");
    adb_close(fd);
}
コード例 #18
0
ファイル: console.cpp プロジェクト: MoKee/android_system_core
int adb_send_emulator_command(int argc, const char** argv, const char* serial) {
    int fd = connect_to_console(serial);
    if (fd == -1) {
        return 1;
    }

    std::string commands = adb_construct_auth_command();

    for (int i = 1; i < argc; i++) {
        commands.append(argv[i]);
        commands.push_back(i == argc - 1 ? '\n' : ' ');
    }

    commands.append("quit\n");

    if (!WriteFdExactly(fd, commands)) {
        fprintf(stderr, "error: cannot write to emulator: %s\n",
                strerror(errno));
        adb_close(fd);
        return 1;
    }

    // Drain output that the emulator console has sent us to prevent a problem
    // on Windows where if adb closes the socket without reading all the data,
    // the emulator's next call to recv() will have an ECONNABORTED error,
    // preventing the emulator from reading the command that adb has sent.
    // https://code.google.com/p/android/issues/detail?id=21021
    int result;
    std::string emulator_output;
    do {
        char buf[BUFSIZ];
        result = adb_read(fd, buf, sizeof(buf));
        // Keep reading until zero bytes (orderly/graceful shutdown) or an
        // error. If 'adb emu kill' is executed, the emulator calls exit() with
        // the socket open (and shutdown(SD_SEND) was not called), which causes
        // Windows to send a TCP RST segment which causes adb to get ECONNRESET.
        // Any other emu command is followed by the quit command that we
        // appended above, and that causes the emulator to close the socket
        // which should cause zero bytes (orderly/graceful shutdown) to be
        // returned.
        if (result > 0) emulator_output.append(buf, result);
    } while (result > 0);

    // Note: the following messages are expected to be quite stable from emulator.
    //
    // Emulator console will send the following message upon connection:
    //
    // Android Console: Authentication required
    // Android Console: type 'auth <auth_token>' to authenticate
    // Android Console: you can find your <auth_token> in
    // '/<path-to-home>/.emulator_console_auth_token'
    // OK\r\n
    //
    // and the following after authentication:
    // Android Console: type 'help' for a list of commands
    // OK\r\n
    //
    // So try search and skip first two "OK\r\n", print the rest.
    //
    const std::string delims = "OK\r\n";
    size_t found = 0;
    for (int i = 0; i < 2; ++i) {
        const size_t result = emulator_output.find(delims, found);
        if (result == std::string::npos) {
            break;
        } else {
            found = result + delims.size();
        }
    }

    printf("%s", emulator_output.c_str() + found);
    adb_close(fd);

    return 0;
}