Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 802c54eb authored by David Pursell's avatar David Pursell Committed by Josh Gao
Browse files

adb: relax serial matching rules.

Currently targeting a device by serial requires matching the serial
number exactly. This CL relaxes the matching rules for local transports
to ignore protocol prefixes and make the port optional:
  [tcp:|udp:]<hostname>[:port]

The purpose of this is to allow a user to set ANDROID_SERIAL to
something like "tcp:100.100.100.100" and have it work for both fastboot
and adb (assuming the device comes up at 100.100.100.100 in both
modes).

This CL also adds some unit tests for the modified functions to make
sure they work as expected.

Bug: 27340240
Change-Id: I006e0c70c84331ab44d05d0a0f462d06592eb879
(cherry picked from commit 3f902aad)
parent f22bc60f
Loading
Loading
Loading
Loading
+9 −0
Original line number Original line Diff line number Diff line
@@ -114,4 +114,13 @@ asocket *create_remote_socket(unsigned id, atransport *t);
void connect_to_remote(asocket *s, const char *destination);
void connect_to_remote(asocket *s, const char *destination);
void connect_to_smartsocket(asocket *s);
void connect_to_smartsocket(asocket *s);


// Internal functions that are only made available here for testing purposes.
namespace internal {

#if ADB_HOST
char* skip_host_serial(const char* service);
#endif

}  // namespace internal

#endif  // __ADB_SOCKET_H
#endif  // __ADB_SOCKET_H
+46 −0
Original line number Original line Diff line number Diff line
@@ -270,3 +270,49 @@ TEST_F(LocalSocketTest, close_socket_in_CLOSE_WAIT_state) {
}
}


#endif  // defined(__linux__)
#endif  // defined(__linux__)

#if ADB_HOST

// Checks that skip_host_serial(serial) returns a pointer to the part of |serial| which matches
// |expected|, otherwise logs the failure to gtest.
void VerifySkipHostSerial(const std::string& serial, const char* expected) {
    const char* result = internal::skip_host_serial(serial.c_str());
    if (expected == nullptr) {
        EXPECT_EQ(nullptr, result);
    } else {
        EXPECT_STREQ(expected, result);
    }
}

// Check [tcp:|udp:]<serial>[:<port>]:<command> format.
TEST(socket_test, test_skip_host_serial) {
    for (const std::string& protocol : {"", "tcp:", "udp:"}) {
        VerifySkipHostSerial(protocol, nullptr);
        VerifySkipHostSerial(protocol + "foo", nullptr);

        VerifySkipHostSerial(protocol + "foo:bar", ":bar");
        VerifySkipHostSerial(protocol + "foo:bar:baz", ":bar:baz");

        VerifySkipHostSerial(protocol + "foo:123:bar", ":bar");
        VerifySkipHostSerial(protocol + "foo:123:456", ":456");
        VerifySkipHostSerial(protocol + "foo:123:bar:baz", ":bar:baz");

        // Don't register a port unless it's all numbers and ends with ':'.
        VerifySkipHostSerial(protocol + "foo:123", ":123");
        VerifySkipHostSerial(protocol + "foo:123bar:baz", ":123bar:baz");
    }
}

// Check <prefix>:<serial>:<command> format.
TEST(socket_test, test_skip_host_serial_prefix) {
    for (const std::string& prefix : {"usb:", "product:", "model:", "device:"}) {
        VerifySkipHostSerial(prefix, nullptr);
        VerifySkipHostSerial(prefix + "foo", nullptr);

        VerifySkipHostSerial(prefix + "foo:bar", ":bar");
        VerifySkipHostSerial(prefix + "foo:bar:baz", ":bar:baz");
        VerifySkipHostSerial(prefix + "foo:123:bar", ":123:bar");
    }
}

#endif  // ADB_HOST
+33 −29
Original line number Original line Diff line number Diff line
@@ -26,6 +26,8 @@
#include <unistd.h>
#include <unistd.h>


#include <algorithm>
#include <algorithm>
#include <string>
#include <vector>


#if !ADB_HOST
#if !ADB_HOST
#include "cutils/properties.h"
#include "cutils/properties.h"
@@ -623,43 +625,43 @@ static unsigned unhex(unsigned char *s, int len)


#if ADB_HOST
#if ADB_HOST


#define PREFIX(str) { str, sizeof(str) - 1 }
namespace internal {
static const struct prefix_struct {

    const char *str;
// Returns the position in |service| following the target serial parameter. Serial format can be
    const size_t len;
// any of:
} prefixes[] = {
//   * [tcp:|udp:]<serial>[:<port>]:<command>
    PREFIX("usb:"),
//   * <prefix>:<serial>:<command>
    PREFIX("product:"),
// Where <port> must be a base-10 number and <prefix> may be any of {usb,product,model,device}.
    PREFIX("model:"),
//
    PREFIX("device:"),
// The returned pointer will point to the ':' just before <command>, or nullptr if not found.
};
char* skip_host_serial(const char* service) {
static const int num_prefixes = (sizeof(prefixes) / sizeof(prefixes[0]));
    static const std::vector<std::string>& prefixes =
        *(new std::vector<std::string>{"usb:", "product:", "model:", "device:"});


/* skip_host_serial return the position in a string
    for (const std::string& prefix : prefixes) {
   skipping over the 'serial' parameter in the ADB protocol,
        if (!strncmp(service, prefix.c_str(), prefix.length())) {
   where parameter string may be a host:port string containing
            return strchr(service + prefix.length(), ':');
   the protocol delimiter (colon). */
        }
static char *skip_host_serial(char *service) {
    }
    char *first_colon, *serial_end;
    int i;


    for (i = 0; i < num_prefixes; i++) {
    // For fastboot compatibility, ignore protocol prefixes.
        if (!strncmp(service, prefixes[i].str, prefixes[i].len))
    if (!strncmp(service, "tcp:", 4) || !strncmp(service, "udp:", 4)) {
            return strchr(service + prefixes[i].len, ':');
        service += 4;
    }
    }


    first_colon = strchr(service, ':');
    char* first_colon = strchr(service, ':');
    if (!first_colon) {
    if (!first_colon) {
        /* No colon in service string. */
        // No colon in service string.
        return NULL;
        return nullptr;
    }
    }
    serial_end = first_colon;

    char* serial_end = first_colon;
    if (isdigit(serial_end[1])) {
    if (isdigit(serial_end[1])) {
        serial_end++;
        serial_end++;
        while ((*serial_end) && isdigit(*serial_end)) {
        while (*serial_end && isdigit(*serial_end)) {
            serial_end++;
            serial_end++;
        }
        }
        if ((*serial_end) != ':') {
        if (*serial_end != ':') {
            // Something other than numbers was found, reset the end.
            // Something other than numbers was found, reset the end.
            serial_end = first_colon;
            serial_end = first_colon;
        }
        }
@@ -667,6 +669,8 @@ static char *skip_host_serial(char *service) {
    return serial_end;
    return serial_end;
}
}


}  // namespace internal

#endif // ADB_HOST
#endif // ADB_HOST


static int smart_socket_enqueue(asocket *s, apacket *p)
static int smart_socket_enqueue(asocket *s, apacket *p)
@@ -725,7 +729,7 @@ static int smart_socket_enqueue(asocket *s, apacket *p)
        service += strlen("host-serial:");
        service += strlen("host-serial:");


        // serial number should follow "host:" and could be a host:port string.
        // serial number should follow "host:" and could be a host:port string.
        serial_end = skip_host_serial(service);
        serial_end = internal::skip_host_serial(service);
        if (serial_end) {
        if (serial_end) {
            *serial_end = 0; // terminate string
            *serial_end = 0; // terminate string
            serial = service;
            serial = service;
+39 −5
Original line number Original line Diff line number Diff line
@@ -30,6 +30,7 @@
#include <list>
#include <list>


#include <android-base/logging.h>
#include <android-base/logging.h>
#include <android-base/parsenetaddress.h>
#include <android-base/stringprintf.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/strings.h>


@@ -679,11 +680,7 @@ atransport* acquire_one_transport(TransportType type, const char* serial,


        // Check for matching serial number.
        // Check for matching serial number.
        if (serial) {
        if (serial) {
            if ((t->serial && !strcmp(serial, t->serial)) ||
            if (t->MatchesTarget(serial)) {
                (t->devpath && !strcmp(serial, t->devpath)) ||
                qual_match(serial, "product:", t->product, false) ||
                qual_match(serial, "model:", t->model, true) ||
                qual_match(serial, "device:", t->device, false)) {
                if (result) {
                if (result) {
                    *error_out = "more than one device";
                    *error_out = "more than one device";
                    if (is_ambiguous) *is_ambiguous = true;
                    if (is_ambiguous) *is_ambiguous = true;
@@ -835,6 +832,43 @@ void atransport::RunDisconnects() {
    disconnects_.clear();
    disconnects_.clear();
}
}


bool atransport::MatchesTarget(const std::string& target) const {
    if (serial) {
        if (target == serial) {
            return true;
        } else if (type == kTransportLocal) {
            // Local transports can match [tcp:|udp:]<hostname>[:port].
            const char* local_target_ptr = target.c_str();

            // For fastboot compatibility, ignore protocol prefixes.
            if (android::base::StartsWith(target, "tcp:") ||
                    android::base::StartsWith(target, "udp:")) {
                local_target_ptr += 4;
            }

            // Parse our |serial| and the given |target| to check if the hostnames and ports match.
            std::string serial_host, error;
            int serial_port = -1;
            if (android::base::ParseNetAddress(serial, &serial_host, &serial_port, nullptr,
                                               &error)) {
                // |target| may omit the port to default to ours.
                std::string target_host;
                int target_port = serial_port;
                if (android::base::ParseNetAddress(local_target_ptr, &target_host, &target_port,
                                                   nullptr, &error) &&
                        serial_host == target_host && serial_port == target_port) {
                    return true;
                }
            }
        }
    }

    return (devpath && target == devpath) ||
           qual_match(target.c_str(), "product:", product, false) ||
           qual_match(target.c_str(), "model:", model, true) ||
           qual_match(target.c_str(), "device:", device, false);
}

#if ADB_HOST
#if ADB_HOST


static void append_transport_info(std::string* result, const char* key,
static void append_transport_info(std::string* result, const char* key,
+15 −0
Original line number Original line Diff line number Diff line
@@ -107,6 +107,21 @@ public:
    void RemoveDisconnect(adisconnect* disconnect);
    void RemoveDisconnect(adisconnect* disconnect);
    void RunDisconnects();
    void RunDisconnects();


    // Returns true if |target| matches this transport. A matching |target| can be any of:
    //   * <serial>
    //   * <devpath>
    //   * product:<product>
    //   * model:<model>
    //   * device:<device>
    //
    // If this is a local transport, serial will also match [tcp:|udp:]<hostname>[:port] targets.
    // For example, serial "100.100.100.100:5555" would match any of:
    //   * 100.100.100.100
    //   * tcp:100.100.100.100
    //   * udp:100.100.100.100:5555
    // This is to make it easier to use the same network target for both fastboot and adb.
    bool MatchesTarget(const std::string& target) const;

private:
private:
    // A set of features transmitted in the banner with the initial connection.
    // A set of features transmitted in the banner with the initial connection.
    // This is stored in the banner as 'features=feature0,feature1,etc'.
    // This is stored in the banner as 'features=feature0,feature1,etc'.
Loading