Merge "adb: add help text for USB permission errors."

This commit is contained in:
David Pursell 2015-12-08 16:12:22 +00:00 committed by Gerrit Code Review
commit 663e949b2b
7 changed files with 107 additions and 15 deletions

View file

@ -214,18 +214,23 @@ void local_init(int port);
void local_connect(int port);
int local_connect_arbitrary_ports(int console_port, int adb_port, std::string* error);
/* usb host/client interface */
// USB host/client interface.
void usb_init();
int usb_write(usb_handle *h, const void *data, int len);
int usb_read(usb_handle *h, void *data, int len);
int usb_close(usb_handle *h);
void usb_kick(usb_handle *h);
/* used for USB device detection */
// USB device detection.
#if ADB_HOST
int is_adb_interface(int vid, int pid, int usb_class, int usb_subclass, int usb_protocol);
#endif
// USB permission error help text. The short version will be one line, long may be multi-line.
// Returns a string message to print, or an empty string if no problems could be found.
std::string UsbNoPermissionsShortHelpText();
std::string UsbNoPermissionsLongHelpText();
int adb_commandline(int argc, const char **argv);
ConnectionState connection_state(atransport *t);

View file

@ -674,7 +674,11 @@ atransport* acquire_one_transport(TransportType type, const char* serial,
adb_mutex_lock(&transport_lock);
for (const auto& t : transport_list) {
if (t->connection_state == kCsNoPerm) {
*error_out = "insufficient permissions for device";
*error_out = UsbNoPermissionsLongHelpText();
// If we couldn't figure out a reasonable help message default to something generic.
if (error_out->empty()) {
*error_out = "insufficient permissions for device";
}
continue;
}
@ -748,17 +752,20 @@ atransport* acquire_one_transport(TransportType type, const char* serial,
return result;
}
const char* atransport::connection_state_name() const {
const std::string atransport::connection_state_name() const {
switch (connection_state) {
case kCsOffline: return "offline";
case kCsBootloader: return "bootloader";
case kCsDevice: return "device";
case kCsHost: return "host";
case kCsRecovery: return "recovery";
case kCsNoPerm: return "no permissions";
case kCsSideload: return "sideload";
case kCsUnauthorized: return "unauthorized";
default: return "unknown";
case kCsOffline: return "offline";
case kCsBootloader: return "bootloader";
case kCsDevice: return "device";
case kCsHost: return "host";
case kCsRecovery: return "recovery";
case kCsNoPerm: {
std::string message = UsbNoPermissionsShortHelpText();
return message.empty() ? "no permissions" : message;
}
case kCsSideload: return "sideload";
case kCsUnauthorized: return "unauthorized";
default: return "unknown";
}
}
@ -866,7 +873,8 @@ static void append_transport(const atransport* t, std::string* result,
*result += '\t';
*result += t->connection_state_name();
} else {
android::base::StringAppendF(result, "%-22s %s", serial, t->connection_state_name());
android::base::StringAppendF(result, "%-22s %s", serial,
t->connection_state_name().c_str());
append_transport_info(result, "", t->devpath, false);
append_transport_info(result, "product:", t->product, false);

View file

@ -90,7 +90,7 @@ public:
fdevent auth_fde;
size_t failed_auth_attempts = 0;
const char* connection_state_name() const;
const std::string connection_state_name() const;
void update_version(int version, size_t payload);
int get_protocol_version() const;

View file

@ -22,6 +22,7 @@
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <grp.h>
#include <linux/usb/ch9.h>
#include <linux/usbdevice_fs.h>
#include <linux/version.h>
@ -595,3 +596,54 @@ void usb_init() {
fatal_errno("cannot create device_poll thread");
}
}
static const char kPermissionsHelpUrl[] = "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() {
errno = 0;
group* plugdev_group = getgrnam("plugdev");
if (plugdev_group == nullptr) {
if (errno != 0) {
D("failed to read plugdev group info: %s", strerror(errno));
}
// We can't give any generally useful advice here, just let the caller print the help URL.
return nullptr;
}
// getgroups(2) indicates that the group_member() 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 "udev requires plugdev group membership";
}
// Short help text must be a single line, and will look something like:
// 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);
}
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.
std::string UsbNoPermissionsLongHelpText() {
std::string header = "USB permission failure";
const char* problem = GetUdevProblem();
if (problem != nullptr) {
header += android::base::StringPrintf(": %s", problem);
}
return android::base::StringPrintf("%s.\nSee [%s] for more information.",
header.c_str(), kPermissionsHelpUrl);
}

View file

@ -571,3 +571,12 @@ void usb_kick(usb_handle *h)
{
h->kick(h);
}
// kCsNoPerm is a host-side issue, we can just ignore it here.
std::string UsbNoPermissionsShortHelpText() {
return "";
}
std::string UsbNoPermissionsLongHelpText() {
return "";
}

View file

@ -552,3 +552,12 @@ void usb_kick(usb_handle *handle)
handle->interface = 0;
}
}
// kCsNoPerm is Linux-only.
std::string UsbNoPermissionsShortHelpText() {
return "";
}
std::string UsbNoPermissionsLongHelpText() {
return "";
}

View file

@ -659,3 +659,12 @@ static void kick_devices() {
}
adb_mutex_unlock(&usb_lock);
}
// kCsNoPerm is Linux-only.
std::string UsbNoPermissionsShortHelpText() {
return "";
}
std::string UsbNoPermissionsLongHelpText() {
return "";
}