Merge "Improve udev failure diagnostics." am: 5ad9ba12df am: 94202c2b9d am: 1ad393daba

am: 9edecb4d62

Change-Id: I3b7e34d524f87f24da2a7b77f84e0144f3b92526
This commit is contained in:
Elliott Hughes 2017-05-05 22:40:39 +00:00 committed by android-build-merger
commit 8cf938d1dd
2 changed files with 93 additions and 57 deletions

View file

@ -159,6 +159,20 @@ static std::string get_device_address(libusb_device* device) {
libusb_get_device_address(device));
}
static std::string get_device_serial_path(libusb_device* device) {
uint8_t ports[7];
int port_count = libusb_get_port_numbers(device, ports, 7);
if (port_count < 0) return "";
std::string path =
StringPrintf("/sys/bus/usb/devices/%d-%d", libusb_get_bus_number(device), ports[0]);
for (int port = 1; port < port_count; ++port) {
path += StringPrintf(".%d", ports[port]);
}
path += "/serial";
return path;
}
static bool endpoint_is_output(uint8_t endpoint) {
return (endpoint & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_OUT;
}
@ -291,49 +305,67 @@ static void poll_for_devices() {
}
}
libusb_device_handle* handle_raw;
bool writable = true;
libusb_device_handle* handle_raw = nullptr;
rc = libusb_open(device, &handle_raw);
if (rc != 0) {
LOG(WARNING) << "failed to open usb device at " << device_address << ": "
<< libusb_error_name(rc);
continue;
}
unique_device_handle handle(handle_raw);
LOG(DEBUG) << "successfully opened adb device at " << device_address << ", "
<< StringPrintf("bulk_in = %#x, bulk_out = %#x", bulk_in, bulk_out);
device_serial.resize(255);
rc = libusb_get_string_descriptor_ascii(
handle_raw, device_desc.iSerialNumber,
reinterpret_cast<unsigned char*>(&device_serial[0]), device_serial.length());
if (rc == 0) {
LOG(WARNING) << "received empty serial from device at " << device_address;
continue;
} else if (rc < 0) {
LOG(WARNING) << "failed to get serial from device at " << device_address
<< libusb_error_name(rc);
continue;
}
device_serial.resize(rc);
LOG(DEBUG) << "successfully opened adb device at " << device_address << ", "
<< StringPrintf("bulk_in = %#x, bulk_out = %#x", bulk_in, bulk_out);
// WARNING: this isn't released via RAII.
rc = libusb_claim_interface(handle.get(), interface_num);
if (rc != 0) {
LOG(WARNING) << "failed to claim adb interface for device '" << device_serial << "'"
<< libusb_error_name(rc);
continue;
}
for (uint8_t endpoint : {bulk_in, bulk_out}) {
rc = libusb_clear_halt(handle.get(), endpoint);
if (rc != 0) {
LOG(WARNING) << "failed to clear halt on device '" << device_serial
<< "' endpoint 0x" << std::hex << endpoint << ": "
device_serial.resize(255);
rc = libusb_get_string_descriptor_ascii(
handle_raw, device_desc.iSerialNumber,
reinterpret_cast<unsigned char*>(&device_serial[0]), device_serial.length());
if (rc == 0) {
LOG(WARNING) << "received empty serial from device at " << device_address;
continue;
} else if (rc < 0) {
LOG(WARNING) << "failed to get serial from device at " << device_address
<< libusb_error_name(rc);
libusb_release_interface(handle.get(), interface_num);
continue;
}
device_serial.resize(rc);
// WARNING: this isn't released via RAII.
rc = libusb_claim_interface(handle.get(), interface_num);
if (rc != 0) {
LOG(WARNING) << "failed to claim adb interface for device '" << device_serial
<< "'" << libusb_error_name(rc);
continue;
}
for (uint8_t endpoint : {bulk_in, bulk_out}) {
rc = libusb_clear_halt(handle.get(), endpoint);
if (rc != 0) {
LOG(WARNING) << "failed to clear halt on device '" << device_serial
<< "' endpoint 0x" << std::hex << endpoint << ": "
<< libusb_error_name(rc);
libusb_release_interface(handle.get(), interface_num);
continue;
}
}
} else {
LOG(WARNING) << "failed to open usb device at " << device_address << ": "
<< libusb_error_name(rc);
writable = false;
#if defined(__linux__)
// libusb doesn't think we should be messing around with devices we don't have
// write access to, but Linux at least lets us get the serial number anyway.
if (!android::base::ReadFileToString(get_device_serial_path(device),
&device_serial)) {
// 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
device_serial = "unknown";
}
device_serial = android::base::Trim(device_serial);
#else
// On Mac OS and Windows, we're screwed. But I don't think this situation actually
// happens on those OSes.
continue;
#endif
}
auto result = std::make_unique<usb_handle>(device_address, device_serial,
@ -346,7 +378,8 @@ static void poll_for_devices() {
usb_handles[device_address] = std::move(result);
}
register_usb_transport(usb_handle_raw, device_serial.c_str(), device_address.c_str(), 1);
register_usb_transport(usb_handle_raw, device_serial.c_str(), device_address.c_str(),
writable);
LOG(INFO) << "registered new usb device '" << device_serial << "'";
}

View file

@ -25,13 +25,14 @@
#if defined(__linux__)
#include <grp.h>
#include <pwd.h>
#endif
static const char kPermissionsHelpUrl[] = "http://developer.android.com/tools/device.html";
// Returns a message describing any potential problems we find with udev, or nullptr if we can't
// find plugdev information (i.e. udev is not installed).
static const char* GetUdevProblem() {
// Returns a message describing any potential problems we find with udev, or an empty string if we
// can't find plugdev information (i.e. udev is not installed).
static std::string GetUdevProblem() {
#if defined(__linux__)
errno = 0;
group* plugdev_group = getgrnam("plugdev");
@ -41,43 +42,45 @@ static const char* GetUdevProblem() {
perror("failed to read plugdev group info");
}
// We can't give any generally useful advice here, just let the caller print the help URL.
return nullptr;
return "";
}
// getgroups(2) indicates that the group_member() may not check the egid so we check it
// getgroups(2) indicates that the GNU group_member(3) may not check the egid so we check it
// additionally just to be sure.
if (group_member(plugdev_group->gr_gid) || getegid() == plugdev_group->gr_gid) {
// The user is in plugdev so the problem is likely with the udev rules.
return "verify udev rules";
return "user in plugdev group; are your udev rules wrong?";
}
return "udev requires plugdev group membership";
passwd* pwd = getpwuid(getuid());
return android::base::StringPrintf("user %s is not in the plugdev group",
pwd ? pwd->pw_name : "?");
#else
return nullptr;
return "";
#endif
}
// Short help text must be a single line, and will look something like:
// no permissions (reason); see <URL>
//
// no permissions (reason); see [URL]
std::string UsbNoPermissionsShortHelpText() {
std::string help_text = "no permissions";
const char* problem = GetUdevProblem();
if (problem != nullptr) {
help_text += android::base::StringPrintf(" (%s)", problem);
}
std::string problem(GetUdevProblem());
if (!problem.empty()) help_text += " (" + problem + ")";
return android::base::StringPrintf("%s; see [%s]", help_text.c_str(), kPermissionsHelpUrl);
}
// Long help text can span multiple lines and should provide more detailed information.
// Long help text can span multiple lines but doesn't currently provide more detailed information:
//
// insufficient permissions for device: reason
// See [URL] for more information
std::string UsbNoPermissionsLongHelpText() {
std::string header = "insufficient permissions for device";
const char* problem = GetUdevProblem();
if (problem != nullptr) {
header += android::base::StringPrintf(": %s", problem);
}
std::string problem(GetUdevProblem());
if (!problem.empty()) header += ": " + problem;
return android::base::StringPrintf("%s.\nSee [%s] for more information.",
header.c_str(), kPermissionsHelpUrl);
return android::base::StringPrintf("%s\nSee [%s] for more information", header.c_str(),
kPermissionsHelpUrl);
}