am 170bde83: (-s ours) resolved conflicts for merge of 7c327194 to lmp-mr1-ub-dev * commit '170bde83ae90e03b7964edf2a00da6f83c406910': libutils: fix overflow in SharedBuffer [DO NOT MERGE]
diff --git a/adb/Android.mk b/adb/Android.mk index 6951904..425bf9b 100644 --- a/adb/Android.mk +++ b/adb/Android.mk
@@ -11,6 +11,13 @@ adb_host_clang := true endif +adb_version := $(shell git -C $(LOCAL_PATH) rev-parse --short=12 HEAD 2>/dev/null)-android + +ADB_COMMON_CFLAGS := \ + -Wall -Werror \ + -Wno-unused-parameter \ + -DADB_REVISION='"$(adb_version)"' \ + # libadb # ========================================================= @@ -37,8 +44,7 @@ transport_test.cpp \ LIBADB_CFLAGS := \ - -Wall -Werror \ - -Wno-unused-parameter \ + $(ADB_COMMON_CFLAGS) \ -Wno-missing-field-initializers \ -fvisibility=hidden \ @@ -124,6 +130,21 @@ include $(BUILD_HOST_NATIVE_TEST) +# adb device tracker (used by ddms) test tool +# ========================================================= + +ifeq ($(HOST_OS),linux) +include $(CLEAR_VARS) +LOCAL_CLANG := $(adb_host_clang) +LOCAL_MODULE := adb_device_tracker_test +LOCAL_CFLAGS := -DADB_HOST=1 $(LIBADB_CFLAGS) +LOCAL_SRC_FILES := test_track_devices.cpp +LOCAL_SHARED_LIBRARIES := liblog libbase +LOCAL_STATIC_LIBRARIES := libadb libcrypto_static libcutils +LOCAL_LDLIBS += -lrt -ldl -lpthread +include $(BUILD_HOST_EXECUTABLE) +endif + # adb host tool # ========================================================= include $(CLEAR_VARS) @@ -154,8 +175,7 @@ file_sync_client.cpp \ LOCAL_CFLAGS += \ - -Wall -Werror \ - -Wno-unused-parameter \ + $(ADB_COMMON_CFLAGS) \ -D_GNU_SOURCE \ -DADB_HOST=1 \ @@ -167,6 +187,7 @@ libbase \ libcrypto_static \ libcutils \ + liblog \ $(EXTRA_STATIC_LIBS) \ # libc++ not available on windows yet @@ -206,18 +227,16 @@ set_verity_enable_state_service.cpp \ LOCAL_CFLAGS := \ + $(ADB_COMMON_CFLAGS) \ -DADB_HOST=0 \ -D_GNU_SOURCE \ - -Wall -Werror \ - -Wno-unused-parameter \ -Wno-deprecated-declarations \ -ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT))) -LOCAL_CFLAGS += -DALLOW_ADBD_ROOT=1 -endif +LOCAL_CFLAGS += -DALLOW_ADBD_NO_AUTH=$(if $(filter userdebug eng,$(TARGET_BUILD_VARIANT)),1,0) ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT))) LOCAL_CFLAGS += -DALLOW_ADBD_DISABLE_VERITY=1 +LOCAL_CFLAGS += -DALLOW_ADBD_ROOT=1 endif LOCAL_MODULE := adbd
diff --git a/adb/adb.cpp b/adb/adb.cpp index de82cd4..f64b19f 100644 --- a/adb/adb.cpp +++ b/adb/adb.cpp
@@ -33,6 +33,7 @@ #include <string> #include <base/stringprintf.h> +#include <base/strings.h> #include "adb_auth.h" #include "adb_io.h" @@ -47,9 +48,7 @@ #include <sys/mount.h> #endif -#if ADB_TRACE ADB_MUTEX_DEFINE( D_lock ); -#endif int HOST = 0; @@ -90,10 +89,8 @@ char timestamp[PATH_MAX]; strftime(timestamp, sizeof(timestamp), "%Y-%m-%d-%H-%M-%S", &now); - char path[PATH_MAX]; - snprintf(path, sizeof(path), "/data/adb/adb-%s-%d", timestamp, getpid()); - - int fd = unix_open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0640); + std::string path = android::base::StringPrintf("/data/adb/adb-%s-%d", timestamp, getpid()); + int fd = unix_open(path.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0640); if (fd == -1) { return; } @@ -321,28 +318,6 @@ #endif } -#if !ADB_HOST -static void send_msg_with_header(int fd, const char* msg, size_t msglen) { - char header[5]; - if (msglen > 0xffff) - msglen = 0xffff; - snprintf(header, sizeof(header), "%04x", (unsigned)msglen); - WriteFdExactly(fd, header, 4); - WriteFdExactly(fd, msg, msglen); -} -#endif - -#if ADB_HOST -static void send_msg_with_okay(int fd, const char* msg, size_t msglen) { - char header[9]; - if (msglen > 0xffff) - msglen = 0xffff; - snprintf(header, sizeof(header), "OKAY%04x", (unsigned)msglen); - WriteFdExactly(fd, header, 8); - WriteFdExactly(fd, msg, msglen); -} -#endif // ADB_HOST - void send_connect(atransport *t) { D("Calling send_connect \n"); @@ -355,113 +330,64 @@ send_packet(cp, t); } -#if ADB_HOST -static const char* connection_state_name(atransport *t) -{ - if (t == NULL) { - return "unknown"; - } - - switch(t->connection_state) { - case CS_BOOTLOADER: - return "bootloader"; - case CS_DEVICE: - return "device"; - case CS_RECOVERY: - return "recovery"; - case CS_SIDELOAD: - return "sideload"; - case CS_OFFLINE: - return "offline"; - case CS_UNAUTHORIZED: - return "unauthorized"; - default: - return "unknown"; - } -} -#endif // ADB_HOST - -/* qual_overwrite is used to overwrite a qualifier string. dst is a - * pointer to a char pointer. It is assumed that if *dst is non-NULL, it - * was malloc'ed and needs to freed. *dst will be set to a dup of src. - */ -static void qual_overwrite(char **dst, const char *src) -{ - if (!dst) - return; - +// qual_overwrite is used to overwrite a qualifier string. dst is a +// pointer to a char pointer. It is assumed that if *dst is non-NULL, it +// was malloc'ed and needs to freed. *dst will be set to a dup of src. +// TODO: switch to std::string for these atransport fields instead. +static void qual_overwrite(char** dst, const std::string& src) { free(*dst); - *dst = NULL; - - if (!src || !*src) - return; - - *dst = strdup(src); + *dst = strdup(src.c_str()); } -void parse_banner(char *banner, atransport *t) -{ - static const char *prop_seps = ";"; - static const char key_val_sep = '='; - char *cp; - char *type; - +void parse_banner(const char* banner, atransport* t) { D("parse_banner: %s\n", banner); - type = banner; - cp = strchr(type, ':'); - if (cp) { - *cp++ = 0; - /* Nothing is done with second field. */ - cp = strchr(cp, ':'); - if (cp) { - char *save; - char *key; - key = adb_strtok_r(cp + 1, prop_seps, &save); - while (key) { - cp = strchr(key, key_val_sep); - if (cp) { - *cp++ = '\0'; - if (!strcmp(key, "ro.product.name")) - qual_overwrite(&t->product, cp); - else if (!strcmp(key, "ro.product.model")) - qual_overwrite(&t->model, cp); - else if (!strcmp(key, "ro.product.device")) - qual_overwrite(&t->device, cp); - } - key = adb_strtok_r(NULL, prop_seps, &save); + + // The format is something like: + // "device::ro.product.name=x;ro.product.model=y;ro.product.device=z;". + std::vector<std::string> pieces = android::base::Split(banner, ":"); + + if (pieces.size() > 2) { + const std::string& props = pieces[2]; + for (auto& prop : android::base::Split(props, ";")) { + // The list of properties was traditionally ;-terminated rather than ;-separated. + if (prop.empty()) continue; + + std::vector<std::string> key_value = android::base::Split(prop, "="); + if (key_value.size() != 2) continue; + + const std::string& key = key_value[0]; + const std::string& value = key_value[1]; + if (key == "ro.product.name") { + qual_overwrite(&t->product, value); + } else if (key == "ro.product.model") { + qual_overwrite(&t->model, value); + } else if (key == "ro.product.device") { + qual_overwrite(&t->device, value); } } } - if(!strcmp(type, "bootloader")){ + const std::string& type = pieces[0]; + if (type == "bootloader") { D("setting connection_state to CS_BOOTLOADER\n"); t->connection_state = CS_BOOTLOADER; update_transports(); - return; - } - - if(!strcmp(type, "device")) { + } else if (type == "device") { D("setting connection_state to CS_DEVICE\n"); t->connection_state = CS_DEVICE; update_transports(); - return; - } - - if(!strcmp(type, "recovery")) { + } else if (type == "recovery") { D("setting connection_state to CS_RECOVERY\n"); t->connection_state = CS_RECOVERY; update_transports(); - return; - } - - if(!strcmp(type, "sideload")) { + } else if (type == "sideload") { D("setting connection_state to CS_SIDELOAD\n"); t->connection_state = CS_SIDELOAD; update_transports(); - return; + } else { + D("setting connection_state to CS_HOST\n"); + t->connection_state = CS_HOST; } - - t->connection_state = CS_HOST; } void handle_packet(apacket *p, atransport *t) @@ -493,11 +419,11 @@ handle_offline(t); } - parse_banner((char*) p->data, t); + parse_banner(reinterpret_cast<const char*>(p->data), t); - if (HOST || !auth_enabled) { + if (HOST || !auth_required) { handle_online(t); - if(!HOST) send_connect(t); + if (!HOST) send_connect(t); } else { send_auth_request(t); } @@ -774,20 +700,11 @@ { if (!strcmp(service, "list-forward")) { // Create the list of forward redirections. - int buffer_size = format_listeners(NULL, 0); - // Add one byte for the trailing zero. - char* buffer = reinterpret_cast<char*>(malloc(buffer_size + 1)); - if (buffer == nullptr) { - sendfailmsg(reply_fd, "not enough memory"); - return 1; - } - (void) format_listeners(buffer, buffer_size + 1); + std::string listeners = format_listeners(); #if ADB_HOST - send_msg_with_okay(reply_fd, buffer, buffer_size); -#else - send_msg_with_header(reply_fd, buffer, buffer_size); + SendOkay(reply_fd); #endif - free(buffer); + SendProtocolString(reply_fd, listeners); return 1; } @@ -795,9 +712,9 @@ remove_all_listeners(); #if ADB_HOST /* On the host: 1st OKAY is connect, 2nd OKAY is status */ - adb_write(reply_fd, "OKAY", 4); + SendOkay(reply_fd); #endif - adb_write(reply_fd, "OKAY", 4); + SendOkay(reply_fd); return 1; } @@ -822,19 +739,19 @@ if (createForward) { // Check forward: parameter format: '<local>;<remote>' if(remote == 0) { - sendfailmsg(reply_fd, "malformed forward spec"); + SendFail(reply_fd, "malformed forward spec"); return 1; } *remote++ = 0; if((local[0] == 0) || (remote[0] == 0) || (remote[0] == '*')) { - sendfailmsg(reply_fd, "malformed forward spec"); + SendFail(reply_fd, "malformed forward spec"); return 1; } } else { // Check killforward: parameter format: '<local>' if (local[0] == 0) { - sendfailmsg(reply_fd, "malformed forward spec"); + SendFail(reply_fd, "malformed forward spec"); return 1; } } @@ -842,7 +759,7 @@ std::string error_msg; transport = acquire_one_transport(CS_ANY, ttype, serial, &error_msg); if (!transport) { - sendfailmsg(reply_fd, error_msg.c_str()); + SendFail(reply_fd, error_msg); return 1; } @@ -855,9 +772,9 @@ if (r == INSTALL_STATUS_OK) { #if ADB_HOST /* On the host: 1st OKAY is connect, 2nd OKAY is status */ - WriteFdExactly(reply_fd, "OKAY", 4); + SendOkay(reply_fd); #endif - WriteFdExactly(reply_fd, "OKAY", 4); + SendOkay(reply_fd); return 1; } @@ -873,7 +790,7 @@ break; case INSTALL_STATUS_LISTENER_NOT_FOUND: message = "listener not found"; break; } - sendfailmsg(reply_fd, message.c_str()); + SendFail(reply_fd, message); return 1; } return 0; @@ -884,7 +801,7 @@ if(!strcmp(service, "kill")) { fprintf(stderr,"adb server killed by remote request\n"); fflush(stdout); - adb_write(reply_fd, "OKAY", 4); + SendOkay(reply_fd); usb_cleanup(); exit(0); } @@ -914,25 +831,25 @@ if (transport) { s->transport = transport; - adb_write(reply_fd, "OKAY", 4); + SendOkay(reply_fd); } else { - sendfailmsg(reply_fd, error_msg.c_str()); + SendFail(reply_fd, error_msg); } return 1; } // return a list of all connected devices if (!strncmp(service, "devices", 7)) { - char buffer[4096]; - int use_long = !strcmp(service+7, "-l"); - if (use_long || service[7] == 0) { - memset(buffer, 0, sizeof(buffer)); - D("Getting device list \n"); - list_transports(buffer, sizeof(buffer), use_long); - D("Wrote device list \n"); - send_msg_with_okay(reply_fd, buffer, strlen(buffer)); + bool long_listing = (strcmp(service+7, "-l") == 0); + if (long_listing || service[7] == 0) { + D("Getting device list...\n"); + std::string device_list = list_transports(long_listing); + D("Sending device list...\n"); + SendOkay(reply_fd); + SendProtocolString(reply_fd, device_list); return 0; } + return 1; } // remove TCP transport @@ -959,15 +876,15 @@ } } - send_msg_with_okay(reply_fd, buffer, strlen(buffer)); + SendOkay(reply_fd); + SendProtocolString(reply_fd, buffer); return 0; } // returns our value for ADB_SERVER_VERSION if (!strcmp(service, "version")) { - char version[12]; - snprintf(version, sizeof version, "%04x", ADB_SERVER_VERSION); - send_msg_with_okay(reply_fd, version, strlen(version)); + SendOkay(reply_fd); + SendProtocolString(reply_fd, android::base::StringPrintf("%04x", ADB_SERVER_VERSION)); return 0; } @@ -977,7 +894,8 @@ if (transport && transport->serial) { out = transport->serial; } - send_msg_with_okay(reply_fd, out, strlen(out)); + SendOkay(reply_fd); + SendProtocolString(reply_fd, out); return 0; } if(!strncmp(service,"get-devpath",strlen("get-devpath"))) { @@ -986,7 +904,8 @@ if (transport && transport->devpath) { out = transport->devpath; } - send_msg_with_okay(reply_fd, out, strlen(out)); + SendOkay(reply_fd); + SendProtocolString(reply_fd, out); return 0; } // indicates a new emulator instance has started @@ -999,8 +918,8 @@ if(!strncmp(service,"get-state",strlen("get-state"))) { transport = acquire_one_transport(CS_ANY, ttype, serial, NULL); - const char *state = connection_state_name(transport); - send_msg_with_okay(reply_fd, state, strlen(state)); + SendOkay(reply_fd); + SendProtocolString(reply_fd, transport->connection_state_name()); return 0; } #endif // ADB_HOST
diff --git a/adb/adb.h b/adb/adb.h index cb2cf60..fd9d0e6 100644 --- a/adb/adb.h +++ b/adb/adb.h
@@ -209,6 +209,8 @@ unsigned char token[TOKEN_SIZE]; fdevent auth_fde; unsigned failed_auth_attempts; + + const char* connection_state_name() const; }; @@ -243,8 +245,6 @@ void remove_socket(asocket *s); void close_all_sockets(atransport *t); -#define LOCAL_CLIENT_PREFIX "emulator-" - asocket *create_local_socket(int fd); asocket *create_local_service_socket(const char *destination); @@ -371,7 +371,6 @@ #define USB_FFS_ADB_IN USB_FFS_ADB_EP(ep2) #endif -int sendfailmsg(int fd, const char *reason); int handle_host_request(char *service, transport_type ttype, char* serial, int reply_fd, asocket *s); void handle_online(atransport *t);
diff --git a/adb/adb_auth.cpp b/adb/adb_auth.cpp index dc01825..cff26d6 100644 --- a/adb/adb_auth.cpp +++ b/adb/adb_auth.cpp
@@ -28,7 +28,7 @@ #include "adb.h" #include "transport.h" -int auth_enabled = 0; +bool auth_required = true; void send_auth_request(atransport *t) {
diff --git a/adb/adb_auth.h b/adb/adb_auth.h index 1e1978d..a13604a 100644 --- a/adb/adb_auth.h +++ b/adb/adb_auth.h
@@ -19,7 +19,7 @@ #include "adb.h" -extern int auth_enabled; +extern bool auth_required; int adb_auth_keygen(const char* filename); void adb_auth_verified(atransport *t);
diff --git a/adb/adb_auth_host.cpp b/adb/adb_auth_host.cpp index 7c2bcfb..61a3777 100644 --- a/adb/adb_auth_host.cpp +++ b/adb/adb_auth_host.cpp
@@ -43,6 +43,7 @@ #include "mincrypt/rsa.h" #undef RSA_verify +#include <base/strings.h> #include <cutils/list.h> #include <openssl/evp.h> @@ -191,7 +192,7 @@ encoded_length = 1 + ((sizeof(pkey) + 2) / 3 * 4); #endif - encoded = reinterpret_cast<uint8_t*>(malloc(encoded_length)); + encoded = new uint8_t[encoded_length]; if (encoded == nullptr) { D("Allocation failure"); goto out; @@ -212,9 +213,7 @@ if (outfile != NULL) { fclose(outfile); } - if (encoded != NULL) { - free(encoded); - } + delete[] encoded; return ret; } @@ -274,30 +273,24 @@ { D("read_key '%s'\n", file); - FILE* f = fopen(file, "r"); - if (!f) { - D("Failed to open '%s'\n", file); + FILE* fp = fopen(file, "r"); + if (!fp) { + D("Failed to open '%s': %s\n", file, strerror(errno)); return 0; } - adb_private_key* key = reinterpret_cast<adb_private_key*>( - malloc(sizeof(adb_private_key))); - if (!key) { - D("Failed to alloc key\n"); - fclose(f); - return 0; - } + adb_private_key* key = new adb_private_key; key->rsa = RSA_new(); - if (!PEM_read_RSAPrivateKey(f, &key->rsa, NULL, NULL)) { + if (!PEM_read_RSAPrivateKey(fp, &key->rsa, NULL, NULL)) { D("Failed to read key\n"); - fclose(f); + fclose(fp); RSA_free(key->rsa); - free(key); + delete key; return 0; } - fclose(f); + fclose(fp); list_add_tail(list, &key->node); return 1; } @@ -362,29 +355,16 @@ return read_key(path, list); } -static void get_vendor_keys(struct listnode *list) -{ - const char *adb_keys_path; - char keys_path[MAX_PAYLOAD]; - char *path; - char *save; - struct stat buf; - - adb_keys_path = getenv("ADB_VENDOR_KEYS"); - if (!adb_keys_path) +static void get_vendor_keys(struct listnode* key_list) { + const char* adb_keys_path = getenv("ADB_VENDOR_KEYS"); + if (adb_keys_path == nullptr) { return; - strncpy(keys_path, adb_keys_path, sizeof(keys_path)); + } - path = adb_strtok_r(keys_path, ENV_PATH_SEPARATOR_STR, &save); - while (path) { - D("Reading: '%s'\n", path); - - if (stat(path, &buf)) - D("Can't read '%s'\n", path); - else if (!read_key(path, list)) - D("Failed to read '%s'\n", path); - - path = adb_strtok_r(NULL, ENV_PATH_SEPARATOR_STR, &save); + for (auto& path : android::base::Split(adb_keys_path, ENV_PATH_SEPARATOR_STR)) { + if (!read_key(path.c_str(), key_list)) { + D("Failed to read '%s'\n", path.c_str()); + } } }
diff --git a/adb/adb_client.cpp b/adb/adb_client.cpp index 4751bff..7bb8e4a 100644 --- a/adb/adb_client.cpp +++ b/adb/adb_client.cpp
@@ -28,6 +28,12 @@ #include <sys/stat.h> #include <sys/types.h> +#include <string> +#include <vector> + +#include <base/stringprintf.h> +#include <base/strings.h> + #include "adb_io.h" static transport_type __adb_transport = kTransportAny; @@ -36,6 +42,28 @@ static int __adb_server_port = DEFAULT_ADB_PORT; static const char* __adb_server_name = NULL; +static std::string perror_str(const char* msg) { + return android::base::StringPrintf("%s: %s", msg, strerror(errno)); +} + +static bool ReadProtocolString(int fd, std::string* s, std::string* error) { + char buf[5]; + if (!ReadFdExactly(fd, buf, 4)) { + *error = perror_str("protocol fault (couldn't read status length)"); + return false; + } + buf[4] = 0; + + unsigned long len = strtoul(buf, 0, 16); + s->resize(len, '\0'); + if (!ReadFdExactly(fd, &(*s)[0], len)) { + *error = perror_str("protocol fault (couldn't read status message)"); + return false; + } + + return true; +} + void adb_set_transport(transport_type type, const char* serial) { __adb_transport = type; @@ -52,179 +80,128 @@ __adb_server_name = hostname; } -int adb_get_emulator_console_port(void) -{ - const char* serial = __adb_serial; - int port; +int adb_get_emulator_console_port() { + if (__adb_serial) { + // The user specified a serial number; is it an emulator? + int port; + return (sscanf(__adb_serial, "emulator-%d", &port) == 1) ? port : -1; + } - if (serial == NULL) { - /* if no specific device was specified, we need to look at */ - /* the list of connected devices, and extract an emulator */ - /* name from it. two emulators is an error */ - char* tmp = adb_query("host:devices"); - char* p = tmp; - if(!tmp) { - printf("no emulator connected\n"); - return -1; - } - while (*p) { - char* q = strchr(p, '\n'); - if (q != NULL) - *q++ = 0; - else - q = p + strlen(p); + // No specific device was given, so get the list of connected + // devices and search for emulators. If there's one, we'll + // take it. If there are more than one, that's an error. + std::string devices; + std::string error; + if (!adb_query("host:devices", &devices, &error)) { + printf("no emulator connected: %s\n", error.c_str()); + return -1; + } - if (!memcmp(p, LOCAL_CLIENT_PREFIX, sizeof(LOCAL_CLIENT_PREFIX)-1)) { - if (serial != NULL) { /* more than one emulator listed */ - free(tmp); - return -2; - } - serial = p; + int port; + size_t emulator_count = 0; + for (auto& device : android::base::Split(devices, "\n")) { + if (sscanf(device.c_str(), "emulator-%d", &port) == 1) { + if (++emulator_count > 1) { + return -2; } - - p = q; } - free(tmp); - - if (serial == NULL) - return -1; /* no emulator found */ } - else { - if (memcmp(serial, LOCAL_CLIENT_PREFIX, sizeof(LOCAL_CLIENT_PREFIX)-1) != 0) - return -1; /* not an emulator */ - } - - serial += sizeof(LOCAL_CLIENT_PREFIX)-1; - port = strtol(serial, NULL, 10); + if (emulator_count == 0) return -1; return port; } -static char __adb_error[256] = { 0 }; - -const char *adb_error(void) -{ - return __adb_error; -} - -static int switch_socket_transport(int fd) -{ - char service[64]; - char tmp[5]; - int len; - - if (__adb_serial) - snprintf(service, sizeof service, "host:transport:%s", __adb_serial); - else { +static int switch_socket_transport(int fd, std::string* error) { + std::string service; + if (__adb_serial) { + service += "host:transport:"; + service += __adb_serial; + } else { const char* transport_type = "???"; - - switch (__adb_transport) { - case kTransportUsb: - transport_type = "transport-usb"; - break; - case kTransportLocal: - transport_type = "transport-local"; - break; - case kTransportAny: - transport_type = "transport-any"; - break; - case kTransportHost: - // no switch necessary - return 0; - break; + switch (__adb_transport) { + case kTransportUsb: + transport_type = "transport-usb"; + break; + case kTransportLocal: + transport_type = "transport-local"; + break; + case kTransportAny: + transport_type = "transport-any"; + break; + case kTransportHost: + // no switch necessary + return 0; } - - snprintf(service, sizeof service, "host:%s", transport_type); + service += "host:"; + service += transport_type; } - len = strlen(service); - snprintf(tmp, sizeof tmp, "%04x", len); - if(!WriteFdExactly(fd, tmp, 4) || !WriteFdExactly(fd, service, len)) { - strcpy(__adb_error, "write failure during connection"); + if (!SendProtocolString(fd, service)) { + *error = perror_str("write failure during connection"); adb_close(fd); return -1; } D("Switch transport in progress\n"); - if(adb_status(fd)) { + if (!adb_status(fd, error)) { adb_close(fd); - D("Switch transport failed\n"); + D("Switch transport failed: %s\n", error->c_str()); return -1; } D("Switch transport success\n"); return 0; } -int adb_status(int fd) -{ - unsigned char buf[5]; - unsigned len; - - if(!ReadFdExactly(fd, buf, 4)) { - strcpy(__adb_error, "protocol fault (no status)"); - return -1; +bool adb_status(int fd, std::string* error) { + char buf[5]; + if (!ReadFdExactly(fd, buf, 4)) { + *error = perror_str("protocol fault (couldn't read status)"); + return false; } - if(!memcmp(buf, "OKAY", 4)) { - return 0; + if (!memcmp(buf, "OKAY", 4)) { + return true; } - if(memcmp(buf, "FAIL", 4)) { - sprintf(__adb_error, - "protocol fault (status %02x %02x %02x %02x?!)", - buf[0], buf[1], buf[2], buf[3]); - return -1; + if (memcmp(buf, "FAIL", 4)) { + *error = android::base::StringPrintf("protocol fault (status %02x %02x %02x %02x?!)", + buf[0], buf[1], buf[2], buf[3]); + return false; } - if(!ReadFdExactly(fd, buf, 4)) { - strcpy(__adb_error, "protocol fault (status len)"); - return -1; - } - buf[4] = 0; - len = strtoul((char*)buf, 0, 16); - if(len > 255) len = 255; - if(!ReadFdExactly(fd, __adb_error, len)) { - strcpy(__adb_error, "protocol fault (status read)"); - return -1; - } - __adb_error[len] = 0; - return -1; + ReadProtocolString(fd, error, error); + return false; } -int _adb_connect(const char *service) -{ - char tmp[5]; - int len; - int fd; - - D("_adb_connect: %s\n", service); - len = strlen(service); - if((len < 1) || (len > 1024)) { - strcpy(__adb_error, "service name too long"); +int _adb_connect(const std::string& service, std::string* error) { + D("_adb_connect: %s\n", service.c_str()); + if (service.empty() || service.size() > 1024) { + *error = android::base::StringPrintf("bad service name length (%d)", + static_cast<int>(service.size())); return -1; } - snprintf(tmp, sizeof tmp, "%04x", len); - if (__adb_server_name) + int fd; + if (__adb_server_name) { fd = socket_network_client(__adb_server_name, __adb_server_port, SOCK_STREAM); - else + } else { fd = socket_loopback_client(__adb_server_port, SOCK_STREAM); - - if(fd < 0) { - strcpy(__adb_error, "cannot connect to daemon"); + } + if (fd < 0) { + *error = perror_str("cannot connect to daemon"); return -2; } - if (memcmp(service,"host",4) != 0 && switch_socket_transport(fd)) { + if (memcmp(&service[0],"host",4) != 0 && switch_socket_transport(fd, error)) { return -1; } - if(!WriteFdExactly(fd, tmp, 4) || !WriteFdExactly(fd, service, len)) { - strcpy(__adb_error, "write failure during connection"); + if(!SendProtocolString(fd, service)) { + *error = perror_str("write failure during connection"); adb_close(fd); return -1; } - if(adb_status(fd)) { + if (!adb_status(fd, error)) { adb_close(fd); return -1; } @@ -233,20 +210,19 @@ return fd; } -int adb_connect(const char *service) -{ +int adb_connect(const std::string& service, std::string* error) { // first query the adb server's version - int fd = _adb_connect("host:version"); + int fd = _adb_connect("host:version", error); - D("adb_connect: service %s\n", service); - if(fd == -2 && __adb_server_name) { + D("adb_connect: service %s\n", service.c_str()); + if (fd == -2 && __adb_server_name) { fprintf(stderr,"** Cannot start server on remote host\n"); return fd; - } else if(fd == -2) { + } else if (fd == -2) { fprintf(stdout,"* daemon not running. starting it now on port %d *\n", __adb_server_port); start_server: - if(launch_server(__adb_server_port)) { + if (launch_server(__adb_server_port)) { fprintf(stderr,"* failed to start daemon *\n"); return -1; } else { @@ -257,31 +233,31 @@ // fall through to _adb_connect } else { // if server was running, check its version to make sure it is not out of date - char buf[100]; - size_t n; int version = ADB_SERVER_VERSION - 1; // if we have a file descriptor, then parse version result - if(fd >= 0) { - if(!ReadFdExactly(fd, buf, 4)) goto error; + if (fd >= 0) { + std::string version_string; + if (!ReadProtocolString(fd, &version_string, error)) { + goto error; + } - buf[4] = 0; - n = strtoul(buf, 0, 16); - if(n > sizeof(buf)) goto error; - if(!ReadFdExactly(fd, buf, n)) goto error; adb_close(fd); - if (sscanf(buf, "%04x", &version) != 1) goto error; + if (sscanf(&version_string[0], "%04x", &version) != 1) { + goto error; + } } else { // if fd is -1, then check for "unknown host service", // which would indicate a version of adb that does not support the version command - if (strcmp(__adb_error, "unknown host service") != 0) + if (*error == "unknown host service") { return fd; + } } - if(version != ADB_SERVER_VERSION) { + if (version != ADB_SERVER_VERSION) { printf("adb server is out of date. killing...\n"); - fd = _adb_connect("host:kill"); + fd = _adb_connect("host:kill", error); adb_close(fd); /* XXX can we better detect its death? */ @@ -291,12 +267,13 @@ } // if the command is start-server, we are done. - if (!strcmp(service, "host:start-server")) + if (service == "host:start-server") { return 0; + } - fd = _adb_connect(service); - if(fd == -1) { - D("_adb_connect error: %s", __adb_error); + fd = _adb_connect(service, error); + if (fd == -1) { + D("_adb_connect error: %s", error->c_str()); } else if(fd == -2) { fprintf(stderr,"** daemon still not running\n"); } @@ -309,15 +286,14 @@ } -int adb_command(const char *service) -{ - int fd = adb_connect(service); - if(fd < 0) { - fprintf(stderr, "error: %s\n", adb_error()); +int adb_command(const std::string& service, std::string* error) { + int fd = adb_connect(service, error); + if (fd < 0) { + fprintf(stderr, "error: %s\n", error->c_str()); return -1; } - if(adb_status(fd)) { + if (!adb_status(fd, error)) { adb_close(fd); return -1; } @@ -325,39 +301,18 @@ return 0; } -char *adb_query(const char *service) -{ - char buf[5]; - unsigned long n; - char* tmp; - - D("adb_query: %s\n", service); - int fd = adb_connect(service); - if(fd < 0) { - fprintf(stderr,"error: %s\n", __adb_error); +bool adb_query(const std::string& service, std::string* result, std::string* error) { + D("adb_query: %s\n", service.c_str()); + int fd = adb_connect(service, error); + if (fd < 0) { + fprintf(stderr,"error: %s\n", error->c_str()); return 0; } - if(!ReadFdExactly(fd, buf, 4)) goto oops; - - buf[4] = 0; - n = strtoul(buf, 0, 16); - if(n >= 0xffff) { - strcpy(__adb_error, "reply is too long (>= 64kB)"); - goto oops; - } - - tmp = reinterpret_cast<char*>(malloc(n + 1)); - if(tmp == 0) goto oops; - - if(!ReadFdExactly(fd, tmp, n) == 0) { - tmp[n] = 0; + result->clear(); + if (!ReadProtocolString(fd, result, error)) { adb_close(fd); - return tmp; + return false; } - free(tmp); - -oops: - adb_close(fd); - return 0; + return true; }
diff --git a/adb/adb_client.h b/adb/adb_client.h index 934362a..96416f5 100644 --- a/adb/adb_client.h +++ b/adb/adb_client.h
@@ -3,23 +3,23 @@ #include "adb.h" +#include <string> + /* connect to adb, connect to the named service, and return ** a valid fd for interacting with that service upon success ** or a negative number on failure */ -int adb_connect(const char *service); -int _adb_connect(const char *service); +int adb_connect(const std::string& service, std::string* error); +int _adb_connect(const std::string& service, std::string* error); /* connect to adb, connect to the named service, return 0 if ** the connection succeeded AND the service returned OKAY */ -int adb_command(const char *service); +int adb_command(const std::string& service, std::string* error); -/* connect to adb, connect to the named service, return -** a malloc'd string of its response upon success or NULL -** on failure. -*/ -char *adb_query(const char *service); +// Connects to the named adb service and fills 'result' with the response. +// Returns true on success; returns false and fills 'error' on failure. +bool adb_query(const std::string& service, std::string* result, std::string* error); /* Set the preferred transport to connect to. */ @@ -45,13 +45,9 @@ */ int adb_send_emulator_command(int argc, const char** argv); -/* return verbose error string from last operation */ -const char *adb_error(void); - -/* read a standard adb status response (OKAY|FAIL) and -** return 0 in the event of OKAY, -1 in the event of FAIL -** or protocol error -*/ -int adb_status(int fd); +// Reads a standard adb status response (OKAY|FAIL) and +// returns true in the event of OKAY, false in the event of FAIL +// or protocol error. +bool adb_status(int fd, std::string* error); #endif
diff --git a/adb/adb_io.cpp b/adb/adb_io.cpp index d89f304..5ae6ec3 100644 --- a/adb/adb_io.cpp +++ b/adb/adb_io.cpp
@@ -16,20 +16,37 @@ #define TRACE_TAG TRACE_RWX -#include "sysdeps.h" #include "adb_io.h" #include <unistd.h> +#include <base/stringprintf.h> + #include "adb_trace.h" -#include "transport.h" +#include "adb_utils.h" +#include "sysdeps.h" + +bool SendProtocolString(int fd, const std::string& s) { + int length = s.size(); + if (length > 0xffff) { + length = 0xffff; + } + + return WriteFdFmt(fd, "%04x", length) && WriteFdExactly(fd, s); +} + +bool SendOkay(int fd) { + return WriteFdExactly(fd, "OKAY", 4); +} + +bool SendFail(int fd, const std::string& reason) { + return WriteFdExactly(fd, "FAIL", 4) && SendProtocolString(fd, reason); +} bool ReadFdExactly(int fd, void* buf, size_t len) { char* p = reinterpret_cast<char*>(buf); -#if ADB_TRACE size_t len0 = len; -#endif D("readx: fd=%d wanted=%zu\n", fd, len); while (len > 0) { @@ -47,12 +64,10 @@ } } -#if ADB_TRACE D("readx: fd=%d wanted=%zu got=%zu\n", fd, len0, len0 - len); if (ADB_TRACING) { dump_hex(reinterpret_cast<const unsigned char*>(buf), len0); } -#endif return true; } @@ -61,12 +76,10 @@ const char* p = reinterpret_cast<const char*>(buf); int r; -#if ADB_TRACE D("writex: fd=%d len=%d: ", fd, (int)len); if (ADB_TRACING) { dump_hex(reinterpret_cast<const unsigned char*>(buf), len); } -#endif while (len > 0) { r = adb_write(fd, p, len); @@ -90,6 +103,21 @@ return true; } -bool WriteStringFully(int fd, const char* str) { +bool WriteFdExactly(int fd, const char* str) { return WriteFdExactly(fd, str, strlen(str)); } + +bool WriteFdExactly(int fd, const std::string& str) { + return WriteFdExactly(fd, str.c_str(), str.size()); +} + +bool WriteFdFmt(int fd, const char* fmt, ...) { + std::string str; + + va_list ap; + va_start(ap, fmt); + android::base::StringAppendV(&str, fmt, ap); + va_end(ap); + + return WriteFdExactly(fd, str); +}
diff --git a/adb/adb_io.h b/adb/adb_io.h index 8d237ce..8d50a6d 100644 --- a/adb/adb_io.h +++ b/adb/adb_io.h
@@ -17,9 +17,19 @@ #ifndef ADB_IO_H #define ADB_IO_H -#include <stdbool.h> #include <sys/types.h> +#include <string> + +// Sends the protocol "OKAY" message. +bool SendOkay(int fd); + +// Sends the protocol "FAIL" message, with the given failure reason. +bool SendFail(int fd, const std::string& reason); + +// Writes a protocol-format string; a four hex digit length followed by the string data. +bool SendProtocolString(int fd, const std::string& s); + /* * Reads exactly len bytes from fd into buf. * @@ -37,9 +47,13 @@ * completed. If the other end of the fd (such as in a socket, pipe, or fifo), * is closed, errno will be set to 0. */ -bool WriteFdExactly(int fd, const void *buf, size_t len); +bool WriteFdExactly(int fd, const void* buf, size_t len); -/* Same as WriteFdExactly, but with an implicit len = strlen(buf). */ -bool WriteStringFully(int fd, const char* str); +// Same as above, but for strings. +bool WriteFdExactly(int fd, const char* s); +bool WriteFdExactly(int fd, const std::string& s); + +// Same as above, but formats the string to send. +bool WriteFdFmt(int fd, const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3))); #endif /* ADB_IO_H */
diff --git a/adb/adb_io_test.cpp b/adb/adb_io_test.cpp index da340b2..8fd5cbf 100644 --- a/adb/adb_io_test.cpp +++ b/adb/adb_io_test.cpp
@@ -139,16 +139,29 @@ ASSERT_EQ(ENOSPC, errno); } -TEST(io, WriteStringFully) { +TEST(io, WriteFdExactly_string) { const char str[] = "Foobar"; TemporaryFile tf; ASSERT_NE(-1, tf.fd); // Test writing a partial string to the file. - ASSERT_TRUE(WriteStringFully(tf.fd, str)) << strerror(errno); + ASSERT_TRUE(WriteFdExactly(tf.fd, str)) << strerror(errno); ASSERT_EQ(0, lseek(tf.fd, SEEK_SET, 0)); std::string s; ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s)); EXPECT_STREQ(str, s.c_str()); } + +TEST(io, WriteFdFmt) { + TemporaryFile tf; + ASSERT_NE(-1, tf.fd); + + // Test writing a partial string to the file. + ASSERT_TRUE(WriteFdFmt(tf.fd, "Foo%s%d", "bar", 123)) << strerror(errno); + ASSERT_EQ(0, lseek(tf.fd, SEEK_SET, 0)); + + std::string s; + ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s)); + EXPECT_STREQ("Foobar123", s.c_str()); +}
diff --git a/adb/adb_listeners.cpp b/adb/adb_listeners.cpp index a1a5ddb..3fc4719 100644 --- a/adb/adb_listeners.cpp +++ b/adb/adb_listeners.cpp
@@ -19,6 +19,8 @@ #include <stdio.h> #include <stdlib.h> +#include <base/stringprintf.h> + #include "sysdeps.h" #include "transport.h" @@ -143,49 +145,17 @@ return -1; } -// Write a single line describing a listener to a user-provided buffer. -// Appends a trailing zero, even in case of truncation, but the function -// returns the full line length. -// If |buffer| is NULL, does not write but returns required size. -static int format_listener(alistener* l, char* buffer, size_t buffer_len) { - // Format is simply: - // - // <device-serial> " " <local-name> " " <remote-name> "\n" - // - int local_len = strlen(l->local_name); - int connect_len = strlen(l->connect_to); - int serial_len = strlen(l->transport->serial); - - if (buffer != NULL) { - snprintf(buffer, buffer_len, "%s %s %s\n", - l->transport->serial, l->local_name, l->connect_to); - } - // NOTE: snprintf() on Windows returns -1 in case of truncation, so - // return the computed line length instead. - return local_len + connect_len + serial_len + 3; -} - -// Write the list of current listeners (network redirections) into a -// user-provided buffer. Appends a trailing zero, even in case of -// trunctaion, but return the full size in bytes. -// If |buffer| is NULL, does not write but returns required size. -int format_listeners(char* buf, size_t buflen) -{ - alistener* l; - int result = 0; - for (l = listener_list.next; l != &listener_list; l = l->next) { +// Write the list of current listeners (network redirections) into a string. +std::string format_listeners() { + std::string result; + for (alistener* l = listener_list.next; l != &listener_list; l = l->next) { // Ignore special listeners like those for *smartsocket* - if (l->connect_to[0] == '*') - continue; - int len = format_listener(l, buf, buflen); - // Ensure there is space for the trailing zero. - result += len; - if (buf != NULL) { - buf += len; - buflen -= len; - if (buflen <= 0) - break; + if (l->connect_to[0] == '*') { + continue; } + // <device-serial> " " <local-name> " " <remote-name> "\n" + android::base::StringAppendF(&result, "%s %s %s\n", + l->transport->serial, l->local_name, l->connect_to); } return result; } @@ -215,13 +185,13 @@ } } -install_status_t install_listener(const char *local_name, +install_status_t install_listener(const std::string& local_name, const char *connect_to, atransport* transport, int no_rebind) { for (alistener* l = listener_list.next; l != &listener_list; l = l->next) { - if (strcmp(local_name, l->local_name) == 0) { + if (local_name == l->local_name) { char* cto; /* can't repurpose a smartsocket */ @@ -256,7 +226,7 @@ goto nomem; } - listener->local_name = strdup(local_name); + listener->local_name = strdup(local_name.c_str()); if (listener->local_name == nullptr) { goto nomem; } @@ -266,9 +236,9 @@ goto nomem; } - listener->fd = local_name_to_fd(local_name); + listener->fd = local_name_to_fd(listener->local_name); if (listener->fd < 0) { - printf("cannot bind '%s': %s\n", local_name, strerror(errno)); + printf("cannot bind '%s': %s\n", listener->local_name, strerror(errno)); free(listener->local_name); free(listener->connect_to); free(listener);
diff --git a/adb/adb_listeners.h b/adb/adb_listeners.h index f55fdee..67168ae 100644 --- a/adb/adb_listeners.h +++ b/adb/adb_listeners.h
@@ -19,6 +19,8 @@ #include "adb.h" +#include <string> + // error/status codes for install_listener. enum install_status_t { INSTALL_STATUS_OK = 0, @@ -34,12 +36,12 @@ void listener_event_func(int _fd, unsigned ev, void *_l); void ss_listener_event_func(int _fd, unsigned ev, void *_l); -install_status_t install_listener(const char *local_name, - const char *connect_to, +install_status_t install_listener(const std::string& local_name, + const char* connect_to, atransport* transport, int no_rebind); -int format_listeners(char* buf, size_t buflen); +std::string format_listeners(); install_status_t remove_listener(const char* local_name, atransport* transport); void remove_all_listeners(void);
diff --git a/adb/adb_main.cpp b/adb/adb_main.cpp index 5acaf80..45a2158 100644 --- a/adb/adb_main.cpp +++ b/adb/adb_main.cpp
@@ -28,6 +28,8 @@ #include "adb_listeners.h" #include "transport.h" +#include <base/stringprintf.h> + #if !ADB_HOST #include <getopt.h> #include <sys/prctl.h> @@ -157,16 +159,6 @@ } #endif /* ADB_HOST */ -/* Constructs a local name of form tcp:port. - * target_str points to the target string, it's content will be overwritten. - * target_size is the capacity of the target string. - * server_port is the port number to use for the local name. - */ -void build_local_name(char* target_str, size_t target_size, int server_port) -{ - snprintf(target_str, target_size, "tcp:%d", server_port); -} - void start_logging(void) { #if defined(_WIN32) @@ -238,9 +230,8 @@ local_init(DEFAULT_ADB_LOCAL_TRANSPORT_PORT); adb_auth_init(); - char local_name[30]; - build_local_name(local_name, sizeof(local_name), server_port); - if(install_listener(local_name, "*smartsocket*", NULL, 0)) { + std::string local_name = android::base::StringPrintf("tcp:%d", server_port); + if (install_listener(local_name, "*smartsocket*", NULL, 0)) { exit(1); } #else @@ -248,10 +239,11 @@ // descriptor will always be open. adbd_cloexec_auth_socket(); - property_get("ro.adb.secure", value, "0"); - auth_enabled = !strcmp(value, "1"); - if (auth_enabled) - adbd_auth_init(); + if (ALLOW_ADBD_NO_AUTH && property_get_bool("ro.adb.secure", 0) == 0) { + auth_required = false; + } + + adbd_auth_init(); // Our external storage path may be different than apps, since // we aren't able to bind mount after dropping root. @@ -295,15 +287,14 @@ D("Local port disabled\n"); } else { - char local_name[30]; if ((root_seclabel != NULL) && (is_selinux_enabled() > 0)) { // b/12587913: fix setcon to allow const pointers if (setcon((char *)root_seclabel) < 0) { exit(1); } } - build_local_name(local_name, sizeof(local_name), server_port); - if(install_listener(local_name, "*smartsocket*", NULL, 0)) { + std::string local_name = android::base::StringPrintf("tcp:%d", server_port); + if (install_listener(local_name, "*smartsocket*", NULL, 0)) { exit(1); } } @@ -368,29 +359,25 @@ } #endif +// TODO(danalbert): Split this file up into adb_main.cpp and adbd_main.cpp. int main(int argc, char **argv) { #if ADB_HOST + // adb client/server adb_sysdeps_init(); -#else - close_stdin(); -#endif adb_trace_init(); - -#if ADB_HOST D("Handling commandline()\n"); return adb_commandline(argc - 1, const_cast<const char**>(argv + 1)); #else - /* If adbd runs inside the emulator this will enable adb tracing via - * adb-debug qemud service in the emulator. */ - adb_qemu_trace_init(); + // adbd while (true) { - int c; - int option_index = 0; static struct option opts[] = { - {"root_seclabel", required_argument, 0, 's' }, - {"device_banner", required_argument, 0, 'b' } + {"root_seclabel", required_argument, nullptr, 's'}, + {"device_banner", required_argument, nullptr, 'b'}, + {"version", no_argument, nullptr, 'v'}, }; - c = getopt_long(argc, argv, "", opts, &option_index); + + int option_index = 0; + int c = getopt_long(argc, argv, "", opts, &option_index); if (c == -1) break; switch (c) { @@ -400,11 +387,24 @@ case 'b': adb_device_banner = optarg; break; + case 'v': + printf("Android Debug Bridge Daemon version %d.%d.%d %s\n", + ADB_VERSION_MAJOR, ADB_VERSION_MINOR, ADB_SERVER_VERSION, + ADB_REVISION); + return 0; default: break; } } + close_stdin(); + + adb_trace_init(); + + /* If adbd runs inside the emulator this will enable adb tracing via + * adb-debug qemud service in the emulator. */ + adb_qemu_trace_init(); + D("Handling main()\n"); return adb_main(0, DEFAULT_ADB_PORT); #endif
diff --git a/adb/adb_trace.h b/adb/adb_trace.h index 32b6ae4..63d4151 100644 --- a/adb/adb_trace.h +++ b/adb/adb_trace.h
@@ -23,9 +23,6 @@ #include <stdio.h> #endif -/* define ADB_TRACE to 1 to enable tracing support, or 0 to disable it */ -#define ADB_TRACE 1 - /* IMPORTANT: if you change the following list, don't * forget to update the corresponding 'tags' table in * the adb_trace_init() function implemented in adb.c @@ -45,8 +42,6 @@ TRACE_FDEVENT, } ; -#if ADB_TRACE - #if !ADB_HOST /* * When running inside the emulator, guest's adbd can connect to 'adb-debug' @@ -97,19 +92,6 @@ errno = save_errno; \ } \ } while (0) -# define DD(...) \ - do { \ - int save_errno = errno; \ - adb_mutex_lock(&D_lock); \ - fprintf(stderr, "%16s: %5d:%5lu | ", \ - __FUNCTION__, \ - getpid(), adb_thread_id()); \ - errno = save_errno; \ - fprintf(stderr, __VA_ARGS__ ); \ - fflush(stderr); \ - adb_mutex_unlock(&D_lock); \ - errno = save_errno; \ - } while (0) #else # define D(...) \ do { \ @@ -129,19 +111,6 @@ __VA_ARGS__ ); \ } \ } while (0) -# define DD(...) \ - do { \ - __android_log_print( \ - ANDROID_LOG_INFO, \ - __FUNCTION__, \ - __VA_ARGS__ ); \ - } while (0) #endif /* ADB_HOST */ -#else -# define D(...) ((void)0) -# define DR(...) ((void)0) -# define DD(...) ((void)0) -# define ADB_TRACING 0 -#endif /* ADB_TRACE */ #endif /* __ADB_TRACE_H */
diff --git a/adb/adb_utils.cpp b/adb/adb_utils.cpp index f10c143..604bd57 100644 --- a/adb/adb_utils.cpp +++ b/adb/adb_utils.cpp
@@ -14,6 +14,8 @@ * limitations under the License. */ +#define TRACE_TAG TRACE_ADB + #include "adb_utils.h" #include <stdlib.h> @@ -21,6 +23,11 @@ #include <sys/types.h> #include <unistd.h> +#include <algorithm> + +#include <base/stringprintf.h> + +#include "adb_trace.h" #include "sysdeps.h" bool getcwd(std::string* s) { @@ -38,11 +45,16 @@ std::string escape_arg(const std::string& s) { std::string result = s; - // Insert a \ before any ' in the string. - for (auto it = result.begin(); it != result.end(); ++it) { - if (*it == '\'') { - it = result.insert(it, '\\') + 1; - } + // Escape any ' in the string (before we single-quote the whole thing). + // The correct way to do this for the shell is to replace ' with '\'' --- that is, + // close the existing single-quoted string, escape a single single-quote, and start + // a new single-quoted string. Like the C preprocessor, the shell will concatenate + // these pieces into one string. + for (size_t i = 0; i < s.size(); ++i) { + if (s[i] == '\'') { + result.insert(i, "'\\'"); + i += 2; + } } // Prefix and suffix the whole string with '. @@ -50,3 +62,25 @@ result.push_back('\''); return result; } + +void dump_hex(const void* data, size_t byte_count) { + byte_count = std::min(byte_count, size_t(16)); + + const uint8_t* p = reinterpret_cast<const uint8_t*>(data); + + std::string line; + for (size_t i = 0; i < byte_count; ++i) { + android::base::StringAppendF(&line, "%02x", p[i]); + } + line.push_back(' '); + + for (size_t i = 0; i < byte_count; ++i) { + int c = p[i]; + if (c < 32 || c > 127) { + c = '.'; + } + line.push_back(c); + } + + DR("%s\n", line.c_str()); +}
diff --git a/adb/adb_utils.h b/adb/adb_utils.h index 4b64afa..84f7d0c 100644 --- a/adb/adb_utils.h +++ b/adb/adb_utils.h
@@ -24,4 +24,6 @@ std::string escape_arg(const std::string& s); +void dump_hex(const void* ptr, size_t byte_count); + #endif
diff --git a/adb/adb_utils_test.cpp b/adb/adb_utils_test.cpp index a395079..052aea5 100644 --- a/adb/adb_utils_test.cpp +++ b/adb/adb_utils_test.cpp
@@ -30,21 +30,21 @@ ASSERT_EQ(R"('abc')", escape_arg("abc")); ASSERT_EQ(R"(' abc')", escape_arg(" abc")); - ASSERT_EQ(R"('\'abc')", escape_arg("'abc")); + ASSERT_EQ(R"(''\''abc')", escape_arg("'abc")); ASSERT_EQ(R"('"abc')", escape_arg("\"abc")); ASSERT_EQ(R"('\abc')", escape_arg("\\abc")); ASSERT_EQ(R"('(abc')", escape_arg("(abc")); ASSERT_EQ(R"(')abc')", escape_arg(")abc")); ASSERT_EQ(R"('abc abc')", escape_arg("abc abc")); - ASSERT_EQ(R"('abc\'abc')", escape_arg("abc'abc")); + ASSERT_EQ(R"('abc'\''abc')", escape_arg("abc'abc")); ASSERT_EQ(R"('abc"abc')", escape_arg("abc\"abc")); ASSERT_EQ(R"('abc\abc')", escape_arg("abc\\abc")); ASSERT_EQ(R"('abc(abc')", escape_arg("abc(abc")); ASSERT_EQ(R"('abc)abc')", escape_arg("abc)abc")); ASSERT_EQ(R"('abc ')", escape_arg("abc ")); - ASSERT_EQ(R"('abc\'')", escape_arg("abc'")); + ASSERT_EQ(R"('abc'\''')", escape_arg("abc'")); ASSERT_EQ(R"('abc"')", escape_arg("abc\"")); ASSERT_EQ(R"('abc\')", escape_arg("abc\\")); ASSERT_EQ(R"('abc(')", escape_arg("abc("));
diff --git a/adb/commandline.cpp b/adb/commandline.cpp index e59a96a..fd9953c 100644 --- a/adb/commandline.cpp +++ b/adb/commandline.cpp
@@ -47,14 +47,9 @@ #include "adb_utils.h" #include "file_sync_service.h" -static int do_cmd(transport_type ttype, const char* serial, const char *cmd, ...); - -static int install_app(transport_type transport, const char* serial, int argc, - const char** argv); -static int install_multiple_app(transport_type transport, const char* serial, int argc, - const char** argv); -static int uninstall_app(transport_type transport, const char* serial, int argc, - const char** argv); +static int install_app(transport_type t, const char* serial, int argc, const char** argv); +static int install_multiple_app(transport_type t, const char* serial, int argc, const char** argv); +static int uninstall_app(transport_type t, const char* serial, int argc, const char** argv); static std::string gProductOutPath; extern int gListenAll; @@ -71,8 +66,8 @@ } static void version(FILE* out) { - fprintf(out, "Android Debug Bridge version %d.%d.%d\n", - ADB_VERSION_MAJOR, ADB_VERSION_MINOR, ADB_SERVER_VERSION); + fprintf(out, "Android Debug Bridge version %d.%d.%d\nRevision %s\n", + ADB_VERSION_MAJOR, ADB_VERSION_MINOR, ADB_SERVER_VERSION, ADB_REVISION); } static void help() { @@ -150,8 +145,15 @@ " - remove a specific reversed socket connection\n" " adb reverse --remove-all - remove all reversed socket connections from device\n" " adb jdwp - list PIDs of processes hosting a JDWP transport\n" - " adb install [-lrtsd] <file>\n" - " adb install-multiple [-lrtsdp] <file...>\n" + " adb install [-lrtsdg] <file>\n" + " - push this package file to the device and install it\n" + " (-l: forward lock application)\n" + " (-r: replace existing application)\n" + " (-t: allow test packages)\n" + " (-s: install application on sdcard)\n" + " (-d: allow version code downgrade)\n" + " (-g: grant all runtime permissions)\n" + " adb install-multiple [-lrtsdpg] <file...>\n" " - push this package file to the device and install it\n" " (-l: forward lock application)\n" " (-r: replace existing application)\n" @@ -159,6 +161,7 @@ " (-s: install application on sdcard)\n" " (-d: allow version code downgrade)\n" " (-p: partial application install)\n" + " (-g: grant all runtime permissions)\n" " adb uninstall [-k] <package> - remove this app package from the device\n" " ('-k' means keep the data and cache directories)\n" " adb bugreport - return all information from the device\n" @@ -201,7 +204,6 @@ " adb get-state - prints: offline | bootloader | device\n" " adb get-serialno - prints: <serial-number>\n" " adb get-devpath - prints: <device-path>\n" - " adb status-window - continuously print device status for a specified device\n" " adb remount - remounts the /system, /vendor (if present) and /oem (if present) partitions on the device read-write\n" " adb reboot [bootloader|recovery]\n" " - reboots the device, optionally into the bootloader or recovery program.\n" @@ -269,23 +271,16 @@ } #endif -static void read_and_dump(int fd) -{ - char buf[4096]; - int len; - - while(fd >= 0) { +static void read_and_dump(int fd) { + while (fd >= 0) { D("read_and_dump(): pre adb_read(fd=%d)\n", fd); - len = adb_read(fd, buf, 4096); + char buf[BUFSIZ]; + int len = adb_read(fd, buf, sizeof(buf)); D("read_and_dump(): post adb_read(fd=%d): len=%d\n", fd, len); - if(len == 0) { + if (len <= 0) { break; } - if(len < 0) { - if(errno == EINTR) continue; - break; - } fwrite(buf, 1, len, stdout); fflush(stdout); } @@ -410,11 +405,12 @@ static int interactive_shell() { adb_thread_t thr; - int fdi, fd; + int fdi; - fd = adb_connect("shell:"); - if(fd < 0) { - fprintf(stderr,"error: %s\n", adb_error()); + std::string error; + int fd = adb_connect("shell:", &error); + if (fd < 0) { + fprintf(stderr,"error: %s\n", error.c_str()); return 1; } fdi = 0; //dup(0); @@ -436,73 +432,62 @@ } -static void format_host_command(char* buffer, size_t buflen, const char* command, transport_type ttype, const char* serial) -{ +static std::string format_host_command(const char* command, transport_type type, const char* serial) { if (serial) { - snprintf(buffer, buflen, "host-serial:%s:%s", serial, command); - } else { - const char* prefix = "host"; - if (ttype == kTransportUsb) - prefix = "host-usb"; - else if (ttype == kTransportLocal) - prefix = "host-local"; - - snprintf(buffer, buflen, "%s:%s", prefix, command); + return android::base::StringPrintf("host-serial:%s:%s", serial, command); } + + const char* prefix = "host"; + if (type == kTransportUsb) { + prefix = "host-usb"; + } else if (type == kTransportLocal) { + prefix = "host-local"; + } + return android::base::StringPrintf("%s:%s", prefix, command); } -static int adb_download_buffer(const char *service, const char *fn, const void* data, int sz, - unsigned progress) +static int adb_download_buffer(const char *service, const char *fn, const void* data, unsigned sz, + bool show_progress) { - char buf[4096]; - unsigned total; - int fd; - - sprintf(buf,"%s:%d", service, sz); - fd = adb_connect(buf); - if(fd < 0) { - fprintf(stderr,"error: %s\n", adb_error()); + std::string error; + int fd = adb_connect(android::base::StringPrintf("%s:%d", service, sz), &error); + if (fd < 0) { + fprintf(stderr,"error: %s\n", error.c_str()); return -1; } int opt = CHUNK_SIZE; opt = adb_setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (const void *) &opt, sizeof(opt)); - total = sz; + unsigned total = sz; const uint8_t* ptr = reinterpret_cast<const uint8_t*>(data); - if(progress) { + if (show_progress) { char *x = strrchr(service, ':'); if(x) service = x + 1; } - while(sz > 0) { + while (sz > 0) { unsigned xfer = (sz > CHUNK_SIZE) ? CHUNK_SIZE : sz; - if(!WriteFdExactly(fd, ptr, xfer)) { - adb_status(fd); - fprintf(stderr,"* failed to write data '%s' *\n", adb_error()); + if (!WriteFdExactly(fd, ptr, xfer)) { + std::string error; + adb_status(fd, &error); + fprintf(stderr,"* failed to write data '%s' *\n", error.c_str()); return -1; } sz -= xfer; ptr += xfer; - if(progress) { + if (show_progress) { printf("sending: '%s' %4d%% \r", fn, (int)(100LL - ((100LL * sz) / (total)))); fflush(stdout); } } - if(progress) { + if (show_progress) { printf("\n"); } - if(!ReadFdExactly(fd, buf, 4)){ - fprintf(stderr,"* error reading response *\n"); - adb_close(fd); - return -1; - } - if(memcmp(buf, "OKAY", 4)) { - buf[4] = 0; - fprintf(stderr,"* error response '%s' *\n", buf); - adb_close(fd); + if (!adb_status(fd, &error)) { + fprintf(stderr,"* error response '%s' *\n", error.c_str()); return -1; } @@ -547,37 +532,39 @@ return -1; } - char buf[100]; - sprintf(buf, "sideload-host:%d:%d", sz, SIDELOAD_HOST_BLOCK_SIZE); - int fd = adb_connect(buf); + std::string service = + android::base::StringPrintf("sideload-host:%d:%d", sz, SIDELOAD_HOST_BLOCK_SIZE); + std::string error; + int fd = adb_connect(service, &error); if (fd < 0) { // Try falling back to the older sideload method. Maybe this // is an older device that doesn't support sideload-host. printf("\n"); - status = adb_download_buffer("sideload", fn, data, sz, 1); + status = adb_download_buffer("sideload", fn, data, sz, true); goto done; } opt = adb_setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (const void *) &opt, sizeof(opt)); while (true) { + char buf[9]; if (!ReadFdExactly(fd, buf, 8)) { - fprintf(stderr, "* failed to read command: %s\n", adb_error()); + fprintf(stderr, "* failed to read command: %s\n", strerror(errno)); status = -1; goto done; } + buf[8] = '\0'; - if (strncmp("DONEDONE", buf, 8) == 0) { + if (strcmp("DONEDONE", buf) == 0) { status = 0; break; } - buf[8] = '\0'; int block = strtol(buf, NULL, 10); size_t offset = block * SIDELOAD_HOST_BLOCK_SIZE; if (offset >= sz) { - fprintf(stderr, "* attempt to read past end: %s\n", adb_error()); + fprintf(stderr, "* attempt to read block %d past end\n", block); status = -1; goto done; } @@ -589,8 +576,8 @@ } if(!WriteFdExactly(fd, start, to_write)) { - adb_status(fd); - fprintf(stderr,"* failed to write data '%s' *\n", adb_error()); + adb_status(fd, &error); + fprintf(stderr,"* failed to write data '%s' *\n", error.c_str()); status = -1; goto done; } @@ -618,50 +605,6 @@ return status; } -static void status_window(transport_type ttype, const char* serial) -{ - char command[4096]; - char *state = 0; - char *laststate = 0; - - /* silence stderr */ -#ifdef _WIN32 - /* XXX: TODO */ -#else - int fd; - fd = unix_open("/dev/null", O_WRONLY); - dup2(fd, 2); - adb_close(fd); -#endif - - format_host_command(command, sizeof command, "get-state", ttype, serial); - - for(;;) { - adb_sleep_ms(250); - - if(state) { - free(state); - state = 0; - } - - state = adb_query(command); - - if(state) { - if(laststate && !strcmp(state,laststate)){ - continue; - } else { - if(laststate) free(laststate); - laststate = strdup(state); - } - } - - printf("%c[2J%c[2H", 27, 27); - printf("Android Debug Bridge\n"); - printf("State: %s\n", state ? state : "offline"); - fflush(stdout); - } -} - /** * Run ppp in "notty" mode against a resource listed as the first parameter * eg: @@ -674,9 +617,6 @@ fprintf(stderr, "error: adb %s not implemented on Win32\n", argv[0]); return -1; #else - pid_t pid; - int fd; - if (argc < 2) { fprintf(stderr, "usage: adb %s <adb service name> [ppp opts]\n", argv[0]); @@ -685,15 +625,15 @@ } const char* adb_service_name = argv[1]; - fd = adb_connect(adb_service_name); - - if(fd < 0) { + std::string error; + int fd = adb_connect(adb_service_name, &error); + if (fd < 0) { fprintf(stderr,"Error: Could not open adb service: %s. Error: %s\n", - adb_service_name, adb_error()); + adb_service_name, error.c_str()); return 1; } - pid = fork(); + pid_t pid = fork(); if (pid < 0) { perror("from fork()"); @@ -734,16 +674,42 @@ #endif /* !defined(_WIN32) */ } -static int send_shell_command(transport_type transport, const char* serial, +static bool wait_for_device(const char* service, transport_type t, const char* serial) { + // Was the caller vague about what they'd like us to wait for? + // If so, check they weren't more specific in their choice of transport type. + if (strcmp(service, "wait-for-device") == 0) { + if (t == kTransportUsb) { + service = "wait-for-usb"; + } else if (t == kTransportLocal) { + service = "wait-for-local"; + } else { + service = "wait-for-any"; + } + } + + std::string cmd = format_host_command(service, t, serial); + std::string error; + if (adb_command(cmd, &error)) { + D("failure: %s *\n", error.c_str()); + fprintf(stderr,"error: %s\n", error.c_str()); + return false; + } + + return true; +} + +static int send_shell_command(transport_type transport_type, const char* serial, const std::string& command) { int fd; while (true) { - fd = adb_connect(command.c_str()); - if (fd >= 0) + std::string error; + fd = adb_connect(command, &error); + if (fd >= 0) { break; + } fprintf(stderr,"- waiting for device -\n"); adb_sleep_ms(1000); - do_cmd(transport, serial, "wait-for-device", 0); + wait_for_device("wait-for-device", transport_type, serial); } read_and_dump(fd); @@ -830,9 +796,10 @@ } D("backup. filename=%s cmd=%s\n", filename, cmd.c_str()); - int fd = adb_connect(cmd.c_str()); + std::string error; + int fd = adb_connect(cmd, &error); if (fd < 0) { - fprintf(stderr, "adb: unable to connect for backup\n"); + fprintf(stderr, "adb: unable to connect for backup: %s\n", error.c_str()); adb_close(outFd); return -1; } @@ -846,21 +813,19 @@ } static int restore(int argc, const char** argv) { - const char* filename; - int fd, tarFd; - if (argc != 2) return usage(); - filename = argv[1]; - tarFd = adb_open(filename, O_RDONLY); + const char* filename = argv[1]; + int tarFd = adb_open(filename, O_RDONLY); if (tarFd < 0) { - fprintf(stderr, "adb: unable to open file %s\n", filename); + fprintf(stderr, "adb: unable to open file %s: %s\n", filename, strerror(errno)); return -1; } - fd = adb_connect("restore:"); + std::string error; + int fd = adb_connect("restore:", &error); if (fd < 0) { - fprintf(stderr, "adb: unable to connect for restore\n"); + fprintf(stderr, "adb: unable to connect for restore: %s\n", error.c_str()); adb_close(tarFd); return -1; } @@ -961,20 +926,30 @@ } } -static int adb_connect_command(const char* command) { - int fd = adb_connect(command); - if (fd != -1) { - read_and_dump(fd); - adb_close(fd); - return 0; +static int adb_connect_command(const std::string& command) { + std::string error; + int fd = adb_connect(command, &error); + if (fd < 0) { + fprintf(stderr, "error: %s\n", error.c_str()); + return 1; } - fprintf(stderr, "Error: %s\n", adb_error()); - return 1; + read_and_dump(fd); + adb_close(fd); + return 0; } -int adb_commandline(int argc, const char **argv) -{ - char buf[4096]; +static int adb_query_command(const std::string& command) { + std::string result; + std::string error; + if (!adb_query(command, &result, &error)) { + fprintf(stderr, "error: %s\n", error.c_str()); + return 1; + } + printf("%s\n", result.c_str()); + return 0; +} + +int adb_commandline(int argc, const char **argv) { int no_daemon = 0; int is_daemon = 0; int is_server = 0; @@ -1115,27 +1090,13 @@ /* handle wait-for-* prefix */ if (!strncmp(argv[0], "wait-for-", strlen("wait-for-"))) { const char* service = argv[0]; - if (!strncmp(service, "wait-for-device", strlen("wait-for-device"))) { - if (ttype == kTransportUsb) { - service = "wait-for-usb"; - } else if (ttype == kTransportLocal) { - service = "wait-for-local"; - } else { - service = "wait-for-any"; - } - } - format_host_command(buf, sizeof buf, service, ttype, serial); - - if (adb_command(buf)) { - D("failure: %s *\n",adb_error()); - fprintf(stderr,"error: %s\n", adb_error()); + if (!wait_for_device(service, ttype, serial)) { return 1; } - /* Allow a command to be run after wait-for-device, - * e.g. 'adb wait-for-device shell'. - */ + // Allow a command to be run after wait-for-device, + // e.g. 'adb wait-for-device shell'. if (argc == 1) { return 0; } @@ -1147,59 +1108,38 @@ /* adb_connect() commands */ if (!strcmp(argv[0], "devices")) { - char *tmp; const char *listopt; - if (argc < 2) + if (argc < 2) { listopt = ""; - else if (argc == 2 && !strcmp(argv[1], "-l")) + } else if (argc == 2 && !strcmp(argv[1], "-l")) { listopt = argv[1]; - else { + } else { fprintf(stderr, "Usage: adb devices [-l]\n"); return 1; } - snprintf(buf, sizeof buf, "host:%s%s", argv[0], listopt); - tmp = adb_query(buf); - if (tmp) { - printf("List of devices attached \n"); - printf("%s\n", tmp); - return 0; - } else { - return 1; - } + + std::string query = android::base::StringPrintf("host:%s%s", argv[0], listopt); + printf("List of devices attached\n"); + return adb_query_command(query); } else if (!strcmp(argv[0], "connect")) { - char *tmp; if (argc != 2) { fprintf(stderr, "Usage: adb connect <host>[:<port>]\n"); return 1; } - snprintf(buf, sizeof buf, "host:connect:%s", argv[1]); - tmp = adb_query(buf); - if (tmp) { - printf("%s\n", tmp); - return 0; - } else { - return 1; - } + + std::string query = android::base::StringPrintf("host:connect:%s", argv[1]); + return adb_query_command(query); } else if (!strcmp(argv[0], "disconnect")) { - char *tmp; if (argc > 2) { fprintf(stderr, "Usage: adb disconnect [<host>[:<port>]]\n"); return 1; } - if (argc == 2) { - snprintf(buf, sizeof buf, "host:disconnect:%s", argv[1]); - } else { - snprintf(buf, sizeof buf, "host:disconnect:"); - } - tmp = adb_query(buf); - if (tmp) { - printf("%s\n", tmp); - return 0; - } else { - return 1; - } + + std::string query = android::base::StringPrintf("host:disconnect:%s", + (argc == 2) ? argv[1] : ""); + return adb_query_command(query); } else if (!strcmp(argv[0], "emu")) { return adb_send_emulator_command(argc, argv); @@ -1223,16 +1163,18 @@ } std::string cmd = "shell:"; - cmd += argv[1]; - argc -= 2; - argv += 2; + --argc; + ++argv; while (argc-- > 0) { - cmd += " " + escape_arg(*argv++); + // We don't escape here, just like ssh(1). http://b/20564385. + cmd += *argv++; + if (*argv) cmd += " "; } while (true) { D("interactive shell loop. cmd=%s\n", cmd.c_str()); - int fd = adb_connect(cmd.c_str()); + std::string error; + int fd = adb_connect(cmd, &error); int r; if (fd >= 0) { D("about to read_and_dump(fd=%d)\n", fd); @@ -1241,14 +1183,14 @@ adb_close(fd); r = 0; } else { - fprintf(stderr,"error: %s\n", adb_error()); + fprintf(stderr,"error: %s\n", error.c_str()); r = -1; } if (persist) { fprintf(stderr,"\n- waiting for device -\n"); adb_sleep_ms(1000); - do_cmd(ttype, serial, "wait-for-device", 0); + wait_for_device("wait-for-device", ttype, serial); } else { if (h) { printf("\x1b[0m"); @@ -1270,9 +1212,10 @@ cmd += " " + escape_arg(*argv++); } - int fd = adb_connect(cmd.c_str()); + std::string error; + int fd = adb_connect(cmd, &error); if (fd < 0) { - fprintf(stderr, "error: %s\n", adb_error()); + fprintf(stderr, "error: %s\n", error.c_str()); return -1; } @@ -1286,8 +1229,8 @@ return 0; } else if (!strcmp(argv[0], "kill-server")) { - int fd; - fd = _adb_connect("host:kill"); + std::string error; + int fd = _adb_connect("host:kill", &error); if (fd == -1) { fprintf(stderr,"* server not running *\n"); return 1; @@ -1311,23 +1254,23 @@ !strcmp(argv[0], "unroot") || !strcmp(argv[0], "disable-verity") || !strcmp(argv[0], "enable-verity")) { - char command[100]; + std::string command; if (!strcmp(argv[0], "reboot-bootloader")) { - snprintf(command, sizeof(command), "reboot:bootloader"); + command = "reboot:bootloader"; } else if (argc > 1) { - snprintf(command, sizeof(command), "%s:%s", argv[0], argv[1]); + command = android::base::StringPrintf("%s:%s", argv[0], argv[1]); } else { - snprintf(command, sizeof(command), "%s:", argv[0]); + command = android::base::StringPrintf("%s:", argv[0]); } return adb_connect_command(command); } else if (!strcmp(argv[0], "bugreport")) { if (argc != 1) return usage(); - do_cmd(ttype, serial, "shell", "bugreport", 0); - return 0; + return send_shell_command(ttype, serial, "shell:bugreport"); } /* adb_command() wrapper commands */ else if (!strcmp(argv[0], "forward") || !strcmp(argv[0], "reverse")) { + std::string cmd; char host_prefix[64]; char reverse = (char) !strcmp(argv[0], "reverse"); char remove = 0; @@ -1375,45 +1318,37 @@ // Implement forward --list if (list) { - if (argc != 1) + if (argc != 1) { return usage(); - snprintf(buf, sizeof buf, "%s:list-forward", host_prefix); - char* forwards = adb_query(buf); - if (forwards == NULL) { - fprintf(stderr, "error: %s\n", adb_error()); - return 1; } - printf("%s", forwards); - free(forwards); - return 0; + + std::string query = android::base::StringPrintf("%s:list-forward", host_prefix); + return adb_query_command(query); } // Implement forward --remove-all else if (remove_all) { - if (argc != 1) - return usage(); - snprintf(buf, sizeof buf, "%s:killforward-all", host_prefix); + if (argc != 1) return usage(); + cmd = android::base::StringPrintf("%s:killforward-all", host_prefix); } // Implement forward --remove <local> else if (remove) { - if (argc != 2) - return usage(); - snprintf(buf, sizeof buf, "%s:killforward:%s", host_prefix, argv[1]); + if (argc != 2) return usage(); + cmd = android::base::StringPrintf("%s:killforward:%s", host_prefix, argv[1]); } // Or implement one of: // forward <local> <remote> // forward --no-rebind <local> <remote> - else - { - if (argc != 3) - return usage(); - const char* command = no_rebind ? "forward:norebind" : "forward"; - snprintf(buf, sizeof buf, "%s:%s:%s;%s", host_prefix, command, argv[1], argv[2]); + else { + if (argc != 3) return usage(); + const char* command = no_rebind ? "forward:norebind" : "forward"; + cmd = android::base::StringPrintf("%s:%s:%s;%s", host_prefix, command, argv[1], argv[2]); } - if (adb_command(buf)) { - fprintf(stderr,"error: %s\n", adb_error()); + std::string error; + if (adb_command(cmd, &error)) { + fprintf(stderr, "error: %s\n", error.c_str()); return 1; } return 0; @@ -1511,22 +1446,9 @@ !strcmp(argv[0],"get-serialno") || !strcmp(argv[0],"get-devpath")) { - char *tmp; - - format_host_command(buf, sizeof buf, argv[0], ttype, serial); - tmp = adb_query(buf); - if (tmp) { - printf("%s\n", tmp); - return 0; - } else { - return 1; - } + return adb_query_command(format_host_command(argv[0], ttype, serial)); } /* other commands */ - else if (!strcmp(argv[0],"status-window")) { - status_window(ttype, serial); - return 0; - } else if (!strcmp(argv[0],"logcat") || !strcmp(argv[0],"lolcat") || !strcmp(argv[0],"longcat")) { return logcat(ttype, serial, argc, argv); } @@ -1534,7 +1456,8 @@ return ppp(argc, argv); } else if (!strcmp(argv[0], "start-server")) { - return adb_connect("host:start-server"); + std::string error; + return adb_connect("host:start-server", &error); } else if (!strcmp(argv[0], "backup")) { return backup(argc, argv); @@ -1563,42 +1486,6 @@ return 1; } -#define MAX_ARGV_LENGTH 16 -static int do_cmd(transport_type ttype, const char* serial, const char *cmd, ...) -{ - const char *argv[MAX_ARGV_LENGTH]; - int argc; - va_list ap; - - va_start(ap, cmd); - argc = 0; - - if (serial) { - argv[argc++] = "-s"; - argv[argc++] = serial; - } else if (ttype == kTransportUsb) { - argv[argc++] = "-d"; - } else if (ttype == kTransportLocal) { - argv[argc++] = "-e"; - } - - argv[argc++] = cmd; - while(argc < MAX_ARGV_LENGTH && - (argv[argc] = va_arg(ap, char*)) != 0) argc++; - assert(argc < MAX_ARGV_LENGTH); - va_end(ap); - -#if 0 - int n; - fprintf(stderr,"argc = %d\n",argc); - for(n = 0; n < argc; n++) { - fprintf(stderr,"argv[%d] = \"%s\"\n", n, argv[n]); - } -#endif - - return adb_commandline(argc, argv); -} - static int pm_command(transport_type transport, const char* serial, int argc, const char** argv) { @@ -1742,9 +1629,10 @@ } // Create install session - int fd = adb_connect(cmd.c_str()); + std::string error; + int fd = adb_connect(cmd, &error); if (fd < 0) { - fprintf(stderr, "Connect error for create: %s\n", adb_error()); + fprintf(stderr, "Connect error for create: %s\n", error.c_str()); return -1; } char buf[BUFSIZ]; @@ -1788,14 +1676,15 @@ int localFd = adb_open(file, O_RDONLY); if (localFd < 0) { - fprintf(stderr, "Failed to open %s: %s\n", file, adb_error()); + fprintf(stderr, "Failed to open %s: %s\n", file, strerror(errno)); success = 0; goto finalize_session; } - int remoteFd = adb_connect(cmd.c_str()); + std::string error; + int remoteFd = adb_connect(cmd, &error); if (remoteFd < 0) { - fprintf(stderr, "Connect error for write: %s\n", adb_error()); + fprintf(stderr, "Connect error for write: %s\n", error.c_str()); adb_close(localFd); success = 0; goto finalize_session; @@ -1817,15 +1706,12 @@ finalize_session: // Commit session if we streamed everything okay; otherwise abandon - if (success) { - snprintf(buf, sizeof(buf), "exec:pm install-commit %d", session_id); - } else { - snprintf(buf, sizeof(buf), "exec:pm install-abandon %d", session_id); - } - - fd = adb_connect(buf); + std::string service = + android::base::StringPrintf("exec:pm install-%s %d", + success ? "commit" : "abandon", session_id); + fd = adb_connect(service, &error); if (fd < 0) { - fprintf(stderr, "Connect error for finalize: %s\n", adb_error()); + fprintf(stderr, "Connect error for finalize: %s\n", error.c_str()); return -1; } read_status_line(fd, buf, sizeof(buf));
diff --git a/adb/file_sync_client.cpp b/adb/file_sync_client.cpp index 49d8783..aded301 100644 --- a/adb/file_sync_client.cpp +++ b/adb/file_sync_client.cpp
@@ -539,11 +539,11 @@ printf("%08x %08x %08x %s\n", mode, size, time, name); } -int do_sync_ls(const char *path) -{ - int fd = adb_connect("sync:"); - if(fd < 0) { - fprintf(stderr,"error: %s\n", adb_error()); +int do_sync_ls(const char* path) { + std::string error; + int fd = adb_connect("sync:", &error); + if (fd < 0) { + fprintf(stderr,"error: %s\n", error.c_str()); return 1; } @@ -743,11 +743,11 @@ { struct stat st; unsigned mode; - int fd; - fd = adb_connect("sync:"); - if(fd < 0) { - fprintf(stderr,"error: %s\n", adb_error()); + std::string error; + int fd = adb_connect("sync:", &error); + if (fd < 0) { + fprintf(stderr,"error: %s\n", error.c_str()); return 1; } @@ -967,11 +967,10 @@ unsigned mode, time; struct stat st; - int fd; - - fd = adb_connect("sync:"); - if(fd < 0) { - fprintf(stderr,"error: %s\n", adb_error()); + std::string error; + int fd = adb_connect("sync:", &error); + if (fd < 0) { + fprintf(stderr,"error: %s\n", error.c_str()); return 1; } @@ -1031,9 +1030,10 @@ { fprintf(stderr, "syncing %s...\n", rpath.c_str()); - int fd = adb_connect("sync:"); + std::string error; + int fd = adb_connect("sync:", &error); if (fd < 0) { - fprintf(stderr, "error: %s\n", adb_error()); + fprintf(stderr, "error: %s\n", error.c_str()); return 1; }
diff --git a/adb/file_sync_service.cpp b/adb/file_sync_service.cpp index e8e9a0f..5d17335 100644 --- a/adb/file_sync_service.cpp +++ b/adb/file_sync_service.cpp
@@ -57,7 +57,7 @@ if(x == 0) return 0; *x = 0; if (should_use_fs_config(name)) { - fs_config(name, 1, &uid, &gid, &mode, &cap); + fs_config(name, 1, NULL, &uid, &gid, &mode, &cap); } ret = adb_mkdir(name, mode); if((ret < 0) && (errno != EEXIST)) { @@ -366,7 +366,7 @@ tmp++; } if (should_use_fs_config(path)) { - fs_config(tmp, 0, &uid, &gid, &mode, &cap); + fs_config(tmp, 0, NULL, &uid, &gid, &mode, &cap); } return handle_send_file(s, path, uid, gid, mode, buffer, do_unlink); }
diff --git a/adb/remount_service.cpp b/adb/remount_service.cpp index 1eaee73..7a3b89a 100644 --- a/adb/remount_service.cpp +++ b/adb/remount_service.cpp
@@ -34,114 +34,89 @@ #include "adb_utils.h" #include "cutils/properties.h" -static int system_ro = 1; -static int vendor_ro = 1; -static int oem_ro = 1; - -/* Returns the device used to mount a directory in /proc/mounts */ -static std::string find_mount(const char *dir) { - FILE* fp; - struct mntent* mentry; - char* device = NULL; - - if ((fp = setmntent("/proc/mounts", "r")) == NULL) { - return NULL; +// Returns the device used to mount a directory in /proc/mounts. +static std::string find_mount(const char* dir) { + std::unique_ptr<FILE, int(*)(FILE*)> fp(setmntent("/proc/mounts", "r"), endmntent); + if (!fp) { + return ""; } - while ((mentry = getmntent(fp)) != NULL) { - if (strcmp(dir, mentry->mnt_dir) == 0) { - device = mentry->mnt_fsname; - break; + + mntent* e; + while ((e = getmntent(fp.get())) != nullptr) { + if (strcmp(dir, e->mnt_dir) == 0) { + return e->mnt_fsname; } } - endmntent(fp); - return device; + return ""; } -int make_block_device_writable(const std::string& dev) { +bool make_block_device_writable(const std::string& dev) { int fd = unix_open(dev.c_str(), O_RDONLY | O_CLOEXEC); if (fd == -1) { - return -1; + return false; } - int result = -1; int OFF = 0; - if (!ioctl(fd, BLKROSET, &OFF)) { - result = 0; - } + bool result = (ioctl(fd, BLKROSET, &OFF) != -1); adb_close(fd); - return result; } -// Init mounts /system as read only, remount to enable writes. -static int remount(const char* dir, int* dir_ro) { - std::string dev(find_mount(dir)); - if (dev.empty() || make_block_device_writable(dev)) { - return -1; +static bool remount_partition(int fd, const char* dir) { + if (!directory_exists(dir)) { + return true; } - - int rc = mount(dev.c_str(), dir, "none", MS_REMOUNT, NULL); - *dir_ro = rc; - return rc; -} - -static bool remount_partition(int fd, const char* partition, int* ro) { - if (!directory_exists(partition)) { + std::string dev = find_mount(dir); + if (dev.empty()) { + return true; + } + if (!make_block_device_writable(dev)) { + WriteFdFmt(fd, "remount of %s failed; couldn't make block device %s writable: %s\n", + dir, dev.c_str(), strerror(errno)); + return false; + } + if (mount(dev.c_str(), dir, "none", MS_REMOUNT, nullptr) == -1) { + WriteFdFmt(fd, "remount of %s failed: %s\n", dir, strerror(errno)); + return false; + } return true; - } - if (remount(partition, ro)) { - char buf[200]; - snprintf(buf, sizeof(buf), "remount of %s failed: %s\n", partition, strerror(errno)); - WriteStringFully(fd, buf); - return false; - } - return true; } void remount_service(int fd, void* cookie) { - char prop_buf[PROPERTY_VALUE_MAX]; - if (getuid() != 0) { - WriteStringFully(fd, "Not running as root. Try \"adb root\" first.\n"); + WriteFdExactly(fd, "Not running as root. Try \"adb root\" first.\n"); adb_close(fd); return; } - bool system_verified = false, vendor_verified = false; + char prop_buf[PROPERTY_VALUE_MAX]; property_get("partition.system.verified", prop_buf, ""); - if (strlen(prop_buf) > 0) { - system_verified = true; - } + bool system_verified = (strlen(prop_buf) > 0); property_get("partition.vendor.verified", prop_buf, ""); - if (strlen(prop_buf) > 0) { - vendor_verified = true; - } + bool vendor_verified = (strlen(prop_buf) > 0); if (system_verified || vendor_verified) { // Allow remount but warn of likely bad effects bool both = system_verified && vendor_verified; - char buffer[200]; - snprintf(buffer, sizeof(buffer), - "dm_verity is enabled on the %s%s%s partition%s.\n", - system_verified ? "system" : "", - both ? " and " : "", - vendor_verified ? "vendor" : "", - both ? "s" : ""); - WriteStringFully(fd, buffer); - snprintf(buffer, sizeof(buffer), - "Use \"adb disable-verity\" to disable verity.\n" - "If you do not, remount may succeed, however, you will still " - "not be able to write to these volumes.\n"); - WriteStringFully(fd, buffer); + WriteFdFmt(fd, + "dm_verity is enabled on the %s%s%s partition%s.\n", + system_verified ? "system" : "", + both ? " and " : "", + vendor_verified ? "vendor" : "", + both ? "s" : ""); + WriteFdExactly(fd, + "Use \"adb disable-verity\" to disable verity.\n" + "If you do not, remount may succeed, however, you will still " + "not be able to write to these volumes.\n"); } bool success = true; - success &= remount_partition(fd, "/system", &system_ro); - success &= remount_partition(fd, "/vendor", &vendor_ro); - success &= remount_partition(fd, "/oem", &oem_ro); + success &= remount_partition(fd, "/system"); + success &= remount_partition(fd, "/vendor"); + success &= remount_partition(fd, "/oem"); - WriteStringFully(fd, success ? "remount succeeded\n" : "remount failed\n"); + WriteFdExactly(fd, success ? "remount succeeded\n" : "remount failed\n"); adb_close(fd); }
diff --git a/adb/remount_service.h b/adb/remount_service.h index e1763cf..7bda1be 100644 --- a/adb/remount_service.h +++ b/adb/remount_service.h
@@ -19,7 +19,7 @@ #include <string> -int make_block_device_writable(const std::string&); +bool make_block_device_writable(const std::string&); void remount_service(int, void*); #endif
diff --git a/adb/services.cpp b/adb/services.cpp index e6c84a4..1847447 100644 --- a/adb/services.cpp +++ b/adb/services.cpp
@@ -31,10 +31,11 @@ #include <unistd.h> #endif +#include <base/file.h> #include <base/stringprintf.h> +#include <base/strings.h> #if !ADB_HOST -#include "base/file.h" #include "cutils/android_reboot.h" #include "cutils/properties.h" #endif @@ -62,74 +63,54 @@ #if !ADB_HOST -void restart_root_service(int fd, void *cookie) -{ - char buf[100]; - char value[PROPERTY_VALUE_MAX]; - +void restart_root_service(int fd, void *cookie) { if (getuid() == 0) { - snprintf(buf, sizeof(buf), "adbd is already running as root\n"); - WriteFdExactly(fd, buf, strlen(buf)); + WriteFdExactly(fd, "adbd is already running as root\n"); adb_close(fd); } else { + char value[PROPERTY_VALUE_MAX]; property_get("ro.debuggable", value, ""); if (strcmp(value, "1") != 0) { - snprintf(buf, sizeof(buf), "adbd cannot run as root in production builds\n"); - WriteFdExactly(fd, buf, strlen(buf)); + WriteFdExactly(fd, "adbd cannot run as root in production builds\n"); adb_close(fd); return; } property_set("service.adb.root", "1"); - snprintf(buf, sizeof(buf), "restarting adbd as root\n"); - WriteFdExactly(fd, buf, strlen(buf)); + WriteFdExactly(fd, "restarting adbd as root\n"); adb_close(fd); } } -void restart_unroot_service(int fd, void *cookie) -{ - char buf[100]; - +void restart_unroot_service(int fd, void *cookie) { if (getuid() != 0) { - snprintf(buf, sizeof(buf), "adbd not running as root\n"); - WriteFdExactly(fd, buf, strlen(buf)); + WriteFdExactly(fd, "adbd not running as root\n"); adb_close(fd); } else { property_set("service.adb.root", "0"); - snprintf(buf, sizeof(buf), "restarting adbd as non root\n"); - WriteFdExactly(fd, buf, strlen(buf)); + WriteFdExactly(fd, "restarting adbd as non root\n"); adb_close(fd); } } -void restart_tcp_service(int fd, void *cookie) -{ - char buf[100]; - char value[PROPERTY_VALUE_MAX]; +void restart_tcp_service(int fd, void *cookie) { int port = (int) (uintptr_t) cookie; - if (port <= 0) { - snprintf(buf, sizeof(buf), "invalid port\n"); - WriteFdExactly(fd, buf, strlen(buf)); + WriteFdFmt(fd, "invalid port %d\n", port); adb_close(fd); return; } + char value[PROPERTY_VALUE_MAX]; snprintf(value, sizeof(value), "%d", port); property_set("service.adb.tcp.port", value); - snprintf(buf, sizeof(buf), "restarting in TCP mode port: %d\n", port); - WriteFdExactly(fd, buf, strlen(buf)); + WriteFdFmt(fd, "restarting in TCP mode port: %d\n", port); adb_close(fd); } -void restart_usb_service(int fd, void *cookie) -{ - char buf[100]; - +void restart_usb_service(int fd, void *cookie) { property_set("service.adb.tcp.port", "0"); - snprintf(buf, sizeof(buf), "restarting in USB mode\n"); - WriteFdExactly(fd, buf, strlen(buf)); + WriteFdExactly(fd, "restarting in USB mode\n"); adb_close(fd); } @@ -142,13 +123,11 @@ reboot_arg = "sideload"; } - char buf[100]; // 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) { - snprintf(buf, sizeof(buf), "'adb root' is required for 'adb reboot sideload'.\n"); - WriteStringFully(fd, buf); + WriteFdExactly(fd, "'adb root' is required for 'adb reboot sideload'.\n"); return false; } @@ -174,15 +153,13 @@ char property_val[PROPERTY_VALUE_MAX]; int ret = snprintf(property_val, sizeof(property_val), "reboot,%s", reboot_arg); if (ret >= static_cast<int>(sizeof(property_val))) { - snprintf(buf, sizeof(buf), "reboot string too long. length=%d\n", ret); - WriteStringFully(fd, buf); + WriteFdFmt(fd, "reboot string too long: %d\n", ret); return false; } ret = property_set(ANDROID_RB_PROPERTY, property_val); if (ret < 0) { - snprintf(buf, sizeof(buf), "reboot failed: %d\n", ret); - WriteStringFully(fd, buf); + WriteFdFmt(fd, "reboot failed: %d\n", ret); return false; } @@ -208,7 +185,7 @@ const char* command = reinterpret_cast<const char*>(arg); if (handle_forward_request(command, kTransportAny, NULL, fd) < 0) { - sendfailmsg(fd, "not a reverse forwarding command"); + SendFail(fd, "not a reverse forwarding command"); } free(arg); adb_close(fd); @@ -551,9 +528,9 @@ std::string error_msg = "unknown error"; atransport* t = acquire_one_transport(sinfo->state, sinfo->transport, sinfo->serial, &error_msg); if (t != 0) { - WriteFdExactly(fd, "OKAY", 4); + SendOkay(fd); } else { - sendfailmsg(fd, error_msg.c_str()); + SendFail(fd, error_msg); } if (sinfo->serial) @@ -563,35 +540,31 @@ D("wait_for_state is done\n"); } -static void connect_device(char* host, char* buffer, int buffer_size) -{ - int port, fd; - char* portstr = strchr(host, ':'); - char hostbuf[100]; - char serial[100]; - int ret; - - strncpy(hostbuf, host, sizeof(hostbuf) - 1); - if (portstr) { - if (portstr - host >= (ptrdiff_t)sizeof(hostbuf)) { - snprintf(buffer, buffer_size, "bad host name %s", host); - return; - } - // zero terminate the host at the point we found the colon - hostbuf[portstr - host] = 0; - if (sscanf(portstr + 1, "%d", &port) != 1) { - snprintf(buffer, buffer_size, "bad port number %s", portstr); - return; - } - } else { - port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT; +static void connect_device(const std::string& host, std::string* response) { + if (host.empty()) { + *response = "empty host name"; + return; } - snprintf(serial, sizeof(serial), "%s:%d", hostbuf, port); + std::vector<std::string> pieces = android::base::Split(host, ":"); + const std::string& hostname = pieces[0]; - fd = socket_network_client_timeout(hostbuf, port, SOCK_STREAM, 10); + int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT; + if (pieces.size() > 1) { + if (sscanf(pieces[1].c_str(), "%d", &port) != 1) { + *response = android::base::StringPrintf("bad port number %s", pieces[1].c_str()); + return; + } + } + + // This may look like we're putting 'host' back together, + // but we're actually inserting the default port if necessary. + std::string serial = android::base::StringPrintf("%s:%d", hostname.c_str(), port); + + int fd = socket_network_client_timeout(hostname.c_str(), port, SOCK_STREAM, 10); if (fd < 0) { - snprintf(buffer, buffer_size, "unable to connect to %s:%d", host, port); + *response = android::base::StringPrintf("unable to connect to %s:%d", + hostname.c_str(), port); return; } @@ -599,85 +572,74 @@ close_on_exec(fd); disable_tcp_nagle(fd); - ret = register_socket_transport(fd, serial, port, 0); + int ret = register_socket_transport(fd, serial.c_str(), port, 0); if (ret < 0) { adb_close(fd); - snprintf(buffer, buffer_size, "already connected to %s", serial); + *response = android::base::StringPrintf("already connected to %s", serial.c_str()); } else { - snprintf(buffer, buffer_size, "connected to %s", serial); + *response = android::base::StringPrintf("connected to %s", serial.c_str()); } } -void connect_emulator(char* port_spec, char* buffer, int buffer_size) -{ - char* port_separator = strchr(port_spec, ','); - if (!port_separator) { - snprintf(buffer, buffer_size, - "unable to parse '%s' as <console port>,<adb port>", - port_spec); +void connect_emulator(const std::string& port_spec, std::string* response) { + std::vector<std::string> pieces = android::base::Split(port_spec, ","); + if (pieces.size() != 2) { + *response = android::base::StringPrintf("unable to parse '%s' as <console port>,<adb port>", + port_spec.c_str()); return; } - // Zero-terminate console port and make port_separator point to 2nd port. - *port_separator++ = 0; - int console_port = strtol(port_spec, NULL, 0); - int adb_port = strtol(port_separator, NULL, 0); - if (!(console_port > 0 && adb_port > 0)) { - *(port_separator - 1) = ','; - snprintf(buffer, buffer_size, - "Invalid port numbers: Expected positive numbers, got '%s'", - port_spec); + int console_port = strtol(pieces[0].c_str(), NULL, 0); + int adb_port = strtol(pieces[1].c_str(), NULL, 0); + if (console_port <= 0 || adb_port <= 0) { + *response = android::base::StringPrintf("Invalid port numbers: %s", port_spec.c_str()); return; } - /* Check if the emulator is already known. - * Note: There's a small but harmless race condition here: An emulator not - * present just yet could be registered by another invocation right - * after doing this check here. However, local_connect protects - * against double-registration too. From here, a better error message - * can be produced. In the case of the race condition, the very specific - * error message won't be shown, but the data doesn't get corrupted. */ + // Check if the emulator is already known. + // Note: There's a small but harmless race condition here: An emulator not + // present just yet could be registered by another invocation right + // after doing this check here. However, local_connect protects + // against double-registration too. From here, a better error message + // can be produced. In the case of the race condition, the very specific + // error message won't be shown, but the data doesn't get corrupted. atransport* known_emulator = find_emulator_transport_by_adb_port(adb_port); - if (known_emulator != NULL) { - snprintf(buffer, buffer_size, - "Emulator on port %d already registered.", adb_port); + if (known_emulator != nullptr) { + *response = android::base::StringPrintf("Emulator already registered on port %d", adb_port); return; } - /* Check if more emulators can be registered. Similar unproblematic - * race condition as above. */ + // Check if more emulators can be registered. Similar unproblematic + // race condition as above. int candidate_slot = get_available_local_transport_index(); if (candidate_slot < 0) { - snprintf(buffer, buffer_size, "Cannot accept more emulators."); + *response = "Cannot accept more emulators"; return; } - /* Preconditions met, try to connect to the emulator. */ + // Preconditions met, try to connect to the emulator. if (!local_connect_arbitrary_ports(console_port, adb_port)) { - snprintf(buffer, buffer_size, - "Connected to emulator on ports %d,%d", console_port, adb_port); + *response = android::base::StringPrintf("Connected to emulator on ports %d,%d", + console_port, adb_port); } else { - snprintf(buffer, buffer_size, - "Could not connect to emulator on ports %d,%d", - console_port, adb_port); + *response = android::base::StringPrintf("Could not connect to emulator on ports %d,%d", + console_port, adb_port); } } static void connect_service(int fd, void* cookie) { - char buf[4096]; - char resp[4096]; char *host = reinterpret_cast<char*>(cookie); + std::string response; if (!strncmp(host, "emu:", 4)) { - connect_emulator(host + 4, buf, sizeof(buf)); + connect_emulator(host + 4, &response); } else { - connect_device(host, buf, sizeof(buf)); + connect_device(host, &response); } // Send response for emulator and device - snprintf(resp, sizeof(resp), "%04x%s",(unsigned)strlen(buf), buf); - WriteFdExactly(fd, resp, strlen(resp)); + SendProtocolString(fd, response); adb_close(fd); } #endif
diff --git a/adb/set_verity_enable_state_service.cpp b/adb/set_verity_enable_state_service.cpp index b75ed4c..bae38cf 100644 --- a/adb/set_verity_enable_state_service.cpp +++ b/adb/set_verity_enable_state_service.cpp
@@ -21,13 +21,13 @@ #include <fcntl.h> #include <inttypes.h> #include <stdarg.h> -#include <stdbool.h> #include <stdio.h> #include <sys/stat.h> #include "cutils/properties.h" #include "adb.h" +#include "adb_io.h" #include "ext4_sb.h" #include "fs_mgr.h" #include "remount_service.h" @@ -41,18 +41,6 @@ static const bool kAllowDisableVerity = false; #endif -__attribute__((__format__(printf, 2, 3))) __nonnull((2)) -static void write_console(int fd, const char* format, ...) -{ - char buffer[256]; - va_list args; - va_start (args, format); - vsnprintf (buffer, sizeof(buffer), format, args); - va_end (args); - - adb_write(fd, buffer, strnlen(buffer, sizeof(buffer))); -} - static int get_target_device_size(int fd, const char *blk_device, uint64_t *device_size) { @@ -64,18 +52,18 @@ data_device = adb_open(blk_device, O_RDONLY | O_CLOEXEC); if (data_device < 0) { - write_console(fd, "Error opening block device (%s)\n", strerror(errno)); + WriteFdFmt(fd, "Error opening block device (%s)\n", strerror(errno)); return -1; } if (lseek64(data_device, 1024, SEEK_SET) < 0) { - write_console(fd, "Error seeking to superblock\n"); + WriteFdFmt(fd, "Error seeking to superblock\n"); adb_close(data_device); return -1; } if (adb_read(data_device, &sb, sizeof(sb)) != sizeof(sb)) { - write_console(fd, "Error reading superblock\n"); + WriteFdFmt(fd, "Error reading superblock\n"); adb_close(data_device); return -1; } @@ -98,74 +86,65 @@ int device = -1; int retval = -1; - if (make_block_device_writable(block_device)) { - write_console(fd, "Could not make block device %s writable (%s).\n", - block_device, strerror(errno)); + if (!make_block_device_writable(block_device)) { + WriteFdFmt(fd, "Could not make block device %s writable (%s).\n", + block_device, strerror(errno)); goto errout; } device = adb_open(block_device, O_RDWR | O_CLOEXEC); if (device == -1) { - write_console(fd, "Could not open block device %s (%s).\n", - block_device, strerror(errno)); - write_console(fd, "Maybe run adb remount?\n"); + WriteFdFmt(fd, "Could not open block device %s (%s).\n", block_device, strerror(errno)); + WriteFdFmt(fd, "Maybe run adb remount?\n"); goto errout; } // find the start of the verity metadata if (get_target_device_size(fd, (char*)block_device, &device_length) < 0) { - write_console(fd, "Could not get target device size.\n"); + WriteFdFmt(fd, "Could not get target device size.\n"); goto errout; } if (lseek64(device, device_length, SEEK_SET) < 0) { - write_console(fd, - "Could not seek to start of verity metadata block.\n"); + WriteFdFmt(fd, "Could not seek to start of verity metadata block.\n"); goto errout; } // check the magic number - if (adb_read(device, &magic_number, sizeof(magic_number)) - != sizeof(magic_number)) { - write_console(fd, "Couldn't read magic number!\n"); + if (adb_read(device, &magic_number, sizeof(magic_number)) != sizeof(magic_number)) { + WriteFdFmt(fd, "Couldn't read magic number!\n"); goto errout; } if (!enable && magic_number == VERITY_METADATA_MAGIC_DISABLE) { - write_console(fd, "Verity already disabled on %s\n", mount_point); + WriteFdFmt(fd, "Verity already disabled on %s\n", mount_point); goto errout; } if (enable && magic_number == VERITY_METADATA_MAGIC_NUMBER) { - write_console(fd, "Verity already enabled on %s\n", mount_point); + WriteFdFmt(fd, "Verity already enabled on %s\n", mount_point); goto errout; } if (magic_number != VERITY_METADATA_MAGIC_NUMBER && magic_number != VERITY_METADATA_MAGIC_DISABLE) { - write_console(fd, - "Couldn't find verity metadata at offset %" PRIu64 "!\n", - device_length); + WriteFdFmt(fd, "Couldn't find verity metadata at offset %" PRIu64 "!\n", device_length); goto errout; } if (lseek64(device, device_length, SEEK_SET) < 0) { - write_console(fd, - "Could not seek to start of verity metadata block.\n"); + WriteFdFmt(fd, "Could not seek to start of verity metadata block.\n"); goto errout; } if (adb_write(device, &new_magic, sizeof(new_magic)) != sizeof(new_magic)) { - write_console( - fd, "Could not set verity %s flag on device %s with error %s\n", - enable ? "enabled" : "disabled", - block_device, strerror(errno)); + WriteFdFmt(fd, "Could not set verity %s flag on device %s with error %s\n", + enable ? "enabled" : "disabled", + block_device, strerror(errno)); goto errout; } - write_console(fd, "Verity %s on %s\n", - enable ? "enabled" : "disabled", - mount_point); + WriteFdFmt(fd, "Verity %s on %s\n", enable ? "enabled" : "disabled", mount_point); retval = 0; errout: if (device != -1) @@ -184,14 +163,13 @@ property_get("ro.secure", propbuf, "0"); if (strcmp(propbuf, "1")) { - write_console(fd, "verity not enabled - ENG build\n"); + WriteFdFmt(fd, "verity not enabled - ENG build\n"); goto errout; } property_get("ro.debuggable", propbuf, "0"); if (strcmp(propbuf, "1")) { - write_console( - fd, "verity cannot be disabled/enabled - USER build\n"); + WriteFdFmt(fd, "verity cannot be disabled/enabled - USER build\n"); goto errout; } @@ -201,8 +179,7 @@ fstab = fs_mgr_read_fstab(fstab_filename); if (!fstab) { - write_console(fd, "Failed to open %s\nMaybe run adb root?\n", - fstab_filename); + WriteFdFmt(fd, "Failed to open %s\nMaybe run adb root?\n", fstab_filename); goto errout; } @@ -218,12 +195,11 @@ } if (any_changed) { - write_console( - fd, "Now reboot your device for settings to take effect\n"); + WriteFdFmt(fd, "Now reboot your device for settings to take effect\n"); } } else { - write_console(fd, "%s-verity only works for userdebug builds\n", - enable ? "enable" : "disable"); + WriteFdFmt(fd, "%s-verity only works for userdebug builds\n", + enable ? "enable" : "disable"); } errout:
diff --git a/adb/sockets.cpp b/adb/sockets.cpp index f468029..32ca17d 100644 --- a/adb/sockets.cpp +++ b/adb/sockets.cpp
@@ -37,23 +37,6 @@ static void local_socket_close_locked(asocket *s); -int sendfailmsg(int fd, const char *reason) -{ - char buf[9]; - int len; - len = strlen(reason); - if (len > 0xffff) { - len = 0xffff; - } - - snprintf(buf, sizeof buf, "FAIL%04x", len); - if (!WriteFdExactly(fd, buf, 8)) { - return -1; - } - - return WriteFdExactly(fd, reason, len) ? 0 : -1; -} - static unsigned local_socket_next_id = 1; static asocket local_socket_list = { @@ -608,7 +591,7 @@ s->ready = local_socket_ready; s->shutdown = NULL; s->close = local_socket_close; - adb_write(s->fd, "OKAY", 4); + SendOkay(s->fd); s->ready(s); } @@ -620,11 +603,11 @@ s->ready = local_socket_ready; s->shutdown = NULL; s->close = local_socket_close; - sendfailmsg(s->fd, "closed"); + SendFail(s->fd, "closed"); s->close(s); } -unsigned unhex(unsigned char *s, int len) +static unsigned unhex(unsigned char *s, int len) { unsigned n = 0, c; @@ -654,6 +637,8 @@ return n; } +#if ADB_HOST + #define PREFIX(str) { str, sizeof(str) - 1 } static const struct prefix_struct { const char *str; @@ -670,7 +655,7 @@ skipping over the 'serial' parameter in the ADB protocol, where parameter string may be a host:port string containing the protocol delimiter (colon). */ -char *skip_host_serial(char *service) { +static char *skip_host_serial(char *service) { char *first_colon, *serial_end; int i; @@ -698,6 +683,8 @@ return serial_end; } +#endif // ADB_HOST + static int smart_socket_enqueue(asocket *s, apacket *p) { unsigned len; @@ -799,7 +786,7 @@ s2 = create_host_service_socket(service, serial); if(s2 == 0) { D( "SS(%d): couldn't create host service '%s'\n", s->id, service ); - sendfailmsg(s->peer->fd, "unknown host service"); + SendFail(s->peer->fd, "unknown host service"); goto fail; } @@ -810,7 +797,7 @@ ** connection, and close this smart socket now ** that its work is done. */ - adb_write(s->peer->fd, "OKAY", 4); + SendOkay(s->peer->fd); s->peer->ready = local_socket_ready; s->peer->shutdown = NULL; @@ -831,7 +818,7 @@ s->transport = acquire_one_transport(CS_ANY, kTransportAny, NULL, &error_msg); if (s->transport == NULL) { - sendfailmsg(s->peer->fd, error_msg.c_str()); + SendFail(s->peer->fd, error_msg); goto fail; } } @@ -841,7 +828,7 @@ /* if there's no remote we fail the connection ** right here and terminate it */ - sendfailmsg(s->peer->fd, "device offline (x)"); + SendFail(s->peer->fd, "device offline (x)"); goto fail; }
diff --git a/adb/sysdeps.h b/adb/sysdeps.h index d9a1518..59e5b0b 100644 --- a/adb/sysdeps.h +++ b/adb/sysdeps.h
@@ -271,8 +271,6 @@ return isalpha(path[0]) && path[1] == ':' && path[2] == '\\'; } -extern char* adb_strtok_r(char *str, const char *delim, char **saveptr); - #else /* !_WIN32 a.k.a. Unix */ #include "fdevent.h" @@ -517,19 +515,11 @@ return path[0] == '/'; } -static __inline__ char* adb_strtok_r(char *str, const char *delim, char **saveptr) -{ - return strtok_r(str, delim, saveptr); -} - static __inline__ unsigned long adb_thread_id() { return (unsigned long)pthread_self(); } -#undef strtok_r -#define strtok_r ___xxx_strtok_r - #endif /* !_WIN32 */ #endif /* _ADB_SYSDEPS_H */
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp index de47638..a21272f 100644 --- a/adb/sysdeps_win32.cpp +++ b/adb/sysdeps_win32.cpp
@@ -22,7 +22,6 @@ #include <windows.h> #include <errno.h> -#include <stdbool.h> #include <stdio.h> #include <stdlib.h> @@ -2151,85 +2150,6 @@ InitializeCriticalSection( &_win32_lock ); } -/* Windows doesn't have strtok_r. Use the one from bionic. */ - -/* - * Copyright (c) 1988 Regents of the University of California. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -char * -adb_strtok_r(char *s, const char *delim, char **last) -{ - char *spanp; - int c, sc; - char *tok; - - - if (s == NULL && (s = *last) == NULL) - return (NULL); - - /* - * Skip (span) leading delimiters (s += strspn(s, delim), sort of). - */ -cont: - c = *s++; - for (spanp = (char *)delim; (sc = *spanp++) != 0;) { - if (c == sc) - goto cont; - } - - if (c == 0) { /* no non-delimiter characters */ - *last = NULL; - return (NULL); - } - tok = s - 1; - - /* - * Scan token (scan for delimiters: s += strcspn(s, delim), sort of). - * Note that delim must have one NUL; we stop if we see that, too. - */ - for (;;) { - c = *s++; - spanp = (char *)delim; - do { - if ((sc = *spanp++) == c) { - if (c == 0) - s = NULL; - else - s[-1] = 0; - *last = s; - return (tok); - } - } while (sc != 0); - } - /* NOTREACHED */ -} - /**************************************************************************/ /**************************************************************************/ /***** *****/
diff --git a/adb/test_track_devices.cpp b/adb/test_track_devices.cpp index 77b3ad9..3e823e9 100644 --- a/adb/test_track_devices.cpp +++ b/adb/test_track_devices.cpp
@@ -1,3 +1,5 @@ +// TODO: replace this with a shell/python script. + /* a simple test program, connects to ADB server, and opens a track-devices session */ #include <netdb.h> #include <sys/socket.h> @@ -6,6 +8,8 @@ #include <errno.h> #include <memory.h> +#include <base/file.h> + static void panic( const char* msg ) { @@ -13,82 +17,49 @@ exit(1); } -static int -unix_write( int fd, const char* buf, int len ) -{ - int result = 0; - while (len > 0) { - int len2 = write(fd, buf, len); - if (len2 < 0) { - if (errno == EINTR || errno == EAGAIN) - continue; - return -1; - } - result += len2; - len -= len2; - buf += len2; +int main(int argc, char* argv[]) { + const char* request = "host:track-devices"; + + if (argv[1] && strcmp(argv[1], "--jdwp") == 0) { + request = "track-jdwp"; } - return result; -} -static int -unix_read( int fd, char* buf, int len ) -{ - int result = 0; - while (len > 0) { - int len2 = read(fd, buf, len); - if (len2 < 0) { - if (errno == EINTR || errno == EAGAIN) - continue; - return -1; - } - result += len2; - len -= len2; - buf += len2; - } - return result; -} - - -int main( void ) -{ - int ret, s; + int ret; struct sockaddr_in server; char buffer[1024]; - const char* request = "host:track-devices"; - int len; memset( &server, 0, sizeof(server) ); server.sin_family = AF_INET; server.sin_port = htons(5037); server.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - s = socket( PF_INET, SOCK_STREAM, 0 ); + int s = socket( PF_INET, SOCK_STREAM, 0 ); ret = connect( s, (struct sockaddr*) &server, sizeof(server) ); if (ret < 0) panic( "could not connect to server" ); /* send the request */ - len = snprintf( buffer, sizeof buffer, "%04x%s", strlen(request), request ); - if (unix_write(s, buffer, len) < 0) + int len = snprintf(buffer, sizeof(buffer), "%04zx%s", strlen(request), request); + if (!android::base::WriteFully(s, buffer, len)) panic( "could not send request" ); /* read the OKAY answer */ - if (unix_read(s, buffer, 4) != 4) + if (!android::base::ReadFully(s, buffer, 4)) panic( "could not read request" ); printf( "server answer: %.*s\n", 4, buffer ); /* now loop */ - for (;;) { + while (true) { char head[5] = "0000"; - if (unix_read(s, head, 4) < 0) + if (!android::base::ReadFully(s, head, 4)) panic("could not read length"); - if ( sscanf( head, "%04x", &len ) != 1 ) + int len; + if (sscanf(head, "%04x", &len) != 1 ) panic("could not decode length"); - if (unix_read(s, buffer, len) != len) + if (!android::base::ReadFully(s, buffer, len)) panic("could not read data"); printf( "received header %.*s (%d bytes):\n%.*s", 4, head, len, len, buffer );
diff --git a/adb/test_track_jdwp.cpp b/adb/test_track_jdwp.cpp deleted file mode 100644 index 8ecc6b8..0000000 --- a/adb/test_track_jdwp.cpp +++ /dev/null
@@ -1,97 +0,0 @@ -/* a simple test program, connects to ADB server, and opens a track-devices session */ -#include <netdb.h> -#include <sys/socket.h> -#include <stdio.h> -#include <stdlib.h> -#include <errno.h> -#include <memory.h> - -static void -panic( const char* msg ) -{ - fprintf(stderr, "PANIC: %s: %s\n", msg, strerror(errno)); - exit(1); -} - -static int -unix_write( int fd, const char* buf, int len ) -{ - int result = 0; - while (len > 0) { - int len2 = write(fd, buf, len); - if (len2 < 0) { - if (errno == EINTR || errno == EAGAIN) - continue; - return -1; - } - result += len2; - len -= len2; - buf += len2; - } - return result; -} - -static int -unix_read( int fd, char* buf, int len ) -{ - int result = 0; - while (len > 0) { - int len2 = read(fd, buf, len); - if (len2 < 0) { - if (errno == EINTR || errno == EAGAIN) - continue; - return -1; - } - result += len2; - len -= len2; - buf += len2; - } - return result; -} - - -int main( void ) -{ - int ret, s; - struct sockaddr_in server; - char buffer[1024]; - const char* request = "track-jdwp"; - int len; - - memset( &server, 0, sizeof(server) ); - server.sin_family = AF_INET; - server.sin_port = htons(5037); - server.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - - s = socket( PF_INET, SOCK_STREAM, 0 ); - ret = connect( s, (struct sockaddr*) &server, sizeof(server) ); - if (ret < 0) panic( "could not connect to server" ); - - /* send the request */ - len = snprintf( buffer, sizeof buffer, "%04x%s", strlen(request), request ); - if (unix_write(s, buffer, len) < 0) - panic( "could not send request" ); - - /* read the OKAY answer */ - if (unix_read(s, buffer, 4) != 4) - panic( "could not read request" ); - - printf( "server answer: %.*s\n", 4, buffer ); - - /* now loop */ - for (;;) { - char head[5] = "0000"; - - if (unix_read(s, head, 4) < 0) - panic("could not read length"); - - if ( sscanf( head, "%04x", &len ) != 1 ) - panic("could not decode length"); - - if (unix_read(s, buffer, len) != len) - panic("could not read data"); - - printf( "received header %.*s (%d bytes):\n%.*s", 4, head, len, len, buffer ); - } - close(s); -}
diff --git a/adb/tests/test_adb.py b/adb/tests/test_adb.py index 52d8056..0ff87d2 100755 --- a/adb/tests/test_adb.py +++ b/adb/tests/test_adb.py
@@ -6,6 +6,7 @@ """ import hashlib import os +import pipes import random import re import shlex @@ -162,6 +163,9 @@ def shell_nocheck(self, cmd): return call_combined(self.adb_cmd + "shell " + cmd) + def install(self, filename): + return call_checked(self.adb_cmd + "install {}".format(pipes.quote(filename))) + def push(self, local, remote): return call_checked(self.adb_cmd + "push {} {}".format(local, remote)) @@ -272,13 +276,36 @@ adb = AdbWrapper() # http://b/19734868 + # Note that this actually matches ssh(1)'s behavior --- it's + # converted to "sh -c echo hello; echo world" which sh interprets + # as "sh -c echo" (with an argument to that shell of "hello"), + # and then "echo world" back in the first shell. result = adb.shell("sh -c 'echo hello; echo world'").splitlines() + self.assertEqual(["", "world"], result) + # If you really wanted "hello" and "world", here's what you'd do: + result = adb.shell("echo hello\;echo world").splitlines() self.assertEqual(["hello", "world"], result) # http://b/15479704 self.assertEqual('t', adb.shell("'true && echo t'").strip()) self.assertEqual('t', adb.shell("sh -c 'true && echo t'").strip()) + # http://b/20564385 + self.assertEqual('t', adb.shell("FOO=a BAR=b echo t").strip()) + self.assertEqual('123Linux', adb.shell("echo -n 123\;uname").strip()) + + def test_install_argument_escaping(self): + """Make sure that install argument escaping works.""" + adb = AdbWrapper() + + # http://b/20323053 + tf = tempfile.NamedTemporaryFile("w", suffix="-text;ls;1.apk") + self.assertIn("-text;ls;1.apk", adb.install(tf.name)) + + # http://b/3090932 + tf = tempfile.NamedTemporaryFile("w", suffix="-Live Hold'em.apk") + self.assertIn("-Live Hold'em.apk", adb.install(tf.name)) + class AdbFile(unittest.TestCase): SCRATCH_DIR = "/data/local/tmp"
diff --git a/adb/transport.cpp b/adb/transport.cpp index d395a80..2cd6ac2 100644 --- a/adb/transport.cpp +++ b/adb/transport.cpp
@@ -26,7 +26,10 @@ #include <string.h> #include <unistd.h> +#include <base/stringprintf.h> + #include "adb.h" +#include "adb_utils.h" static void transport_unref(atransport *t); @@ -42,34 +45,6 @@ ADB_MUTEX_DEFINE( transport_lock ); -#if ADB_TRACE -#define MAX_DUMP_HEX_LEN 16 -void dump_hex(const unsigned char* ptr, size_t len) -{ - int nn, len2 = len; - // Build a string instead of logging each character. - // MAX chars in 2 digit hex, one space, MAX chars, one '\0'. - char buffer[MAX_DUMP_HEX_LEN *2 + 1 + MAX_DUMP_HEX_LEN + 1 ], *pb = buffer; - - if (len2 > MAX_DUMP_HEX_LEN) len2 = MAX_DUMP_HEX_LEN; - - for (nn = 0; nn < len2; nn++) { - sprintf(pb, "%02x", ptr[nn]); - pb += 2; - } - sprintf(pb++, " "); - - for (nn = 0; nn < len2; nn++) { - int c = ptr[nn]; - if (c < 32 || c > 127) - c = '.'; - *pb++ = c; - } - *pb++ = '\0'; - DR("%s\n", buffer); -} -#endif - void kick_transport(atransport* t) { if (t && !t->kicked) @@ -117,10 +92,7 @@ } } -#if ADB_TRACE -static void -dump_packet(const char* name, const char* func, apacket* p) -{ +static void dump_packet(const char* name, const char* func, apacket* p) { unsigned command = p->msg.command; int len = p->msg.data_length; char cmd[9]; @@ -155,7 +127,6 @@ name, func, cmd, arg0, arg1, len); dump_hex(p->data, len); } -#endif /* ADB_TRACE */ static int read_packet(int fd, const char* name, apacket** ppacket) @@ -180,11 +151,9 @@ } } -#if ADB_TRACE if (ADB_TRACING) { dump_packet(name, "from remote", *ppacket); } -#endif return 0; } @@ -199,11 +168,9 @@ name = buff; } -#if ADB_TRACE if (ADB_TRACING) { dump_packet(name, "to remote", *ppacket); } -#endif len = sizeof(ppacket); while(len > 0) { r = adb_write(fd, p, len); @@ -389,17 +356,6 @@ #if ADB_HOST -static int list_transports_msg(char* buffer, size_t bufferlen) -{ - char head[5]; - int len; - - len = list_transports(buffer+4, bufferlen-4, 0); - snprintf(head, sizeof(head), "%04x", len); - memcpy(buffer, head, 4); - len += 4; - return len; -} /* this adds support required by the 'track-devices' service. * this is used to send the content of "list_transport" to any @@ -457,39 +413,29 @@ return -1; } -static int -device_tracker_send( device_tracker* tracker, - const char* buffer, - int len ) -{ - apacket* p = get_apacket(); - asocket* peer = tracker->socket.peer; +static int device_tracker_send(device_tracker* tracker, const std::string& string) { + apacket* p = get_apacket(); + asocket* peer = tracker->socket.peer; - memcpy(p->data, buffer, len); - p->len = len; - return peer->enqueue( peer, p ); + snprintf(reinterpret_cast<char*>(p->data), 5, "%04x", static_cast<int>(string.size())); + memcpy(&p->data[4], string.data(), string.size()); + p->len = 4 + string.size(); + return peer->enqueue(peer, p); } +static void device_tracker_ready(asocket* socket) { + device_tracker* tracker = reinterpret_cast<device_tracker*>(socket); -static void -device_tracker_ready( asocket* socket ) -{ - device_tracker* tracker = (device_tracker*) socket; - - /* we want to send the device list when the tracker connects - * for the first time, even if no update occured */ + // We want to send the device list when the tracker connects + // for the first time, even if no update occurred. if (tracker->update_needed > 0) { - char buffer[1024]; - int len; - tracker->update_needed = 0; - len = list_transports_msg(buffer, sizeof(buffer)); - device_tracker_send(tracker, buffer, len); + std::string transports = list_transports(false); + device_tracker_send(tracker, transports); } } - asocket* create_device_tracker(void) { @@ -510,27 +456,25 @@ } -/* call this function each time the transport list has changed */ -void update_transports(void) { - char buffer[1024]; - int len; - device_tracker* tracker; +// Call this function each time the transport list has changed. +void update_transports() { + std::string transports = list_transports(false); - len = list_transports_msg(buffer, sizeof(buffer)); - - tracker = device_tracker_list; - while (tracker != NULL) { - device_tracker* next = tracker->next; - /* note: this may destroy the tracker if the connection is closed */ - device_tracker_send(tracker, buffer, len); + device_tracker* tracker = device_tracker_list; + while (tracker != nullptr) { + device_tracker* next = tracker->next; + // This may destroy the tracker if the connection is closed. + device_tracker_send(tracker, transports); tracker = next; } } + #else -void update_transports(void) -{ - // nothing to do on the device side + +void update_transports() { + // Nothing to do on the device side. } + #endif // ADB_HOST struct tmsg @@ -804,7 +748,7 @@ int ambiguous = 0; retry: - if (error_out) *error_out = "device not found"; + if (error_out) *error_out = android::base::StringPrintf("device '%s' not found", serial); adb_mutex_lock(&transport_lock); for (t = transport_list.next; t != &transport_list; t = t->next) { @@ -847,7 +791,7 @@ result = t; } else if (ttype == kTransportAny) { if (result) { - if (error_out) *error_out = "more than one device and emulator"; + if (error_out) *error_out = "more than one device/emulator"; ambiguous = 1; result = NULL; break; @@ -895,100 +839,72 @@ return result; } -#if ADB_HOST -static const char *statename(atransport *t) -{ - switch(t->connection_state){ +const char* atransport::connection_state_name() const { + switch (connection_state) { case CS_OFFLINE: return "offline"; case CS_BOOTLOADER: return "bootloader"; case CS_DEVICE: return "device"; case CS_HOST: return "host"; case CS_RECOVERY: return "recovery"; - case CS_SIDELOAD: return "sideload"; case CS_NOPERM: return "no permissions"; + case CS_SIDELOAD: return "sideload"; case CS_UNAUTHORIZED: return "unauthorized"; default: return "unknown"; } } -static void add_qual(char **buf, size_t *buf_size, - const char *prefix, const char *qual, bool sanitize_qual) -{ - if (!buf || !*buf || !buf_size || !*buf_size || !qual || !*qual) +#if ADB_HOST + +static void append_transport_info(std::string* result, const char* key, + const char* value, bool sanitize) { + if (value == nullptr || *value == '\0') { return; - - int prefix_len; - size_t len = snprintf(*buf, *buf_size, "%s%n%s", prefix, &prefix_len, qual); - - if (sanitize_qual) { - for (char* cp = *buf + prefix_len; cp < *buf + len; cp++) { - if (!isalnum(*cp)) - *cp = '_'; - } } - *buf_size -= len; - *buf += len; + *result += ' '; + *result += key; + + for (const char* p = value; *p; ++p) { + result->push_back((!sanitize || isalnum(*p)) ? *p : '_'); + } } -static size_t format_transport(atransport *t, char *buf, size_t bufsize, - int long_listing) -{ +static void append_transport(atransport* t, std::string* result, bool long_listing) { const char* serial = t->serial; - if (!serial || !serial[0]) - serial = "????????????"; + if (!serial || !serial[0]) { + serial = "(no serial number)"; + } if (!long_listing) { - return snprintf(buf, bufsize, "%s\t%s\n", serial, statename(t)); + *result += serial; + *result += '\t'; + *result += t->connection_state_name(); } else { - size_t len, remaining = bufsize; + android::base::StringAppendF(result, "%-22s %s", serial, t->connection_state_name()); - len = snprintf(buf, remaining, "%-22s %s", serial, statename(t)); - remaining -= len; - buf += len; - - add_qual(&buf, &remaining, " ", t->devpath, false); - add_qual(&buf, &remaining, " product:", t->product, false); - add_qual(&buf, &remaining, " model:", t->model, true); - add_qual(&buf, &remaining, " device:", t->device, false); - - len = snprintf(buf, remaining, "\n"); - remaining -= len; - - return bufsize - remaining; + append_transport_info(result, "", t->devpath, false); + append_transport_info(result, "product:", t->product, false); + append_transport_info(result, "model:", t->model, true); + append_transport_info(result, "device:", t->device, false); } + *result += '\n'; } -int list_transports(char *buf, size_t bufsize, int long_listing) -{ - char* p = buf; - char* end = buf + bufsize; - int len; - atransport *t; - - /* XXX OVERRUN PROBLEMS XXX */ +std::string list_transports(bool long_listing) { + std::string result; adb_mutex_lock(&transport_lock); - for(t = transport_list.next; t != &transport_list; t = t->next) { - len = format_transport(t, p, end - p, long_listing); - if (p + len >= end) { - /* discard last line if buffer is too short */ - break; - } - p += len; + for (atransport* t = transport_list.next; t != &transport_list; t = t->next) { + append_transport(t, &result, long_listing); } - p[0] = 0; adb_mutex_unlock(&transport_lock); - return p - buf; + return result; } - /* hack for osx */ void close_usb_devices() { - atransport *t; - adb_mutex_lock(&transport_lock); - for(t = transport_list.next; t != &transport_list; t = t->next) { + for (atransport* t = transport_list.next; t != &transport_list; t = t->next) { if ( !t->kicked ) { t->kicked = 1; t->kick(t);
diff --git a/adb/transport.h b/adb/transport.h index a2077e8..5b6fdac 100644 --- a/adb/transport.h +++ b/adb/transport.h
@@ -23,10 +23,6 @@ #include "adb.h" -#if ADB_TRACE -void dump_hex(const unsigned char* ptr, size_t len); -#endif - /* * Obtain a transport from the available transports. * If state is != CS_ANY, only transports in that state are considered. @@ -45,7 +41,7 @@ ** get_device_transport does an acquire on your behalf before returning */ void init_transport_registration(void); -int list_transports(char* buf, size_t bufsize, int long_listing); +std::string list_transports(bool long_listing); atransport* find_transport(const char* serial); void register_usb_transport(usb_handle* h, const char* serial,
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp index 30e6bf5..b1deffd 100644 --- a/adb/transport_local.cpp +++ b/adb/transport_local.cpp
@@ -25,6 +25,8 @@ #include <string.h> #include <sys/types.h> +#include <base/stringprintf.h> + #if !ADB_HOST #include "cutils/properties.h" #endif @@ -88,7 +90,6 @@ int local_connect_arbitrary_ports(int console_port, int adb_port) { - char buf[64]; int fd = -1; #if ADB_HOST @@ -105,8 +106,8 @@ D("client: connected on remote on fd %d\n", fd); close_on_exec(fd); disable_tcp_nagle(fd); - snprintf(buf, sizeof buf, "%s%d", LOCAL_CLIENT_PREFIX, console_port); - register_socket_transport(fd, buf, adb_port, 1); + std::string serial = android::base::StringPrintf("emulator-%d", console_port); + register_socket_transport(fd, serial.c_str(), adb_port, 1); return 0; } return -1;
diff --git a/adb/usb_linux.cpp b/adb/usb_linux.cpp index 31fd167..71baaee 100644 --- a/adb/usb_linux.cpp +++ b/adb/usb_linux.cpp
@@ -31,11 +31,11 @@ #include <sys/time.h> #include <sys/types.h> #include <unistd.h> -#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20) #include <linux/usb/ch9.h> -#else -#include <linux/usb_ch9.h> -#endif + +#include <base/file.h> +#include <base/stringprintf.h> +#include <base/strings.h> #include "adb.h" #include "transport.h" @@ -567,21 +567,17 @@ return 0; } -static void register_device(const char *dev_name, const char *devpath, +static void register_device(const char* dev_name, const char* dev_path, unsigned char ep_in, unsigned char ep_out, - int interface, int serial_index, unsigned zero_mask) -{ - int n = 0; - char serial[256]; - - /* Since Linux will not reassign the device ID (and dev_name) - ** as long as the device is open, we can add to the list here - ** once we open it and remove from the list when we're finally - ** closed and everything will work out fine. - ** - ** If we have a usb_handle on the list 'o handles with a matching - ** name, we have no further work to do. - */ + int interface, int serial_index, + unsigned zero_mask) { + // Since Linux will not reassign the device ID (and dev_name) as long as the + // device is open, we can add to the list here once we open it and remove + // from the list when we're finally closed and everything will work out + // fine. + // + // If we have a usb_handle on the list 'o handles with a matching name, we + // have no further work to do. adb_mutex_lock(&usb_lock); for (usb_handle* usb = handle_list.next; usb != &handle_list; usb = usb->next) { if (!strcmp(usb->fname, dev_name)) { @@ -602,7 +598,8 @@ adb_cond_init(&usb->notify, 0); adb_mutex_init(&usb->lock, 0); - /* initialize mark to 1 so we don't get garbage collected after the device scan */ + // Initialize mark to 1 so we don't get garbage collected after the device + // scan. usb->mark = 1; usb->reaper_thread = 0; @@ -618,70 +615,33 @@ usb->writeable = 0; } - D("[ usb opened %s%s, fd=%d]\n", usb->fname, (usb->writeable ? "" : " (read-only)"), usb->desc); + D("[ usb opened %s%s, fd=%d]\n", usb->fname, + (usb->writeable ? "" : " (read-only)"), usb->desc); if (usb->writeable) { - n = ioctl(usb->desc, USBDEVFS_CLAIMINTERFACE, &interface); - if (n != 0) { - D("[ usb ioctl(%d, USBDEVFS_CLAIMINTERFACE) failed: %s]\n", usb->desc, strerror(errno)); + if (ioctl(usb->desc, USBDEVFS_CLAIMINTERFACE, &interface) != 0) { + D("[ usb ioctl(%d, USBDEVFS_CLAIMINTERFACE) failed: %s]\n", + usb->desc, strerror(errno)); adb_close(usb->desc); free(usb); return; } } - /* read the device's serial number */ - serial[0] = 0; - memset(serial, 0, sizeof(serial)); - if (serial_index) { - struct usbdevfs_ctrltransfer ctrl; - __u16 buffer[128]; - __u16 languages[128]; - int i, result; - int languageCount = 0; - - memset(languages, 0, sizeof(languages)); - memset(&ctrl, 0, sizeof(ctrl)); - - // read list of supported languages - ctrl.bRequestType = USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE; - ctrl.bRequest = USB_REQ_GET_DESCRIPTOR; - ctrl.wValue = (USB_DT_STRING << 8) | 0; - ctrl.wIndex = 0; - ctrl.wLength = sizeof(languages); - ctrl.data = languages; - ctrl.timeout = 1000; - - result = ioctl(usb->desc, USBDEVFS_CONTROL, &ctrl); - if (result > 0) - languageCount = (result - 2) / 2; - - for (i = 1; i <= languageCount; i++) { - memset(buffer, 0, sizeof(buffer)); - memset(&ctrl, 0, sizeof(ctrl)); - - ctrl.bRequestType = USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE; - ctrl.bRequest = USB_REQ_GET_DESCRIPTOR; - ctrl.wValue = (USB_DT_STRING << 8) | serial_index; - ctrl.wIndex = __le16_to_cpu(languages[i]); - ctrl.wLength = sizeof(buffer); - ctrl.data = buffer; - ctrl.timeout = 1000; - - result = ioctl(usb->desc, USBDEVFS_CONTROL, &ctrl); - if (result > 0) { - int i; - // skip first word, and copy the rest to the serial string, changing shorts to bytes. - result /= 2; - for (i = 1; i < result; i++) - serial[i - 1] = __le16_to_cpu(buffer[i]); - serial[i - 1] = 0; - break; - } - } + // Read the device's serial number. + std::string serial_path = android::base::StringPrintf( + "/sys/bus/usb/devices/%s/serial", dev_path + 4); + std::string serial; + if (!android::base::ReadFileToString(serial_path, &serial)) { + D("[ usb read %s failed: %s ]\n", serial_path.c_str(), strerror(errno)); + // 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 + serial = ""; } + serial = android::base::Trim(serial); - /* add to the end of the active handles */ + // Add to the end of the active handles. adb_mutex_lock(&usb_lock); usb->next = &handle_list; usb->prev = handle_list.prev; @@ -689,23 +649,21 @@ usb->next->prev = usb; adb_mutex_unlock(&usb_lock); - register_usb_transport(usb, serial, devpath, usb->writeable); + register_usb_transport(usb, serial.c_str(), dev_path, usb->writeable); } -void* device_poll_thread(void* unused) -{ +static void* device_poll_thread(void* unused) { D("Created device thread\n"); - for(;;) { - /* XXX use inotify */ + while (true) { + // TODO: Use inotify. find_usb_device("/dev/bus/usb", register_device); kick_disconnected_devices(); sleep(1); } - return NULL; + return nullptr; } -static void sigalrm_handler(int signo) -{ +static void sigalrm_handler(int signo) { // don't need to do anything here }
diff --git a/base/file.cpp b/base/file.cpp index 6b19818..9a340b7 100644 --- a/base/file.cpp +++ b/base/file.cpp
@@ -51,7 +51,7 @@ return false; } bool result = ReadFdToString(fd, content); - TEMP_FAILURE_RETRY(close(fd)); + close(fd); return result; } @@ -102,7 +102,7 @@ ALOGE("android::WriteStringToFile write failed: %s", strerror(errno)); return CleanUpAfterFailedWrite(path); } - TEMP_FAILURE_RETRY(close(fd)); + close(fd); return true; } #endif @@ -116,7 +116,7 @@ } bool result = WriteStringToFd(content, fd); - TEMP_FAILURE_RETRY(close(fd)); + close(fd); return result || CleanUpAfterFailedWrite(path); }
diff --git a/base/file_test.cpp b/base/file_test.cpp index e5cf696..5445a0d 100644 --- a/base/file_test.cpp +++ b/base/file_test.cpp
@@ -60,7 +60,7 @@ << errno; struct stat sb; ASSERT_EQ(0, stat(tf.filename, &sb)); - ASSERT_EQ(0660U, (sb.st_mode & ~S_IFMT)); + ASSERT_EQ(0660U, static_cast<unsigned int>(sb.st_mode & ~S_IFMT)); ASSERT_EQ(getuid(), sb.st_uid); ASSERT_EQ(getgid(), sb.st_gid); std::string s;
diff --git a/base/include/base/strings.h b/base/include/base/strings.h index 3559342..5dbc5fb 100644 --- a/base/include/base/strings.h +++ b/base/include/base/strings.h
@@ -27,8 +27,6 @@ // // The string is split at each occurrence of a character in delimiters. // -// Empty splits will be omitted. I.e. Split("a,,b", ",") -> {"a", "b"} -// // The empty string is not a valid delimiter list. std::vector<std::string> Split(const std::string& s, const std::string& delimiters);
diff --git a/base/strings.cpp b/base/strings.cpp index 6f698d9..d3375d9 100644 --- a/base/strings.cpp +++ b/base/strings.cpp
@@ -32,24 +32,17 @@ const std::string& delimiters) { CHECK_NE(delimiters.size(), 0U); - std::vector<std::string> split; - if (s.size() == 0) { - // Split("", d) returns {} rather than {""}. - return split; - } + std::vector<std::string> result; size_t base = 0; size_t found; do { found = s.find_first_of(delimiters, base); - if (found != base) { - split.push_back(s.substr(base, found - base)); - } - + result.push_back(s.substr(base, found - base)); base = found + 1; } while (found != s.npos); - return split; + return result; } std::string Trim(const std::string& s) {
diff --git a/base/strings_test.cpp b/base/strings_test.cpp index 1bf07a1..46a1ab5 100644 --- a/base/strings_test.cpp +++ b/base/strings_test.cpp
@@ -23,7 +23,8 @@ TEST(strings, split_empty) { std::vector<std::string> parts = android::base::Split("", ","); - ASSERT_EQ(0U, parts.size()); + ASSERT_EQ(1U, parts.size()); + ASSERT_EQ("", parts[0]); } TEST(strings, split_single) { @@ -42,9 +43,10 @@ TEST(strings, split_with_empty_part) { std::vector<std::string> parts = android::base::Split("foo,,bar", ","); - ASSERT_EQ(2U, parts.size()); + ASSERT_EQ(3U, parts.size()); ASSERT_EQ("foo", parts[0]); - ASSERT_EQ("bar", parts[1]); + ASSERT_EQ("", parts[1]); + ASSERT_EQ("bar", parts[2]); } TEST(strings, split_null_char) { @@ -65,9 +67,10 @@ TEST(strings, split_any_with_empty_part) { std::vector<std::string> parts = android::base::Split("foo:,bar", ",:"); - ASSERT_EQ(2U, parts.size()); + ASSERT_EQ(3U, parts.size()); ASSERT_EQ("foo", parts[0]); - ASSERT_EQ("bar", parts[1]); + ASSERT_EQ("", parts[1]); + ASSERT_EQ("bar", parts[2]); } TEST(strings, trim_empty) {
diff --git a/cpio/mkbootfs.c b/cpio/mkbootfs.c index 7175749..0e35323 100644 --- a/cpio/mkbootfs.c +++ b/cpio/mkbootfs.c
@@ -41,6 +41,7 @@ }; static struct fs_config_entry* canned_config = NULL; +static char *target_out_path = NULL; /* Each line in the canned file should be a path plus three ints (uid, * gid, mode). */ @@ -79,7 +80,8 @@ } else { // Use the compiled-in fs_config() function. unsigned st_mode = s->st_mode; - fs_config(path, S_ISDIR(s->st_mode), &s->st_uid, &s->st_gid, &st_mode, &capabilities); + fs_config(path, S_ISDIR(s->st_mode), target_out_path, + &s->st_uid, &s->st_gid, &st_mode, &capabilities); s->st_mode = (typeof(s->st_mode)) st_mode; } } @@ -328,6 +330,12 @@ argc--; argv++; + if (argc > 1 && strcmp(argv[0], "-d") == 0) { + target_out_path = argv[1]; + argc -= 2; + argv += 2; + } + if (argc > 1 && strcmp(argv[0], "-f") == 0) { read_canned_config(argv[1]); argc -= 2;
diff --git a/debuggerd/Android.mk b/debuggerd/Android.mk index dd53296..6cfb541 100644 --- a/debuggerd/Android.mk +++ b/debuggerd/Android.mk
@@ -1,4 +1,12 @@ -LOCAL_PATH:= $(call my-dir) +LOCAL_PATH := $(call my-dir) + +common_cppflags := \ + -std=gnu++11 \ + -W \ + -Wall \ + -Wextra \ + -Wunused \ + -Werror \ include $(CLEAR_VARS) @@ -17,11 +25,7 @@ LOCAL_SRC_FILES_x86 := x86/machine.cpp LOCAL_SRC_FILES_x86_64 := x86_64/machine.cpp -LOCAL_CPPFLAGS := \ - -std=gnu++11 \ - -W -Wall -Wextra \ - -Wunused \ - -Werror \ +LOCAL_CPPFLAGS := $(common_cppflags) ifeq ($(TARGET_IS_64_BIT),true) LOCAL_CPPFLAGS += -DTARGET_IS_64_BIT @@ -70,3 +74,56 @@ LOCAL_MULTILIB := both include $(BUILD_EXECUTABLE) + +debuggerd_test_src_files := \ + utility.cpp \ + test/dump_maps_test.cpp \ + test/dump_memory_test.cpp \ + test/elf_fake.cpp \ + test/log_fake.cpp \ + test/property_fake.cpp \ + test/ptrace_fake.cpp \ + test/selinux_fake.cpp \ + +debuggerd_shared_libraries := \ + libbacktrace \ + libbase \ + libcutils \ + +debuggerd_c_includes := \ + $(LOCAL_PATH)/test \ + +debuggerd_cpp_flags := \ + $(common_cppflags) \ + -Wno-missing-field-initializers \ + +# Only build the host tests on linux. +ifeq ($(HOST_OS),linux) + +include $(CLEAR_VARS) + +LOCAL_MODULE := debuggerd_test +LOCAL_SRC_FILES := $(debuggerd_test_src_files) +LOCAL_SHARED_LIBRARIES := $(debuggerd_shared_libraries) +LOCAL_C_INCLUDES := $(debuggerd_c_includes) +LOCAL_CPPFLAGS := $(debuggerd_cpp_flags) + +LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32 +LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64 +LOCAL_MULTILIB := both +include $(BUILD_HOST_NATIVE_TEST) + +endif + +include $(CLEAR_VARS) + +LOCAL_MODULE := debuggerd_test +LOCAL_SRC_FILES := $(debuggerd_test_src_files) +LOCAL_SHARED_LIBRARIES := $(debuggerd_shared_libraries) +LOCAL_C_INCLUDES := $(debuggerd_c_includes) +LOCAL_CPPFLAGS := $(debuggerd_cpp_flags) + +LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32 +LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64 +LOCAL_MULTILIB := both +include $(BUILD_NATIVE_TEST)
diff --git a/debuggerd/arm/machine.cpp b/debuggerd/arm/machine.cpp index 50e78c5..b7d6997 100644 --- a/debuggerd/arm/machine.cpp +++ b/debuggerd/arm/machine.cpp
@@ -16,53 +16,39 @@ */ #include <errno.h> -#include <stddef.h> -#include <stdio.h> -#include <stdlib.h> +#include <stdint.h> #include <string.h> #include <sys/ptrace.h> -#include <sys/types.h> -#include <sys/user.h> -#include "../utility.h" -#include "../machine.h" +#include <backtrace/Backtrace.h> -void dump_memory_and_code(log_t* log, pid_t tid) { +#include "machine.h" +#include "utility.h" + +void dump_memory_and_code(log_t* log, Backtrace* backtrace) { pt_regs regs; - if (ptrace(PTRACE_GETREGS, tid, 0, ®s)) { + if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, ®s)) { + _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno)); return; } - static const char REG_NAMES[] = "r0r1r2r3r4r5r6r7r8r9slfpipsp"; + static const char reg_names[] = "r0r1r2r3r4r5r6r7r8r9slfpipsp"; for (int reg = 0; reg < 14; reg++) { - // this may not be a valid way to access, but it'll do for now - uintptr_t addr = regs.uregs[reg]; - - // Don't bother if it looks like a small int or ~= null, or if - // it's in the kernel area. - if (addr < 4096 || addr >= 0xc0000000) { - continue; - } - - _LOG(log, logtype::MEMORY, "\nmemory near %.2s:\n", ®_NAMES[reg * 2]); - dump_memory(log, tid, addr); + dump_memory(log, backtrace, regs.uregs[reg], "memory near %.2s:", ®_names[reg * 2]); } - // explicitly allow upload of code dump logging - _LOG(log, logtype::MEMORY, "\ncode around pc:\n"); - dump_memory(log, tid, static_cast<uintptr_t>(regs.ARM_pc)); + dump_memory(log, backtrace, static_cast<uintptr_t>(regs.ARM_pc), "code around pc:"); if (regs.ARM_pc != regs.ARM_lr) { - _LOG(log, logtype::MEMORY, "\ncode around lr:\n"); - dump_memory(log, tid, static_cast<uintptr_t>(regs.ARM_lr)); + dump_memory(log, backtrace, static_cast<uintptr_t>(regs.ARM_lr), "code around lr:"); } } void dump_registers(log_t* log, pid_t tid) { pt_regs r; if (ptrace(PTRACE_GETREGS, tid, 0, &r)) { - _LOG(log, logtype::REGISTERS, "cannot get registers: %s\n", strerror(errno)); + _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno)); return; } @@ -82,7 +68,7 @@ user_vfp vfp_regs; if (ptrace(PTRACE_GETVFPREGS, tid, 0, &vfp_regs)) { - _LOG(log, logtype::FP_REGISTERS, "cannot get FP registers: %s\n", strerror(errno)); + _LOG(log, logtype::ERROR, "cannot get FP registers: %s\n", strerror(errno)); return; }
diff --git a/debuggerd/arm64/machine.cpp b/debuggerd/arm64/machine.cpp index 8b17d53..2e097da 100644 --- a/debuggerd/arm64/machine.cpp +++ b/debuggerd/arm64/machine.cpp
@@ -17,50 +17,37 @@ #include <elf.h> #include <errno.h> -#include <inttypes.h> +#include <stdint.h> #include <string.h> -#include <sys/types.h> #include <sys/ptrace.h> -#include <sys/user.h> #include <sys/uio.h> -#include "../utility.h" -#include "../machine.h" +#include <backtrace/Backtrace.h> -void dump_memory_and_code(log_t* log, pid_t tid) { - struct user_pt_regs regs; - struct iovec io; - io.iov_base = ®s; - io.iov_len = sizeof(regs); +#include "machine.h" +#include "utility.h" - if (ptrace(PTRACE_GETREGSET, tid, (void*)NT_PRSTATUS, &io) == -1) { - _LOG(log, logtype::ERROR, "%s: ptrace failed to get registers: %s\n", - __func__, strerror(errno)); - return; - } +void dump_memory_and_code(log_t* log, Backtrace* backtrace) { + struct user_pt_regs regs; + struct iovec io; + io.iov_base = ®s; + io.iov_len = sizeof(regs); - for (int reg = 0; reg < 31; reg++) { - uintptr_t addr = regs.regs[reg]; + if (ptrace(PTRACE_GETREGSET, backtrace->Tid(), reinterpret_cast<void*>(NT_PRSTATUS), &io) == -1) { + _LOG(log, logtype::ERROR, "%s: ptrace failed to get registers: %s", + __func__, strerror(errno)); + return; + } - /* - * Don't bother if it looks like a small int or ~= null, or if - * it's in the kernel area. - */ - if (addr < 4096 || addr >= (1UL<<63)) { - continue; - } + for (int reg = 0; reg < 31; reg++) { + dump_memory(log, backtrace, regs.regs[reg], "memory near x%d:", reg); + } - _LOG(log, logtype::MEMORY, "\nmemory near x%d:\n", reg); - dump_memory(log, tid, addr); - } + dump_memory(log, backtrace, static_cast<uintptr_t>(regs.pc), "code around pc:"); - _LOG(log, logtype::MEMORY, "\ncode around pc:\n"); - dump_memory(log, tid, (uintptr_t)regs.pc); - - if (regs.pc != regs.sp) { - _LOG(log, logtype::MEMORY, "\ncode around sp:\n"); - dump_memory(log, tid, (uintptr_t)regs.sp); - } + if (regs.pc != regs.sp) { + dump_memory(log, backtrace, static_cast<uintptr_t>(regs.sp), "code around sp:"); + } } void dump_registers(log_t* log, pid_t tid) {
diff --git a/debuggerd/backtrace.cpp b/debuggerd/backtrace.cpp index c2a1dbc..b8084c5 100644 --- a/debuggerd/backtrace.cpp +++ b/debuggerd/backtrace.cpp
@@ -14,6 +14,8 @@ * limitations under the License. */ +#define LOG_TAG "DEBUG" + #include <stddef.h> #include <stdlib.h> #include <string.h> @@ -26,8 +28,11 @@ #include <sys/types.h> #include <sys/ptrace.h> +#include <memory> + #include <backtrace/Backtrace.h> -#include <UniquePtr.h> + +#include <log/log.h> #include "backtrace.h" @@ -92,9 +97,11 @@ return; } - UniquePtr<Backtrace> backtrace(Backtrace::Create(tid, BACKTRACE_CURRENT_THREAD)); + std::unique_ptr<Backtrace> backtrace(Backtrace::Create(tid, BACKTRACE_CURRENT_THREAD)); if (backtrace->Unwind(0)) { dump_backtrace_to_log(backtrace.get(), log, " "); + } else { + ALOGE("Unwind failed: tid = %d", tid); } if (!attached && ptrace(PTRACE_DETACH, tid, 0, 0) != 0) {
diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp index 039b8ec..b84a4e5 100644 --- a/debuggerd/debuggerd.cpp +++ b/debuggerd/debuggerd.cpp
@@ -279,7 +279,7 @@ char ehdr[EI_NIDENT]; ssize_t bytes = TEMP_FAILURE_RETRY(read(fd, &ehdr, sizeof(ehdr))); - TEMP_FAILURE_RETRY(close(fd)); + close(fd); if (bytes != (ssize_t) sizeof(ehdr) || memcmp(ELFMAG, ehdr, SELFMAG) != 0) { return false; } @@ -304,14 +304,14 @@ if (TEMP_FAILURE_RETRY(write(sock_fd, &msg, sizeof(msg))) != (ssize_t) sizeof(msg)) { ALOGE("Failed to write request to debuggerd32 socket: %s", strerror(errno)); - TEMP_FAILURE_RETRY(close(sock_fd)); + close(sock_fd); return; } char ack; if (TEMP_FAILURE_RETRY(read(sock_fd, &ack, 1)) == -1) { ALOGE("Failed to read ack from debuggerd32 socket: %s", strerror(errno)); - TEMP_FAILURE_RETRY(close(sock_fd)); + close(sock_fd); return; } @@ -338,7 +338,7 @@ break; } } - TEMP_FAILURE_RETRY(close(sock_fd)); + close(sock_fd); } #endif @@ -365,7 +365,7 @@ ALOGE("debuggerd: Not allowed to redirect action %d to 32 bit debuggerd\n", request.action); } - TEMP_FAILURE_RETRY(close(fd)); + close(fd); return; } #endif
diff --git a/debuggerd/machine.h b/debuggerd/machine.h index fca9fbe..e65b147 100644 --- a/debuggerd/machine.h +++ b/debuggerd/machine.h
@@ -19,9 +19,11 @@ #include <sys/types.h> +#include <backtrace/Backtrace.h> + #include "utility.h" -void dump_memory_and_code(log_t* log, pid_t tid); +void dump_memory_and_code(log_t* log, Backtrace* backtrace); void dump_registers(log_t* log, pid_t tid); #endif // _DEBUGGERD_MACHINE_H
diff --git a/debuggerd/mips/machine.cpp b/debuggerd/mips/machine.cpp index 1145963..f7b8a86 100644 --- a/debuggerd/mips/machine.cpp +++ b/debuggerd/mips/machine.cpp
@@ -14,30 +14,29 @@ * limitations under the License. */ -#include <stddef.h> -#include <stdlib.h> -#include <string.h> -#include <stdio.h> #include <errno.h> -#include <sys/types.h> +#include <inttypes.h> +#include <stdint.h> +#include <string.h> #include <sys/ptrace.h> -#include <sys/user.h> +#include <backtrace/Backtrace.h> -#include "../utility.h" -#include "../machine.h" +#include "machine.h" +#include "utility.h" -#define R(x) (static_cast<unsigned int>(x)) +#define R(x) (static_cast<uintptr_t>(x)) // If configured to do so, dump memory around *all* registers // for the crashing thread. -void dump_memory_and_code(log_t* log, pid_t tid) { +void dump_memory_and_code(log_t* log, Backtrace* backtrace) { pt_regs r; - if (ptrace(PTRACE_GETREGS, tid, 0, &r)) { + if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, &r)) { + _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno)); return; } - static const char REG_NAMES[] = "$0atv0v1a0a1a2a3t0t1t2t3t4t5t6t7s0s1s2s3s4s5s6s7t8t9k0k1gpsps8ra"; + static const char reg_names[] = "$0atv0v1a0a1a2a3t0t1t2t3t4t5t6t7s0s1s2s3s4s5s6s7t8t9k0k1gpsps8ra"; for (int reg = 0; reg < 32; reg++) { // skip uninteresting registers @@ -48,27 +47,14 @@ ) continue; - uintptr_t addr = R(r.regs[reg]); - - // Don't bother if it looks like a small int or ~= null, or if - // it's in the kernel area. - if (addr < 4096 || addr >= 0x80000000) { - continue; - } - - _LOG(log, logtype::MEMORY, "\nmemory near %.2s:\n", ®_NAMES[reg * 2]); - dump_memory(log, tid, addr); + dump_memory(log, backtrace, R(r.regs[reg]), "memory near %.2s:", ®_names[reg * 2]); } - unsigned int pc = R(r.cp0_epc); - unsigned int ra = R(r.regs[31]); - - _LOG(log, logtype::MEMORY, "\ncode around pc:\n"); - dump_memory(log, tid, (uintptr_t)pc); - + uintptr_t pc = R(r.cp0_epc); + uintptr_t ra = R(r.regs[31]); + dump_memory(log, backtrace, pc, "code around pc:"); if (pc != ra) { - _LOG(log, logtype::MEMORY, "\ncode around ra:\n"); - dump_memory(log, tid, (uintptr_t)ra); + dump_memory(log, backtrace, ra, "code around ra:"); } } @@ -79,22 +65,31 @@ return; } - _LOG(log, logtype::REGISTERS, " zr %08x at %08x v0 %08x v1 %08x\n", + _LOG(log, logtype::REGISTERS, " zr %08" PRIxPTR " at %08" PRIxPTR + " v0 %08" PRIxPTR " v1 %08" PRIxPTR "\n", R(r.regs[0]), R(r.regs[1]), R(r.regs[2]), R(r.regs[3])); - _LOG(log, logtype::REGISTERS, " a0 %08x a1 %08x a2 %08x a3 %08x\n", + _LOG(log, logtype::REGISTERS, " a0 %08" PRIxPTR " a1 %08" PRIxPTR + " a2 %08" PRIxPTR " a3 %08" PRIxPTR "\n", R(r.regs[4]), R(r.regs[5]), R(r.regs[6]), R(r.regs[7])); - _LOG(log, logtype::REGISTERS, " t0 %08x t1 %08x t2 %08x t3 %08x\n", + _LOG(log, logtype::REGISTERS, " t0 %08" PRIxPTR " t1 %08" PRIxPTR + " t2 %08" PRIxPTR " t3 %08" PRIxPTR "\n", R(r.regs[8]), R(r.regs[9]), R(r.regs[10]), R(r.regs[11])); - _LOG(log, logtype::REGISTERS, " t4 %08x t5 %08x t6 %08x t7 %08x\n", + _LOG(log, logtype::REGISTERS, " t4 %08" PRIxPTR " t5 %08" PRIxPTR + " t6 %08" PRIxPTR " t7 %08" PRIxPTR "\n", R(r.regs[12]), R(r.regs[13]), R(r.regs[14]), R(r.regs[15])); - _LOG(log, logtype::REGISTERS, " s0 %08x s1 %08x s2 %08x s3 %08x\n", + _LOG(log, logtype::REGISTERS, " s0 %08" PRIxPTR " s1 %08" PRIxPTR + " s2 %08" PRIxPTR " s3 %08" PRIxPTR "\n", R(r.regs[16]), R(r.regs[17]), R(r.regs[18]), R(r.regs[19])); - _LOG(log, logtype::REGISTERS, " s4 %08x s5 %08x s6 %08x s7 %08x\n", + _LOG(log, logtype::REGISTERS, " s4 %08" PRIxPTR " s5 %08" PRIxPTR + " s6 %08" PRIxPTR " s7 %08" PRIxPTR "\n", R(r.regs[20]), R(r.regs[21]), R(r.regs[22]), R(r.regs[23])); - _LOG(log, logtype::REGISTERS, " t8 %08x t9 %08x k0 %08x k1 %08x\n", + _LOG(log, logtype::REGISTERS, " t8 %08" PRIxPTR " t9 %08" PRIxPTR + " k0 %08" PRIxPTR " k1 %08" PRIxPTR "\n", R(r.regs[24]), R(r.regs[25]), R(r.regs[26]), R(r.regs[27])); - _LOG(log, logtype::REGISTERS, " gp %08x sp %08x s8 %08x ra %08x\n", + _LOG(log, logtype::REGISTERS, " gp %08" PRIxPTR " sp %08" PRIxPTR + " s8 %08" PRIxPTR " ra %08" PRIxPTR "\n", R(r.regs[28]), R(r.regs[29]), R(r.regs[30]), R(r.regs[31])); - _LOG(log, logtype::REGISTERS, " hi %08x lo %08x bva %08x epc %08x\n", + _LOG(log, logtype::REGISTERS, " hi %08" PRIxPTR " lo %08" PRIxPTR + " bva %08" PRIxPTR " epc %08" PRIxPTR "\n", R(r.hi), R(r.lo), R(r.cp0_badvaddr), R(r.cp0_epc)); }
diff --git a/debuggerd/mips64/machine.cpp b/debuggerd/mips64/machine.cpp index ef9092f..293dcf6 100644 --- a/debuggerd/mips64/machine.cpp +++ b/debuggerd/mips64/machine.cpp
@@ -14,30 +14,29 @@ * limitations under the License. */ -#include <stddef.h> -#include <stdlib.h> -#include <string.h> -#include <stdio.h> #include <errno.h> -#include <sys/types.h> +#include <inttypes.h> +#include <stdint.h> +#include <string.h> #include <sys/ptrace.h> -#include <sys/user.h> +#include <backtrace/Backtrace.h> -#include "../utility.h" -#include "../machine.h" +#include "machine.h" +#include "utility.h" -#define R(x) (static_cast<unsigned long>(x)) +#define R(x) (static_cast<uintptr_t>(x)) // If configured to do so, dump memory around *all* registers // for the crashing thread. -void dump_memory_and_code(log_t* log, pid_t tid) { +void dump_memory_and_code(log_t* log, Backtrace* backtrace) { pt_regs r; - if (ptrace(PTRACE_GETREGS, tid, 0, &r)) { + if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, &r)) { + _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno)); return; } - static const char REG_NAMES[] = "$0atv0v1a0a1a2a3a4a5a6a7t0t1t2t3s0s1s2s3s4s5s6s7t8t9k0k1gpsps8ra"; + static const char reg_names[] = "$0atv0v1a0a1a2a3a4a5a6a7t0t1t2t3s0s1s2s3s4s5s6s7t8t9k0k1gpsps8ra"; for (int reg = 0; reg < 32; reg++) { // skip uninteresting registers @@ -48,27 +47,14 @@ ) continue; - uintptr_t addr = R(r.regs[reg]); - - // Don't bother if it looks like a small int or ~= null, or if - // it's in the kernel area. - if (addr < 4096 || addr >= 0x4000000000000000) { - continue; - } - - _LOG(log, logtype::MEMORY, "\nmemory near %.2s:\n", ®_NAMES[reg * 2]); - dump_memory(log, tid, addr); + dump_memory(log, backtrace, R(r.regs[reg]), "memory near %.2s:", ®_names[reg * 2]); } - unsigned long pc = R(r.cp0_epc); - unsigned long ra = R(r.regs[31]); - - _LOG(log, logtype::MEMORY, "\ncode around pc:\n"); - dump_memory(log, tid, (uintptr_t)pc); - + uintptr_t pc = R(r.cp0_epc); + uintptr_t ra = R(r.regs[31]); + dump_memory(log, backtrace, pc, "code around pc:"); if (pc != ra) { - _LOG(log, logtype::MEMORY, "\ncode around ra:\n"); - dump_memory(log, tid, (uintptr_t)ra); + dump_memory(log, backtrace, ra, "code around ra:"); } } @@ -79,22 +65,31 @@ return; } - _LOG(log, logtype::REGISTERS, " zr %016lx at %016lx v0 %016lx v1 %016lx\n", + _LOG(log, logtype::REGISTERS, " zr %016" PRIxPTR " at %016" PRIxPTR + " v0 %016" PRIxPTR " v1 %016" PRIxPTR "\n", R(r.regs[0]), R(r.regs[1]), R(r.regs[2]), R(r.regs[3])); - _LOG(log, logtype::REGISTERS, " a0 %016lx a1 %016lx a2 %016lx a3 %016lx\n", + _LOG(log, logtype::REGISTERS, " a0 %016" PRIxPTR " a1 %016" PRIxPTR + " a2 %016" PRIxPTR " a3 %016" PRIxPTR "\n", R(r.regs[4]), R(r.regs[5]), R(r.regs[6]), R(r.regs[7])); - _LOG(log, logtype::REGISTERS, " a4 %016lx a5 %016lx a6 %016lx a7 %016lx\n", + _LOG(log, logtype::REGISTERS, " a4 %016" PRIxPTR " a5 %016" PRIxPTR + " a6 %016" PRIxPTR " a7 %016" PRIxPTR "\n", R(r.regs[8]), R(r.regs[9]), R(r.regs[10]), R(r.regs[11])); - _LOG(log, logtype::REGISTERS, " t0 %016lx t1 %016lx t2 %016lx t3 %016lx\n", + _LOG(log, logtype::REGISTERS, " t0 %016" PRIxPTR " t1 %016" PRIxPTR + " t2 %016" PRIxPTR " t3 %016" PRIxPTR "\n", R(r.regs[12]), R(r.regs[13]), R(r.regs[14]), R(r.regs[15])); - _LOG(log, logtype::REGISTERS, " s0 %016lx s1 %016lx s2 %016lx s3 %016lx\n", + _LOG(log, logtype::REGISTERS, " s0 %016" PRIxPTR " s1 %016" PRIxPTR + " s2 %016" PRIxPTR " s3 %016" PRIxPTR "\n", R(r.regs[16]), R(r.regs[17]), R(r.regs[18]), R(r.regs[19])); - _LOG(log, logtype::REGISTERS, " s4 %016lx s5 %016lx s6 %016lx s7 %016lx\n", + _LOG(log, logtype::REGISTERS, " s4 %016" PRIxPTR " s5 %016" PRIxPTR + " s6 %016" PRIxPTR " s7 %016" PRIxPTR "\n", R(r.regs[20]), R(r.regs[21]), R(r.regs[22]), R(r.regs[23])); - _LOG(log, logtype::REGISTERS, " t8 %016lx t9 %016lx k0 %016lx k1 %016lx\n", + _LOG(log, logtype::REGISTERS, " t8 %016" PRIxPTR " t9 %016" PRIxPTR + " k0 %016" PRIxPTR " k1 %016" PRIxPTR "\n", R(r.regs[24]), R(r.regs[25]), R(r.regs[26]), R(r.regs[27])); - _LOG(log, logtype::REGISTERS, " gp %016lx sp %016lx s8 %016lx ra %016lx\n", + _LOG(log, logtype::REGISTERS, " gp %016" PRIxPTR " sp %016" PRIxPTR + " s8 %016" PRIxPTR " ra %016" PRIxPTR "\n", R(r.regs[28]), R(r.regs[29]), R(r.regs[30]), R(r.regs[31])); - _LOG(log, logtype::REGISTERS, " hi %016lx lo %016lx bva %016lx epc %016lx\n", + _LOG(log, logtype::REGISTERS, " hi %016" PRIxPTR " lo %016" PRIxPTR + " bva %016" PRIxPTR " epc %016" PRIxPTR "\n", R(r.hi), R(r.lo), R(r.cp0_badvaddr), R(r.cp0_epc)); }
diff --git a/debuggerd/test/BacktraceMock.h b/debuggerd/test/BacktraceMock.h new file mode 100644 index 0000000..5c252ab --- /dev/null +++ b/debuggerd/test/BacktraceMock.h
@@ -0,0 +1,101 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _DEBUGGERD_TEST_BACKTRACE_MOCK_H +#define _DEBUGGERD_TEST_BACKTRACE_MOCK_H + +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ucontext.h> + +#include <string> +#include <vector> + +#include <backtrace/Backtrace.h> +#include <backtrace/BacktraceMap.h> + +class BacktraceMapMock : public BacktraceMap { + public: + BacktraceMapMock() : BacktraceMap(0) {} + virtual ~BacktraceMapMock() {} + + void AddMap(backtrace_map_t& map) { + maps_.push_back(map); + } +}; + + +class BacktraceMock : public Backtrace { + public: + BacktraceMock(BacktraceMapMock* map) : Backtrace(0, 0, map) { + if (map_ == nullptr) { + abort(); + } + } + virtual ~BacktraceMock() {} + + virtual bool Unwind(size_t, ucontext_t*) { return false; } + virtual bool ReadWord(uintptr_t, word_t*) { return false;} + + virtual std::string GetFunctionNameRaw(uintptr_t, uintptr_t*) { return ""; } + + virtual size_t Read(uintptr_t addr, uint8_t* buffer, size_t bytes) { + size_t offset = 0; + if (last_read_addr_ > 0) { + offset = addr - last_read_addr_; + } + size_t bytes_available = buffer_.size() - offset; + + if (bytes_partial_read_ > 0) { + // Do a partial read. + if (bytes > bytes_partial_read_) { + bytes = bytes_partial_read_; + } + bytes_partial_read_ -= bytes; + } else if (bytes > bytes_available) { + bytes = bytes_available; + } + + if (bytes > 0) { + memcpy(buffer, buffer_.data() + offset, bytes); + } + + last_read_addr_ = addr; + return bytes; + } + + void SetReadData(uint8_t* buffer, size_t bytes) { + buffer_.resize(bytes); + memcpy(buffer_.data(), buffer, bytes); + bytes_partial_read_ = 0; + last_read_addr_ = 0; + } + + void SetPartialReadAmount(size_t bytes) { + if (bytes > buffer_.size()) { + abort(); + } + bytes_partial_read_ = bytes; + } + + private: + std::vector<uint8_t> buffer_; + size_t bytes_partial_read_ = 0; + uintptr_t last_read_addr_ = 0; +}; + +#endif // _DEBUGGERD_TEST_BACKTRACE_MOCK_H
diff --git a/debuggerd/test/dump_maps_test.cpp b/debuggerd/test/dump_maps_test.cpp new file mode 100644 index 0000000..230f4f5 --- /dev/null +++ b/debuggerd/test/dump_maps_test.cpp
@@ -0,0 +1,571 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdlib.h> + +#include <memory> +#include <string> + +#include <gtest/gtest.h> +#include <base/file.h> + +#include "utility.h" + +#include "BacktraceMock.h" +#include "elf_fake.h" +#include "host_signal_fixup.h" +#include "log_fake.h" +#include "ptrace_fake.h" + +// In order to test this code, we need to include the tombstone.cpp code. +// Including it, also allows us to override the ptrace function. +#define ptrace ptrace_fake + +#include "tombstone.cpp" + +void dump_registers(log_t*, pid_t) { +} + +void dump_memory_and_code(log_t*, Backtrace*) { +} + +void dump_backtrace_to_log(Backtrace*, log_t*, char const*) { +} + +class DumpMapsTest : public ::testing::Test { + protected: + virtual void SetUp() { + map_mock_.reset(new BacktraceMapMock()); + backtrace_mock_.reset(new BacktraceMock(map_mock_.get())); + + char tmp_file[256]; + const char data_template[] = "/data/local/tmp/debuggerd_memory_testXXXXXX"; + memcpy(tmp_file, data_template, sizeof(data_template)); + int tombstone_fd = mkstemp(tmp_file); + if (tombstone_fd == -1) { + const char tmp_template[] = "/tmp/debuggerd_memory_testXXXXXX"; + memcpy(tmp_file, tmp_template, sizeof(tmp_template)); + tombstone_fd = mkstemp(tmp_file); + if (tombstone_fd == -1) { + abort(); + } + } + if (unlink(tmp_file) == -1) { + abort(); + } + + log_.tfd = tombstone_fd; + log_.amfd = -1; + log_.crashed_tid = 12; + log_.current_tid = 12; + log_.should_retrieve_logcat = false; + + resetLogs(); + elf_set_fake_build_id(""); + siginfo_t si; + si.si_signo = SIGPIPE; + ptrace_set_fake_getsiginfo(si); + } + + virtual void TearDown() { + if (log_.tfd >= 0) { + close(log_.tfd); + } + } + + std::unique_ptr<BacktraceMapMock> map_mock_; + std::unique_ptr<BacktraceMock> backtrace_mock_; + + log_t log_; +}; + +TEST_F(DumpMapsTest, single_map) { + backtrace_map_t map; +#if defined(__LP64__) + map.start = 0x123456789abcd000UL; + map.end = 0x123456789abdf000UL; +#else + map.start = 0x1234000; + map.end = 0x1235000; +#endif + map_mock_->AddMap(map); + + dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100); + + std::string tombstone_contents; + ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); + ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); + const char* expected_dump = \ +"\nmemory map:\n" +#if defined(__LP64__) +" 12345678'9abcd000-12345678'9abdefff --- 0 12000\n"; +#else +" 01234000-01234fff --- 0 1000\n"; +#endif + ASSERT_STREQ(expected_dump, tombstone_contents.c_str()); + + // Verify that the log buf is empty, and no error messages. + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ("", getFakeLogPrint().c_str()); +} + +TEST_F(DumpMapsTest, single_map_elf_build_id) { + backtrace_map_t map; +#if defined(__LP64__) + map.start = 0x123456789abcd000UL; + map.end = 0x123456789abdf000UL; +#else + map.start = 0x1234000; + map.end = 0x1235000; +#endif + map.flags = PROT_READ; + map.name = "/system/lib/libfake.so"; + map_mock_->AddMap(map); + + elf_set_fake_build_id("abcdef1234567890abcdef1234567890"); + dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100); + + std::string tombstone_contents; + ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); + ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); + const char* expected_dump = \ +"\nmemory map:\n" +#if defined(__LP64__) +" 12345678'9abcd000-12345678'9abdefff r-- 0 12000 /system/lib/libfake.so (BuildId: abcdef1234567890abcdef1234567890)\n"; +#else +" 01234000-01234fff r-- 0 1000 /system/lib/libfake.so (BuildId: abcdef1234567890abcdef1234567890)\n"; +#endif + ASSERT_STREQ(expected_dump, tombstone_contents.c_str()); + + // Verify that the log buf is empty, and no error messages. + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ("", getFakeLogPrint().c_str()); +} + +// Even though build id is present, it should not be printed in either of +// these cases. +TEST_F(DumpMapsTest, single_map_no_build_id) { + backtrace_map_t map; +#if defined(__LP64__) + map.start = 0x123456789abcd000UL; + map.end = 0x123456789abdf000UL; +#else + map.start = 0x1234000; + map.end = 0x1235000; +#endif + map.flags = PROT_WRITE; + map_mock_->AddMap(map); + + map.name = "/system/lib/libfake.so"; + map_mock_->AddMap(map); + + elf_set_fake_build_id("abcdef1234567890abcdef1234567890"); + dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100); + + std::string tombstone_contents; + ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); + ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); + const char* expected_dump = \ +"\nmemory map:\n" +#if defined(__LP64__) +" 12345678'9abcd000-12345678'9abdefff -w- 0 12000\n" +" 12345678'9abcd000-12345678'9abdefff -w- 0 12000 /system/lib/libfake.so\n"; +#else +" 01234000-01234fff -w- 0 1000\n" +" 01234000-01234fff -w- 0 1000 /system/lib/libfake.so\n"; +#endif + ASSERT_STREQ(expected_dump, tombstone_contents.c_str()); + + // Verify that the log buf is empty, and no error messages. + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ("", getFakeLogPrint().c_str()); +} + +TEST_F(DumpMapsTest, multiple_maps) { + backtrace_map_t map; + + map.start = 0xa234000; + map.end = 0xa235000; + map_mock_->AddMap(map); + + map.start = 0xa334000; + map.end = 0xa335000; + map.offset = 0xf000; + map.flags = PROT_READ; + map_mock_->AddMap(map); + + map.start = 0xa434000; + map.end = 0xa435000; + map.offset = 0x1000; + map.load_base = 0xd000; + map.flags = PROT_WRITE; + map_mock_->AddMap(map); + + map.start = 0xa534000; + map.end = 0xa535000; + map.offset = 0x3000; + map.load_base = 0x2000; + map.flags = PROT_EXEC; + map_mock_->AddMap(map); + + map.start = 0xa634000; + map.end = 0xa635000; + map.offset = 0; + map.load_base = 0; + map.flags = PROT_READ | PROT_WRITE | PROT_EXEC; + map.name = "/system/lib/fake.so"; + map_mock_->AddMap(map); + + dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100); + + std::string tombstone_contents; + ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); + ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); + const char* expected_dump = \ +"\nmemory map:\n" +#if defined(__LP64__) +" 00000000'0a234000-00000000'0a234fff --- 0 1000\n" +" 00000000'0a334000-00000000'0a334fff r-- f000 1000\n" +" 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load base 0xd000)\n" +" 00000000'0a534000-00000000'0a534fff --x 3000 1000 (load base 0x2000)\n" +" 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n"; +#else +" 0a234000-0a234fff --- 0 1000\n" +" 0a334000-0a334fff r-- f000 1000\n" +" 0a434000-0a434fff -w- 1000 1000 (load base 0xd000)\n" +" 0a534000-0a534fff --x 3000 1000 (load base 0x2000)\n" +" 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n"; +#endif + ASSERT_STREQ(expected_dump, tombstone_contents.c_str()); + + // Verify that the log buf is empty, and no error messages. + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ("", getFakeLogPrint().c_str()); +} + +TEST_F(DumpMapsTest, multiple_maps_fault_address_before) { + backtrace_map_t map; + + map.start = 0xa434000; + map.end = 0xa435000; + map.offset = 0x1000; + map.load_base = 0xd000; + map.flags = PROT_WRITE; + map_mock_->AddMap(map); + + map.start = 0xa534000; + map.end = 0xa535000; + map.offset = 0x3000; + map.load_base = 0x2000; + map.flags = PROT_EXEC; + map_mock_->AddMap(map); + + map.start = 0xa634000; + map.end = 0xa635000; + map.offset = 0; + map.load_base = 0; + map.flags = PROT_READ | PROT_WRITE | PROT_EXEC; + map.name = "/system/lib/fake.so"; + map_mock_->AddMap(map); + + siginfo_t si; + si.si_signo = SIGBUS; + si.si_addr = reinterpret_cast<void*>(0x1000); + ptrace_set_fake_getsiginfo(si); + dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100); + + std::string tombstone_contents; + ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); + ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); + const char* expected_dump = \ +"\nmemory map: (fault address prefixed with --->)\n" +#if defined(__LP64__) +"--->Fault address falls at 00000000'00001000 before any mapped regions\n" +" 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load base 0xd000)\n" +" 00000000'0a534000-00000000'0a534fff --x 3000 1000 (load base 0x2000)\n" +" 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n"; +#else +"--->Fault address falls at 00001000 before any mapped regions\n" +" 0a434000-0a434fff -w- 1000 1000 (load base 0xd000)\n" +" 0a534000-0a534fff --x 3000 1000 (load base 0x2000)\n" +" 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n"; +#endif + ASSERT_STREQ(expected_dump, tombstone_contents.c_str()); + + // Verify that the log buf is empty, and no error messages. + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ("", getFakeLogPrint().c_str()); +} + +TEST_F(DumpMapsTest, multiple_maps_fault_address_between) { + backtrace_map_t map; + + map.start = 0xa434000; + map.end = 0xa435000; + map.offset = 0x1000; + map.load_base = 0xd000; + map.flags = PROT_WRITE; + map_mock_->AddMap(map); + + map.start = 0xa534000; + map.end = 0xa535000; + map.offset = 0x3000; + map.load_base = 0x2000; + map.flags = PROT_EXEC; + map_mock_->AddMap(map); + + map.start = 0xa634000; + map.end = 0xa635000; + map.offset = 0; + map.load_base = 0; + map.flags = PROT_READ | PROT_WRITE | PROT_EXEC; + map.name = "/system/lib/fake.so"; + map_mock_->AddMap(map); + + siginfo_t si; + si.si_signo = SIGBUS; + si.si_addr = reinterpret_cast<void*>(0xa533000); + ptrace_set_fake_getsiginfo(si); + dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100); + + std::string tombstone_contents; + ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); + ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); + const char* expected_dump = \ +"\nmemory map: (fault address prefixed with --->)\n" +#if defined(__LP64__) +" 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load base 0xd000)\n" +"--->Fault address falls at 00000000'0a533000 between mapped regions\n" +" 00000000'0a534000-00000000'0a534fff --x 3000 1000 (load base 0x2000)\n" +" 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n"; +#else +" 0a434000-0a434fff -w- 1000 1000 (load base 0xd000)\n" +"--->Fault address falls at 0a533000 between mapped regions\n" +" 0a534000-0a534fff --x 3000 1000 (load base 0x2000)\n" +" 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n"; +#endif + ASSERT_STREQ(expected_dump, tombstone_contents.c_str()); + + // Verify that the log buf is empty, and no error messages. + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ("", getFakeLogPrint().c_str()); +} + +TEST_F(DumpMapsTest, multiple_maps_fault_address_in_map) { + backtrace_map_t map; + + map.start = 0xa434000; + map.end = 0xa435000; + map.offset = 0x1000; + map.load_base = 0xd000; + map.flags = PROT_WRITE; + map_mock_->AddMap(map); + + map.start = 0xa534000; + map.end = 0xa535000; + map.offset = 0x3000; + map.load_base = 0x2000; + map.flags = PROT_EXEC; + map_mock_->AddMap(map); + + map.start = 0xa634000; + map.end = 0xa635000; + map.offset = 0; + map.load_base = 0; + map.flags = PROT_READ | PROT_WRITE | PROT_EXEC; + map.name = "/system/lib/fake.so"; + map_mock_->AddMap(map); + + siginfo_t si; + si.si_signo = SIGBUS; + si.si_addr = reinterpret_cast<void*>(0xa534040); + ptrace_set_fake_getsiginfo(si); + dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100); + + std::string tombstone_contents; + ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); + ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); + const char* expected_dump = \ +"\nmemory map: (fault address prefixed with --->)\n" +#if defined(__LP64__) +" 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load base 0xd000)\n" +"--->00000000'0a534000-00000000'0a534fff --x 3000 1000 (load base 0x2000)\n" +" 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n"; +#else +" 0a434000-0a434fff -w- 1000 1000 (load base 0xd000)\n" +"--->0a534000-0a534fff --x 3000 1000 (load base 0x2000)\n" +" 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n"; +#endif + ASSERT_STREQ(expected_dump, tombstone_contents.c_str()); + + // Verify that the log buf is empty, and no error messages. + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ("", getFakeLogPrint().c_str()); +} + +TEST_F(DumpMapsTest, multiple_maps_fault_address_after) { + backtrace_map_t map; + + map.start = 0xa434000; + map.end = 0xa435000; + map.offset = 0x1000; + map.load_base = 0xd000; + map.flags = PROT_WRITE; + map_mock_->AddMap(map); + + map.start = 0xa534000; + map.end = 0xa535000; + map.offset = 0x3000; + map.load_base = 0x2000; + map.flags = PROT_EXEC; + map_mock_->AddMap(map); + + map.start = 0xa634000; + map.end = 0xa635000; + map.offset = 0; + map.load_base = 0; + map.flags = PROT_READ | PROT_WRITE | PROT_EXEC; + map.name = "/system/lib/fake.so"; + map_mock_->AddMap(map); + + siginfo_t si; + si.si_signo = SIGBUS; +#if defined(__LP64__) + si.si_addr = reinterpret_cast<void*>(0x12345a534040UL); +#else + si.si_addr = reinterpret_cast<void*>(0xf534040UL); +#endif + ptrace_set_fake_getsiginfo(si); + dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100); + + std::string tombstone_contents; + ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); + ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); + const char* expected_dump = \ +"\nmemory map: (fault address prefixed with --->)\n" +#if defined(__LP64__) +" 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load base 0xd000)\n" +" 00000000'0a534000-00000000'0a534fff --x 3000 1000 (load base 0x2000)\n" +" 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n" +"--->Fault address falls at 00001234'5a534040 after any mapped regions\n"; +#else +" 0a434000-0a434fff -w- 1000 1000 (load base 0xd000)\n" +" 0a534000-0a534fff --x 3000 1000 (load base 0x2000)\n" +" 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n" +"--->Fault address falls at 0f534040 after any mapped regions\n"; +#endif + ASSERT_STREQ(expected_dump, tombstone_contents.c_str()); + + // Verify that the log buf is empty, and no error messages. + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ("", getFakeLogPrint().c_str()); +} + +TEST_F(DumpMapsTest, multiple_maps_getsiginfo_fail) { + backtrace_map_t map; + + map.start = 0xa434000; + map.end = 0xa435000; + map.offset = 0x1000; + map.load_base = 0xd000; + map.flags = PROT_WRITE; + map_mock_->AddMap(map); + + siginfo_t si; + si.si_signo = 0; + ptrace_set_fake_getsiginfo(si); + dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100); + + std::string tombstone_contents; + ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); + ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); + const char* expected_dump = \ +"Cannot get siginfo for 100: Bad address\n" +"\nmemory map:\n" +#if defined(__LP64__) +" 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load base 0xd000)\n"; +#else +" 0a434000-0a434fff -w- 1000 1000 (load base 0xd000)\n"; +#endif + ASSERT_STREQ(expected_dump, tombstone_contents.c_str()); + + // Verify that the log buf is empty, and no error messages. + ASSERT_STREQ("DEBUG Cannot get siginfo for 100: Bad address\n", + getFakeLogBuf().c_str()); + ASSERT_STREQ("", getFakeLogPrint().c_str()); +} + +TEST_F(DumpMapsTest, multiple_maps_check_signal_has_si_addr) { + backtrace_map_t map; + + map.start = 0xa434000; + map.end = 0xa435000; + map.flags = PROT_WRITE; + map_mock_->AddMap(map); + + for (int i = 1; i < 255; i++) { + ASSERT_TRUE(ftruncate(log_.tfd, 0) == 0); + ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); + + siginfo_t si; + si.si_signo = i; + si.si_addr = reinterpret_cast<void*>(0x1000); + ptrace_set_fake_getsiginfo(si); + dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100); + + std::string tombstone_contents; + ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); + ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); + bool has_addr = false; + switch (si.si_signo) { + case SIGBUS: + case SIGFPE: + case SIGILL: + case SIGSEGV: + case SIGTRAP: + has_addr = true; + break; + } + + const char* expected_addr_dump = \ +"\nmemory map: (fault address prefixed with --->)\n" +#if defined(__LP64__) +"--->Fault address falls at 00000000'00001000 before any mapped regions\n" +" 00000000'0a434000-00000000'0a434fff -w- 0 1000\n"; +#else +"--->Fault address falls at 00001000 before any mapped regions\n" +" 0a434000-0a434fff -w- 0 1000\n"; +#endif + const char* expected_dump = \ +"\nmemory map:\n" +#if defined(__LP64__) +" 00000000'0a434000-00000000'0a434fff -w- 0 1000\n"; +#else +" 0a434000-0a434fff -w- 0 1000\n"; +#endif + if (has_addr) { + ASSERT_STREQ(expected_addr_dump, tombstone_contents.c_str()) + << "Signal " << si.si_signo << " expected to include an address."; + } else { + ASSERT_STREQ(expected_dump, tombstone_contents.c_str()) + << "Signal " << si.si_signo << " is not expected to include an address."; + } + + // Verify that the log buf is empty, and no error messages. + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ("", getFakeLogPrint().c_str()); + } +}
diff --git a/debuggerd/test/dump_memory_test.cpp b/debuggerd/test/dump_memory_test.cpp new file mode 100644 index 0000000..fcb0108 --- /dev/null +++ b/debuggerd/test/dump_memory_test.cpp
@@ -0,0 +1,504 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdlib.h> + +#include <memory> +#include <string> + +#include <gtest/gtest.h> +#include <base/file.h> + +#include "BacktraceMock.h" +#include "log_fake.h" +#include "utility.h" + +const char g_expected_full_dump[] = +"\nmemory near r1:\n" +#if defined(__LP64__) +" 0000000012345658 0706050403020100 0f0e0d0c0b0a0908 ................\n" +" 0000000012345668 1716151413121110 1f1e1d1c1b1a1918 ................\n" +" 0000000012345678 2726252423222120 2f2e2d2c2b2a2928 !\"#$%&'()*+,-./\n" +" 0000000012345688 3736353433323130 3f3e3d3c3b3a3938 0123456789:;<=>?\n" +" 0000000012345698 4746454443424140 4f4e4d4c4b4a4948 @ABCDEFGHIJKLMNO\n" +" 00000000123456a8 5756555453525150 5f5e5d5c5b5a5958 PQRSTUVWXYZ[\\]^_\n" +" 00000000123456b8 6766656463626160 6f6e6d6c6b6a6968 `abcdefghijklmno\n" +" 00000000123456c8 7776757473727170 7f7e7d7c7b7a7978 pqrstuvwxyz{|}~.\n" +" 00000000123456d8 8786858483828180 8f8e8d8c8b8a8988 ................\n" +" 00000000123456e8 9796959493929190 9f9e9d9c9b9a9998 ................\n" +" 00000000123456f8 a7a6a5a4a3a2a1a0 afaeadacabaaa9a8 ................\n" +" 0000000012345708 b7b6b5b4b3b2b1b0 bfbebdbcbbbab9b8 ................\n" +" 0000000012345718 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8 ................\n" +" 0000000012345728 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8 ................\n" +" 0000000012345738 e7e6e5e4e3e2e1e0 efeeedecebeae9e8 ................\n" +" 0000000012345748 f7f6f5f4f3f2f1f0 fffefdfcfbfaf9f8 ................\n"; +#else +" 12345658 03020100 07060504 0b0a0908 0f0e0d0c ................\n" +" 12345668 13121110 17161514 1b1a1918 1f1e1d1c ................\n" +" 12345678 23222120 27262524 2b2a2928 2f2e2d2c !\"#$%&'()*+,-./\n" +" 12345688 33323130 37363534 3b3a3938 3f3e3d3c 0123456789:;<=>?\n" +" 12345698 43424140 47464544 4b4a4948 4f4e4d4c @ABCDEFGHIJKLMNO\n" +" 123456a8 53525150 57565554 5b5a5958 5f5e5d5c PQRSTUVWXYZ[\\]^_\n" +" 123456b8 63626160 67666564 6b6a6968 6f6e6d6c `abcdefghijklmno\n" +" 123456c8 73727170 77767574 7b7a7978 7f7e7d7c pqrstuvwxyz{|}~.\n" +" 123456d8 83828180 87868584 8b8a8988 8f8e8d8c ................\n" +" 123456e8 93929190 97969594 9b9a9998 9f9e9d9c ................\n" +" 123456f8 a3a2a1a0 a7a6a5a4 abaaa9a8 afaeadac ................\n" +" 12345708 b3b2b1b0 b7b6b5b4 bbbab9b8 bfbebdbc ................\n" +" 12345718 c3c2c1c0 c7c6c5c4 cbcac9c8 cfcecdcc ................\n" +" 12345728 d3d2d1d0 d7d6d5d4 dbdad9d8 dfdedddc ................\n" +" 12345738 e3e2e1e0 e7e6e5e4 ebeae9e8 efeeedec ................\n" +" 12345748 f3f2f1f0 f7f6f5f4 fbfaf9f8 fffefdfc ................\n"; +#endif + +const char g_expected_partial_dump[] = \ +"\nmemory near pc:\n" +#if defined(__LP64__) +" 00000000123455e0 0706050403020100 0f0e0d0c0b0a0908 ................\n" +" 00000000123455f0 1716151413121110 1f1e1d1c1b1a1918 ................\n" +" 0000000012345600 2726252423222120 2f2e2d2c2b2a2928 !\"#$%&'()*+,-./\n" +" 0000000012345610 3736353433323130 3f3e3d3c3b3a3938 0123456789:;<=>?\n" +" 0000000012345620 4746454443424140 4f4e4d4c4b4a4948 @ABCDEFGHIJKLMNO\n" +" 0000000012345630 5756555453525150 5f5e5d5c5b5a5958 PQRSTUVWXYZ[\\]^_\n" +" 0000000012345640 6766656463626160 ---------------- `abcdefg........\n" +" 0000000012345650 ---------------- ---------------- ................\n" +" 0000000012345660 ---------------- ---------------- ................\n" +" 0000000012345670 ---------------- ---------------- ................\n" +" 0000000012345680 ---------------- ---------------- ................\n" +" 0000000012345690 ---------------- ---------------- ................\n" +" 00000000123456a0 ---------------- ---------------- ................\n" +" 00000000123456b0 ---------------- ---------------- ................\n" +" 00000000123456c0 ---------------- ---------------- ................\n" +" 00000000123456d0 ---------------- ---------------- ................\n"; +#else +" 123455e0 03020100 07060504 0b0a0908 0f0e0d0c ................\n" +" 123455f0 13121110 17161514 1b1a1918 1f1e1d1c ................\n" +" 12345600 23222120 27262524 2b2a2928 2f2e2d2c !\"#$%&'()*+,-./\n" +" 12345610 33323130 37363534 3b3a3938 3f3e3d3c 0123456789:;<=>?\n" +" 12345620 43424140 47464544 4b4a4948 4f4e4d4c @ABCDEFGHIJKLMNO\n" +" 12345630 53525150 57565554 5b5a5958 5f5e5d5c PQRSTUVWXYZ[\\]^_\n" +" 12345640 63626160 67666564 -------- -------- `abcdefg........\n" +" 12345650 -------- -------- -------- -------- ................\n" +" 12345660 -------- -------- -------- -------- ................\n" +" 12345670 -------- -------- -------- -------- ................\n" +" 12345680 -------- -------- -------- -------- ................\n" +" 12345690 -------- -------- -------- -------- ................\n" +" 123456a0 -------- -------- -------- -------- ................\n" +" 123456b0 -------- -------- -------- -------- ................\n" +" 123456c0 -------- -------- -------- -------- ................\n" +" 123456d0 -------- -------- -------- -------- ................\n"; +#endif + +class DumpMemoryTest : public ::testing::Test { + protected: + virtual void SetUp() { + map_mock_.reset(new BacktraceMapMock()); + backtrace_mock_.reset(new BacktraceMock(map_mock_.get())); + + char tmp_file[256]; + const char data_template[] = "/data/local/tmp/debuggerd_memory_testXXXXXX"; + memcpy(tmp_file, data_template, sizeof(data_template)); + int tombstone_fd = mkstemp(tmp_file); + if (tombstone_fd == -1) { + const char tmp_template[] = "/tmp/debuggerd_memory_testXXXXXX"; + memcpy(tmp_file, tmp_template, sizeof(tmp_template)); + tombstone_fd = mkstemp(tmp_file); + if (tombstone_fd == -1) { + abort(); + } + } + if (unlink(tmp_file) == -1) { + abort(); + } + + log_.tfd = tombstone_fd; + log_.amfd = -1; + log_.crashed_tid = 12; + log_.current_tid = 12; + log_.should_retrieve_logcat = false; + + resetLogs(); + } + + virtual void TearDown() { + if (log_.tfd >= 0) { + close(log_.tfd); + } + } + + std::unique_ptr<BacktraceMapMock> map_mock_; + std::unique_ptr<BacktraceMock> backtrace_mock_; + + log_t log_; +}; + +TEST_F(DumpMemoryTest, aligned_addr) { + uint8_t buffer[256]; + for (size_t i = 0; i < sizeof(buffer); i++) { + buffer[i] = i; + } + backtrace_mock_->SetReadData(buffer, sizeof(buffer)); + + dump_memory(&log_, backtrace_mock_.get(), 0x12345678, "memory near %.2s:", "r1"); + + std::string tombstone_contents; + ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); + ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); + ASSERT_STREQ(g_expected_full_dump, tombstone_contents.c_str()); + + // Verify that the log buf is empty, and no error messages. + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ("", getFakeLogPrint().c_str()); +} + +TEST_F(DumpMemoryTest, partial_read) { + uint8_t buffer[256]; + for (size_t i = 0; i < sizeof(buffer); i++) { + buffer[i] = i; + } + backtrace_mock_->SetReadData(buffer, sizeof(buffer)); + backtrace_mock_->SetPartialReadAmount(96); + + dump_memory(&log_, backtrace_mock_.get(), 0x12345679, "memory near %.2s:", "r1"); + + std::string tombstone_contents; + ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); + ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); + ASSERT_STREQ(g_expected_full_dump, tombstone_contents.c_str()); + + // Verify that the log buf is empty, and no error messages. + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ("", getFakeLogPrint().c_str()); +} + +TEST_F(DumpMemoryTest, unaligned_addr) { + uint8_t buffer[256]; + for (size_t i = 0; i < sizeof(buffer); i++) { + buffer[i] = i; + } + backtrace_mock_->SetReadData(buffer, sizeof(buffer)); + + dump_memory(&log_, backtrace_mock_.get(), 0x12345679, "memory near %.2s:", "r1"); + + std::string tombstone_contents; + ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); + ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); + ASSERT_STREQ(g_expected_full_dump, tombstone_contents.c_str()); + + // Verify that the log buf is empty, and no error messages. + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ("", getFakeLogPrint().c_str()); +} + +TEST_F(DumpMemoryTest, memory_unreadable) { + dump_memory(&log_, backtrace_mock_.get(), 0xa2345678, "memory near pc:"); + + std::string tombstone_contents; + ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); + ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); + const char* expected_dump = \ +"\nmemory near pc:\n" +#if defined(__LP64__) +" 00000000a2345658 ---------------- ---------------- ................\n" +" 00000000a2345668 ---------------- ---------------- ................\n" +" 00000000a2345678 ---------------- ---------------- ................\n" +" 00000000a2345688 ---------------- ---------------- ................\n" +" 00000000a2345698 ---------------- ---------------- ................\n" +" 00000000a23456a8 ---------------- ---------------- ................\n" +" 00000000a23456b8 ---------------- ---------------- ................\n" +" 00000000a23456c8 ---------------- ---------------- ................\n" +" 00000000a23456d8 ---------------- ---------------- ................\n" +" 00000000a23456e8 ---------------- ---------------- ................\n" +" 00000000a23456f8 ---------------- ---------------- ................\n" +" 00000000a2345708 ---------------- ---------------- ................\n" +" 00000000a2345718 ---------------- ---------------- ................\n" +" 00000000a2345728 ---------------- ---------------- ................\n" +" 00000000a2345738 ---------------- ---------------- ................\n" +" 00000000a2345748 ---------------- ---------------- ................\n"; +#else +" a2345658 -------- -------- -------- -------- ................\n" +" a2345668 -------- -------- -------- -------- ................\n" +" a2345678 -------- -------- -------- -------- ................\n" +" a2345688 -------- -------- -------- -------- ................\n" +" a2345698 -------- -------- -------- -------- ................\n" +" a23456a8 -------- -------- -------- -------- ................\n" +" a23456b8 -------- -------- -------- -------- ................\n" +" a23456c8 -------- -------- -------- -------- ................\n" +" a23456d8 -------- -------- -------- -------- ................\n" +" a23456e8 -------- -------- -------- -------- ................\n" +" a23456f8 -------- -------- -------- -------- ................\n" +" a2345708 -------- -------- -------- -------- ................\n" +" a2345718 -------- -------- -------- -------- ................\n" +" a2345728 -------- -------- -------- -------- ................\n" +" a2345738 -------- -------- -------- -------- ................\n" +" a2345748 -------- -------- -------- -------- ................\n"; +#endif + ASSERT_STREQ(expected_dump, tombstone_contents.c_str()); + + // Verify that the log buf is empty, and no error messages. + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ("", getFakeLogPrint().c_str()); +} + +TEST_F(DumpMemoryTest, memory_partially_unreadable) { + uint8_t buffer[104]; + for (size_t i = 0; i < sizeof(buffer); i++) { + buffer[i] = i; + } + backtrace_mock_->SetReadData(buffer, sizeof(buffer)); + + dump_memory(&log_, backtrace_mock_.get(), 0x12345600, "memory near pc:"); + + std::string tombstone_contents; + ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); + ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); + ASSERT_STREQ(g_expected_partial_dump, tombstone_contents.c_str()); + + // Verify that the log buf is empty, and no error messages. + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ("", getFakeLogPrint().c_str()); +} + +TEST_F(DumpMemoryTest, memory_partially_unreadable_unaligned_return) { + uint8_t buffer[104]; + for (size_t i = 0; i < sizeof(buffer); i++) { + buffer[i] = i; + } + backtrace_mock_->SetReadData(buffer, sizeof(buffer)); + backtrace_mock_->SetPartialReadAmount(102); + + dump_memory(&log_, backtrace_mock_.get(), 0x12345600, "memory near pc:"); + + std::string tombstone_contents; + ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); + ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); + ASSERT_STREQ(g_expected_partial_dump, tombstone_contents.c_str()); + +#if defined(__LP64__) + ASSERT_STREQ("DEBUG Bytes read 102, is not a multiple of 8\n", getFakeLogPrint().c_str()); +#else + ASSERT_STREQ("DEBUG Bytes read 102, is not a multiple of 4\n", getFakeLogPrint().c_str()); +#endif + + // Verify that the log buf is empty, and no error messages. + ASSERT_STREQ("", getFakeLogBuf().c_str()); +} + +TEST_F(DumpMemoryTest, memory_partially_unreadable_two_unaligned_reads) { + uint8_t buffer[106]; + for (size_t i = 0; i < sizeof(buffer); i++) { + buffer[i] = i; + } + backtrace_mock_->SetReadData(buffer, sizeof(buffer)); + backtrace_mock_->SetPartialReadAmount(45); + + dump_memory(&log_, backtrace_mock_.get(), 0x12345600, "memory near pc:"); + + std::string tombstone_contents; + ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); + ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); + ASSERT_STREQ(g_expected_partial_dump, tombstone_contents.c_str()); + +#if defined(__LP64__) + ASSERT_STREQ("DEBUG Bytes read 45, is not a multiple of 8\n" + "DEBUG Bytes after second read 106, is not a multiple of 8\n", + getFakeLogPrint().c_str()); +#else + ASSERT_STREQ("DEBUG Bytes read 45, is not a multiple of 4\n" + "DEBUG Bytes after second read 106, is not a multiple of 4\n", + getFakeLogPrint().c_str()); +#endif + + // Verify that the log buf is empty, and no error messages. + ASSERT_STREQ("", getFakeLogBuf().c_str()); +} + +TEST_F(DumpMemoryTest, address_low_fence) { + uint8_t buffer[256]; + memset(buffer, 0, sizeof(buffer)); + backtrace_mock_->SetReadData(buffer, sizeof(buffer)); + + dump_memory(&log_, backtrace_mock_.get(), 0x1000, "memory near %.2s:", "r1"); + + std::string tombstone_contents; + ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); + ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); + const char* expected_dump = \ +"\nmemory near r1:\n" +#if defined(__LP64__) +" 0000000000001000 0000000000000000 0000000000000000 ................\n" +" 0000000000001010 0000000000000000 0000000000000000 ................\n" +" 0000000000001020 0000000000000000 0000000000000000 ................\n" +" 0000000000001030 0000000000000000 0000000000000000 ................\n" +" 0000000000001040 0000000000000000 0000000000000000 ................\n" +" 0000000000001050 0000000000000000 0000000000000000 ................\n" +" 0000000000001060 0000000000000000 0000000000000000 ................\n" +" 0000000000001070 0000000000000000 0000000000000000 ................\n" +" 0000000000001080 0000000000000000 0000000000000000 ................\n" +" 0000000000001090 0000000000000000 0000000000000000 ................\n" +" 00000000000010a0 0000000000000000 0000000000000000 ................\n" +" 00000000000010b0 0000000000000000 0000000000000000 ................\n" +" 00000000000010c0 0000000000000000 0000000000000000 ................\n" +" 00000000000010d0 0000000000000000 0000000000000000 ................\n" +" 00000000000010e0 0000000000000000 0000000000000000 ................\n" +" 00000000000010f0 0000000000000000 0000000000000000 ................\n"; +#else +" 00001000 00000000 00000000 00000000 00000000 ................\n" +" 00001010 00000000 00000000 00000000 00000000 ................\n" +" 00001020 00000000 00000000 00000000 00000000 ................\n" +" 00001030 00000000 00000000 00000000 00000000 ................\n" +" 00001040 00000000 00000000 00000000 00000000 ................\n" +" 00001050 00000000 00000000 00000000 00000000 ................\n" +" 00001060 00000000 00000000 00000000 00000000 ................\n" +" 00001070 00000000 00000000 00000000 00000000 ................\n" +" 00001080 00000000 00000000 00000000 00000000 ................\n" +" 00001090 00000000 00000000 00000000 00000000 ................\n" +" 000010a0 00000000 00000000 00000000 00000000 ................\n" +" 000010b0 00000000 00000000 00000000 00000000 ................\n" +" 000010c0 00000000 00000000 00000000 00000000 ................\n" +" 000010d0 00000000 00000000 00000000 00000000 ................\n" +" 000010e0 00000000 00000000 00000000 00000000 ................\n" +" 000010f0 00000000 00000000 00000000 00000000 ................\n"; +#endif + ASSERT_STREQ(expected_dump, tombstone_contents.c_str()); + + // Verify that the log buf is empty, and no error messages. + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ("", getFakeLogPrint().c_str()); +} + +TEST_F(DumpMemoryTest, memory_address_too_low) { + uint8_t buffer[256]; + memset(buffer, 0, sizeof(buffer)); + backtrace_mock_->SetReadData(buffer, sizeof(buffer)); + + dump_memory(&log_, backtrace_mock_.get(), 0, "memory near %.2s:", "r1"); + + std::string tombstone_contents; + ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); + ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); + ASSERT_STREQ("", tombstone_contents.c_str()); + + // Verify that the log buf is empty, and no error messages. + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ("", getFakeLogPrint().c_str()); +} + +TEST_F(DumpMemoryTest, memory_address_too_high) { + uint8_t buffer[256]; + memset(buffer, 0, sizeof(buffer)); + backtrace_mock_->SetReadData(buffer, sizeof(buffer)); + +#if defined(__LP64__) + dump_memory(&log_, backtrace_mock_.get(), 0x4000000000000000UL, "memory near %.2s:", "r1"); + dump_memory(&log_, backtrace_mock_.get(), 0x4000000000000000UL - 32, "memory near %.2s:", "r1"); + dump_memory(&log_, backtrace_mock_.get(), 0x4000000000000000UL - 216, "memory near %.2s:", "r1"); +#else + dump_memory(&log_, backtrace_mock_.get(), 0xffff0000, "memory near %.2s:", "r1"); + dump_memory(&log_, backtrace_mock_.get(), 0xffff0000 - 32, "memory near %.2s:", "r1"); + dump_memory(&log_, backtrace_mock_.get(), 0xffff0000 - 220, "memory near %.2s:", "r1"); +#endif + + std::string tombstone_contents; + ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); + ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); + ASSERT_STREQ("", tombstone_contents.c_str()); + + // Verify that the log buf is empty, and no error messages. + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ("", getFakeLogPrint().c_str()); +} + +TEST_F(DumpMemoryTest, memory_address_would_overflow) { + uint8_t buffer[256]; + memset(buffer, 0, sizeof(buffer)); + backtrace_mock_->SetReadData(buffer, sizeof(buffer)); + +#if defined(__LP64__) + dump_memory(&log_, backtrace_mock_.get(), 0xfffffffffffffff0, "memory near %.2s:", "r1"); +#else + dump_memory(&log_, backtrace_mock_.get(), 0xfffffff0, "memory near %.2s:", "r1"); +#endif + + std::string tombstone_contents; + ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); + ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); + ASSERT_STREQ("", tombstone_contents.c_str()); + + // Verify that the log buf is empty, and no error messages. + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ("", getFakeLogPrint().c_str()); +} + +TEST_F(DumpMemoryTest, memory_address_nearly_too_high) { + uint8_t buffer[256]; + for (size_t i = 0; i < sizeof(buffer); i++) { + buffer[i] = i; + } + backtrace_mock_->SetReadData(buffer, sizeof(buffer)); + +#if defined(__LP64__) + dump_memory(&log_, backtrace_mock_.get(), 0x4000000000000000UL - 224, "memory near %.2s:", "r4"); +#else + dump_memory(&log_, backtrace_mock_.get(), 0xffff0000 - 224, "memory near %.2s:", "r4"); +#endif + + std::string tombstone_contents; + ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); + ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); + const char* expected_dump = \ +"\nmemory near r4:\n" +#if defined(__LP64__) +" 3fffffffffffff00 0706050403020100 0f0e0d0c0b0a0908 ................\n" +" 3fffffffffffff10 1716151413121110 1f1e1d1c1b1a1918 ................\n" +" 3fffffffffffff20 2726252423222120 2f2e2d2c2b2a2928 !\"#$%&'()*+,-./\n" +" 3fffffffffffff30 3736353433323130 3f3e3d3c3b3a3938 0123456789:;<=>?\n" +" 3fffffffffffff40 4746454443424140 4f4e4d4c4b4a4948 @ABCDEFGHIJKLMNO\n" +" 3fffffffffffff50 5756555453525150 5f5e5d5c5b5a5958 PQRSTUVWXYZ[\\]^_\n" +" 3fffffffffffff60 6766656463626160 6f6e6d6c6b6a6968 `abcdefghijklmno\n" +" 3fffffffffffff70 7776757473727170 7f7e7d7c7b7a7978 pqrstuvwxyz{|}~.\n" +" 3fffffffffffff80 8786858483828180 8f8e8d8c8b8a8988 ................\n" +" 3fffffffffffff90 9796959493929190 9f9e9d9c9b9a9998 ................\n" +" 3fffffffffffffa0 a7a6a5a4a3a2a1a0 afaeadacabaaa9a8 ................\n" +" 3fffffffffffffb0 b7b6b5b4b3b2b1b0 bfbebdbcbbbab9b8 ................\n" +" 3fffffffffffffc0 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8 ................\n" +" 3fffffffffffffd0 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8 ................\n" +" 3fffffffffffffe0 e7e6e5e4e3e2e1e0 efeeedecebeae9e8 ................\n" +" 3ffffffffffffff0 f7f6f5f4f3f2f1f0 fffefdfcfbfaf9f8 ................\n"; +#else +" fffeff00 03020100 07060504 0b0a0908 0f0e0d0c ................\n" +" fffeff10 13121110 17161514 1b1a1918 1f1e1d1c ................\n" +" fffeff20 23222120 27262524 2b2a2928 2f2e2d2c !\"#$%&'()*+,-./\n" +" fffeff30 33323130 37363534 3b3a3938 3f3e3d3c 0123456789:;<=>?\n" +" fffeff40 43424140 47464544 4b4a4948 4f4e4d4c @ABCDEFGHIJKLMNO\n" +" fffeff50 53525150 57565554 5b5a5958 5f5e5d5c PQRSTUVWXYZ[\\]^_\n" +" fffeff60 63626160 67666564 6b6a6968 6f6e6d6c `abcdefghijklmno\n" +" fffeff70 73727170 77767574 7b7a7978 7f7e7d7c pqrstuvwxyz{|}~.\n" +" fffeff80 83828180 87868584 8b8a8988 8f8e8d8c ................\n" +" fffeff90 93929190 97969594 9b9a9998 9f9e9d9c ................\n" +" fffeffa0 a3a2a1a0 a7a6a5a4 abaaa9a8 afaeadac ................\n" +" fffeffb0 b3b2b1b0 b7b6b5b4 bbbab9b8 bfbebdbc ................\n" +" fffeffc0 c3c2c1c0 c7c6c5c4 cbcac9c8 cfcecdcc ................\n" +" fffeffd0 d3d2d1d0 d7d6d5d4 dbdad9d8 dfdedddc ................\n" +" fffeffe0 e3e2e1e0 e7e6e5e4 ebeae9e8 efeeedec ................\n" +" fffefff0 f3f2f1f0 f7f6f5f4 fbfaf9f8 fffefdfc ................\n"; +#endif + ASSERT_STREQ(expected_dump, tombstone_contents.c_str()); + + // Verify that the log buf is empty, and no error messages. + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ("", getFakeLogPrint().c_str()); +}
diff --git a/debuggerd/test/elf_fake.cpp b/debuggerd/test/elf_fake.cpp new file mode 100644 index 0000000..bb52b59 --- /dev/null +++ b/debuggerd/test/elf_fake.cpp
@@ -0,0 +1,35 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdint.h> + +#include <string> + +class Backtrace; + +std::string g_build_id; + +void elf_set_fake_build_id(const std::string& build_id) { + g_build_id = build_id; +} + +bool elf_get_build_id(Backtrace*, uintptr_t, std::string* build_id) { + if (g_build_id != "") { + *build_id = g_build_id; + return true; + } + return false; +}
diff --git a/debuggerd/test/elf_fake.h b/debuggerd/test/elf_fake.h new file mode 100644 index 0000000..08a8454 --- /dev/null +++ b/debuggerd/test/elf_fake.h
@@ -0,0 +1,24 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _DEBUGGERD_TEST_ELF_FAKE_H +#define _DEBUGGERD_TEST_ELF_FAKE_H + +#include <string> + +void elf_set_fake_build_id(const std::string&); + +#endif // _DEBUGGERD_TEST_ELF_FAKE_H
diff --git a/debuggerd/test/host_signal_fixup.h b/debuggerd/test/host_signal_fixup.h new file mode 100644 index 0000000..c7796ef --- /dev/null +++ b/debuggerd/test/host_signal_fixup.h
@@ -0,0 +1,65 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _DEBUGGERD_TEST_HOST_SIGNAL_FIXUP_H +#define _DEBUGGERD_TEST_HOST_SIGNAL_FIXUP_H + +#include <signal.h> + +#if !defined(__BIONIC__) + +// In order to compile parts of debuggerd for the host, we need to +// define these values. + +#if !defined(NSIGILL) +#define NSIGILL ILL_BADSTK +#endif + +#if !defined(BUS_MCEERR_AR) +#define BUS_MCEERR_AR 4 +#endif +#if !defined(BUS_MCEERR_AO) +#define BUS_MCEERR_AO 5 +#endif +#if !defined(NSIGBUS) +#define NSIGBUS BUS_MCEERR_AO +#endif + +#if !defined(NSIGFPE) +#define NSIGFPE FPE_FLTSUB +#endif + +#if !defined(NSIGSEGV) +#define NSIGSEGV SEGV_ACCERR +#endif + +#if !defined(TRAP_BRANCH) +#define TRAP_BRANCH 3 +#endif +#if !defined(TRAP_HWBKPT) +#define TRAP_HWBKPT 4 +#endif +#if !defined(NSIGTRAP) +#define NSIGTRAP TRAP_HWBKPT +#endif + +#if !defined(SI_DETHREAD) +#define SI_DETHREAD -7 +#endif + +#endif + +#endif // _DEBUGGERD_TEST_HOST_SIGNAL_FIXUP_H
diff --git a/debuggerd/test/log_fake.cpp b/debuggerd/test/log_fake.cpp new file mode 100644 index 0000000..26523ad --- /dev/null +++ b/debuggerd/test/log_fake.cpp
@@ -0,0 +1,91 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdarg.h> + +#include <string> + +#include <base/stringprintf.h> +#include <log/log.h> +#include <log/logger.h> + +// Forward declarations. +class Backtrace; +struct EventTagMap; +struct AndroidLogEntry; + +std::string g_fake_log_buf; + +std::string g_fake_log_print; + +void resetLogs() { + g_fake_log_buf = ""; + g_fake_log_print = ""; +} + +std::string getFakeLogBuf() { + return g_fake_log_buf; +} + +std::string getFakeLogPrint() { + return g_fake_log_print; +} + +extern "C" int __android_log_buf_write(int, int, const char* tag, const char* msg) { + g_fake_log_buf += tag; + g_fake_log_buf += ' '; + g_fake_log_buf += msg; + return 1; +} + +extern "C" int __android_log_print(int, const char* tag, const char* fmt, ...) { + g_fake_log_print += tag; + g_fake_log_print += ' '; + + va_list ap; + va_start(ap, fmt); + android::base::StringAppendV(&g_fake_log_print, fmt, ap); + va_end(ap); + + g_fake_log_print += '\n'; + + return 1; +} + +extern "C" log_id_t android_name_to_log_id(const char*) { + return LOG_ID_SYSTEM; +} + +extern "C" struct logger_list* android_logger_list_open(log_id_t, int, unsigned int, pid_t) { + return nullptr; +} + +extern "C" int android_logger_list_read(struct logger_list*, struct log_msg*) { + return 0; +} + +extern "C" EventTagMap* android_openEventTagMap(const char*) { + return nullptr; +} + +extern "C" int android_log_processBinaryLogBuffer( + struct logger_entry*, + AndroidLogEntry*, const EventTagMap*, char*, int) { + return 0; +} + +extern "C" void android_logger_list_free(struct logger_list*) { +}
diff --git a/debuggerd/test/log_fake.h b/debuggerd/test/log_fake.h new file mode 100644 index 0000000..5418fce --- /dev/null +++ b/debuggerd/test/log_fake.h
@@ -0,0 +1,26 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _DEBUGGERD_TEST_LOG_FAKE_H +#define _DEBUGGERD_TEST_LOG_FAKE_H + +#include <string> + +void resetLogs(); +std::string getFakeLogBuf(); +std::string getFakeLogPrint(); + +#endif // _DEBUGGERD_TEST_LOG_FAKE_H
diff --git a/debuggerd/test/property_fake.cpp b/debuggerd/test/property_fake.cpp new file mode 100644 index 0000000..02069f1 --- /dev/null +++ b/debuggerd/test/property_fake.cpp
@@ -0,0 +1,45 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <string.h> + +#include <string> +#include <unordered_map> + +#include <sys/system_properties.h> + +std::unordered_map<std::string, std::string> g_properties; + +extern "C" int property_set(const char* name, const char* value) { + if (g_properties.count(name) != 0) { + g_properties.erase(name); + } + g_properties[name] = value; + return 0; +} + +extern "C" int property_get(const char* key, char* value, const char* default_value) { + if (g_properties.count(key) == 0) { + if (default_value == nullptr) { + return 0; + } + strncpy(value, default_value, PROP_VALUE_MAX-1); + } else { + strncpy(value, g_properties[key].c_str(), PROP_VALUE_MAX-1); + } + value[PROP_VALUE_MAX-1] = '\0'; + return strlen(value); +}
diff --git a/debuggerd/test/ptrace_fake.cpp b/debuggerd/test/ptrace_fake.cpp new file mode 100644 index 0000000..f40cbd4 --- /dev/null +++ b/debuggerd/test/ptrace_fake.cpp
@@ -0,0 +1,53 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <errno.h> +#include <signal.h> +#include <stdarg.h> +#include <sys/ptrace.h> + +#include <string> + +#include "ptrace_fake.h" + +siginfo_t g_fake_si = {.si_signo = 0}; + +void ptrace_set_fake_getsiginfo(const siginfo_t& si) { + g_fake_si = si; +} + +#if !defined(__BIONIC__) +extern "C" long ptrace_fake(enum __ptrace_request request, ...) { +#else +extern "C" long ptrace_fake(int request, ...) { +#endif + if (request == PTRACE_GETSIGINFO) { + if (g_fake_si.si_signo == 0) { + errno = EFAULT; + return -1; + } + + va_list ap; + va_start(ap, request); + va_arg(ap, int); + va_arg(ap, int); + siginfo_t* si = va_arg(ap, siginfo*); + va_end(ap); + *si = g_fake_si; + return 0; + } + return -1; +}
diff --git a/debuggerd/test/ptrace_fake.h b/debuggerd/test/ptrace_fake.h new file mode 100644 index 0000000..fdbb663 --- /dev/null +++ b/debuggerd/test/ptrace_fake.h
@@ -0,0 +1,24 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _DEBUGGERD_TEST_PTRACE_FAKE_H +#define _DEBUGGERD_TEST_PTRACE_FAKE_H + +#include <signal.h> + +void ptrace_set_fake_getsiginfo(const siginfo_t&); + +#endif // _DEBUGGERD_TEST_PTRACE_FAKE_H
diff --git a/debuggerd/test/selinux_fake.cpp b/debuggerd/test/selinux_fake.cpp new file mode 100644 index 0000000..acdd0a9 --- /dev/null +++ b/debuggerd/test/selinux_fake.cpp
@@ -0,0 +1,19 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +extern "C" int selinux_android_restorecon(const char*, unsigned int) { + return 0; +}
diff --git a/debuggerd/test/sys/system_properties.h b/debuggerd/test/sys/system_properties.h new file mode 100644 index 0000000..9d44345 --- /dev/null +++ b/debuggerd/test/sys/system_properties.h
@@ -0,0 +1,38 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _DEBUGGERD_TEST_SYS_SYSTEM_PROPERTIES_H +#define _DEBUGGERD_TEST_SYS_SYSTEM_PROPERTIES_H + +// This is just enough to get the property code to compile on +// the host. + +#define PROP_NAME_MAX 32 +#define PROP_VALUE_MAX 92 + +#endif // _DEBUGGERD_TEST_SYS_SYSTEM_PROPERTIES_H
diff --git a/debuggerd/tombstone.cpp b/debuggerd/tombstone.cpp index 094ab48..b0ad274 100644 --- a/debuggerd/tombstone.cpp +++ b/debuggerd/tombstone.cpp
@@ -32,6 +32,9 @@ #include <sys/stat.h> #include <sys/un.h> +#include <memory> +#include <string> + #include <private/android_filesystem_config.h> #include <base/stringprintf.h> @@ -45,10 +48,6 @@ #include <selinux/android.h> -#include <UniquePtr.h> - -#include <string> - #include "backtrace.h" #include "elf_utils.h" #include "machine.h" @@ -318,16 +317,28 @@ } } +static std::string get_addr_string(uintptr_t addr) { + std::string addr_str; +#if defined(__LP64__) + addr_str = android::base::StringPrintf("%08x'%08x", + static_cast<uint32_t>(addr >> 32), + static_cast<uint32_t>(addr & 0xffffffff)); +#else + addr_str = android::base::StringPrintf("%08x", addr); +#endif + return addr_str; +} + static void dump_all_maps(Backtrace* backtrace, BacktraceMap* map, log_t* log, pid_t tid) { bool print_fault_address_marker = false; uintptr_t addr = 0; siginfo_t si; memset(&si, 0, sizeof(si)); - if (ptrace(PTRACE_GETSIGINFO, tid, 0, &si)) { - _LOG(log, logtype::ERROR, "cannot get siginfo for %d: %s\n", tid, strerror(errno)); - } else { + if (ptrace(PTRACE_GETSIGINFO, tid, 0, &si) != -1) { print_fault_address_marker = signal_has_si_addr(si.si_signo); addr = reinterpret_cast<uintptr_t>(si.si_addr); + } else { + _LOG(log, logtype::ERROR, "Cannot get siginfo for %d: %s\n", tid, strerror(errno)); } _LOG(log, logtype::MAPS, "\n"); @@ -336,8 +347,8 @@ } else { _LOG(log, logtype::MAPS, "memory map: (fault address prefixed with --->)\n"); if (map->begin() != map->end() && addr < map->begin()->start) { - _LOG(log, logtype::MAPS, "--->Fault address falls at %" PRIPTR " before any mapped regions\n", - addr); + _LOG(log, logtype::MAPS, "--->Fault address falls at %s before any mapped regions\n", + get_addr_string(addr).c_str()); print_fault_address_marker = false; } } @@ -347,15 +358,15 @@ line = " "; if (print_fault_address_marker) { if (addr < it->start) { - _LOG(log, logtype::MAPS, "--->Fault address falls at %" PRIPTR " between mapped regions\n", - addr); + _LOG(log, logtype::MAPS, "--->Fault address falls at %s between mapped regions\n", + get_addr_string(addr).c_str()); print_fault_address_marker = false; } else if (addr >= it->start && addr < it->end) { line = "--->"; print_fault_address_marker = false; } } - line += android::base::StringPrintf("%" PRIPTR "-%" PRIPTR " ", it->start, it->end - 1); + line += get_addr_string(it->start) + '-' + get_addr_string(it->end - 1) + ' '; if (it->flags & PROT_READ) { line += 'r'; } else { @@ -371,19 +382,28 @@ } else { line += '-'; } - line += android::base::StringPrintf(" %8" PRIxPTR, it->end - it->start); + line += android::base::StringPrintf(" %8" PRIxPTR " %8" PRIxPTR, + it->offset, it->end - it->start); + bool space_needed = true; if (it->name.length() > 0) { + space_needed = false; line += " " + it->name; std::string build_id; if ((it->flags & PROT_READ) && elf_get_build_id(backtrace, it->start, &build_id)) { line += " (BuildId: " + build_id + ")"; } } + if (it->load_base != 0) { + if (space_needed) { + line += ' '; + } + line += android::base::StringPrintf(" (load base 0x%" PRIxPTR ")", it->load_base); + } _LOG(log, logtype::MAPS, "%s\n", line.c_str()); } if (print_fault_address_marker) { - _LOG(log, logtype::MAPS, "--->Fault address falls at %" PRIPTR " after any mapped regions\n", - addr); + _LOG(log, logtype::MAPS, "--->Fault address falls at %s after any mapped regions\n", + get_addr_string(addr).c_str()); } } @@ -441,9 +461,11 @@ dump_thread_info(log, pid, new_tid); dump_registers(log, new_tid); - UniquePtr<Backtrace> backtrace(Backtrace::Create(pid, new_tid, map)); + std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, new_tid, map)); if (backtrace->Unwind(0)) { dump_backtrace_and_stack(backtrace.get(), log); + } else { + ALOGE("Unwind of sibling failed: pid = %d, tid = %d", pid, new_tid); } log->current_tid = log->crashed_tid; @@ -640,15 +662,19 @@ dump_signal_info(log, tid, signal, si_code); } - UniquePtr<BacktraceMap> map(BacktraceMap::Create(pid)); - UniquePtr<Backtrace> backtrace(Backtrace::Create(pid, tid, map.get())); + std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(pid)); + std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, tid, map.get())); dump_abort_message(backtrace.get(), log, abort_msg_address); dump_registers(log, tid); if (backtrace->Unwind(0)) { dump_backtrace_and_stack(backtrace.get(), log); + } else { + ALOGE("Unwind failed: pid = %d, tid = %d", pid, tid); } - dump_memory_and_code(log, tid); - dump_all_maps(backtrace.get(), map.get(), log, tid); + dump_memory_and_code(log, backtrace.get()); + if (map.get() != nullptr) { + dump_all_maps(backtrace.get(), map.get(), log, tid); + } if (want_logs) { dump_logs(log, pid, 5); @@ -792,7 +818,7 @@ *detach_failed = dump_crash(&log, pid, tid, signal, original_si_code, abort_msg_address, dump_sibling_threads, total_sleep_time_usec); - ALOGI("\nTombstone written to: %s\n", path); + _LOG(&log, logtype::BACKTRACE, "\nTombstone written to: %s\n", path); // Either of these file descriptors can be -1, any error is ignored. close(amfd);
diff --git a/debuggerd/utility.cpp b/debuggerd/utility.cpp index e10feff..9f340a8 100644 --- a/debuggerd/utility.cpp +++ b/debuggerd/utility.cpp
@@ -26,25 +26,13 @@ #include <sys/wait.h> #include <backtrace/Backtrace.h> +#include <base/file.h> +#include <base/stringprintf.h> #include <log/log.h> const int SLEEP_TIME_USEC = 50000; // 0.05 seconds const int MAX_TOTAL_SLEEP_USEC = 10000000; // 10 seconds -static int write_to_am(int fd, const char* buf, int len) { - int to_write = len; - while (to_write > 0) { - int written = TEMP_FAILURE_RETRY(write(fd, buf + len - to_write, to_write)); - if (written < 0) { - // hard failure - ALOGE("AM write failure (%d / %s)\n", errno, strerror(errno)); - return -1; - } - to_write -= written; - } - return len; -} - // Whitelist output desired in the logcat output. bool is_allowed_in_logcat(enum logtype ltype) { if ((ltype == ERROR) @@ -80,11 +68,11 @@ } if (write_to_logcat) { - __android_log_buf_write(LOG_ID_CRASH, ANDROID_LOG_INFO, LOG_TAG, buf); + __android_log_buf_write(LOG_ID_CRASH, ANDROID_LOG_FATAL, LOG_TAG, buf); if (write_to_activitymanager) { - int written = write_to_am(log->amfd, buf, len); - if (written <= 0) { + if (!android::base::WriteFully(log->amfd, buf, len)) { // timeout or other failure on write; stop informing the activity manager + ALOGE("AM write failed: %s", strerror(errno)); log->amfd = -1; } } @@ -131,68 +119,91 @@ return -1; } -void dump_memory(log_t* log, pid_t tid, uintptr_t addr) { - char code_buffer[64]; - char ascii_buffer[32]; - uintptr_t p, end; +#define MEMORY_BYTES_TO_DUMP 256 +#define MEMORY_BYTES_PER_LINE 16 - p = addr & ~(sizeof(long) - 1); - /* Dump 32 bytes before addr */ - p -= 32; - if (p > addr) { - /* catch underflow */ - p = 0; - } - /* Dump 256 bytes */ - end = p + 256; - /* catch overflow; 'end - p' has to be multiples of 16 */ - while (end < p) { - end -= 16; - } +void dump_memory(log_t* log, Backtrace* backtrace, uintptr_t addr, const char* fmt, ...) { + std::string log_msg; + va_list ap; + va_start(ap, fmt); + android::base::StringAppendV(&log_msg, fmt, ap); + va_end(ap); - /* Dump the code around PC as: - * addr contents ascii - * 0000000000008d34 ef000000e8bd0090 e1b00000512fff1e ............../Q - * 0000000000008d44 ea00b1f9e92d0090 e3a070fcef000000 ......-..p...... - * On 32-bit machines, there are still 16 bytes per line but addresses and - * words are of course presented differently. - */ - while (p < end) { - char* asc_out = ascii_buffer; + // Align the address to sizeof(long) and start 32 bytes before the address. + addr &= ~(sizeof(long) - 1); + if (addr >= 4128) { + addr -= 32; + } - int len = snprintf(code_buffer, sizeof(code_buffer), "%" PRIPTR " ", p); - - for (size_t i = 0; i < 16/sizeof(long); i++) { - long data = ptrace(PTRACE_PEEKTEXT, tid, (void*)p, NULL); - if (data == -1 && errno != 0) { - // ptrace failed, probably because we're dumping memory in an - // unmapped or inaccessible page. -#ifdef __LP64__ - len += sprintf(code_buffer + len, "---------------- "); + // Don't bother if the address looks too low, or looks too high. + if (addr < 4096 || +#if defined(__LP64__) + addr > 0x4000000000000000UL - MEMORY_BYTES_TO_DUMP) { #else - len += sprintf(code_buffer + len, "-------- "); + addr > 0xffff0000 - MEMORY_BYTES_TO_DUMP) { #endif - } else { - len += sprintf(code_buffer + len, "%" PRIPTR " ", - static_cast<uintptr_t>(data)); - } + return; + } - for (size_t j = 0; j < sizeof(long); j++) { - /* - * Our isprint() allows high-ASCII characters that display - * differently (often badly) in different viewers, so we - * just use a simpler test. - */ - char val = (data >> (j*8)) & 0xff; - if (val >= 0x20 && val < 0x7f) { - *asc_out++ = val; - } else { - *asc_out++ = '.'; - } - } - p += sizeof(long); - } - *asc_out = '\0'; - _LOG(log, logtype::MEMORY, " %s %s\n", code_buffer, ascii_buffer); + _LOG(log, logtype::MEMORY, "\n%s\n", log_msg.c_str()); + + // Dump 256 bytes + uintptr_t data[MEMORY_BYTES_TO_DUMP/sizeof(uintptr_t)]; + memset(data, 0, MEMORY_BYTES_TO_DUMP); + size_t bytes = backtrace->Read(addr, reinterpret_cast<uint8_t*>(data), sizeof(data)); + if (bytes % sizeof(uintptr_t) != 0) { + // This should never happen, but just in case. + ALOGE("Bytes read %zu, is not a multiple of %zu", bytes, sizeof(uintptr_t)); + bytes &= ~(sizeof(uintptr_t) - 1); + } + + if (bytes < MEMORY_BYTES_TO_DUMP && bytes > 0) { + // Try to do one more read. This could happen if a read crosses a map, but + // the maps do not have any break between them. Only requires one extra + // read because a map has to contain at least one page, and the total + // number of bytes to dump is smaller than a page. + size_t bytes2 = backtrace->Read(addr + bytes, reinterpret_cast<uint8_t*>(data) + bytes, + sizeof(data) - bytes); + bytes += bytes2; + if (bytes2 > 0 && bytes % sizeof(uintptr_t) != 0) { + // This should never happen, but we'll try and continue any way. + ALOGE("Bytes after second read %zu, is not a multiple of %zu", bytes, sizeof(uintptr_t)); + bytes &= ~(sizeof(uintptr_t) - 1); } + } + + // Dump the code around memory as: + // addr contents ascii + // 0000000000008d34 ef000000e8bd0090 e1b00000512fff1e ............../Q + // 0000000000008d44 ea00b1f9e92d0090 e3a070fcef000000 ......-..p...... + // On 32-bit machines, there are still 16 bytes per line but addresses and + // words are of course presented differently. + uintptr_t* data_ptr = data; + for (size_t line = 0; line < MEMORY_BYTES_TO_DUMP / MEMORY_BYTES_PER_LINE; line++) { + std::string logline; + android::base::StringAppendF(&logline, " %" PRIPTR, addr); + + addr += MEMORY_BYTES_PER_LINE; + std::string ascii; + for (size_t i = 0; i < MEMORY_BYTES_PER_LINE / sizeof(uintptr_t); i++, data_ptr++) { + if (bytes >= sizeof(uintptr_t)) { + bytes -= sizeof(uintptr_t); + android::base::StringAppendF(&logline, " %" PRIPTR, *data_ptr); + + // Fill out the ascii string from the data. + uint8_t* ptr = reinterpret_cast<uint8_t*>(data_ptr); + for (size_t val = 0; val < sizeof(uintptr_t); val++, ptr++) { + if (*ptr >= 0x20 && *ptr < 0x7f) { + ascii += *ptr; + } else { + ascii += '.'; + } + } + } else { + logline += ' ' + std::string(sizeof(uintptr_t) * 2, '-'); + ascii += std::string(sizeof(uintptr_t), '.'); + } + } + _LOG(log, logtype::MEMORY, "%s %s\n", logline.c_str(), ascii.c_str()); + } }
diff --git a/debuggerd/utility.h b/debuggerd/utility.h index 49b46e8..263374d 100644 --- a/debuggerd/utility.h +++ b/debuggerd/utility.h
@@ -21,6 +21,8 @@ #include <stdbool.h> #include <sys/types.h> +#include <backtrace/Backtrace.h> + // Figure out the abi based on defined macros. #if defined(__arm__) #define ABI_STRING "arm" @@ -75,6 +77,6 @@ int wait_for_sigstop(pid_t, int*, bool*); -void dump_memory(log_t* log, pid_t tid, uintptr_t addr); +void dump_memory(log_t* log, Backtrace* backtrace, uintptr_t addr, const char* fmt, ...); #endif // _DEBUGGERD_UTILITY_H
diff --git a/debuggerd/x86/machine.cpp b/debuggerd/x86/machine.cpp index 57330c1..c5f9259 100644 --- a/debuggerd/x86/machine.cpp +++ b/debuggerd/x86/machine.cpp
@@ -14,18 +14,31 @@ * limitations under the License. */ -#include <stddef.h> -#include <stdlib.h> -#include <string.h> -#include <stdio.h> #include <errno.h> -#include <sys/types.h> +#include <stdint.h> +#include <string.h> #include <sys/ptrace.h> -#include "../utility.h" -#include "../machine.h" +#include <backtrace/Backtrace.h> -void dump_memory_and_code(log_t*, pid_t) { +#include "machine.h" +#include "utility.h" + +void dump_memory_and_code(log_t* log, Backtrace* backtrace) { + struct pt_regs r; + if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, &r) == -1) { + _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno)); + return; + } + + dump_memory(log, backtrace, static_cast<uintptr_t>(r.eax), "memory near eax:"); + dump_memory(log, backtrace, static_cast<uintptr_t>(r.ebx), "memory near ebx:"); + dump_memory(log, backtrace, static_cast<uintptr_t>(r.ecx), "memory near ecx:"); + dump_memory(log, backtrace, static_cast<uintptr_t>(r.edx), "memory near edx:"); + dump_memory(log, backtrace, static_cast<uintptr_t>(r.esi), "memory near esi:"); + dump_memory(log, backtrace, static_cast<uintptr_t>(r.edi), "memory near edi:"); + + dump_memory(log, backtrace, static_cast<uintptr_t>(r.eip), "code around eip:"); } void dump_registers(log_t* log, pid_t tid) { @@ -34,6 +47,7 @@ _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno)); return; } + _LOG(log, logtype::REGISTERS, " eax %08lx ebx %08lx ecx %08lx edx %08lx\n", r.eax, r.ebx, r.ecx, r.edx); _LOG(log, logtype::REGISTERS, " esi %08lx edi %08lx\n",
diff --git a/debuggerd/x86_64/machine.cpp b/debuggerd/x86_64/machine.cpp old mode 100755 new mode 100644 index af4f35a..4f09a5d --- a/debuggerd/x86_64/machine.cpp +++ b/debuggerd/x86_64/machine.cpp
@@ -14,38 +14,51 @@ ** limitations under the License. */ -#include <stddef.h> -#include <stdbool.h> -#include <stdlib.h> -#include <string.h> -#include <stdio.h> #include <errno.h> -#include <sys/types.h> +#include <stdint.h> #include <sys/ptrace.h> +#include <string.h> #include <sys/user.h> -#include "../utility.h" -#include "../machine.h" +#include <backtrace/Backtrace.h> -void dump_memory_and_code(log_t*, pid_t) { +#include "machine.h" +#include "utility.h" + +void dump_memory_and_code(log_t* log, Backtrace* backtrace) { + struct user_regs_struct r; + if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, &r) == -1) { + _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno)); + return; + } + + dump_memory(log, backtrace, static_cast<uintptr_t>(r.rax), "memory near rax:"); + dump_memory(log, backtrace, static_cast<uintptr_t>(r.rbx), "memory near rbx:"); + dump_memory(log, backtrace, static_cast<uintptr_t>(r.rcx), "memory near rcx:"); + dump_memory(log, backtrace, static_cast<uintptr_t>(r.rdx), "memory near rdx:"); + dump_memory(log, backtrace, static_cast<uintptr_t>(r.rsi), "memory near rsi:"); + dump_memory(log, backtrace, static_cast<uintptr_t>(r.rdi), "memory near rdi:"); + + dump_memory(log, backtrace, static_cast<uintptr_t>(r.rip), "code around rip:"); } void dump_registers(log_t* log, pid_t tid) { - struct user_regs_struct r; - if (ptrace(PTRACE_GETREGS, tid, 0, &r) == -1) { - _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno)); - return; - } - _LOG(log, logtype::REGISTERS, " rax %016lx rbx %016lx rcx %016lx rdx %016lx\n", - r.rax, r.rbx, r.rcx, r.rdx); - _LOG(log, logtype::REGISTERS, " rsi %016lx rdi %016lx\n", - r.rsi, r.rdi); - _LOG(log, logtype::REGISTERS, " r8 %016lx r9 %016lx r10 %016lx r11 %016lx\n", - r.r8, r.r9, r.r10, r.r11); - _LOG(log, logtype::REGISTERS, " r12 %016lx r13 %016lx r14 %016lx r15 %016lx\n", - r.r12, r.r13, r.r14, r.r15); - _LOG(log, logtype::REGISTERS, " cs %016lx ss %016lx\n", - r.cs, r.ss); - _LOG(log, logtype::REGISTERS, " rip %016lx rbp %016lx rsp %016lx eflags %016lx\n", - r.rip, r.rbp, r.rsp, r.eflags); + struct user_regs_struct r; + if (ptrace(PTRACE_GETREGS, tid, 0, &r) == -1) { + _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno)); + return; + } + + _LOG(log, logtype::REGISTERS, " rax %016lx rbx %016lx rcx %016lx rdx %016lx\n", + r.rax, r.rbx, r.rcx, r.rdx); + _LOG(log, logtype::REGISTERS, " rsi %016lx rdi %016lx\n", + r.rsi, r.rdi); + _LOG(log, logtype::REGISTERS, " r8 %016lx r9 %016lx r10 %016lx r11 %016lx\n", + r.r8, r.r9, r.r10, r.r11); + _LOG(log, logtype::REGISTERS, " r12 %016lx r13 %016lx r14 %016lx r15 %016lx\n", + r.r12, r.r13, r.r14, r.r15); + _LOG(log, logtype::REGISTERS, " cs %016lx ss %016lx\n", + r.cs, r.ss); + _LOG(log, logtype::REGISTERS, " rip %016lx rbp %016lx rsp %016lx eflags %016lx\n", + r.rip, r.rbp, r.rsp, r.eflags); }
diff --git a/fastboot/Android.mk b/fastboot/Android.mk index 7b2975b..66a470a 100644 --- a/fastboot/Android.mk +++ b/fastboot/Android.mk
@@ -14,6 +14,8 @@ LOCAL_PATH:= $(call my-dir) +fastboot_version := $(shell git -C $(LOCAL_PATH) rev-parse --short=12 HEAD 2>/dev/null)-android + include $(CLEAR_VARS) LOCAL_C_INCLUDES := $(LOCAL_PATH)/../mkbootimg \ @@ -23,7 +25,9 @@ LOCAL_MODULE := fastboot LOCAL_MODULE_TAGS := debug LOCAL_CONLYFLAGS += -std=gnu99 -LOCAL_CFLAGS += -Wall -Wextra -Werror +LOCAL_CFLAGS += -Wall -Wextra -Werror -Wunreachable-code + +LOCAL_CFLAGS += -DFASTBOOT_REVISION='"$(fastboot_version)"' ifeq ($(HOST_OS),linux) LOCAL_SRC_FILES += usb_linux.c util_linux.c @@ -31,8 +35,7 @@ ifeq ($(HOST_OS),darwin) LOCAL_SRC_FILES += usb_osx.c util_osx.c - LOCAL_LDLIBS += -lpthread -framework CoreFoundation -framework IOKit \ - -framework Carbon + LOCAL_LDLIBS += -lpthread -framework CoreFoundation -framework IOKit -framework Carbon LOCAL_CFLAGS += -Wno-unused-parameter endif @@ -58,7 +61,8 @@ libsparse_host \ libutils \ liblog \ - libz + libz \ + libbase ifneq ($(HOST_OS),windows) LOCAL_STATIC_LIBRARIES += libselinux
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp index e139bcd..be80cce 100644 --- a/fastboot/fastboot.cpp +++ b/fastboot/fastboot.cpp
@@ -59,7 +59,6 @@ char cur_product[FB_RESPONSE_SZ + 1]; -static usb_handle *usb = 0; static const char *serial = 0; static const char *product = 0; static const char *cmdline = 0; @@ -280,14 +279,24 @@ " flashall flash boot, system, vendor and if found,\n" " recovery\n" " flash <partition> [ <filename> ] write a file to a flash partition\n" + " flashing lock locks the device. Prevents flashing" + " partitions\n" + " flashing unlock unlocks the device. Allows user to" + " flash any partition except the ones" + " that are related to bootloader\n" + " flashing lock_critical Prevents flashing bootloader related" + " partitions\n" + " flashing unlock_critical Enables flashing bootloader related" + " partitions\n" + " flashing get_unlock_ability Queries bootloader to see if the" + " device is unlocked\n" " erase <partition> erase a flash partition\n" " format[:[<fs type>][:[<size>]] <partition> format a flash partition.\n" " Can override the fs type and/or\n" " size the bootloader reports.\n" " getvar <variable> display a bootloader variable\n" - " boot <kernel> [ <ramdisk> [ <second> ] ] download and boot kernel\n" - " flash:raw boot <kernel> [ <ramdisk> [ <second> ] ] create bootimage and \n" - " flash it\n" + " boot <kernel> [ <ramdisk> ] download and boot kernel\n" + " flash:raw boot <kernel> [ <ramdisk> ] create bootimage and flash it\n" " devices list all connected devices\n" " continue continue with autoboot\n" " reboot [bootloader] reboot device, optionally into bootloader\n" @@ -315,11 +324,10 @@ } void *load_bootable_image(const char *kernel, const char *ramdisk, - const char *secondstage, unsigned *sz, - const char *cmdline) + unsigned *sz, const char *cmdline) { - void *kdata = 0, *rdata = 0, *sdata = 0; - unsigned ksize = 0, rsize = 0, ssize = 0; + void *kdata = 0, *rdata = 0; + unsigned ksize = 0, rsize = 0; void *bdata; unsigned bsize; @@ -355,18 +363,10 @@ } } - if (secondstage) { - sdata = load_file(secondstage, &ssize); - if(sdata == 0) { - fprintf(stderr,"cannot load '%s': %s\n", secondstage, strerror(errno)); - return 0; - } - } - fprintf(stderr,"creating boot image...\n"); bdata = mkbootimg(kdata, ksize, kernel_offset, rdata, rsize, ramdisk_offset, - sdata, ssize, second_offset, + 0, 0, second_offset, page_size, base_addr, tags_offset, &bsize); if(bdata == 0) { fprintf(stderr,"failed to create boot.img\n"); @@ -406,6 +406,35 @@ return data; } +#if defined(_WIN32) + +// TODO: move this to somewhere it can be shared. + +#include <windows.h> + +// Windows' tmpfile(3) requires administrator rights because +// it creates temporary files in the root directory. +static FILE* win32_tmpfile() { + char temp_path[PATH_MAX]; + DWORD nchars = GetTempPath(sizeof(temp_path), temp_path); + if (nchars == 0 || nchars >= sizeof(temp_path)) { + fprintf(stderr, "GetTempPath failed, error %ld\n", GetLastError()); + return nullptr; + } + + char filename[PATH_MAX]; + if (GetTempFileName(temp_path, "fastboot", 0, filename) == 0) { + fprintf(stderr, "GetTempFileName failed, error %ld\n", GetLastError()); + return nullptr; + } + + return fopen(filename, "w+bTD"); +} + +#define tmpfile win32_tmpfile + +#endif + static int unzip_to_file(ZipArchiveHandle zip, char* entry_name) { FILE* fp = tmpfile(); if (fp == NULL) { @@ -608,7 +637,7 @@ * erase partitions of type ext4 before flashing a filesystem so no stale * inodes are left lying around. Otherwise, e2fsck gets very upset. */ -static int needs_erase(const char *part) +static int needs_erase(usb_handle* usb, const char *part) { /* The function fb_format_supported() currently returns the value * we want, so just call it. @@ -711,29 +740,33 @@ ZipArchiveHandle zip; int error = OpenArchive(filename, &zip); if (error != 0) { + CloseArchive(zip); die("failed to open zip file '%s': %s", filename, ErrorCodeString(error)); } unsigned sz; void* data = unzip_file(zip, "android-info.txt", &sz); if (data == 0) { + CloseArchive(zip); die("update package '%s' has no android-info.txt", filename); } setup_requirements(reinterpret_cast<char*>(data), sz); - for (size_t i = 0; i < ARRAY_SIZE(images); i++) { + for (size_t i = 0; i < ARRAY_SIZE(images); ++i) { int fd = unzip_to_file(zip, images[i].img_name); - if (fd < 0) { - if (images[i].is_optional) + if (fd == -1) { + if (images[i].is_optional) { continue; - die("update package missing %s", images[i].img_name); + } + CloseArchive(zip); + exit(1); // unzip_to_file already explained why. } fastboot_buffer buf; int rc = load_buf_fd(usb, fd, &buf); if (rc) die("cannot load %s from flash", images[i].img_name); do_update_signature(zip, images[i].sig_name); - if (erase_first && needs_erase(images[i].part_name)) { + if (erase_first && needs_erase(usb, images[i].part_name)) { fb_queue_erase(images[i].part_name); } flash_buf(images[i].part_name, &buf); @@ -788,7 +821,7 @@ die("could not load %s\n", images[i].img_name); } do_send_signature(fname); - if (erase_first && needs_erase(images[i].part_name)) { + if (erase_first && needs_erase(usb, images[i].part_name)) { fb_queue_erase(images[i].part_name); } flash_buf(images[i].part_name, &buf); @@ -856,7 +889,8 @@ return num; } -void fb_perform_format(const char *partition, int skip_if_not_supported, +void fb_perform_format(usb_handle* usb, + const char *partition, int skip_if_not_supported, const char *type_override, const char *size_override) { char pTypeBuff[FB_RESPONSE_SZ + 1], pSizeBuff[FB_RESPONSE_SZ + 1]; @@ -960,8 +994,9 @@ {"page_size", required_argument, 0, 'n'}, {"ramdisk_offset", required_argument, 0, 'r'}, {"tags_offset", required_argument, 0, 't'}, - {"help", 0, 0, 'h'}, - {"unbuffered", 0, 0, 0}, + {"help", no_argument, 0, 'h'}, + {"unbuffered", no_argument, 0, 0}, + {"version", no_argument, 0, 0}, {0, 0, 0, 0} }; @@ -1033,6 +1068,9 @@ if (strcmp("unbuffered", longopts[longindex].name) == 0) { setvbuf(stdout, NULL, _IONBF, 0); setvbuf(stderr, NULL, _IONBF, 0); + } else if (strcmp("version", longopts[longindex].name) == 0) { + fprintf(stdout, "fastboot version %s\n", FASTBOOT_REVISION); + return 0; } break; default: @@ -1059,7 +1097,7 @@ return 0; } - usb = open_device(); + usb_handle* usb = open_device(); while (argc > 0) { if(!strcmp(*argv, "getvar")) { @@ -1101,10 +1139,10 @@ } if (type_override && !type_override[0]) type_override = NULL; if (size_override && !size_override[0]) size_override = NULL; - if (erase_first && needs_erase(argv[1])) { + if (erase_first && needs_erase(usb, argv[1])) { fb_queue_erase(argv[1]); } - fb_perform_format(argv[1], 0, type_override, size_override); + fb_perform_format(usb, argv[1], 0, type_override, size_override); skip(2); } else if(!strcmp(*argv, "signature")) { require(2); @@ -1134,7 +1172,6 @@ } else if(!strcmp(*argv, "boot")) { char *kname = 0; char *rname = 0; - char *sname = 0; skip(1); if (argc > 0) { kname = argv[0]; @@ -1144,11 +1181,7 @@ rname = argv[0]; skip(1); } - if (argc > 0) { - sname = argv[0]; - skip(1); - } - data = load_bootable_image(kname, rname, sname, &sz, cmdline); + data = load_bootable_image(kname, rname, &sz, cmdline); if (data == 0) return 1; fb_queue_download("boot.img", data, sz); fb_queue_command("boot", "booting"); @@ -1164,7 +1197,7 @@ skip(2); } if (fname == 0) die("cannot determine image filename for '%s'", pname); - if (erase_first && needs_erase(pname)) { + if (erase_first && needs_erase(usb, pname)) { fb_queue_erase(pname); } do_flash(usb, pname, fname); @@ -1172,18 +1205,14 @@ char *pname = argv[1]; char *kname = argv[2]; char *rname = 0; - char *sname = 0; require(3); - skip(3); - if (argc > 0) { - rname = argv[0]; - skip(1); + if(argc > 3) { + rname = argv[3]; + skip(4); + } else { + skip(3); } - if (argc > 0) { - sname = argv[0]; - skip(1); - } - data = load_bootable_image(kname, rname, sname, &sz, cmdline); + data = load_bootable_image(kname, rname, &sz, cmdline); if (data == 0) die("cannot load bootable image"); fb_queue_flash(pname, data, sz); } else if(!strcmp(*argv, "flashall")) { @@ -1201,6 +1230,16 @@ wants_reboot = 1; } else if(!strcmp(*argv, "oem")) { argc = do_oem_command(argc, argv); + } else if(!strcmp(*argv, "flashing") && argc == 2) { + if(!strcmp(*(argv+1), "unlock") || !strcmp(*(argv+1), "lock") + || !strcmp(*(argv+1), "unlock_critical") + || !strcmp(*(argv+1), "lock_critical") + || !strcmp(*(argv+1), "get_unlock_ability")) { + argc = do_oem_command(argc, argv); + } else { + usage(); + return 1; + } } else { usage(); return 1; @@ -1209,9 +1248,9 @@ if (wants_wipe) { fb_queue_erase("userdata"); - fb_perform_format("userdata", 1, NULL, NULL); + fb_perform_format(usb, "userdata", 1, NULL, NULL); fb_queue_erase("cache"); - fb_perform_format("cache", 1, NULL, NULL); + fb_perform_format(usb, "cache", 1, NULL, NULL); } if (wants_reboot) { fb_queue_reboot();
diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h index 1786e49..481c501 100644 --- a/fastboot/fastboot.h +++ b/fastboot/fastboot.h
@@ -69,7 +69,7 @@ /* util stuff */ double now(); char *mkmsg(const char *fmt, ...); -void die(const char *fmt, ...); +__attribute__((__noreturn__)) void die(const char *fmt, ...); void get_my_path(char *path);
diff --git a/fingerprintd/Android.mk b/fingerprintd/Android.mk new file mode 100644 index 0000000..48b9525 --- /dev/null +++ b/fingerprintd/Android.mk
@@ -0,0 +1,33 @@ +# +# Copyright (C) 2015 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_CFLAGS := -Wall -Wextra -Werror -Wunused +LOCAL_SRC_FILES := \ + FingerprintDaemonProxy.cpp \ + IFingerprintDaemon.cpp \ + IFingerprintDaemonCallback.cpp \ + fingerprintd.cpp +LOCAL_MODULE := fingerprintd +LOCAL_SHARED_LIBRARIES := \ + libbinder \ + liblog \ + libhardware \ + libutils \ + libkeystore_binder +include $(BUILD_EXECUTABLE)
diff --git a/fingerprintd/FingerprintDaemonProxy.cpp b/fingerprintd/FingerprintDaemonProxy.cpp new file mode 100644 index 0000000..beb95de --- /dev/null +++ b/fingerprintd/FingerprintDaemonProxy.cpp
@@ -0,0 +1,253 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "fingerprintd" + +#include <binder/IServiceManager.h> +#include <hardware/hardware.h> +#include <hardware/fingerprint.h> +#include <hardware/hw_auth_token.h> +#include <keystore/IKeystoreService.h> +#include <keystore/keystore.h> // for error codes +#include <utils/Log.h> + +#include "FingerprintDaemonProxy.h" + +namespace android { + +FingerprintDaemonProxy* FingerprintDaemonProxy::sInstance = NULL; + +// Supported fingerprint HAL version +static const uint16_t kVersion = HARDWARE_MODULE_API_VERSION(2, 0); + +FingerprintDaemonProxy::FingerprintDaemonProxy() : mModule(NULL), mDevice(NULL), mCallback(NULL) { + +} + +FingerprintDaemonProxy::~FingerprintDaemonProxy() { + closeHal(); +} + +void FingerprintDaemonProxy::hal_notify_callback(const fingerprint_msg_t *msg) { + FingerprintDaemonProxy* instance = FingerprintDaemonProxy::getInstance(); + const sp<IFingerprintDaemonCallback> callback = instance->mCallback; + if (callback == NULL) { + ALOGE("Invalid callback object"); + return; + } + const int64_t device = (int64_t) instance->mDevice; + switch (msg->type) { + case FINGERPRINT_ERROR: + ALOGD("onError(%d)", msg->data.error); + callback->onError(device, msg->data.error); + break; + case FINGERPRINT_ACQUIRED: + ALOGD("onAcquired(%d)", msg->data.acquired.acquired_info); + callback->onAcquired(device, msg->data.acquired.acquired_info); + break; + case FINGERPRINT_AUTHENTICATED: + ALOGD("onAuthenticated(fid=%d, gid=%d)", + msg->data.authenticated.finger.fid, + msg->data.authenticated.finger.gid); + if (msg->data.authenticated.finger.fid != 0) { + const uint8_t* hat = reinterpret_cast<const uint8_t *>(&msg->data.authenticated.hat); + instance->notifyKeystore(hat, sizeof(msg->data.authenticated.hat)); + } + callback->onAuthenticated(device, + msg->data.authenticated.finger.fid, + msg->data.authenticated.finger.gid); + break; + case FINGERPRINT_TEMPLATE_ENROLLING: + ALOGD("onEnrollResult(fid=%d, gid=%d, rem=%d)", + msg->data.enroll.finger.fid, + msg->data.enroll.finger.gid, + msg->data.enroll.samples_remaining); + callback->onEnrollResult(device, + msg->data.enroll.finger.fid, + msg->data.enroll.finger.gid, + msg->data.enroll.samples_remaining); + break; + case FINGERPRINT_TEMPLATE_REMOVED: + ALOGD("onRemove(fid=%d, gid=%d)", + msg->data.removed.finger.fid, + msg->data.removed.finger.gid); + callback->onRemoved(device, + msg->data.removed.finger.fid, + msg->data.removed.finger.gid); + break; + default: + ALOGE("invalid msg type: %d", msg->type); + return; + } +} + +void FingerprintDaemonProxy::notifyKeystore(const uint8_t *auth_token, const size_t auth_token_length) { + if (auth_token != NULL && auth_token_length > 0) { + // TODO: cache service? + sp < IServiceManager > sm = defaultServiceManager(); + sp < IBinder > binder = sm->getService(String16("android.security.keystore")); + sp < IKeystoreService > service = interface_cast < IKeystoreService > (binder); + if (service != NULL) { + status_t ret = service->addAuthToken(auth_token, auth_token_length); + if (ret != ResponseCode::NO_ERROR) { + ALOGE("Falure sending auth token to KeyStore: %d", ret); + } + } else { + ALOGE("Unable to communicate with KeyStore"); + } + } +} + +void FingerprintDaemonProxy::init(const sp<IFingerprintDaemonCallback>& callback) { + if (mCallback != NULL && IInterface::asBinder(callback) != IInterface::asBinder(mCallback)) { + IInterface::asBinder(mCallback)->unlinkToDeath(this); + } + IInterface::asBinder(callback)->linkToDeath(this); + mCallback = callback; +} + +int32_t FingerprintDaemonProxy::enroll(const uint8_t* token, ssize_t tokenSize, int32_t groupId, + int32_t timeout) { + ALOG(LOG_VERBOSE, LOG_TAG, "enroll(gid=%d, timeout=%d)\n", groupId, timeout); + if (tokenSize != sizeof(hw_auth_token_t) ) { + ALOG(LOG_VERBOSE, LOG_TAG, "enroll() : invalid token size %zu\n", tokenSize); + return -1; + } + const hw_auth_token_t* authToken = reinterpret_cast<const hw_auth_token_t*>(token); + return mDevice->enroll(mDevice, authToken, groupId, timeout); +} + +uint64_t FingerprintDaemonProxy::preEnroll() { + return mDevice->pre_enroll(mDevice); +} + +int32_t FingerprintDaemonProxy::postEnroll() { + return mDevice->post_enroll(mDevice); +} + +int32_t FingerprintDaemonProxy::stopEnrollment() { + ALOG(LOG_VERBOSE, LOG_TAG, "stopEnrollment()\n"); + return mDevice->cancel(mDevice); +} + +int32_t FingerprintDaemonProxy::authenticate(uint64_t sessionId, uint32_t groupId) { + ALOG(LOG_VERBOSE, LOG_TAG, "authenticate(sid=%" PRId64 ", gid=%d)\n", sessionId, groupId); + return mDevice->authenticate(mDevice, sessionId, groupId); +} + +int32_t FingerprintDaemonProxy::stopAuthentication() { + ALOG(LOG_VERBOSE, LOG_TAG, "stopAuthentication()\n"); + return mDevice->cancel(mDevice); +} + +int32_t FingerprintDaemonProxy::remove(int32_t fingerId, int32_t groupId) { + ALOG(LOG_VERBOSE, LOG_TAG, "remove(fid=%d, gid=%d)\n", fingerId, groupId); + return mDevice->remove(mDevice, groupId, fingerId); +} + +uint64_t FingerprintDaemonProxy::getAuthenticatorId() { + return mDevice->get_authenticator_id(mDevice); +} + +int32_t FingerprintDaemonProxy::setActiveGroup(int32_t groupId, const uint8_t* path, + ssize_t pathlen) { + if (pathlen >= PATH_MAX || pathlen <= 0) { + ALOGE("Bad path length: %zd", pathlen); + return -1; + } + // Convert to null-terminated string + char path_name[PATH_MAX]; + memcpy(path_name, path, pathlen); + path_name[pathlen] = '\0'; + ALOG(LOG_VERBOSE, LOG_TAG, "setActiveGroup(%d, %s, %zu)", groupId, path_name, pathlen); + return mDevice->set_active_group(mDevice, groupId, path_name); +} + +int64_t FingerprintDaemonProxy::openHal() { + ALOG(LOG_VERBOSE, LOG_TAG, "nativeOpenHal()\n"); + int err; + const hw_module_t *hw_module = NULL; + if (0 != (err = hw_get_module(FINGERPRINT_HARDWARE_MODULE_ID, &hw_module))) { + ALOGE("Can't open fingerprint HW Module, error: %d", err); + return 0; + } + if (NULL == hw_module) { + ALOGE("No valid fingerprint module"); + return 0; + } + + mModule = reinterpret_cast<const fingerprint_module_t*>(hw_module); + + if (mModule->common.methods->open == NULL) { + ALOGE("No valid open method"); + return 0; + } + + hw_device_t *device = NULL; + + if (0 != (err = mModule->common.methods->open(hw_module, NULL, &device))) { + ALOGE("Can't open fingerprint methods, error: %d", err); + return 0; + } + + if (kVersion != device->version) { + ALOGE("Wrong fp version. Expected %d, got %d", kVersion, device->version); + // return 0; // FIXME + } + + mDevice = reinterpret_cast<fingerprint_device_t*>(device); + err = mDevice->set_notify(mDevice, hal_notify_callback); + if (err < 0) { + ALOGE("Failed in call to set_notify(), err=%d", err); + return 0; + } + + // Sanity check - remove + if (mDevice->notify != hal_notify_callback) { + ALOGE("NOTIFY not set properly: %p != %p", mDevice->notify, hal_notify_callback); + } + + ALOG(LOG_VERBOSE, LOG_TAG, "fingerprint HAL successfully initialized"); + return reinterpret_cast<int64_t>(mDevice); // This is just a handle +} + +int32_t FingerprintDaemonProxy::closeHal() { + ALOG(LOG_VERBOSE, LOG_TAG, "nativeCloseHal()\n"); + if (mDevice == NULL) { + ALOGE("No valid device"); + return -ENOSYS; + } + int err; + if (0 != (err = mDevice->common.close(reinterpret_cast<hw_device_t*>(mDevice)))) { + ALOGE("Can't close fingerprint module, error: %d", err); + return err; + } + mDevice = NULL; + return 0; +} + +void FingerprintDaemonProxy::binderDied(const wp<IBinder>& who) { + ALOGD("binder died"); + int err; + if (0 != (err = closeHal())) { + ALOGE("Can't close fingerprint device, error: %d", err); + } + if (IInterface::asBinder(mCallback) == who) { + mCallback = NULL; + } +} + +}
diff --git a/fingerprintd/FingerprintDaemonProxy.h b/fingerprintd/FingerprintDaemonProxy.h new file mode 100644 index 0000000..871c0e6 --- /dev/null +++ b/fingerprintd/FingerprintDaemonProxy.h
@@ -0,0 +1,63 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FINGERPRINT_DAEMON_PROXY_H_ +#define FINGERPRINT_DAEMON_PROXY_H_ + +#include "IFingerprintDaemon.h" +#include "IFingerprintDaemonCallback.h" + +namespace android { + +class FingerprintDaemonProxy : public BnFingerprintDaemon { + public: + static FingerprintDaemonProxy* getInstance() { + if (sInstance == NULL) { + sInstance = new FingerprintDaemonProxy(); + } + return sInstance; + } + + // These reflect binder methods. + virtual void init(const sp<IFingerprintDaemonCallback>& callback); + virtual int32_t enroll(const uint8_t* token, ssize_t tokenLength, int32_t groupId, int32_t timeout); + virtual uint64_t preEnroll(); + virtual int32_t postEnroll(); + virtual int32_t stopEnrollment(); + virtual int32_t authenticate(uint64_t sessionId, uint32_t groupId); + virtual int32_t stopAuthentication(); + virtual int32_t remove(int32_t fingerId, int32_t groupId); + virtual uint64_t getAuthenticatorId(); + virtual int32_t setActiveGroup(int32_t groupId, const uint8_t* path, ssize_t pathLen); + virtual int64_t openHal(); + virtual int32_t closeHal(); + + private: + FingerprintDaemonProxy(); + virtual ~FingerprintDaemonProxy(); + void binderDied(const wp<IBinder>& who); + void notifyKeystore(const uint8_t *auth_token, const size_t auth_token_length); + static void hal_notify_callback(const fingerprint_msg_t *msg); + + static FingerprintDaemonProxy* sInstance; + fingerprint_module_t const* mModule; + fingerprint_device_t* mDevice; + sp<IFingerprintDaemonCallback> mCallback; +}; + +} // namespace android + +#endif // FINGERPRINT_DAEMON_PROXY_H_
diff --git a/fingerprintd/IFingerprintDaemon.cpp b/fingerprintd/IFingerprintDaemon.cpp new file mode 100644 index 0000000..7131793 --- /dev/null +++ b/fingerprintd/IFingerprintDaemon.cpp
@@ -0,0 +1,195 @@ +/* + * Copyright 2015, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#include <inttypes.h> + +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> +#include <binder/PermissionCache.h> +#include <utils/String16.h> +#include <utils/Looper.h> +#include <keystore/IKeystoreService.h> +#include <keystore/keystore.h> // for error code +#include <hardware/hardware.h> +#include <hardware/fingerprint.h> +#include <hardware/hw_auth_token.h> +#include "IFingerprintDaemon.h" +#include "IFingerprintDaemonCallback.h" + +namespace android { + +static const String16 USE_FINGERPRINT_PERMISSION("android.permission.USE_FINGERPRINT"); +static const String16 MANAGE_FINGERPRINT_PERMISSION("android.permission.MANAGE_FINGERPRINT"); +static const String16 HAL_FINGERPRINT_PERMISSION("android.permission.MANAGE_FINGERPRINT"); // TODO +static const String16 DUMP_PERMISSION("android.permission.DUMP"); + +const android::String16 +IFingerprintDaemon::descriptor("android.hardware.fingerprint.IFingerprintDaemon"); + +const android::String16& +IFingerprintDaemon::getInterfaceDescriptor() const { + return IFingerprintDaemon::descriptor; +} + +status_t BnFingerprintDaemon::onTransact(uint32_t code, const Parcel& data, Parcel* reply, + uint32_t flags) { + switch(code) { + case AUTHENTICATE: { + CHECK_INTERFACE(IFingerprintDaemon, data, reply); + if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) { + return PERMISSION_DENIED; + } + const uint64_t sessionId = data.readInt64(); + const uint32_t groupId = data.readInt32(); + const int32_t ret = authenticate(sessionId, groupId); + reply->writeNoException(); + reply->writeInt32(ret); + return NO_ERROR; + }; + case CANCEL_AUTHENTICATION: { + CHECK_INTERFACE(IFingerprintDaemon, data, reply); + if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) { + return PERMISSION_DENIED; + } + const int32_t ret = stopAuthentication(); + reply->writeNoException(); + reply->writeInt32(ret); + return NO_ERROR; + } + case ENROLL: { + CHECK_INTERFACE(IFingerprintDaemon, data, reply); + if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) { + return PERMISSION_DENIED; + } + const ssize_t tokenSize = data.readInt32(); + const uint8_t* token = static_cast<const uint8_t *>(data.readInplace(tokenSize)); + const int32_t groupId = data.readInt32(); + const int32_t timeout = data.readInt32(); + const int32_t ret = enroll(token, tokenSize, groupId, timeout); + reply->writeNoException(); + reply->writeInt32(ret); + return NO_ERROR; + } + case CANCEL_ENROLLMENT: { + CHECK_INTERFACE(IFingerprintDaemon, data, reply); + if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) { + return PERMISSION_DENIED; + } + const int32_t ret = stopEnrollment(); + reply->writeNoException(); + reply->writeInt32(ret); + return NO_ERROR; + } + case PRE_ENROLL: { + CHECK_INTERFACE(IFingerprintDaemon, data, reply); + if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) { + return PERMISSION_DENIED; + } + const uint64_t ret = preEnroll(); + reply->writeNoException(); + reply->writeInt64(ret); + return NO_ERROR; + } + case POST_ENROLL: { + CHECK_INTERFACE(IFingerprintDaemon, data, reply); + if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) { + return PERMISSION_DENIED; + } + const int32_t ret = postEnroll(); + reply->writeNoException(); + reply->writeInt32(ret); + return NO_ERROR; + } + case REMOVE: { + CHECK_INTERFACE(IFingerprintDaemon, data, reply); + if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) { + return PERMISSION_DENIED; + } + const int32_t fingerId = data.readInt32(); + const int32_t groupId = data.readInt32(); + const int32_t ret = remove(fingerId, groupId); + reply->writeNoException(); + reply->writeInt32(ret); + return NO_ERROR; + } + case GET_AUTHENTICATOR_ID: { + CHECK_INTERFACE(IFingerprintDaemon, data, reply); + if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) { + return PERMISSION_DENIED; + } + const uint64_t ret = getAuthenticatorId(); + reply->writeNoException(); + reply->writeInt64(ret); + return NO_ERROR; + } + case SET_ACTIVE_GROUP: { + CHECK_INTERFACE(IFingerprintDaemon, data, reply); + if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) { + return PERMISSION_DENIED; + } + const int32_t group = data.readInt32(); + const ssize_t pathSize = data.readInt32(); + const uint8_t* path = static_cast<const uint8_t *>(data.readInplace(pathSize)); + const int32_t ret = setActiveGroup(group, path, pathSize); + reply->writeNoException(); + reply->writeInt32(ret); + return NO_ERROR; + } + case OPEN_HAL: { + CHECK_INTERFACE(IFingerprintDaemon, data, reply); + if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) { + return PERMISSION_DENIED; + } + const int64_t ret = openHal(); + reply->writeNoException(); + reply->writeInt64(ret); + return NO_ERROR; + } + case CLOSE_HAL: { + CHECK_INTERFACE(IFingerprintDaemon, data, reply); + if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) { + return PERMISSION_DENIED; + } + const int32_t ret = closeHal(); + reply->writeNoException(); + reply->writeInt32(ret); + return NO_ERROR; + } + case INIT: { + CHECK_INTERFACE(IFingerprintDaemon, data, reply); + if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) { + return PERMISSION_DENIED; + } + sp<IFingerprintDaemonCallback> callback = + interface_cast<IFingerprintDaemonCallback>(data.readStrongBinder()); + init(callback); + reply->writeNoException(); + return NO_ERROR; + } + default: + return BBinder::onTransact(code, data, reply, flags); + } +}; + +bool BnFingerprintDaemon::checkPermission(const String16& permission) { + const IPCThreadState* ipc = IPCThreadState::self(); + const int calling_pid = ipc->getCallingPid(); + const int calling_uid = ipc->getCallingUid(); + return PermissionCache::checkPermission(permission, calling_pid, calling_uid); +} + + +}; // namespace android
diff --git a/fingerprintd/IFingerprintDaemon.h b/fingerprintd/IFingerprintDaemon.h new file mode 100644 index 0000000..1eb4ac1 --- /dev/null +++ b/fingerprintd/IFingerprintDaemon.h
@@ -0,0 +1,86 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef IFINGERPRINT_DAEMON_H_ +#define IFINGERPRINT_DAEMON_H_ + +#include <binder/IInterface.h> +#include <binder/Parcel.h> + +namespace android { + +class IFingerprintDaemonCallback; + +/* +* Abstract base class for native implementation of FingerprintService. +* +* Note: This must be kept manually in sync with IFingerprintDaemon.aidl +*/ +class IFingerprintDaemon : public IInterface, public IBinder::DeathRecipient { + public: + enum { + AUTHENTICATE = IBinder::FIRST_CALL_TRANSACTION + 0, + CANCEL_AUTHENTICATION = IBinder::FIRST_CALL_TRANSACTION + 1, + ENROLL = IBinder::FIRST_CALL_TRANSACTION + 2, + CANCEL_ENROLLMENT = IBinder::FIRST_CALL_TRANSACTION + 3, + PRE_ENROLL = IBinder::FIRST_CALL_TRANSACTION + 4, + REMOVE = IBinder::FIRST_CALL_TRANSACTION + 5, + GET_AUTHENTICATOR_ID = IBinder::FIRST_CALL_TRANSACTION + 6, + SET_ACTIVE_GROUP = IBinder::FIRST_CALL_TRANSACTION + 7, + OPEN_HAL = IBinder::FIRST_CALL_TRANSACTION + 8, + CLOSE_HAL = IBinder::FIRST_CALL_TRANSACTION + 9, + INIT = IBinder::FIRST_CALL_TRANSACTION + 10, + POST_ENROLL = IBinder::FIRST_CALL_TRANSACTION + 11, + }; + + IFingerprintDaemon() { } + virtual ~IFingerprintDaemon() { } + virtual const android::String16& getInterfaceDescriptor() const; + + // Binder interface methods + virtual void init(const sp<IFingerprintDaemonCallback>& callback) = 0; + virtual int32_t enroll(const uint8_t* token, ssize_t tokenLength, int32_t groupId, + int32_t timeout) = 0; + virtual uint64_t preEnroll() = 0; + virtual int32_t postEnroll() = 0; + virtual int32_t stopEnrollment() = 0; + virtual int32_t authenticate(uint64_t sessionId, uint32_t groupId) = 0; + virtual int32_t stopAuthentication() = 0; + virtual int32_t remove(int32_t fingerId, int32_t groupId) = 0; + virtual uint64_t getAuthenticatorId() = 0; + virtual int32_t setActiveGroup(int32_t groupId, const uint8_t* path, ssize_t pathLen) = 0; + virtual int64_t openHal() = 0; + virtual int32_t closeHal() = 0; + + // DECLARE_META_INTERFACE - C++ client interface not needed + static const android::String16 descriptor; + static void hal_notify_callback(const fingerprint_msg_t *msg); +}; + +// ---------------------------------------------------------------------------- + +class BnFingerprintDaemon: public BnInterface<IFingerprintDaemon> { + public: + virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, + uint32_t flags = 0); + private: + bool checkPermission(const String16& permission); +}; + +} // namespace android + +#endif // IFINGERPRINT_DAEMON_H_ +
diff --git a/fingerprintd/IFingerprintDaemonCallback.cpp b/fingerprintd/IFingerprintDaemonCallback.cpp new file mode 100644 index 0000000..44d8020 --- /dev/null +++ b/fingerprintd/IFingerprintDaemonCallback.cpp
@@ -0,0 +1,91 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "IFingerprintDaemonCallback" +#include <stdint.h> +#include <sys/types.h> +#include <utils/Log.h> +#include <binder/Parcel.h> + +#include "IFingerprintDaemonCallback.h" + +namespace android { + +class BpFingerprintDaemonCallback : public BpInterface<IFingerprintDaemonCallback> +{ +public: + BpFingerprintDaemonCallback(const sp<IBinder>& impl) : + BpInterface<IFingerprintDaemonCallback>(impl) { + } + virtual status_t onEnrollResult(int64_t devId, int32_t fpId, int32_t gpId, int32_t rem) { + Parcel data, reply; + data.writeInterfaceToken(IFingerprintDaemonCallback::getInterfaceDescriptor()); + data.writeInt64(devId); + data.writeInt32(fpId); + data.writeInt32(gpId); + data.writeInt32(rem); + return remote()->transact(ON_ENROLL_RESULT, data, &reply, IBinder::FLAG_ONEWAY); + } + + virtual status_t onAcquired(int64_t devId, int32_t acquiredInfo) { + Parcel data, reply; + data.writeInterfaceToken(IFingerprintDaemonCallback::getInterfaceDescriptor()); + data.writeInt64(devId); + data.writeInt32(acquiredInfo); + return remote()->transact(ON_ACQUIRED, data, &reply, IBinder::FLAG_ONEWAY); + } + + virtual status_t onAuthenticated(int64_t devId, int32_t fpId, int32_t gpId) { + Parcel data, reply; + data.writeInterfaceToken(IFingerprintDaemonCallback::getInterfaceDescriptor()); + data.writeInt64(devId); + data.writeInt32(fpId); + data.writeInt32(gpId); + return remote()->transact(ON_AUTHENTICATED, data, &reply, IBinder::FLAG_ONEWAY); + } + + virtual status_t onError(int64_t devId, int32_t error) { + Parcel data, reply; + data.writeInterfaceToken(IFingerprintDaemonCallback::getInterfaceDescriptor()); + data.writeInt64(devId); + data.writeInt32(error); + return remote()->transact(ON_ERROR, data, &reply, IBinder::FLAG_ONEWAY); + } + + virtual status_t onRemoved(int64_t devId, int32_t fpId, int32_t gpId) { + Parcel data, reply; + data.writeInterfaceToken(IFingerprintDaemonCallback::getInterfaceDescriptor()); + data.writeInt64(devId); + data.writeInt32(fpId); + data.writeInt32(gpId); + return remote()->transact(ON_REMOVED, data, &reply, IBinder::FLAG_ONEWAY); + } + + virtual status_t onEnumerate(int64_t devId, const int32_t* fpIds, const int32_t* gpIds, + int32_t sz) { + Parcel data, reply; + data.writeInterfaceToken(IFingerprintDaemonCallback::getInterfaceDescriptor()); + data.writeInt64(devId); + data.writeInt32Array(sz, fpIds); + data.writeInt32Array(sz, gpIds); + return remote()->transact(ON_ENUMERATE, data, &reply, IBinder::FLAG_ONEWAY); + } +}; + +IMPLEMENT_META_INTERFACE(FingerprintDaemonCallback, + "android.hardware.fingerprint.IFingerprintDaemonCallback"); + +}; // namespace android
diff --git a/fingerprintd/IFingerprintDaemonCallback.h b/fingerprintd/IFingerprintDaemonCallback.h new file mode 100644 index 0000000..6e32213 --- /dev/null +++ b/fingerprintd/IFingerprintDaemonCallback.h
@@ -0,0 +1,55 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef IFINGERPRINT_DAEMON_CALLBACK_H_ +#define IFINGERPRINT_DAEMON_CALLBACK_H_ + +#include <inttypes.h> +#include <utils/Errors.h> +#include <binder/IInterface.h> +#include <binder/Parcel.h> + +namespace android { + +/* +* Communication channel back to FingerprintService.java +*/ +class IFingerprintDaemonCallback : public IInterface { + public: + // must be kept in sync with IFingerprintService.aidl + enum { + ON_ENROLL_RESULT = IBinder::FIRST_CALL_TRANSACTION + 0, + ON_ACQUIRED = IBinder::FIRST_CALL_TRANSACTION + 1, + ON_AUTHENTICATED = IBinder::FIRST_CALL_TRANSACTION + 2, + ON_ERROR = IBinder::FIRST_CALL_TRANSACTION + 3, + ON_REMOVED = IBinder::FIRST_CALL_TRANSACTION + 4, + ON_ENUMERATE = IBinder::FIRST_CALL_TRANSACTION + 5, + }; + + virtual status_t onEnrollResult(int64_t devId, int32_t fpId, int32_t gpId, int32_t rem) = 0; + virtual status_t onAcquired(int64_t devId, int32_t acquiredInfo) = 0; + virtual status_t onAuthenticated(int64_t devId, int32_t fingerId, int32_t groupId) = 0; + virtual status_t onError(int64_t devId, int32_t error) = 0; + virtual status_t onRemoved(int64_t devId, int32_t fingerId, int32_t groupId) = 0; + virtual status_t onEnumerate(int64_t devId, const int32_t* fpIds, const int32_t* gpIds, + int32_t sz) = 0; + + DECLARE_META_INTERFACE(FingerprintDaemonCallback); +}; + +}; // namespace android + +#endif // IFINGERPRINT_DAEMON_CALLBACK_H_
diff --git a/fingerprintd/fingerprintd.cpp b/fingerprintd/fingerprintd.cpp new file mode 100644 index 0000000..8fa7ed1 --- /dev/null +++ b/fingerprintd/fingerprintd.cpp
@@ -0,0 +1,55 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "fingerprintd" + +#include <cutils/log.h> +#include <utils/Log.h> + +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> +#include <binder/PermissionCache.h> +#include <utils/String16.h> + +#include <keystore/IKeystoreService.h> +#include <keystore/keystore.h> // for error codes + +#include <hardware/hardware.h> +#include <hardware/fingerprint.h> +#include <hardware/hw_auth_token.h> + +#include "FingerprintDaemonProxy.h" + +int main() { + ALOGI("Starting " LOG_TAG); + android::sp<android::IServiceManager> serviceManager = android::defaultServiceManager(); + android::sp<android::FingerprintDaemonProxy> proxy = + android::FingerprintDaemonProxy::getInstance(); + android::status_t ret = serviceManager->addService( + android::FingerprintDaemonProxy::descriptor, proxy); + if (ret != android::OK) { + ALOGE("Couldn't register " LOG_TAG " binder service!"); + return -1; + } + + /* + * We're the only thread in existence, so we're just going to process + * Binder transaction as a single-threaded program. + */ + android::IPCThreadState::self()->joinThreadPool(); + ALOGI("Done"); + return 0; +}
diff --git a/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.c index 5f639b7..15d44ef 100644 --- a/fs_mgr/fs_mgr.c +++ b/fs_mgr/fs_mgr.c
@@ -31,7 +31,7 @@ #include <dirent.h> #include <ext4.h> #include <ext4_sb.h> -#include <ext4_crypt.h> +#include <ext4_crypt_init_extensions.h> #include <linux/loop.h> #include <private/android_filesystem_config.h> @@ -206,7 +206,7 @@ } rc = ioctl(fd, BLKROSET, &ON); - TEMP_FAILURE_RETRY(close(fd)); + close(fd); return rc; } @@ -486,16 +486,6 @@ return FS_MGR_MNTALL_FAIL; } - // Link it to the normal place so ext4_crypt functions work normally - strlcat(tmp_mnt, "/unencrypted", sizeof(tmp_mnt)); - char link_path[PATH_MAX]; - strlcpy(link_path, rec->mount_point, sizeof(link_path)); - strlcat(link_path, "/unencrypted", sizeof(link_path)); - if (symlink(tmp_mnt, link_path)) { - ERROR("Error creating symlink to unencrypted directory\n"); - return FS_MGR_MNTALL_FAIL; - } - return FS_MGR_MNTALL_DEV_NON_DEFAULT_FILE_ENCRYPTED; }
diff --git a/fs_mgr/fs_mgr_format.c b/fs_mgr/fs_mgr_format.c index b5b92b5..c73045d 100644 --- a/fs_mgr/fs_mgr_format.c +++ b/fs_mgr/fs_mgr_format.c
@@ -52,7 +52,7 @@ info.len = ((off64_t)nr_sec * 512); /* Use make_ext4fs_internal to avoid wiping an already-wiped partition. */ - rc = make_ext4fs_internal(fd, NULL, fs_mnt_point, 0, 0, 0, 0, 0, 0, 0, 0, NULL); + rc = make_ext4fs_internal(fd, NULL, NULL, fs_mnt_point, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL); if (rc) { ERROR("make_ext4fs returned %d.\n", rc); }
diff --git a/fs_mgr/fs_mgr_fstab.c b/fs_mgr/fs_mgr_fstab.c index 51f398e..f24af1f 100644 --- a/fs_mgr/fs_mgr_fstab.c +++ b/fs_mgr/fs_mgr_fstab.c
@@ -72,6 +72,7 @@ { "zramsize=", MF_ZRAMSIZE }, { "verify", MF_VERIFY }, { "noemulatedsd", MF_NOEMULATEDSD }, + { "notrim", MF_NOTRIM }, { "formattable", MF_FORMATTABLE }, { "defaults", 0 }, { 0, 0 }, @@ -470,6 +471,11 @@ return fstab->fs_mgr_flags & MF_NOEMULATEDSD; } +int fs_mgr_is_notrim(struct fstab_rec *fstab) +{ + return fstab->fs_mgr_flags & MF_NOTRIM; +} + int fs_mgr_is_formattable(struct fstab_rec *fstab) { return fstab->fs_mgr_flags & (MF_FORMATTABLE);
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h index 2d252e4..682fd11 100644 --- a/fs_mgr/fs_mgr_priv.h +++ b/fs_mgr/fs_mgr_priv.h
@@ -76,8 +76,9 @@ #define MF_FORCECRYPT 0x400 #define MF_NOEMULATEDSD 0x800 /* no emulated sdcard daemon, sd card is the only external storage */ -#define MF_FORMATTABLE 0x1000 +#define MF_NOTRIM 0x1000 #define MF_FILEENCRYPTION 0x2000 +#define MF_FORMATTABLE 0x4000 #define DM_BUF_SIZE 4096
diff --git a/fs_mgr/fs_mgr_verity.c b/fs_mgr/fs_mgr_verity.c index 6ef46ba..2d1abbe 100644 --- a/fs_mgr/fs_mgr_verity.c +++ b/fs_mgr/fs_mgr_verity.c
@@ -169,20 +169,20 @@ if (TEMP_FAILURE_RETRY(lseek64(data_device, 1024, SEEK_SET)) < 0) { ERROR("Error seeking to superblock"); - TEMP_FAILURE_RETRY(close(data_device)); + close(data_device); return -1; } if (TEMP_FAILURE_RETRY(read(data_device, &sb, sizeof(sb))) != sizeof(sb)) { ERROR("Error reading superblock"); - TEMP_FAILURE_RETRY(close(data_device)); + close(data_device); return -1; } ext4_parse_sb(&sb, &info); *device_size = info.len; - TEMP_FAILURE_RETRY(close(data_device)); + close(data_device); return 0; } @@ -301,7 +301,7 @@ out: if (device != -1) - TEMP_FAILURE_RETRY(close(device)); + close(device); if (retval != FS_MGR_SETUP_VERITY_SUCCESS) { free(*signature); @@ -470,7 +470,7 @@ out: if (fd != -1) { - TEMP_FAILURE_RETRY(close(fd)); + close(fd); } return rc; @@ -622,7 +622,7 @@ out: if (fd != -1) { - TEMP_FAILURE_RETRY(close(fd)); + close(fd); } return rc; @@ -670,7 +670,7 @@ out: if (fd != -1) { - TEMP_FAILURE_RETRY(close(fd)); + close(fd); } return rc; @@ -745,7 +745,7 @@ free(signature); if (fd != -1) { - TEMP_FAILURE_RETRY(close(fd)); + close(fd); } return rc; @@ -767,8 +767,24 @@ static int load_verity_state(struct fstab_rec *fstab, int *mode) { - off64_t offset = 0; + char propbuf[PROPERTY_VALUE_MAX]; int match = 0; + off64_t offset = 0; + + /* use the kernel parameter if set */ + property_get("ro.boot.veritymode", propbuf, ""); + + if (*propbuf != '\0') { + if (!strcmp(propbuf, "enforcing")) { + *mode = VERITY_MODE_DEFAULT; + return 0; + } else if (!strcmp(propbuf, "logging")) { + *mode = VERITY_MODE_LOGGING; + return 0; + } else { + INFO("Unknown value %s for veritymode; ignoring", propbuf); + } + } if (get_verity_state_offset(fstab, &offset) < 0) { /* fall back to stateless behavior */ @@ -855,6 +871,13 @@ struct dm_ioctl *io = (struct dm_ioctl *) buffer; struct fstab *fstab = NULL; + /* check if we need to store the state */ + property_get("ro.boot.veritymode", propbuf, ""); + + if (*propbuf != '\0') { + return 0; /* state is kept by the bootloader */ + } + fd = TEMP_FAILURE_RETRY(open("/dev/device-mapper", O_RDWR | O_CLOEXEC)); if (fd == -1) { @@ -913,7 +936,7 @@ } if (fd) { - TEMP_FAILURE_RETRY(close(fd)); + close(fd); } return rc;
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h index 68af452..27fccf7 100644 --- a/fs_mgr/include/fs_mgr.h +++ b/fs_mgr/include/fs_mgr.h
@@ -103,6 +103,7 @@ int fs_mgr_is_encryptable(const struct fstab_rec *fstab); int fs_mgr_is_file_encrypted(const struct fstab_rec *fstab); int fs_mgr_is_noemulatedsd(const struct fstab_rec *fstab); +int fs_mgr_is_notrim(struct fstab_rec *fstab); int fs_mgr_is_formattable(struct fstab_rec *fstab); int fs_mgr_swapon_all(struct fstab *fstab);
diff --git a/gatekeeperd/Android.mk b/gatekeeperd/Android.mk new file mode 100644 index 0000000..55b2d5e --- /dev/null +++ b/gatekeeperd/Android.mk
@@ -0,0 +1,41 @@ +# +# Copyright (C) 2015 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_CFLAGS := -Wall -Wextra -Werror -Wunused +LOCAL_SRC_FILES := \ + SoftGateKeeperDevice.cpp \ + IGateKeeperService.cpp \ + gatekeeperd.cpp \ + IUserManager.cpp + +LOCAL_MODULE := gatekeeperd +LOCAL_SHARED_LIBRARIES := \ + libbinder \ + libgatekeeper \ + liblog \ + libhardware \ + libbase \ + libutils \ + libcrypto \ + libkeystore_binder +LOCAL_STATIC_LIBRARIES := libscrypt_static +LOCAL_C_INCLUDES := external/scrypt/lib/crypto +include $(BUILD_EXECUTABLE) + +include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/gatekeeperd/IGateKeeperService.cpp b/gatekeeperd/IGateKeeperService.cpp new file mode 100644 index 0000000..95fbfd1 --- /dev/null +++ b/gatekeeperd/IGateKeeperService.cpp
@@ -0,0 +1,167 @@ +/* + * Copyright 2015, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#define LOG_TAG "GateKeeperService" +#include <utils/Log.h> + +#include "IGateKeeperService.h" + +namespace android { + +const android::String16 IGateKeeperService::descriptor("android.service.gatekeeper.IGateKeeperService"); +const android::String16& IGateKeeperService::getInterfaceDescriptor() const { + return IGateKeeperService::descriptor; +} + +status_t BnGateKeeperService::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { + switch(code) { + case ENROLL: { + CHECK_INTERFACE(IGateKeeperService, data, reply); + uint32_t uid = data.readInt32(); + + ssize_t currentPasswordHandleSize = data.readInt32(); + const uint8_t *currentPasswordHandle = + static_cast<const uint8_t *>(data.readInplace(currentPasswordHandleSize)); + if (!currentPasswordHandle) currentPasswordHandleSize = 0; + + ssize_t currentPasswordSize = data.readInt32(); + const uint8_t *currentPassword = + static_cast<const uint8_t *>(data.readInplace(currentPasswordSize)); + if (!currentPassword) currentPasswordSize = 0; + + ssize_t desiredPasswordSize = data.readInt32(); + const uint8_t *desiredPassword = + static_cast<const uint8_t *>(data.readInplace(desiredPasswordSize)); + if (!desiredPassword) desiredPasswordSize = 0; + + uint8_t *out = NULL; + uint32_t outSize = 0; + int ret = enroll(uid, currentPasswordHandle, currentPasswordHandleSize, + currentPassword, currentPasswordSize, desiredPassword, + desiredPasswordSize, &out, &outSize); + + reply->writeNoException(); + reply->writeInt32(1); + if (ret == 0 && outSize > 0 && out != NULL) { + reply->writeInt32(GATEKEEPER_RESPONSE_OK); + reply->writeInt32(0); + reply->writeInt32(outSize); + reply->writeInt32(outSize); + void *buf = reply->writeInplace(outSize); + memcpy(buf, out, outSize); + delete[] out; + } else if (ret > 0) { + reply->writeInt32(GATEKEEPER_RESPONSE_RETRY); + reply->writeInt32(ret); + } else { + reply->writeInt32(GATEKEEPER_RESPONSE_ERROR); + } + return NO_ERROR; + } + case VERIFY: { + CHECK_INTERFACE(IGateKeeperService, data, reply); + uint32_t uid = data.readInt32(); + ssize_t currentPasswordHandleSize = data.readInt32(); + const uint8_t *currentPasswordHandle = + static_cast<const uint8_t *>(data.readInplace(currentPasswordHandleSize)); + if (!currentPasswordHandle) currentPasswordHandleSize = 0; + + ssize_t currentPasswordSize = data.readInt32(); + const uint8_t *currentPassword = + static_cast<const uint8_t *>(data.readInplace(currentPasswordSize)); + if (!currentPassword) currentPasswordSize = 0; + + bool request_reenroll = false; + int ret = verify(uid, (uint8_t *) currentPasswordHandle, + currentPasswordHandleSize, (uint8_t *) currentPassword, currentPasswordSize, + &request_reenroll); + + reply->writeNoException(); + reply->writeInt32(1); + if (ret == 0) { + reply->writeInt32(GATEKEEPER_RESPONSE_OK); + reply->writeInt32(request_reenroll ? 1 : 0); + reply->writeInt32(0); // no payload returned from this call + } else if (ret > 0) { + reply->writeInt32(GATEKEEPER_RESPONSE_RETRY); + reply->writeInt32(ret); + } else { + reply->writeInt32(GATEKEEPER_RESPONSE_ERROR); + } + return NO_ERROR; + } + case VERIFY_CHALLENGE: { + CHECK_INTERFACE(IGateKeeperService, data, reply); + uint32_t uid = data.readInt32(); + uint64_t challenge = data.readInt64(); + ssize_t currentPasswordHandleSize = data.readInt32(); + const uint8_t *currentPasswordHandle = + static_cast<const uint8_t *>(data.readInplace(currentPasswordHandleSize)); + if (!currentPasswordHandle) currentPasswordHandleSize = 0; + + ssize_t currentPasswordSize = data.readInt32(); + const uint8_t *currentPassword = + static_cast<const uint8_t *>(data.readInplace(currentPasswordSize)); + if (!currentPassword) currentPasswordSize = 0; + + + uint8_t *out = NULL; + uint32_t outSize = 0; + bool request_reenroll = false; + int ret = verifyChallenge(uid, challenge, (uint8_t *) currentPasswordHandle, + currentPasswordHandleSize, (uint8_t *) currentPassword, currentPasswordSize, + &out, &outSize, &request_reenroll); + reply->writeNoException(); + reply->writeInt32(1); + if (ret == 0 && outSize > 0 && out != NULL) { + reply->writeInt32(GATEKEEPER_RESPONSE_OK); + reply->writeInt32(request_reenroll ? 1 : 0); + reply->writeInt32(outSize); + reply->writeInt32(outSize); + void *buf = reply->writeInplace(outSize); + memcpy(buf, out, outSize); + delete[] out; + } else if (ret > 0) { + reply->writeInt32(GATEKEEPER_RESPONSE_RETRY); + reply->writeInt32(ret); + } else { + reply->writeInt32(GATEKEEPER_RESPONSE_ERROR); + } + return NO_ERROR; + } + case GET_SECURE_USER_ID: { + CHECK_INTERFACE(IGateKeeperService, data, reply); + uint32_t uid = data.readInt32(); + uint64_t sid = getSecureUserId(uid); + reply->writeNoException(); + reply->writeInt64(sid); + return NO_ERROR; + } + case CLEAR_SECURE_USER_ID: { + CHECK_INTERFACE(IGateKeeperService, data, reply); + uint32_t uid = data.readInt32(); + clearSecureUserId(uid); + reply->writeNoException(); + return NO_ERROR; + } + default: + return BBinder::onTransact(code, data, reply, flags); + } +}; + + +}; // namespace android
diff --git a/gatekeeperd/IGateKeeperService.h b/gatekeeperd/IGateKeeperService.h new file mode 100644 index 0000000..f070486 --- /dev/null +++ b/gatekeeperd/IGateKeeperService.h
@@ -0,0 +1,111 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef IGATEKEEPER_SERVICE_H_ +#define IGATEKEEPER_SERVICE_H_ + +#include <binder/IInterface.h> +#include <binder/Parcel.h> + +namespace android { + +/* + * This must be kept manually in sync with frameworks/base's IGateKeeperService.aidl + */ +class IGateKeeperService : public IInterface { +public: + enum { + ENROLL = IBinder::FIRST_CALL_TRANSACTION + 0, + VERIFY = IBinder::FIRST_CALL_TRANSACTION + 1, + VERIFY_CHALLENGE = IBinder::FIRST_CALL_TRANSACTION + 2, + GET_SECURE_USER_ID = IBinder::FIRST_CALL_TRANSACTION + 3, + CLEAR_SECURE_USER_ID = IBinder::FIRST_CALL_TRANSACTION + 4, + }; + + enum { + GATEKEEPER_RESPONSE_OK = 0, + GATEKEEPER_RESPONSE_RETRY = 1, + GATEKEEPER_RESPONSE_ERROR = -1, + }; + + // DECLARE_META_INTERFACE - C++ client interface not needed + static const android::String16 descriptor; + virtual const android::String16& getInterfaceDescriptor() const; + IGateKeeperService() {} + virtual ~IGateKeeperService() {} + + /** + * Enrolls a password with the GateKeeper. Returns 0 on success, negative on failure. + * Returns: + * - 0 on success + * - A timestamp T > 0 if the call has failed due to throttling and should not + * be reattempted until T milliseconds have elapsed + * - -1 on failure + */ + virtual int enroll(uint32_t uid, + const uint8_t *current_password_handle, uint32_t current_password_handle_length, + const uint8_t *current_password, uint32_t current_password_length, + const uint8_t *desired_password, uint32_t desired_password_length, + uint8_t **enrolled_password_handle, uint32_t *enrolled_password_handle_length) = 0; + + /** + * Verifies a password previously enrolled with the GateKeeper. + * Returns: + * - 0 on success + * - A timestamp T > 0 if the call has failed due to throttling and should not + * be reattempted until T milliseconds have elapsed + * - -1 on failure + */ + virtual int verify(uint32_t uid, const uint8_t *enrolled_password_handle, + uint32_t enrolled_password_handle_length, + const uint8_t *provided_password, uint32_t provided_password_length, + bool *request_reenroll) = 0; + + /** + * Verifies a password previously enrolled with the GateKeeper. + * Returns: + * - 0 on success + * - A timestamp T > 0 if the call has failed due to throttling and should not + * be reattempted until T milliseconds have elapsed + * - -1 on failure + */ + virtual int verifyChallenge(uint32_t uid, uint64_t challenge, + const uint8_t *enrolled_password_handle, uint32_t enrolled_password_handle_length, + const uint8_t *provided_password, uint32_t provided_password_length, + uint8_t **auth_token, uint32_t *auth_token_length, bool *request_reenroll) = 0; + /** + * Returns the secure user ID for the provided android user + */ + virtual uint64_t getSecureUserId(uint32_t uid) = 0; + + /** + * Clears the secure user ID associated with the user. + */ + virtual void clearSecureUserId(uint32_t uid) = 0; +}; + +// ---------------------------------------------------------------------------- + +class BnGateKeeperService: public BnInterface<IGateKeeperService> { +public: + virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, + uint32_t flags = 0); +}; + +} // namespace android + +#endif +
diff --git a/gatekeeperd/IUserManager.cpp b/gatekeeperd/IUserManager.cpp new file mode 100644 index 0000000..8645fc2 --- /dev/null +++ b/gatekeeperd/IUserManager.cpp
@@ -0,0 +1,57 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "IUserManager" +#include <stdint.h> +#include <sys/types.h> +#include <utils/Log.h> +#include <binder/Parcel.h> + +#include "IUserManager.h" + +namespace android { + +class BpUserManager : public BpInterface<IUserManager> +{ +public: + BpUserManager(const sp<IBinder>& impl) : + BpInterface<IUserManager>(impl) { + } + virtual int32_t getCredentialOwnerProfile(int32_t user_id) { + Parcel data, reply; + data.writeInterfaceToken(IUserManager::getInterfaceDescriptor()); + data.writeInt32(user_id); + status_t rc = remote()->transact(GET_CREDENTIAL_OWNER_PROFILE, data, &reply, 0); + if (rc != NO_ERROR) { + ALOGE("%s: failed (%d)\n", __func__, rc); + return -1; + } + + int32_t exception = reply.readExceptionCode(); + if (exception != 0) { + ALOGE("%s: got exception (%d)\n", __func__, exception); + return -1; + } + + return reply.readInt32(); + } + +}; + +IMPLEMENT_META_INTERFACE(UserManager, "android.os.IUserManager"); + +}; // namespace android +
diff --git a/gatekeeperd/IUserManager.h b/gatekeeperd/IUserManager.h new file mode 100644 index 0000000..640e9b5 --- /dev/null +++ b/gatekeeperd/IUserManager.h
@@ -0,0 +1,46 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef IUSERMANAGER_H_ +#define IUSERMANAGER_H_ + +#include <inttypes.h> +#include <utils/Errors.h> +#include <binder/IInterface.h> +#include <binder/Parcel.h> +#include <utils/Vector.h> + +namespace android { + +/* +* Communication channel to UserManager +*/ +class IUserManager : public IInterface { + public: + // must be kept in sync with IUserManager.aidl + enum { + GET_CREDENTIAL_OWNER_PROFILE = IBinder::FIRST_CALL_TRANSACTION + 0, + }; + + virtual int32_t getCredentialOwnerProfile(int32_t user_id) = 0; + + DECLARE_META_INTERFACE(UserManager); +}; + +}; // namespace android + +#endif // IUSERMANAGER_H_ +
diff --git a/gatekeeperd/SoftGateKeeper.h b/gatekeeperd/SoftGateKeeper.h new file mode 100644 index 0000000..75fe11d --- /dev/null +++ b/gatekeeperd/SoftGateKeeper.h
@@ -0,0 +1,180 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef SOFT_GATEKEEPER_H_ +#define SOFT_GATEKEEPER_H_ + +extern "C" { +#include <openssl/rand.h> +#include <openssl/sha.h> + +#include <crypto_scrypt.h> +} + +#include <UniquePtr.h> +#include <gatekeeper/gatekeeper.h> +#include <iostream> +#include <unordered_map> + +namespace gatekeeper { + +struct fast_hash_t { + uint64_t salt; + uint8_t digest[SHA256_DIGEST_LENGTH]; +}; + +class SoftGateKeeper : public GateKeeper { +public: + static const uint32_t SIGNATURE_LENGTH_BYTES = 32; + + // scrypt params + static const uint64_t N = 16384; + static const uint32_t r = 8; + static const uint32_t p = 1; + + static const int MAX_UINT_32_CHARS = 11; + + SoftGateKeeper() { + key_.reset(new uint8_t[SIGNATURE_LENGTH_BYTES]); + memset(key_.get(), 0, SIGNATURE_LENGTH_BYTES); + } + + virtual ~SoftGateKeeper() { + } + + virtual bool GetAuthTokenKey(const uint8_t **auth_token_key, + uint32_t *length) const { + if (auth_token_key == NULL || length == NULL) return false; + uint8_t *auth_token_key_copy = new uint8_t[SIGNATURE_LENGTH_BYTES]; + memcpy(auth_token_key_copy, key_.get(), SIGNATURE_LENGTH_BYTES); + + *auth_token_key = auth_token_key_copy; + *length = SIGNATURE_LENGTH_BYTES; + return true; + } + + virtual void GetPasswordKey(const uint8_t **password_key, uint32_t *length) { + if (password_key == NULL || length == NULL) return; + uint8_t *password_key_copy = new uint8_t[SIGNATURE_LENGTH_BYTES]; + memcpy(password_key_copy, key_.get(), SIGNATURE_LENGTH_BYTES); + + *password_key = password_key_copy; + *length = SIGNATURE_LENGTH_BYTES; + } + + virtual void ComputePasswordSignature(uint8_t *signature, uint32_t signature_length, + const uint8_t *, uint32_t, const uint8_t *password, + uint32_t password_length, salt_t salt) const { + if (signature == NULL) return; + crypto_scrypt(password, password_length, reinterpret_cast<uint8_t *>(&salt), + sizeof(salt), N, r, p, signature, signature_length); + } + + virtual void GetRandom(void *random, uint32_t requested_length) const { + if (random == NULL) return; + RAND_pseudo_bytes((uint8_t *) random, requested_length); + } + + virtual void ComputeSignature(uint8_t *signature, uint32_t signature_length, + const uint8_t *, uint32_t, const uint8_t *, const uint32_t) const { + if (signature == NULL) return; + memset(signature, 0, signature_length); + } + + virtual uint64_t GetMillisecondsSinceBoot() const { + struct timespec time; + int res = clock_gettime(CLOCK_BOOTTIME, &time); + if (res < 0) return 0; + return (time.tv_sec * 1000) + (time.tv_nsec / 1000 / 1000); + } + + virtual bool IsHardwareBacked() const { + return false; + } + + virtual bool GetFailureRecord(uint32_t uid, secure_id_t user_id, failure_record_t *record, + bool /* secure */) { + failure_record_t *stored = &failure_map_[uid]; + if (user_id != stored->secure_user_id) { + stored->secure_user_id = user_id; + stored->last_checked_timestamp = 0; + stored->failure_counter = 0; + } + memcpy(record, stored, sizeof(*record)); + return true; + } + + virtual bool ClearFailureRecord(uint32_t uid, secure_id_t user_id, bool /* secure */) { + failure_record_t *stored = &failure_map_[uid]; + stored->secure_user_id = user_id; + stored->last_checked_timestamp = 0; + stored->failure_counter = 0; + return true; + } + + virtual bool WriteFailureRecord(uint32_t uid, failure_record_t *record, bool /* secure */) { + failure_map_[uid] = *record; + return true; + } + + fast_hash_t ComputeFastHash(const SizedBuffer &password, uint64_t salt) { + fast_hash_t fast_hash; + size_t digest_size = password.length + sizeof(salt); + std::unique_ptr<uint8_t[]> digest(new uint8_t[digest_size]); + memcpy(digest.get(), &salt, sizeof(salt)); + memcpy(digest.get() + sizeof(salt), password.buffer.get(), password.length); + + SHA256(digest.get(), digest_size, (uint8_t *) &fast_hash.digest); + + fast_hash.salt = salt; + return fast_hash; + } + + bool VerifyFast(const fast_hash_t &fast_hash, const SizedBuffer &password) { + fast_hash_t computed = ComputeFastHash(password, fast_hash.salt); + return memcmp(computed.digest, fast_hash.digest, SHA256_DIGEST_LENGTH) == 0; + } + + bool DoVerify(const password_handle_t *expected_handle, const SizedBuffer &password) { + FastHashMap::const_iterator it = fast_hash_map_.find(expected_handle->user_id); + if (it != fast_hash_map_.end() && VerifyFast(it->second, password)) { + return true; + } else { + if (GateKeeper::DoVerify(expected_handle, password)) { + uint64_t salt; + GetRandom(&salt, sizeof(salt)); + fast_hash_map_[expected_handle->user_id] = ComputeFastHash(password, salt); + return true; + } + } + + return false; + } + +private: + + typedef std::unordered_map<uint32_t, failure_record_t> FailureRecordMap; + typedef std::unordered_map<uint64_t, fast_hash_t> FastHashMap; + + UniquePtr<uint8_t[]> key_; + FailureRecordMap failure_map_; + FastHashMap fast_hash_map_; +}; +} + +#endif // SOFT_GATEKEEPER_H_ +
diff --git a/gatekeeperd/SoftGateKeeperDevice.cpp b/gatekeeperd/SoftGateKeeperDevice.cpp new file mode 100644 index 0000000..f5e2ce6 --- /dev/null +++ b/gatekeeperd/SoftGateKeeperDevice.cpp
@@ -0,0 +1,110 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "SoftGateKeeper.h" +#include "SoftGateKeeperDevice.h" + +namespace android { + +int SoftGateKeeperDevice::enroll(uint32_t uid, + const uint8_t *current_password_handle, uint32_t current_password_handle_length, + const uint8_t *current_password, uint32_t current_password_length, + const uint8_t *desired_password, uint32_t desired_password_length, + uint8_t **enrolled_password_handle, uint32_t *enrolled_password_handle_length) { + + if (enrolled_password_handle == NULL || enrolled_password_handle_length == NULL || + desired_password == NULL || desired_password_length == 0) + return -EINVAL; + + // Current password and current password handle go together + if (current_password_handle == NULL || current_password_handle_length == 0 || + current_password == NULL || current_password_length == 0) { + current_password_handle = NULL; + current_password_handle_length = 0; + current_password = NULL; + current_password_length = 0; + } + + SizedBuffer desired_password_buffer(desired_password_length); + memcpy(desired_password_buffer.buffer.get(), desired_password, desired_password_length); + + SizedBuffer current_password_handle_buffer(current_password_handle_length); + if (current_password_handle) { + memcpy(current_password_handle_buffer.buffer.get(), current_password_handle, + current_password_handle_length); + } + + SizedBuffer current_password_buffer(current_password_length); + if (current_password) { + memcpy(current_password_buffer.buffer.get(), current_password, current_password_length); + } + + EnrollRequest request(uid, ¤t_password_handle_buffer, &desired_password_buffer, + ¤t_password_buffer); + EnrollResponse response; + + impl_->Enroll(request, &response); + + if (response.error == ERROR_RETRY) { + return response.retry_timeout; + } else if (response.error != ERROR_NONE) { + return -EINVAL; + } + + *enrolled_password_handle = response.enrolled_password_handle.buffer.release(); + *enrolled_password_handle_length = response.enrolled_password_handle.length; + return 0; +} + +int SoftGateKeeperDevice::verify(uint32_t uid, + uint64_t challenge, const uint8_t *enrolled_password_handle, + uint32_t enrolled_password_handle_length, const uint8_t *provided_password, + uint32_t provided_password_length, uint8_t **auth_token, uint32_t *auth_token_length, + bool *request_reenroll) { + + if (enrolled_password_handle == NULL || + provided_password == NULL) { + return -EINVAL; + } + + SizedBuffer password_handle_buffer(enrolled_password_handle_length); + memcpy(password_handle_buffer.buffer.get(), enrolled_password_handle, + enrolled_password_handle_length); + SizedBuffer provided_password_buffer(provided_password_length); + memcpy(provided_password_buffer.buffer.get(), provided_password, provided_password_length); + + VerifyRequest request(uid, challenge, &password_handle_buffer, &provided_password_buffer); + VerifyResponse response; + + impl_->Verify(request, &response); + + if (response.error == ERROR_RETRY) { + return response.retry_timeout; + } else if (response.error != ERROR_NONE) { + return -EINVAL; + } + + if (auth_token != NULL && auth_token_length != NULL) { + *auth_token = response.auth_token.buffer.release(); + *auth_token_length = response.auth_token.length; + } + + if (request_reenroll != NULL) { + *request_reenroll = response.request_reenroll; + } + + return 0; +} +} // namespace android
diff --git a/gatekeeperd/SoftGateKeeperDevice.h b/gatekeeperd/SoftGateKeeperDevice.h new file mode 100644 index 0000000..3463c29 --- /dev/null +++ b/gatekeeperd/SoftGateKeeperDevice.h
@@ -0,0 +1,76 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SOFT_GATEKEEPER_DEVICE_H_ +#define SOFT_GATEKEEPER_DEVICE_H_ + +#include "SoftGateKeeper.h" + +#include <UniquePtr.h> + +using namespace gatekeeper; + +namespace android { + +/** + * Software based GateKeeper implementation + */ +class SoftGateKeeperDevice { +public: + SoftGateKeeperDevice() { + impl_.reset(new SoftGateKeeper()); + } + + // Wrappers to translate the gatekeeper HAL API to the Kegyuard Messages API. + + /** + * Enrolls password_payload, which should be derived from a user selected pin or password, + * with the authentication factor private key used only for enrolling authentication + * factor data. + * + * Returns: 0 on success or an error code less than 0 on error. + * On error, enrolled_password_handle will not be allocated. + */ + int enroll(uint32_t uid, + const uint8_t *current_password_handle, uint32_t current_password_handle_length, + const uint8_t *current_password, uint32_t current_password_length, + const uint8_t *desired_password, uint32_t desired_password_length, + uint8_t **enrolled_password_handle, uint32_t *enrolled_password_handle_length); + + /** + * Verifies provided_password matches enrolled_password_handle. + * + * Implementations of this module may retain the result of this call + * to attest to the recency of authentication. + * + * On success, writes the address of a verification token to auth_token, + * usable to attest password verification to other trusted services. Clients + * may pass NULL for this value. + * + * Returns: 0 on success or an error code less than 0 on error + * On error, verification token will not be allocated + */ + int verify(uint32_t uid, uint64_t challenge, + const uint8_t *enrolled_password_handle, uint32_t enrolled_password_handle_length, + const uint8_t *provided_password, uint32_t provided_password_length, + uint8_t **auth_token, uint32_t *auth_token_length, bool *request_reenroll); +private: + UniquePtr<SoftGateKeeper> impl_; +}; + +} // namespace gatekeeper + +#endif //SOFT_GATEKEEPER_DEVICE_H_
diff --git a/gatekeeperd/gatekeeperd.cpp b/gatekeeperd/gatekeeperd.cpp new file mode 100644 index 0000000..b4fdab0 --- /dev/null +++ b/gatekeeperd/gatekeeperd.cpp
@@ -0,0 +1,351 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "gatekeeperd" + +#include "IGateKeeperService.h" + +#include <errno.h> +#include <stdint.h> +#include <inttypes.h> +#include <fcntl.h> +#include <unistd.h> + +#include <cutils/log.h> +#include <utils/Log.h> + +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> +#include <binder/PermissionCache.h> +#include <utils/String16.h> +#include <utils/Log.h> + +#include <keystore/IKeystoreService.h> +#include <keystore/keystore.h> // For error code +#include <gatekeeper/password_handle.h> // for password_handle_t +#include <hardware/gatekeeper.h> +#include <hardware/hw_auth_token.h> + +#include "SoftGateKeeperDevice.h" +#include "IUserManager.h" + +namespace android { + +static const String16 KEYGUARD_PERMISSION("android.permission.ACCESS_KEYGUARD_SECURE_STORAGE"); +static const String16 DUMP_PERMISSION("android.permission.DUMP"); + +class GateKeeperProxy : public BnGateKeeperService { +public: + GateKeeperProxy() { + int ret = hw_get_module_by_class(GATEKEEPER_HARDWARE_MODULE_ID, NULL, &module); + device = NULL; + + if (ret < 0) { + ALOGW("falling back to software GateKeeper"); + soft_device.reset(new SoftGateKeeperDevice()); + } else { + ret = gatekeeper_open(module, &device); + if (ret < 0) + LOG_ALWAYS_FATAL_IF(ret < 0, "Unable to open GateKeeper HAL"); + } + + if (mark_cold_boot()) { + ALOGI("cold boot: clearing state"); + if (device != NULL && device->delete_all_users != NULL) { + device->delete_all_users(device); + } + } + } + + virtual ~GateKeeperProxy() { + if (device) gatekeeper_close(device); + } + + void store_sid(uint32_t uid, uint64_t sid) { + char filename[21]; + sprintf(filename, "%u", uid); + int fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR); + if (fd < 0) { + ALOGE("could not open file: %s: %s", filename, strerror(errno)); + return; + } + write(fd, &sid, sizeof(sid)); + close(fd); + } + + bool mark_cold_boot() { + const char *filename = ".coldboot"; + if (access(filename, F_OK) == -1) { + int fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR); + if (fd < 0) { + ALOGE("could not open file: %s : %s", filename, strerror(errno)); + return false; + } + close(fd); + return true; + } + return false; + } + + void maybe_store_sid(uint32_t uid, uint64_t sid) { + char filename[21]; + sprintf(filename, "%u", uid); + if (access(filename, F_OK) == -1) { + store_sid(uid, sid); + } + } + + uint64_t read_sid(uint32_t uid) { + char filename[21]; + uint64_t sid; + sprintf(filename, "%u", uid); + int fd = open(filename, O_RDONLY); + if (fd < 0) return 0; + read(fd, &sid, sizeof(sid)); + close(fd); + return sid; + } + + void clear_sid(uint32_t uid) { + char filename[21]; + sprintf(filename, "%u", uid); + if (remove(filename) < 0) { + ALOGE("%s: could not remove file [%s], attempting 0 write", __func__, strerror(errno)); + store_sid(uid, 0); + } + } + + virtual int enroll(uint32_t uid, + const uint8_t *current_password_handle, uint32_t current_password_handle_length, + const uint8_t *current_password, uint32_t current_password_length, + const uint8_t *desired_password, uint32_t desired_password_length, + uint8_t **enrolled_password_handle, uint32_t *enrolled_password_handle_length) { + IPCThreadState* ipc = IPCThreadState::self(); + const int calling_pid = ipc->getCallingPid(); + const int calling_uid = ipc->getCallingUid(); + if (!PermissionCache::checkPermission(KEYGUARD_PERMISSION, calling_pid, calling_uid)) { + return PERMISSION_DENIED; + } + + // need a desired password to enroll + if (desired_password_length == 0) return -EINVAL; + + int ret; + if (device) { + const gatekeeper::password_handle_t *handle = + reinterpret_cast<const gatekeeper::password_handle_t *>(current_password_handle); + + if (handle != NULL && handle->version != 0 && !handle->hardware_backed) { + // handle is being re-enrolled from a software version. HAL probably won't accept + // the handle as valid, so we nullify it and enroll from scratch + current_password_handle = NULL; + current_password_handle_length = 0; + current_password = NULL; + current_password_length = 0; + } + + ret = device->enroll(device, uid, current_password_handle, current_password_handle_length, + current_password, current_password_length, + desired_password, desired_password_length, + enrolled_password_handle, enrolled_password_handle_length); + } else { + ret = soft_device->enroll(uid, + current_password_handle, current_password_handle_length, + current_password, current_password_length, + desired_password, desired_password_length, + enrolled_password_handle, enrolled_password_handle_length); + } + + if (ret == 0) { + gatekeeper::password_handle_t *handle = + reinterpret_cast<gatekeeper::password_handle_t *>(*enrolled_password_handle); + store_sid(uid, handle->user_id); + bool rr; + + // immediately verify this password so we don't ask the user to enter it again + // if they just created it. + verify(uid, *enrolled_password_handle, sizeof(password_handle_t), desired_password, + desired_password_length, &rr); + } + + return ret; + } + + virtual int verify(uint32_t uid, + const uint8_t *enrolled_password_handle, uint32_t enrolled_password_handle_length, + const uint8_t *provided_password, uint32_t provided_password_length, bool *request_reenroll) { + uint8_t *auth_token; + uint32_t auth_token_length; + return verifyChallenge(uid, 0, enrolled_password_handle, enrolled_password_handle_length, + provided_password, provided_password_length, + &auth_token, &auth_token_length, request_reenroll); + } + + virtual int verifyChallenge(uint32_t uid, uint64_t challenge, + const uint8_t *enrolled_password_handle, uint32_t enrolled_password_handle_length, + const uint8_t *provided_password, uint32_t provided_password_length, + uint8_t **auth_token, uint32_t *auth_token_length, bool *request_reenroll) { + IPCThreadState* ipc = IPCThreadState::self(); + const int calling_pid = ipc->getCallingPid(); + const int calling_uid = ipc->getCallingUid(); + if (!PermissionCache::checkPermission(KEYGUARD_PERMISSION, calling_pid, calling_uid)) { + return PERMISSION_DENIED; + } + + // can't verify if we're missing either param + if ((enrolled_password_handle_length | provided_password_length) == 0) + return -EINVAL; + + int ret; + if (device) { + const gatekeeper::password_handle_t *handle = + reinterpret_cast<const gatekeeper::password_handle_t *>(enrolled_password_handle); + // handle version 0 does not have hardware backed flag, and thus cannot be upgraded to + // a HAL if there was none before + if (handle->version == 0 || handle->hardware_backed) { + ret = device->verify(device, uid, challenge, + enrolled_password_handle, enrolled_password_handle_length, + provided_password, provided_password_length, auth_token, auth_token_length, + request_reenroll); + } else { + // upgrade scenario, a HAL has been added to this device where there was none before + SoftGateKeeperDevice soft_dev; + ret = soft_dev.verify(uid, challenge, + enrolled_password_handle, enrolled_password_handle_length, + provided_password, provided_password_length, auth_token, auth_token_length, + request_reenroll); + + if (ret == 0) { + // success! re-enroll with HAL + *request_reenroll = true; + } + } + } else { + ret = soft_device->verify(uid, challenge, + enrolled_password_handle, enrolled_password_handle_length, + provided_password, provided_password_length, auth_token, auth_token_length, + request_reenroll); + } + + if (ret == 0 && *auth_token != NULL && *auth_token_length > 0) { + // TODO: cache service? + sp<IServiceManager> sm = defaultServiceManager(); + sp<IBinder> binder = sm->getService(String16("android.security.keystore")); + sp<IKeystoreService> service = interface_cast<IKeystoreService>(binder); + if (service != NULL) { + status_t ret = service->addAuthToken(*auth_token, *auth_token_length); + if (ret != ResponseCode::NO_ERROR) { + ALOGE("Falure sending auth token to KeyStore: %d", ret); + } + } else { + ALOGE("Unable to communicate with KeyStore"); + } + } + + if (ret == 0) { + maybe_store_sid(uid, reinterpret_cast<const gatekeeper::password_handle_t *>( + enrolled_password_handle)->user_id); + } + + return ret; + } + + virtual uint64_t getSecureUserId(uint32_t uid) { + uint64_t sid = read_sid(uid); + if (sid == 0) { + // might be a work profile, look up the parent + sp<IServiceManager> sm = defaultServiceManager(); + sp<IBinder> binder = sm->getService(String16("user")); + sp<IUserManager> um = interface_cast<IUserManager>(binder); + int32_t parent = um->getCredentialOwnerProfile(uid); + if (parent < 0) { + return 0; + } else if (parent != (int32_t) uid) { + return read_sid(parent); + } + } + return sid; + + } + + virtual void clearSecureUserId(uint32_t uid) { + IPCThreadState* ipc = IPCThreadState::self(); + const int calling_pid = ipc->getCallingPid(); + const int calling_uid = ipc->getCallingUid(); + if (!PermissionCache::checkPermission(KEYGUARD_PERMISSION, calling_pid, calling_uid)) { + ALOGE("%s: permission denied for [%d:%d]", __func__, calling_pid, calling_uid); + return; + } + clear_sid(uid); + + if (device != NULL && device->delete_user != NULL) { + device->delete_user(device, uid); + } + } + + virtual status_t dump(int fd, const Vector<String16> &) { + IPCThreadState* ipc = IPCThreadState::self(); + const int pid = ipc->getCallingPid(); + const int uid = ipc->getCallingUid(); + if (!PermissionCache::checkPermission(DUMP_PERMISSION, pid, uid)) { + return PERMISSION_DENIED; + } + + if (device == NULL) { + const char *result = "Device not available"; + write(fd, result, strlen(result) + 1); + } else { + const char *result = "OK"; + write(fd, result, strlen(result) + 1); + } + + return NO_ERROR; + } + +private: + gatekeeper_device_t *device; + UniquePtr<SoftGateKeeperDevice> soft_device; + const hw_module_t *module; +}; +}// namespace android + +int main(int argc, char* argv[]) { + ALOGI("Starting gatekeeperd..."); + if (argc < 2) { + ALOGE("A directory must be specified!"); + return 1; + } + if (chdir(argv[1]) == -1) { + ALOGE("chdir: %s: %s", argv[1], strerror(errno)); + return 1; + } + + android::sp<android::IServiceManager> sm = android::defaultServiceManager(); + android::sp<android::GateKeeperProxy> proxy = new android::GateKeeperProxy(); + android::status_t ret = sm->addService( + android::String16("android.service.gatekeeper.IGateKeeperService"), proxy); + if (ret != android::OK) { + ALOGE("Couldn't register binder service!"); + return -1; + } + + /* + * We're the only thread in existence, so we're just going to process + * Binder transaction as a single-threaded program. + */ + android::IPCThreadState::self()->joinThreadPool(); + return 0; +}
diff --git a/gatekeeperd/tests/Android.mk b/gatekeeperd/tests/Android.mk new file mode 100644 index 0000000..6fc4ac0 --- /dev/null +++ b/gatekeeperd/tests/Android.mk
@@ -0,0 +1,29 @@ +# +# Copyright (C) 2015 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := gatekeeperd-unit-tests +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk +LOCAL_CFLAGS += -g -Wall -Werror -std=gnu++11 -Wno-missing-field-initializers +LOCAL_SHARED_LIBRARIES := libgatekeeper libcrypto +LOCAL_STATIC_LIBRARIES := libscrypt_static +LOCAL_C_INCLUDES := external/scrypt/lib/crypto +LOCAL_SRC_FILES := \ + gatekeeper_test.cpp +include $(BUILD_NATIVE_TEST) +
diff --git a/gatekeeperd/tests/gatekeeper_test.cpp b/gatekeeperd/tests/gatekeeper_test.cpp new file mode 100644 index 0000000..c504f92 --- /dev/null +++ b/gatekeeperd/tests/gatekeeper_test.cpp
@@ -0,0 +1,206 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <arpa/inet.h> +#include <iostream> + +#include <gtest/gtest.h> +#include <UniquePtr.h> + +#include <hardware/hw_auth_token.h> + +#include "../SoftGateKeeper.h" + +using ::gatekeeper::SizedBuffer; +using ::testing::Test; +using ::gatekeeper::EnrollRequest; +using ::gatekeeper::EnrollResponse; +using ::gatekeeper::VerifyRequest; +using ::gatekeeper::VerifyResponse; +using ::gatekeeper::SoftGateKeeper; +using ::gatekeeper::secure_id_t; + +static void do_enroll(SoftGateKeeper &gatekeeper, EnrollResponse *response) { + SizedBuffer password; + + password.buffer.reset(new uint8_t[16]); + password.length = 16; + memset(password.buffer.get(), 0, 16); + EnrollRequest request(0, NULL, &password, NULL); + + gatekeeper.Enroll(request, response); +} + +TEST(GateKeeperTest, EnrollSuccess) { + SoftGateKeeper gatekeeper; + EnrollResponse response; + do_enroll(gatekeeper, &response); + ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, response.error); +} + +TEST(GateKeeperTest, EnrollBogusData) { + SoftGateKeeper gatekeeper; + SizedBuffer password; + EnrollResponse response; + + EnrollRequest request(0, NULL, &password, NULL); + + gatekeeper.Enroll(request, &response); + + ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_INVALID, response.error); +} + +TEST(GateKeeperTest, VerifySuccess) { + SoftGateKeeper gatekeeper; + SizedBuffer provided_password; + EnrollResponse enroll_response; + + provided_password.buffer.reset(new uint8_t[16]); + provided_password.length = 16; + memset(provided_password.buffer.get(), 0, 16); + + do_enroll(gatekeeper, &enroll_response); + ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, enroll_response.error); + VerifyRequest request(0, 1, &enroll_response.enrolled_password_handle, + &provided_password); + VerifyResponse response; + + gatekeeper.Verify(request, &response); + + ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, response.error); + + hw_auth_token_t *auth_token = + reinterpret_cast<hw_auth_token_t *>(response.auth_token.buffer.get()); + + ASSERT_EQ((uint32_t) HW_AUTH_PASSWORD, ntohl(auth_token->authenticator_type)); + ASSERT_EQ((uint64_t) 1, auth_token->challenge); + ASSERT_NE(~((uint32_t) 0), auth_token->timestamp); + ASSERT_NE((uint64_t) 0, auth_token->user_id); + ASSERT_NE((uint64_t) 0, auth_token->authenticator_id); +} + +TEST(GateKeeperTest, TrustedReEnroll) { + SoftGateKeeper gatekeeper; + SizedBuffer provided_password; + EnrollResponse enroll_response; + SizedBuffer password_handle; + + // do_enroll enrolls an all 0 password + provided_password.buffer.reset(new uint8_t[16]); + provided_password.length = 16; + memset(provided_password.buffer.get(), 0, 16); + do_enroll(gatekeeper, &enroll_response); + ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, enroll_response.error); + + // keep a copy of the handle + password_handle.buffer.reset(new uint8_t[enroll_response.enrolled_password_handle.length]); + password_handle.length = enroll_response.enrolled_password_handle.length; + memcpy(password_handle.buffer.get(), enroll_response.enrolled_password_handle.buffer.get(), + password_handle.length); + + // verify first password + VerifyRequest request(0, 0, &enroll_response.enrolled_password_handle, + &provided_password); + VerifyResponse response; + gatekeeper.Verify(request, &response); + ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, response.error); + hw_auth_token_t *auth_token = + reinterpret_cast<hw_auth_token_t *>(response.auth_token.buffer.get()); + + secure_id_t secure_id = auth_token->user_id; + + // enroll new password + provided_password.buffer.reset(new uint8_t[16]); + provided_password.length = 16; + memset(provided_password.buffer.get(), 0, 16); + SizedBuffer password; + password.buffer.reset(new uint8_t[16]); + memset(password.buffer.get(), 1, 16); + password.length = 16; + EnrollRequest enroll_request(0, &password_handle, &password, &provided_password); + gatekeeper.Enroll(enroll_request, &enroll_response); + ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, enroll_response.error); + + // verify new password + password.buffer.reset(new uint8_t[16]); + memset(password.buffer.get(), 1, 16); + password.length = 16; + VerifyRequest new_request(0, 0, &enroll_response.enrolled_password_handle, + &password); + gatekeeper.Verify(new_request, &response); + ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, response.error); + ASSERT_EQ(secure_id, + reinterpret_cast<hw_auth_token_t *>(response.auth_token.buffer.get())->user_id); +} + + +TEST(GateKeeperTest, UntrustedReEnroll) { + SoftGateKeeper gatekeeper; + SizedBuffer provided_password; + EnrollResponse enroll_response; + + // do_enroll enrolls an all 0 password + provided_password.buffer.reset(new uint8_t[16]); + provided_password.length = 16; + memset(provided_password.buffer.get(), 0, 16); + do_enroll(gatekeeper, &enroll_response); + ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, enroll_response.error); + + // verify first password + VerifyRequest request(0, 0, &enroll_response.enrolled_password_handle, + &provided_password); + VerifyResponse response; + gatekeeper.Verify(request, &response); + ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, response.error); + hw_auth_token_t *auth_token = + reinterpret_cast<hw_auth_token_t *>(response.auth_token.buffer.get()); + + secure_id_t secure_id = auth_token->user_id; + + // enroll new password + SizedBuffer password; + password.buffer.reset(new uint8_t[16]); + memset(password.buffer.get(), 1, 16); + password.length = 16; + EnrollRequest enroll_request(0, NULL, &password, NULL); + gatekeeper.Enroll(enroll_request, &enroll_response); + ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, enroll_response.error); + + // verify new password + password.buffer.reset(new uint8_t[16]); + memset(password.buffer.get(), 1, 16); + password.length = 16; + VerifyRequest new_request(0, 0, &enroll_response.enrolled_password_handle, + &password); + gatekeeper.Verify(new_request, &response); + ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, response.error); + ASSERT_NE(secure_id, + reinterpret_cast<hw_auth_token_t *>(response.auth_token.buffer.get())->user_id); +} + + +TEST(GateKeeperTest, VerifyBogusData) { + SoftGateKeeper gatekeeper; + SizedBuffer provided_password; + SizedBuffer password_handle; + VerifyResponse response; + + VerifyRequest request(0, 0, &provided_password, &password_handle); + + gatekeeper.Verify(request, &response); + + ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_INVALID, response.error); +}
diff --git a/healthd/healthd.cpp b/healthd/healthd.cpp index 1fee855..b0002cc 100644 --- a/healthd/healthd.cpp +++ b/healthd/healthd.cpp
@@ -53,6 +53,7 @@ .batteryCurrentAvgPath = String8(String8::kEmptyString), .batteryChargeCounterPath = String8(String8::kEmptyString), .energyCounter = NULL, + .boot_min_cap = 0, .screen_on = NULL, };
diff --git a/healthd/healthd.h b/healthd/healthd.h index 4704f0b..84b6d76 100644 --- a/healthd/healthd.h +++ b/healthd/healthd.h
@@ -67,6 +67,7 @@ android::String8 batteryChargeCounterPath; int (*energyCounter)(int64_t *); + int boot_min_cap; bool (*screen_on)(android::BatteryProperties *props); };
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp index 78f8403..6800ad2 100644 --- a/healthd/healthd_mode_charger.cpp +++ b/healthd/healthd_mode_charger.cpp
@@ -116,6 +116,7 @@ struct animation *batt_anim; GRSurface* surf_unknown; + int boot_min_cap; }; static struct frame batt_anim_frames[] = { @@ -520,19 +521,29 @@ LOGW("[%" PRId64 "] booting from charger mode\n", now); property_set("sys.boot_from_charger_mode", "1"); } else { - LOGW("[%" PRId64 "] rebooting\n", now); - android_reboot(ANDROID_RB_RESTART, 0, 0); + if (charger->batt_anim->capacity >= charger->boot_min_cap) { + LOGW("[%" PRId64 "] rebooting\n", now); + android_reboot(ANDROID_RB_RESTART, 0, 0); + } else { + LOGV("[%" PRId64 "] ignore power-button press, battery level " + "less than minimum\n", now); + } } } else { /* if the key is pressed but timeout hasn't expired, * make sure we wake up at the right-ish time to check */ set_next_key_check(charger, key, POWER_ON_KEY_TIME); + + /* Turn on the display and kick animation on power-key press + * rather than on key release + */ + kick_animation(charger->batt_anim); + request_suspend(false); } } else { /* if the power key got released, force screen state cycle */ if (key->pending) { - request_suspend(false); kick_animation(charger->batt_anim); } } @@ -555,6 +566,11 @@ return; if (!charger->charger_connected) { + + /* Last cycle would have stopped at the extreme top of battery-icon + * Need to show the correct level corresponding to capacity. + */ + kick_animation(charger->batt_anim); request_suspend(false); if (charger->next_pwr_check == -1) { charger->next_pwr_check = now + UNPLUGGED_SHUTDOWN_TIME; @@ -705,4 +721,5 @@ charger->next_key_check = -1; charger->next_pwr_check = -1; healthd_config = config; + charger->boot_min_cap = config->boot_min_cap; }
diff --git a/include/backtrace/BacktraceMap.h b/include/backtrace/BacktraceMap.h index da96307..bb18aa2 100644 --- a/include/backtrace/BacktraceMap.h +++ b/include/backtrace/BacktraceMap.h
@@ -33,11 +33,11 @@ #include <string> struct backtrace_map_t { - backtrace_map_t(): start(0), end(0), flags(0) {} - - uintptr_t start; - uintptr_t end; - int flags; + uintptr_t start = 0; + uintptr_t end = 0; + uintptr_t offset = 0; + uintptr_t load_base = 0; + int flags = 0; std::string name; }; @@ -82,6 +82,14 @@ return map.end > 0; } + static uintptr_t GetRelativePc(const backtrace_map_t& map, uintptr_t pc) { + if (IsValid(map)) { + return pc - map.start + map.load_base; + } else { + return pc; + } + } + protected: BacktraceMap(pid_t pid);
diff --git a/include/cutils/sched_policy.h b/include/cutils/sched_policy.h index ba84ce3..6a8d570 100644 --- a/include/cutils/sched_policy.h +++ b/include/cutils/sched_policy.h
@@ -34,6 +34,8 @@ SP_SYSTEM_DEFAULT = SP_FOREGROUND, } SchedPolicy; +extern int set_cpuset_policy(int tid, SchedPolicy policy); + /* Assign thread tid to the cgroup associated with the specified policy. * If the thread is a thread group leader, that is it's gettid() == getpid(), * then the other threads in the same thread group are _not_ affected.
diff --git a/include/cutils/threads.h b/include/cutils/threads.h index 3133cdb..5727494 100644 --- a/include/cutils/threads.h +++ b/include/cutils/threads.h
@@ -17,6 +17,14 @@ #ifndef _LIBS_CUTILS_THREADS_H #define _LIBS_CUTILS_THREADS_H +#include <sys/types.h> + +#if !defined(_WIN32) +#include <pthread.h> +#else +#include <windows.h> +#endif + #ifdef __cplusplus extern "C" { #endif @@ -29,10 +37,9 @@ /***********************************************************************/ /***********************************************************************/ -#if !defined(_WIN32) +extern pid_t gettid(); -#include <pthread.h> -#include <sys/types.h> +#if !defined(_WIN32) typedef struct { pthread_mutex_t lock; @@ -40,14 +47,10 @@ pthread_key_t tls; } thread_store_t; -extern pid_t gettid(); - #define THREAD_STORE_INITIALIZER { PTHREAD_MUTEX_INITIALIZER, 0, 0 } #else // !defined(_WIN32) -#include <windows.h> - typedef struct { int lock_init; int has_tls;
diff --git a/include/log/log.h b/include/log/log.h index ce253e2..0b17574 100644 --- a/include/log/log.h +++ b/include/log/log.h
@@ -492,6 +492,7 @@ EVENT_TYPE_LONG = 1, EVENT_TYPE_STRING = 2, EVENT_TYPE_LIST = 3, + EVENT_TYPE_FLOAT = 4, } AndroidEventLogType; #define sizeof_AndroidEventLogType sizeof(typeof_AndroidEventLogType) #define typeof_AndroidEventLogType unsigned char @@ -510,6 +511,13 @@ sizeof(longBuf)); \ } #endif +#ifndef LOG_EVENT_FLOAT +#define LOG_EVENT_FLOAT(_tag, _value) { \ + float floatBuf = _value; \ + (void) android_btWriteLog(_tag, EVENT_TYPE_FLOAT, &floatBuf, \ + sizeof(floatBuf)); \ + } +#endif #ifndef LOG_EVENT_STRING #define LOG_EVENT_STRING(_tag, _value) \ (void) __android_log_bswrite(_tag, _value); @@ -590,6 +598,7 @@ LOG_ID_EVENTS = 2, LOG_ID_SYSTEM = 3, LOG_ID_CRASH = 4, + LOG_ID_KERNEL = 5, #endif LOG_ID_MAX
diff --git a/include/log/logprint.h b/include/log/logprint.h index 1e42b47..4b812cc 100644 --- a/include/log/logprint.h +++ b/include/log/logprint.h
@@ -36,7 +36,10 @@ FORMAT_TIME, FORMAT_THREADTIME, FORMAT_LONG, - FORMAT_COLOR, + /* The following three are modifiers to above formats */ + FORMAT_MODIFIER_COLOR, /* converts priority to color */ + FORMAT_MODIFIER_TIME_USEC, /* switches from msec to usec time precision */ + FORMAT_MODIFIER_PRINTABLE, /* converts non-printable to printable escapes */ } AndroidLogPrintFormat; typedef struct AndroidLogFormat_t AndroidLogFormat; @@ -56,7 +59,8 @@ void android_log_format_free(AndroidLogFormat *p_format); -void android_log_setPrintFormat(AndroidLogFormat *p_format, +/* currently returns 0 if format is a modifier, 1 if not */ +int android_log_setPrintFormat(AndroidLogFormat *p_format, AndroidLogPrintFormat format); /** @@ -64,7 +68,7 @@ */ AndroidLogPrintFormat android_log_formatFromString(const char *s); -/** +/** * filterExpression: a single filter expression * eg "AT:d" * @@ -74,12 +78,12 @@ * */ -int android_log_addFilterRule(AndroidLogFormat *p_format, +int android_log_addFilterRule(AndroidLogFormat *p_format, const char *filterExpression); -/** - * filterString: a whitespace-separated set of filter expressions +/** + * filterString: a whitespace-separated set of filter expressions * eg "AT:d *:i" * * returns 0 on success and -1 on invalid expression @@ -92,7 +96,7 @@ const char *filterString); -/** +/** * returns 1 if this log line should be printed based on its priority * and tag, and 0 if it should not */ @@ -129,7 +133,7 @@ * Returns NULL on malloc error */ -char *android_log_formatLogLine ( +char *android_log_formatLogLine ( AndroidLogFormat *p_format, char *defaultBuffer, size_t defaultBufferSize,
diff --git a/include/nativebridge/native_bridge.h b/include/nativebridge/native_bridge.h index 523dc49..18300bc 100644 --- a/include/nativebridge/native_bridge.h +++ b/include/nativebridge/native_bridge.h
@@ -18,6 +18,7 @@ #define NATIVE_BRIDGE_H_ #include "jni.h" +#include <signal.h> #include <stdint.h> #include <sys/types.h> @@ -26,6 +27,12 @@ struct NativeBridgeRuntimeCallbacks; struct NativeBridgeRuntimeValues; +// Function pointer type for sigaction. This is mostly the signature of a signal handler, except +// for the return type. The runtime needs to know whether the signal was handled or should be given +// to the chain. +typedef bool (*NativeBridgeSignalHandlerFn)(int, siginfo_t*, void*); + + // Open the native bridge, if any. Should be called by Runtime::Init(). A null library filename // signals that we do not want to load a native bridge. bool LoadNativeBridge(const char* native_bridge_library_filename, @@ -63,6 +70,16 @@ // True if native library is valid and is for an ABI that is supported by native bridge. bool NativeBridgeIsSupported(const char* libpath); +// Returns the version number of the native bridge. This information is available after a +// successful LoadNativeBridge() and before closing it, that is, as long as NativeBridgeAvailable() +// returns true. Returns 0 otherwise. +uint32_t NativeBridgeGetVersion(); + +// Returns a signal handler that the bridge would like to be managed. Only valid for a native +// bridge supporting the version 2 interface. Will return null if the bridge does not support +// version 2, or if it doesn't have a signal handler it wants to be known. +NativeBridgeSignalHandlerFn NativeBridgeGetSignalHandler(int signal); + // Returns whether we have seen a native bridge error. This could happen because the library // was not found, rejected, could not be initialized and so on. // @@ -127,6 +144,31 @@ // NULL if not supported by native bridge. // Otherwise, return all environment values to be set after fork. const struct NativeBridgeRuntimeValues* (*getAppEnv)(const char* instruction_set); + + // Added callbacks in version 2. + + // Check whether the bridge is compatible with the given version. A bridge may decide not to be + // forwards- or backwards-compatible, and libnativebridge will then stop using it. + // + // Parameters: + // bridge_version [IN] the version of libnativebridge. + // Returns: + // true iff the native bridge supports the given version of libnativebridge. + bool (*isCompatibleWith)(uint32_t bridge_version); + + // A callback to retrieve a native bridge's signal handler for the specified signal. The runtime + // will ensure that the signal handler is being called after the runtime's own handler, but before + // all chained handlers. The native bridge should not try to install the handler by itself, as + // that will potentially lead to cycles. + // + // Parameters: + // signal [IN] the signal for which the handler is asked for. Currently, only SIGSEGV is + // supported by the runtime. + // Returns: + // NULL if the native bridge doesn't use a handler or doesn't want it to be managed by the + // runtime. + // Otherwise, a pointer to the signal handler. + NativeBridgeSignalHandlerFn (*getSignalHandler)(int signal); }; // Runtime interfaces to native bridge.
diff --git a/include/netutils/dhcp.h b/include/netutils/dhcp.h index de6bc82..008dbd8 100644 --- a/include/netutils/dhcp.h +++ b/include/netutils/dhcp.h
@@ -23,26 +23,18 @@ __BEGIN_DECLS extern int do_dhcp(char *iname); -extern int dhcp_do_request(const char *ifname, - char *ipaddr, - char *gateway, - uint32_t *prefixLength, - char *dns[], - char *server, - uint32_t *lease, - char *vendorInfo, - char *domain, - char *mtu); -extern int dhcp_do_request_renew(const char *ifname, - char *ipaddr, - char *gateway, - uint32_t *prefixLength, - char *dns[], - char *server, - uint32_t *lease, - char *vendorInfo, - char *domain, - char *mtu); +extern int dhcp_start(const char *ifname); +extern int dhcp_start_renew(const char *ifname); +extern int dhcp_get_results(const char *ifname, + char *ipaddr, + char *gateway, + uint32_t *prefixLength, + char *dns[], + char *server, + uint32_t *lease, + char *vendorInfo, + char *domain, + char *mtu); extern int dhcp_stop(const char *ifname); extern int dhcp_release_lease(const char *ifname); extern char *dhcp_get_errmsg();
diff --git a/include/private/android_filesystem_config.h b/include/private/android_filesystem_config.h index 02fe2b5..2ed27dc 100644 --- a/include/private/android_filesystem_config.h +++ b/include/private/android_filesystem_config.h
@@ -206,13 +206,13 @@ * Used in: * build/tools/fs_config/fs_config.c * build/tools/fs_get_stats/fs_get_stats.c - * external/genext2fs/genext2fs.c + * system/extras/ext4_utils/make_ext4fs_main.c * external/squashfs-tools/squashfs-tools/android.c * system/core/cpio/mkbootfs.c * system/core/adb/file_sync_service.cpp * system/extras/ext4_utils/canned_fs_config.c */ -void fs_config(const char *path, int dir, +void fs_config(const char *path, int dir, const char *target_out_path, unsigned *uid, unsigned *gid, unsigned *mode, uint64_t *capabilities); ssize_t fs_config_generate(char *buffer, size_t length, const struct fs_path_config *pc);
diff --git a/include/system/audio.h b/include/system/audio.h deleted file mode 100644 index 181a171..0000000 --- a/include/system/audio.h +++ /dev/null
@@ -1,1373 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#ifndef ANDROID_AUDIO_CORE_H -#define ANDROID_AUDIO_CORE_H - -#include <stdbool.h> -#include <stdint.h> -#include <stdio.h> -#include <sys/cdefs.h> -#include <sys/types.h> - -#include <cutils/bitops.h> - -__BEGIN_DECLS - -/* The enums were moved here mostly from - * frameworks/base/include/media/AudioSystem.h - */ - -/* device address used to refer to the standard remote submix */ -#define AUDIO_REMOTE_SUBMIX_DEVICE_ADDRESS "0" - -/* AudioFlinger and AudioPolicy services use I/O handles to identify audio sources and sinks */ -typedef int audio_io_handle_t; -#define AUDIO_IO_HANDLE_NONE 0 - -/* Audio stream types */ -typedef enum { - /* These values must kept in sync with - * frameworks/base/media/java/android/media/AudioSystem.java - */ - AUDIO_STREAM_DEFAULT = -1, - AUDIO_STREAM_MIN = 0, - AUDIO_STREAM_VOICE_CALL = 0, - AUDIO_STREAM_SYSTEM = 1, - AUDIO_STREAM_RING = 2, - AUDIO_STREAM_MUSIC = 3, - AUDIO_STREAM_ALARM = 4, - AUDIO_STREAM_NOTIFICATION = 5, - AUDIO_STREAM_BLUETOOTH_SCO = 6, - AUDIO_STREAM_ENFORCED_AUDIBLE = 7, /* Sounds that cannot be muted by user - * and must be routed to speaker - */ - AUDIO_STREAM_DTMF = 8, - AUDIO_STREAM_TTS = 9, /* Transmitted Through Speaker. - * Plays over speaker only, silent on other devices. - */ - AUDIO_STREAM_ACCESSIBILITY = 10, /* For accessibility talk back prompts */ - AUDIO_STREAM_REROUTING = 11, /* For dynamic policy output mixes */ - AUDIO_STREAM_PATCH = 12, /* For internal audio flinger tracks. Fixed volume */ - AUDIO_STREAM_PUBLIC_CNT = AUDIO_STREAM_TTS + 1, - AUDIO_STREAM_CNT = AUDIO_STREAM_PATCH + 1, -} audio_stream_type_t; - -/* Do not change these values without updating their counterparts - * in frameworks/base/media/java/android/media/AudioAttributes.java - */ -typedef enum { - AUDIO_CONTENT_TYPE_UNKNOWN = 0, - AUDIO_CONTENT_TYPE_SPEECH = 1, - AUDIO_CONTENT_TYPE_MUSIC = 2, - AUDIO_CONTENT_TYPE_MOVIE = 3, - AUDIO_CONTENT_TYPE_SONIFICATION = 4, - - AUDIO_CONTENT_TYPE_CNT, - AUDIO_CONTENT_TYPE_MAX = AUDIO_CONTENT_TYPE_CNT - 1, -} audio_content_type_t; - -/* Do not change these values without updating their counterparts - * in frameworks/base/media/java/android/media/AudioAttributes.java - */ -typedef enum { - AUDIO_USAGE_UNKNOWN = 0, - AUDIO_USAGE_MEDIA = 1, - AUDIO_USAGE_VOICE_COMMUNICATION = 2, - AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING = 3, - AUDIO_USAGE_ALARM = 4, - AUDIO_USAGE_NOTIFICATION = 5, - AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE = 6, - AUDIO_USAGE_NOTIFICATION_COMMUNICATION_REQUEST = 7, - AUDIO_USAGE_NOTIFICATION_COMMUNICATION_INSTANT = 8, - AUDIO_USAGE_NOTIFICATION_COMMUNICATION_DELAYED = 9, - AUDIO_USAGE_NOTIFICATION_EVENT = 10, - AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY = 11, - AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE = 12, - AUDIO_USAGE_ASSISTANCE_SONIFICATION = 13, - AUDIO_USAGE_GAME = 14, - AUDIO_USAGE_VIRTUAL_SOURCE = 15, - - AUDIO_USAGE_CNT, - AUDIO_USAGE_MAX = AUDIO_USAGE_CNT - 1, -} audio_usage_t; - -typedef uint32_t audio_flags_mask_t; - -/* Do not change these values without updating their counterparts - * in frameworks/base/media/java/android/media/AudioAttributes.java - */ -enum { - AUDIO_FLAG_AUDIBILITY_ENFORCED = 0x1, - AUDIO_FLAG_SECURE = 0x2, - AUDIO_FLAG_SCO = 0x4, - AUDIO_FLAG_BEACON = 0x8, - AUDIO_FLAG_HW_AV_SYNC = 0x10, - AUDIO_FLAG_HW_HOTWORD = 0x20, -}; - -/* Do not change these values without updating their counterparts - * in frameworks/base/media/java/android/media/MediaRecorder.java, - * frameworks/av/services/audiopolicy/AudioPolicyService.cpp, - * and system/media/audio_effects/include/audio_effects/audio_effects_conf.h! - */ -typedef enum { - AUDIO_SOURCE_DEFAULT = 0, - AUDIO_SOURCE_MIC = 1, - AUDIO_SOURCE_VOICE_UPLINK = 2, - AUDIO_SOURCE_VOICE_DOWNLINK = 3, - AUDIO_SOURCE_VOICE_CALL = 4, - AUDIO_SOURCE_CAMCORDER = 5, - AUDIO_SOURCE_VOICE_RECOGNITION = 6, - AUDIO_SOURCE_VOICE_COMMUNICATION = 7, - AUDIO_SOURCE_REMOTE_SUBMIX = 8, /* Source for the mix to be presented remotely. */ - /* An example of remote presentation is Wifi Display */ - /* where a dongle attached to a TV can be used to */ - /* play the mix captured by this audio source. */ - AUDIO_SOURCE_CNT, - AUDIO_SOURCE_MAX = AUDIO_SOURCE_CNT - 1, - AUDIO_SOURCE_FM_TUNER = 1998, - AUDIO_SOURCE_HOTWORD = 1999, /* A low-priority, preemptible audio source for - for background software hotword detection. - Same tuning as AUDIO_SOURCE_VOICE_RECOGNITION. - Used only internally to the framework. Not exposed - at the audio HAL. */ -} audio_source_t; - -/* Audio attributes */ -#define AUDIO_ATTRIBUTES_TAGS_MAX_SIZE 256 -typedef struct { - audio_content_type_t content_type; - audio_usage_t usage; - audio_source_t source; - audio_flags_mask_t flags; - char tags[AUDIO_ATTRIBUTES_TAGS_MAX_SIZE]; /* UTF8 */ -} audio_attributes_t; - -/* special audio session values - * (XXX: should this be living in the audio effects land?) - */ -typedef enum { - /* session for effects attached to a particular output stream - * (value must be less than 0) - */ - AUDIO_SESSION_OUTPUT_STAGE = -1, - - /* session for effects applied to output mix. These effects can - * be moved by audio policy manager to another output stream - * (value must be 0) - */ - AUDIO_SESSION_OUTPUT_MIX = 0, - - /* application does not specify an explicit session ID to be used, - * and requests a new session ID to be allocated - * TODO use unique values for AUDIO_SESSION_OUTPUT_MIX and AUDIO_SESSION_ALLOCATE, - * after all uses have been updated from 0 to the appropriate symbol, and have been tested. - */ - AUDIO_SESSION_ALLOCATE = 0, -} audio_session_t; - -/* a unique ID allocated by AudioFlinger for use as a audio_io_handle_t or audio_session_t */ -typedef int audio_unique_id_t; - -#define AUDIO_UNIQUE_ID_ALLOCATE AUDIO_SESSION_ALLOCATE - -/* Audio sub formats (see enum audio_format). */ - -/* PCM sub formats */ -typedef enum { - /* All of these are in native byte order */ - AUDIO_FORMAT_PCM_SUB_16_BIT = 0x1, /* DO NOT CHANGE - PCM signed 16 bits */ - AUDIO_FORMAT_PCM_SUB_8_BIT = 0x2, /* DO NOT CHANGE - PCM unsigned 8 bits */ - AUDIO_FORMAT_PCM_SUB_32_BIT = 0x3, /* PCM signed .31 fixed point */ - AUDIO_FORMAT_PCM_SUB_8_24_BIT = 0x4, /* PCM signed 7.24 fixed point */ - AUDIO_FORMAT_PCM_SUB_FLOAT = 0x5, /* PCM single-precision floating point */ - AUDIO_FORMAT_PCM_SUB_24_BIT_PACKED = 0x6, /* PCM signed .23 fixed point packed in 3 bytes */ -} audio_format_pcm_sub_fmt_t; - -/* The audio_format_*_sub_fmt_t declarations are not currently used */ - -/* MP3 sub format field definition : can use 11 LSBs in the same way as MP3 - * frame header to specify bit rate, stereo mode, version... - */ -typedef enum { - AUDIO_FORMAT_MP3_SUB_NONE = 0x0, -} audio_format_mp3_sub_fmt_t; - -/* AMR NB/WB sub format field definition: specify frame block interleaving, - * bandwidth efficient or octet aligned, encoding mode for recording... - */ -typedef enum { - AUDIO_FORMAT_AMR_SUB_NONE = 0x0, -} audio_format_amr_sub_fmt_t; - -/* AAC sub format field definition: specify profile or bitrate for recording... */ -typedef enum { - AUDIO_FORMAT_AAC_SUB_MAIN = 0x1, - AUDIO_FORMAT_AAC_SUB_LC = 0x2, - AUDIO_FORMAT_AAC_SUB_SSR = 0x4, - AUDIO_FORMAT_AAC_SUB_LTP = 0x8, - AUDIO_FORMAT_AAC_SUB_HE_V1 = 0x10, - AUDIO_FORMAT_AAC_SUB_SCALABLE = 0x20, - AUDIO_FORMAT_AAC_SUB_ERLC = 0x40, - AUDIO_FORMAT_AAC_SUB_LD = 0x80, - AUDIO_FORMAT_AAC_SUB_HE_V2 = 0x100, - AUDIO_FORMAT_AAC_SUB_ELD = 0x200, -} audio_format_aac_sub_fmt_t; - -/* VORBIS sub format field definition: specify quality for recording... */ -typedef enum { - AUDIO_FORMAT_VORBIS_SUB_NONE = 0x0, -} audio_format_vorbis_sub_fmt_t; - -/* Audio format consists of a main format field (upper 8 bits) and a sub format - * field (lower 24 bits). - * - * The main format indicates the main codec type. The sub format field - * indicates options and parameters for each format. The sub format is mainly - * used for record to indicate for instance the requested bitrate or profile. - * It can also be used for certain formats to give informations not present in - * the encoded audio stream (e.g. octet alignement for AMR). - */ -typedef enum { - AUDIO_FORMAT_INVALID = 0xFFFFFFFFUL, - AUDIO_FORMAT_DEFAULT = 0, - AUDIO_FORMAT_PCM = 0x00000000UL, /* DO NOT CHANGE */ - AUDIO_FORMAT_MP3 = 0x01000000UL, - AUDIO_FORMAT_AMR_NB = 0x02000000UL, - AUDIO_FORMAT_AMR_WB = 0x03000000UL, - AUDIO_FORMAT_AAC = 0x04000000UL, - AUDIO_FORMAT_HE_AAC_V1 = 0x05000000UL, /* Deprecated, Use AUDIO_FORMAT_AAC_HE_V1*/ - AUDIO_FORMAT_HE_AAC_V2 = 0x06000000UL, /* Deprecated, Use AUDIO_FORMAT_AAC_HE_V2*/ - AUDIO_FORMAT_VORBIS = 0x07000000UL, - AUDIO_FORMAT_OPUS = 0x08000000UL, - AUDIO_FORMAT_AC3 = 0x09000000UL, - AUDIO_FORMAT_E_AC3 = 0x0A000000UL, - AUDIO_FORMAT_MAIN_MASK = 0xFF000000UL, - AUDIO_FORMAT_SUB_MASK = 0x00FFFFFFUL, - - /* Aliases */ - /* note != AudioFormat.ENCODING_PCM_16BIT */ - AUDIO_FORMAT_PCM_16_BIT = (AUDIO_FORMAT_PCM | - AUDIO_FORMAT_PCM_SUB_16_BIT), - /* note != AudioFormat.ENCODING_PCM_8BIT */ - AUDIO_FORMAT_PCM_8_BIT = (AUDIO_FORMAT_PCM | - AUDIO_FORMAT_PCM_SUB_8_BIT), - AUDIO_FORMAT_PCM_32_BIT = (AUDIO_FORMAT_PCM | - AUDIO_FORMAT_PCM_SUB_32_BIT), - AUDIO_FORMAT_PCM_8_24_BIT = (AUDIO_FORMAT_PCM | - AUDIO_FORMAT_PCM_SUB_8_24_BIT), - AUDIO_FORMAT_PCM_FLOAT = (AUDIO_FORMAT_PCM | - AUDIO_FORMAT_PCM_SUB_FLOAT), - AUDIO_FORMAT_PCM_24_BIT_PACKED = (AUDIO_FORMAT_PCM | - AUDIO_FORMAT_PCM_SUB_24_BIT_PACKED), - AUDIO_FORMAT_AAC_MAIN = (AUDIO_FORMAT_AAC | - AUDIO_FORMAT_AAC_SUB_MAIN), - AUDIO_FORMAT_AAC_LC = (AUDIO_FORMAT_AAC | - AUDIO_FORMAT_AAC_SUB_LC), - AUDIO_FORMAT_AAC_SSR = (AUDIO_FORMAT_AAC | - AUDIO_FORMAT_AAC_SUB_SSR), - AUDIO_FORMAT_AAC_LTP = (AUDIO_FORMAT_AAC | - AUDIO_FORMAT_AAC_SUB_LTP), - AUDIO_FORMAT_AAC_HE_V1 = (AUDIO_FORMAT_AAC | - AUDIO_FORMAT_AAC_SUB_HE_V1), - AUDIO_FORMAT_AAC_SCALABLE = (AUDIO_FORMAT_AAC | - AUDIO_FORMAT_AAC_SUB_SCALABLE), - AUDIO_FORMAT_AAC_ERLC = (AUDIO_FORMAT_AAC | - AUDIO_FORMAT_AAC_SUB_ERLC), - AUDIO_FORMAT_AAC_LD = (AUDIO_FORMAT_AAC | - AUDIO_FORMAT_AAC_SUB_LD), - AUDIO_FORMAT_AAC_HE_V2 = (AUDIO_FORMAT_AAC | - AUDIO_FORMAT_AAC_SUB_HE_V2), - AUDIO_FORMAT_AAC_ELD = (AUDIO_FORMAT_AAC | - AUDIO_FORMAT_AAC_SUB_ELD), -} audio_format_t; - -/* For the channel mask for position assignment representation */ -enum { - -/* These can be a complete audio_channel_mask_t. */ - - AUDIO_CHANNEL_NONE = 0x0, - AUDIO_CHANNEL_INVALID = 0xC0000000, - -/* These can be the bits portion of an audio_channel_mask_t - * with representation AUDIO_CHANNEL_REPRESENTATION_POSITION. - * Using these bits as a complete audio_channel_mask_t is deprecated. - */ - - /* output channels */ - AUDIO_CHANNEL_OUT_FRONT_LEFT = 0x1, - AUDIO_CHANNEL_OUT_FRONT_RIGHT = 0x2, - AUDIO_CHANNEL_OUT_FRONT_CENTER = 0x4, - AUDIO_CHANNEL_OUT_LOW_FREQUENCY = 0x8, - AUDIO_CHANNEL_OUT_BACK_LEFT = 0x10, - AUDIO_CHANNEL_OUT_BACK_RIGHT = 0x20, - AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER = 0x40, - AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER = 0x80, - AUDIO_CHANNEL_OUT_BACK_CENTER = 0x100, - AUDIO_CHANNEL_OUT_SIDE_LEFT = 0x200, - AUDIO_CHANNEL_OUT_SIDE_RIGHT = 0x400, - AUDIO_CHANNEL_OUT_TOP_CENTER = 0x800, - AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT = 0x1000, - AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER = 0x2000, - AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT = 0x4000, - AUDIO_CHANNEL_OUT_TOP_BACK_LEFT = 0x8000, - AUDIO_CHANNEL_OUT_TOP_BACK_CENTER = 0x10000, - AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT = 0x20000, - -/* TODO: should these be considered complete channel masks, or only bits? */ - - AUDIO_CHANNEL_OUT_MONO = AUDIO_CHANNEL_OUT_FRONT_LEFT, - AUDIO_CHANNEL_OUT_STEREO = (AUDIO_CHANNEL_OUT_FRONT_LEFT | - AUDIO_CHANNEL_OUT_FRONT_RIGHT), - AUDIO_CHANNEL_OUT_QUAD = (AUDIO_CHANNEL_OUT_FRONT_LEFT | - AUDIO_CHANNEL_OUT_FRONT_RIGHT | - AUDIO_CHANNEL_OUT_BACK_LEFT | - AUDIO_CHANNEL_OUT_BACK_RIGHT), - AUDIO_CHANNEL_OUT_QUAD_BACK = AUDIO_CHANNEL_OUT_QUAD, - /* like AUDIO_CHANNEL_OUT_QUAD_BACK with *_SIDE_* instead of *_BACK_* */ - AUDIO_CHANNEL_OUT_QUAD_SIDE = (AUDIO_CHANNEL_OUT_FRONT_LEFT | - AUDIO_CHANNEL_OUT_FRONT_RIGHT | - AUDIO_CHANNEL_OUT_SIDE_LEFT | - AUDIO_CHANNEL_OUT_SIDE_RIGHT), - AUDIO_CHANNEL_OUT_5POINT1 = (AUDIO_CHANNEL_OUT_FRONT_LEFT | - AUDIO_CHANNEL_OUT_FRONT_RIGHT | - AUDIO_CHANNEL_OUT_FRONT_CENTER | - AUDIO_CHANNEL_OUT_LOW_FREQUENCY | - AUDIO_CHANNEL_OUT_BACK_LEFT | - AUDIO_CHANNEL_OUT_BACK_RIGHT), - AUDIO_CHANNEL_OUT_5POINT1_BACK = AUDIO_CHANNEL_OUT_5POINT1, - /* like AUDIO_CHANNEL_OUT_5POINT1_BACK with *_SIDE_* instead of *_BACK_* */ - AUDIO_CHANNEL_OUT_5POINT1_SIDE = (AUDIO_CHANNEL_OUT_FRONT_LEFT | - AUDIO_CHANNEL_OUT_FRONT_RIGHT | - AUDIO_CHANNEL_OUT_FRONT_CENTER | - AUDIO_CHANNEL_OUT_LOW_FREQUENCY | - AUDIO_CHANNEL_OUT_SIDE_LEFT | - AUDIO_CHANNEL_OUT_SIDE_RIGHT), - // matches the correct AudioFormat.CHANNEL_OUT_7POINT1_SURROUND definition for 7.1 - AUDIO_CHANNEL_OUT_7POINT1 = (AUDIO_CHANNEL_OUT_FRONT_LEFT | - AUDIO_CHANNEL_OUT_FRONT_RIGHT | - AUDIO_CHANNEL_OUT_FRONT_CENTER | - AUDIO_CHANNEL_OUT_LOW_FREQUENCY | - AUDIO_CHANNEL_OUT_BACK_LEFT | - AUDIO_CHANNEL_OUT_BACK_RIGHT | - AUDIO_CHANNEL_OUT_SIDE_LEFT | - AUDIO_CHANNEL_OUT_SIDE_RIGHT), - AUDIO_CHANNEL_OUT_ALL = (AUDIO_CHANNEL_OUT_FRONT_LEFT | - AUDIO_CHANNEL_OUT_FRONT_RIGHT | - AUDIO_CHANNEL_OUT_FRONT_CENTER | - AUDIO_CHANNEL_OUT_LOW_FREQUENCY | - AUDIO_CHANNEL_OUT_BACK_LEFT | - AUDIO_CHANNEL_OUT_BACK_RIGHT | - AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER | - AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER | - AUDIO_CHANNEL_OUT_BACK_CENTER| - AUDIO_CHANNEL_OUT_SIDE_LEFT| - AUDIO_CHANNEL_OUT_SIDE_RIGHT| - AUDIO_CHANNEL_OUT_TOP_CENTER| - AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT| - AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER| - AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT| - AUDIO_CHANNEL_OUT_TOP_BACK_LEFT| - AUDIO_CHANNEL_OUT_TOP_BACK_CENTER| - AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT), - -/* These are bits only, not complete values */ - - /* input channels */ - AUDIO_CHANNEL_IN_LEFT = 0x4, - AUDIO_CHANNEL_IN_RIGHT = 0x8, - AUDIO_CHANNEL_IN_FRONT = 0x10, - AUDIO_CHANNEL_IN_BACK = 0x20, - AUDIO_CHANNEL_IN_LEFT_PROCESSED = 0x40, - AUDIO_CHANNEL_IN_RIGHT_PROCESSED = 0x80, - AUDIO_CHANNEL_IN_FRONT_PROCESSED = 0x100, - AUDIO_CHANNEL_IN_BACK_PROCESSED = 0x200, - AUDIO_CHANNEL_IN_PRESSURE = 0x400, - AUDIO_CHANNEL_IN_X_AXIS = 0x800, - AUDIO_CHANNEL_IN_Y_AXIS = 0x1000, - AUDIO_CHANNEL_IN_Z_AXIS = 0x2000, - AUDIO_CHANNEL_IN_VOICE_UPLINK = 0x4000, - AUDIO_CHANNEL_IN_VOICE_DNLINK = 0x8000, - -/* TODO: should these be considered complete channel masks, or only bits, or deprecated? */ - - AUDIO_CHANNEL_IN_MONO = AUDIO_CHANNEL_IN_FRONT, - AUDIO_CHANNEL_IN_STEREO = (AUDIO_CHANNEL_IN_LEFT | AUDIO_CHANNEL_IN_RIGHT), - AUDIO_CHANNEL_IN_FRONT_BACK = (AUDIO_CHANNEL_IN_FRONT | AUDIO_CHANNEL_IN_BACK), - AUDIO_CHANNEL_IN_ALL = (AUDIO_CHANNEL_IN_LEFT | - AUDIO_CHANNEL_IN_RIGHT | - AUDIO_CHANNEL_IN_FRONT | - AUDIO_CHANNEL_IN_BACK| - AUDIO_CHANNEL_IN_LEFT_PROCESSED | - AUDIO_CHANNEL_IN_RIGHT_PROCESSED | - AUDIO_CHANNEL_IN_FRONT_PROCESSED | - AUDIO_CHANNEL_IN_BACK_PROCESSED| - AUDIO_CHANNEL_IN_PRESSURE | - AUDIO_CHANNEL_IN_X_AXIS | - AUDIO_CHANNEL_IN_Y_AXIS | - AUDIO_CHANNEL_IN_Z_AXIS | - AUDIO_CHANNEL_IN_VOICE_UPLINK | - AUDIO_CHANNEL_IN_VOICE_DNLINK), -}; - -/* A channel mask per se only defines the presence or absence of a channel, not the order. - * But see AUDIO_INTERLEAVE_* below for the platform convention of order. - * - * audio_channel_mask_t is an opaque type and its internal layout should not - * be assumed as it may change in the future. - * Instead, always use the functions declared in this header to examine. - * - * These are the current representations: - * - * AUDIO_CHANNEL_REPRESENTATION_POSITION - * is a channel mask representation for position assignment. - * Each low-order bit corresponds to the spatial position of a transducer (output), - * or interpretation of channel (input). - * The user of a channel mask needs to know the context of whether it is for output or input. - * The constants AUDIO_CHANNEL_OUT_* or AUDIO_CHANNEL_IN_* apply to the bits portion. - * It is not permitted for no bits to be set. - * - * AUDIO_CHANNEL_REPRESENTATION_INDEX - * is a channel mask representation for index assignment. - * Each low-order bit corresponds to a selected channel. - * There is no platform interpretation of the various bits. - * There is no concept of output or input. - * It is not permitted for no bits to be set. - * - * All other representations are reserved for future use. - * - * Warning: current representation distinguishes between input and output, but this will not the be - * case in future revisions of the platform. Wherever there is an ambiguity between input and output - * that is currently resolved by checking the channel mask, the implementer should look for ways to - * fix it with additional information outside of the mask. - */ -typedef uint32_t audio_channel_mask_t; - -/* Maximum number of channels for all representations */ -#define AUDIO_CHANNEL_COUNT_MAX 30 - -/* log(2) of maximum number of representations, not part of public API */ -#define AUDIO_CHANNEL_REPRESENTATION_LOG2 2 - -/* Representations */ -typedef enum { - AUDIO_CHANNEL_REPRESENTATION_POSITION = 0, // must be zero for compatibility - // 1 is reserved for future use - AUDIO_CHANNEL_REPRESENTATION_INDEX = 2, - // 3 is reserved for future use -} audio_channel_representation_t; - -/* The return value is undefined if the channel mask is invalid. */ -static inline uint32_t audio_channel_mask_get_bits(audio_channel_mask_t channel) -{ - return channel & ((1 << AUDIO_CHANNEL_COUNT_MAX) - 1); -} - -/* The return value is undefined if the channel mask is invalid. */ -static inline audio_channel_representation_t audio_channel_mask_get_representation( - audio_channel_mask_t channel) -{ - // The right shift should be sufficient, but also "and" for safety in case mask is not 32 bits - return (audio_channel_representation_t) - ((channel >> AUDIO_CHANNEL_COUNT_MAX) & ((1 << AUDIO_CHANNEL_REPRESENTATION_LOG2) - 1)); -} - -/* Returns true if the channel mask is valid, - * or returns false for AUDIO_CHANNEL_NONE, AUDIO_CHANNEL_INVALID, and other invalid values. - * This function is unable to determine whether a channel mask for position assignment - * is invalid because an output mask has an invalid output bit set, - * or because an input mask has an invalid input bit set. - * All other APIs that take a channel mask assume that it is valid. - */ -static inline bool audio_channel_mask_is_valid(audio_channel_mask_t channel) -{ - uint32_t bits = audio_channel_mask_get_bits(channel); - audio_channel_representation_t representation = audio_channel_mask_get_representation(channel); - switch (representation) { - case AUDIO_CHANNEL_REPRESENTATION_POSITION: - case AUDIO_CHANNEL_REPRESENTATION_INDEX: - break; - default: - bits = 0; - break; - } - return bits != 0; -} - -/* Not part of public API */ -static inline audio_channel_mask_t audio_channel_mask_from_representation_and_bits( - audio_channel_representation_t representation, uint32_t bits) -{ - return (audio_channel_mask_t) ((representation << AUDIO_CHANNEL_COUNT_MAX) | bits); -} - -/* Expresses the convention when stereo audio samples are stored interleaved - * in an array. This should improve readability by allowing code to use - * symbolic indices instead of hard-coded [0] and [1]. - * - * For multi-channel beyond stereo, the platform convention is that channels - * are interleaved in order from least significant channel mask bit - * to most significant channel mask bit, with unused bits skipped. - * Any exceptions to this convention will be noted at the appropriate API. - */ -enum { - AUDIO_INTERLEAVE_LEFT = 0, - AUDIO_INTERLEAVE_RIGHT = 1, -}; - -typedef enum { - AUDIO_MODE_INVALID = -2, - AUDIO_MODE_CURRENT = -1, - AUDIO_MODE_NORMAL = 0, - AUDIO_MODE_RINGTONE = 1, - AUDIO_MODE_IN_CALL = 2, - AUDIO_MODE_IN_COMMUNICATION = 3, - - AUDIO_MODE_CNT, - AUDIO_MODE_MAX = AUDIO_MODE_CNT - 1, -} audio_mode_t; - -/* This enum is deprecated */ -typedef enum { - AUDIO_IN_ACOUSTICS_NONE = 0, - AUDIO_IN_ACOUSTICS_AGC_ENABLE = 0x0001, - AUDIO_IN_ACOUSTICS_AGC_DISABLE = 0, - AUDIO_IN_ACOUSTICS_NS_ENABLE = 0x0002, - AUDIO_IN_ACOUSTICS_NS_DISABLE = 0, - AUDIO_IN_ACOUSTICS_TX_IIR_ENABLE = 0x0004, - AUDIO_IN_ACOUSTICS_TX_DISABLE = 0, -} audio_in_acoustics_t; - -enum { - AUDIO_DEVICE_NONE = 0x0, - /* reserved bits */ - AUDIO_DEVICE_BIT_IN = 0x80000000, - AUDIO_DEVICE_BIT_DEFAULT = 0x40000000, - /* output devices */ - AUDIO_DEVICE_OUT_EARPIECE = 0x1, - AUDIO_DEVICE_OUT_SPEAKER = 0x2, - AUDIO_DEVICE_OUT_WIRED_HEADSET = 0x4, - AUDIO_DEVICE_OUT_WIRED_HEADPHONE = 0x8, - AUDIO_DEVICE_OUT_BLUETOOTH_SCO = 0x10, - AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET = 0x20, - AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT = 0x40, - AUDIO_DEVICE_OUT_BLUETOOTH_A2DP = 0x80, - AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES = 0x100, - AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER = 0x200, - AUDIO_DEVICE_OUT_AUX_DIGITAL = 0x400, - AUDIO_DEVICE_OUT_HDMI = AUDIO_DEVICE_OUT_AUX_DIGITAL, - /* uses an analog connection (multiplexed over the USB connector pins for instance) */ - AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET = 0x800, - AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET = 0x1000, - /* USB accessory mode: your Android device is a USB device and the dock is a USB host */ - AUDIO_DEVICE_OUT_USB_ACCESSORY = 0x2000, - /* USB host mode: your Android device is a USB host and the dock is a USB device */ - AUDIO_DEVICE_OUT_USB_DEVICE = 0x4000, - AUDIO_DEVICE_OUT_REMOTE_SUBMIX = 0x8000, - /* Telephony voice TX path */ - AUDIO_DEVICE_OUT_TELEPHONY_TX = 0x10000, - /* Analog jack with line impedance detected */ - AUDIO_DEVICE_OUT_LINE = 0x20000, - /* HDMI Audio Return Channel */ - AUDIO_DEVICE_OUT_HDMI_ARC = 0x40000, - /* S/PDIF out */ - AUDIO_DEVICE_OUT_SPDIF = 0x80000, - /* FM transmitter out */ - AUDIO_DEVICE_OUT_FM = 0x100000, - /* Line out for av devices */ - AUDIO_DEVICE_OUT_AUX_LINE = 0x200000, - /* limited-output speaker device for acoustic safety */ - AUDIO_DEVICE_OUT_SPEAKER_SAFE = 0x400000, - AUDIO_DEVICE_OUT_DEFAULT = AUDIO_DEVICE_BIT_DEFAULT, - AUDIO_DEVICE_OUT_ALL = (AUDIO_DEVICE_OUT_EARPIECE | - AUDIO_DEVICE_OUT_SPEAKER | - AUDIO_DEVICE_OUT_WIRED_HEADSET | - AUDIO_DEVICE_OUT_WIRED_HEADPHONE | - AUDIO_DEVICE_OUT_BLUETOOTH_SCO | - AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET | - AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT | - AUDIO_DEVICE_OUT_BLUETOOTH_A2DP | - AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES | - AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER | - AUDIO_DEVICE_OUT_HDMI | - AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET | - AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET | - AUDIO_DEVICE_OUT_USB_ACCESSORY | - AUDIO_DEVICE_OUT_USB_DEVICE | - AUDIO_DEVICE_OUT_REMOTE_SUBMIX | - AUDIO_DEVICE_OUT_TELEPHONY_TX | - AUDIO_DEVICE_OUT_LINE | - AUDIO_DEVICE_OUT_HDMI_ARC | - AUDIO_DEVICE_OUT_SPDIF | - AUDIO_DEVICE_OUT_FM | - AUDIO_DEVICE_OUT_AUX_LINE | - AUDIO_DEVICE_OUT_SPEAKER_SAFE | - AUDIO_DEVICE_OUT_DEFAULT), - AUDIO_DEVICE_OUT_ALL_A2DP = (AUDIO_DEVICE_OUT_BLUETOOTH_A2DP | - AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES | - AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER), - AUDIO_DEVICE_OUT_ALL_SCO = (AUDIO_DEVICE_OUT_BLUETOOTH_SCO | - AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET | - AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT), - AUDIO_DEVICE_OUT_ALL_USB = (AUDIO_DEVICE_OUT_USB_ACCESSORY | - AUDIO_DEVICE_OUT_USB_DEVICE), - - /* input devices */ - AUDIO_DEVICE_IN_COMMUNICATION = AUDIO_DEVICE_BIT_IN | 0x1, - AUDIO_DEVICE_IN_AMBIENT = AUDIO_DEVICE_BIT_IN | 0x2, - AUDIO_DEVICE_IN_BUILTIN_MIC = AUDIO_DEVICE_BIT_IN | 0x4, - AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET = AUDIO_DEVICE_BIT_IN | 0x8, - AUDIO_DEVICE_IN_WIRED_HEADSET = AUDIO_DEVICE_BIT_IN | 0x10, - AUDIO_DEVICE_IN_AUX_DIGITAL = AUDIO_DEVICE_BIT_IN | 0x20, - AUDIO_DEVICE_IN_HDMI = AUDIO_DEVICE_IN_AUX_DIGITAL, - /* Telephony voice RX path */ - AUDIO_DEVICE_IN_VOICE_CALL = AUDIO_DEVICE_BIT_IN | 0x40, - AUDIO_DEVICE_IN_TELEPHONY_RX = AUDIO_DEVICE_IN_VOICE_CALL, - AUDIO_DEVICE_IN_BACK_MIC = AUDIO_DEVICE_BIT_IN | 0x80, - AUDIO_DEVICE_IN_REMOTE_SUBMIX = AUDIO_DEVICE_BIT_IN | 0x100, - AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET = AUDIO_DEVICE_BIT_IN | 0x200, - AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET = AUDIO_DEVICE_BIT_IN | 0x400, - AUDIO_DEVICE_IN_USB_ACCESSORY = AUDIO_DEVICE_BIT_IN | 0x800, - AUDIO_DEVICE_IN_USB_DEVICE = AUDIO_DEVICE_BIT_IN | 0x1000, - /* FM tuner input */ - AUDIO_DEVICE_IN_FM_TUNER = AUDIO_DEVICE_BIT_IN | 0x2000, - /* TV tuner input */ - AUDIO_DEVICE_IN_TV_TUNER = AUDIO_DEVICE_BIT_IN | 0x4000, - /* Analog jack with line impedance detected */ - AUDIO_DEVICE_IN_LINE = AUDIO_DEVICE_BIT_IN | 0x8000, - /* S/PDIF in */ - AUDIO_DEVICE_IN_SPDIF = AUDIO_DEVICE_BIT_IN | 0x10000, - AUDIO_DEVICE_IN_BLUETOOTH_A2DP = AUDIO_DEVICE_BIT_IN | 0x20000, - AUDIO_DEVICE_IN_LOOPBACK = AUDIO_DEVICE_BIT_IN | 0x40000, - AUDIO_DEVICE_IN_DEFAULT = AUDIO_DEVICE_BIT_IN | AUDIO_DEVICE_BIT_DEFAULT, - - AUDIO_DEVICE_IN_ALL = (AUDIO_DEVICE_IN_COMMUNICATION | - AUDIO_DEVICE_IN_AMBIENT | - AUDIO_DEVICE_IN_BUILTIN_MIC | - AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET | - AUDIO_DEVICE_IN_WIRED_HEADSET | - AUDIO_DEVICE_IN_HDMI | - AUDIO_DEVICE_IN_TELEPHONY_RX | - AUDIO_DEVICE_IN_BACK_MIC | - AUDIO_DEVICE_IN_REMOTE_SUBMIX | - AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET | - AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET | - AUDIO_DEVICE_IN_USB_ACCESSORY | - AUDIO_DEVICE_IN_USB_DEVICE | - AUDIO_DEVICE_IN_FM_TUNER | - AUDIO_DEVICE_IN_TV_TUNER | - AUDIO_DEVICE_IN_LINE | - AUDIO_DEVICE_IN_SPDIF | - AUDIO_DEVICE_IN_BLUETOOTH_A2DP | - AUDIO_DEVICE_IN_LOOPBACK | - AUDIO_DEVICE_IN_DEFAULT), - AUDIO_DEVICE_IN_ALL_SCO = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET, - AUDIO_DEVICE_IN_ALL_USB = (AUDIO_DEVICE_IN_USB_ACCESSORY | - AUDIO_DEVICE_IN_USB_DEVICE), -}; - -typedef uint32_t audio_devices_t; - -/* the audio output flags serve two purposes: - * - when an AudioTrack is created they indicate a "wish" to be connected to an - * output stream with attributes corresponding to the specified flags - * - when present in an output profile descriptor listed for a particular audio - * hardware module, they indicate that an output stream can be opened that - * supports the attributes indicated by the flags. - * the audio policy manager will try to match the flags in the request - * (when getOuput() is called) to an available output stream. - */ -typedef enum { - AUDIO_OUTPUT_FLAG_NONE = 0x0, // no attributes - AUDIO_OUTPUT_FLAG_DIRECT = 0x1, // this output directly connects a track - // to one output stream: no software mixer - AUDIO_OUTPUT_FLAG_PRIMARY = 0x2, // this output is the primary output of - // the device. It is unique and must be - // present. It is opened by default and - // receives routing, audio mode and volume - // controls related to voice calls. - AUDIO_OUTPUT_FLAG_FAST = 0x4, // output supports "fast tracks", - // defined elsewhere - AUDIO_OUTPUT_FLAG_DEEP_BUFFER = 0x8, // use deep audio buffers - AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD = 0x10, // offload playback of compressed - // streams to hardware codec - AUDIO_OUTPUT_FLAG_NON_BLOCKING = 0x20, // use non-blocking write - AUDIO_OUTPUT_FLAG_HW_AV_SYNC = 0x40 // output uses a hardware A/V synchronization source -} audio_output_flags_t; - -/* The audio input flags are analogous to audio output flags. - * Currently they are used only when an AudioRecord is created, - * to indicate a preference to be connected to an input stream with - * attributes corresponding to the specified flags. - */ -typedef enum { - AUDIO_INPUT_FLAG_NONE = 0x0, // no attributes - AUDIO_INPUT_FLAG_FAST = 0x1, // prefer an input that supports "fast tracks" - AUDIO_INPUT_FLAG_HW_HOTWORD = 0x2, // prefer an input that captures from hw hotword source -} audio_input_flags_t; - -/* Additional information about compressed streams offloaded to - * hardware playback - * The version and size fields must be initialized by the caller by using - * one of the constants defined here. - */ -typedef struct { - uint16_t version; // version of the info structure - uint16_t size; // total size of the structure including version and size - uint32_t sample_rate; // sample rate in Hz - audio_channel_mask_t channel_mask; // channel mask - audio_format_t format; // audio format - audio_stream_type_t stream_type; // stream type - uint32_t bit_rate; // bit rate in bits per second - int64_t duration_us; // duration in microseconds, -1 if unknown - bool has_video; // true if stream is tied to a video stream - bool is_streaming; // true if streaming, false if local playback -} audio_offload_info_t; - -#define AUDIO_MAKE_OFFLOAD_INFO_VERSION(maj,min) \ - ((((maj) & 0xff) << 8) | ((min) & 0xff)) - -#define AUDIO_OFFLOAD_INFO_VERSION_0_1 AUDIO_MAKE_OFFLOAD_INFO_VERSION(0, 1) -#define AUDIO_OFFLOAD_INFO_VERSION_CURRENT AUDIO_OFFLOAD_INFO_VERSION_0_1 - -static const audio_offload_info_t AUDIO_INFO_INITIALIZER = { - version: AUDIO_OFFLOAD_INFO_VERSION_CURRENT, - size: sizeof(audio_offload_info_t), - sample_rate: 0, - channel_mask: 0, - format: AUDIO_FORMAT_DEFAULT, - stream_type: AUDIO_STREAM_VOICE_CALL, - bit_rate: 0, - duration_us: 0, - has_video: false, - is_streaming: false -}; - -/* common audio stream configuration parameters - * You should memset() the entire structure to zero before use to - * ensure forward compatibility - */ -struct audio_config { - uint32_t sample_rate; - audio_channel_mask_t channel_mask; - audio_format_t format; - audio_offload_info_t offload_info; - size_t frame_count; -}; -typedef struct audio_config audio_config_t; - -static const audio_config_t AUDIO_CONFIG_INITIALIZER = { - sample_rate: 0, - channel_mask: AUDIO_CHANNEL_NONE, - format: AUDIO_FORMAT_DEFAULT, - offload_info: { - version: AUDIO_OFFLOAD_INFO_VERSION_CURRENT, - size: sizeof(audio_offload_info_t), - sample_rate: 0, - channel_mask: 0, - format: AUDIO_FORMAT_DEFAULT, - stream_type: AUDIO_STREAM_VOICE_CALL, - bit_rate: 0, - duration_us: 0, - has_video: false, - is_streaming: false - }, - frame_count: 0, -}; - - -/* audio hw module handle functions or structures referencing a module */ -typedef int audio_module_handle_t; - -/****************************** - * Volume control - *****************************/ - -/* If the audio hardware supports gain control on some audio paths, - * the platform can expose them in the audio_policy.conf file. The audio HAL - * will then implement gain control functions that will use the following data - * structures. */ - -/* Type of gain control exposed by an audio port */ -#define AUDIO_GAIN_MODE_JOINT 0x1 /* supports joint channel gain control */ -#define AUDIO_GAIN_MODE_CHANNELS 0x2 /* supports separate channel gain control */ -#define AUDIO_GAIN_MODE_RAMP 0x4 /* supports gain ramps */ - -typedef uint32_t audio_gain_mode_t; - - -/* An audio_gain struct is a representation of a gain stage. - * A gain stage is always attached to an audio port. */ -struct audio_gain { - audio_gain_mode_t mode; /* e.g. AUDIO_GAIN_MODE_JOINT */ - audio_channel_mask_t channel_mask; /* channels which gain an be controlled. - N/A if AUDIO_GAIN_MODE_CHANNELS is not supported */ - int min_value; /* minimum gain value in millibels */ - int max_value; /* maximum gain value in millibels */ - int default_value; /* default gain value in millibels */ - unsigned int step_value; /* gain step in millibels */ - unsigned int min_ramp_ms; /* minimum ramp duration in ms */ - unsigned int max_ramp_ms; /* maximum ramp duration in ms */ -}; - -/* The gain configuration structure is used to get or set the gain values of a - * given port */ -struct audio_gain_config { - int index; /* index of the corresponding audio_gain in the - audio_port gains[] table */ - audio_gain_mode_t mode; /* mode requested for this command */ - audio_channel_mask_t channel_mask; /* channels which gain value follows. - N/A in joint mode */ - int values[sizeof(audio_channel_mask_t) * 8]; /* gain values in millibels - for each channel ordered from LSb to MSb in - channel mask. The number of values is 1 in joint - mode or popcount(channel_mask) */ - unsigned int ramp_duration_ms; /* ramp duration in ms */ -}; - -/****************************** - * Routing control - *****************************/ - -/* Types defined here are used to describe an audio source or sink at internal - * framework interfaces (audio policy, patch panel) or at the audio HAL. - * Sink and sources are grouped in a concept of “audio port” representing an - * audio end point at the edge of the system managed by the module exposing - * the interface. */ - -/* Audio port role: either source or sink */ -typedef enum { - AUDIO_PORT_ROLE_NONE, - AUDIO_PORT_ROLE_SOURCE, - AUDIO_PORT_ROLE_SINK, -} audio_port_role_t; - -/* Audio port type indicates if it is a session (e.g AudioTrack), - * a mix (e.g PlaybackThread output) or a physical device - * (e.g AUDIO_DEVICE_OUT_SPEAKER) */ -typedef enum { - AUDIO_PORT_TYPE_NONE, - AUDIO_PORT_TYPE_DEVICE, - AUDIO_PORT_TYPE_MIX, - AUDIO_PORT_TYPE_SESSION, -} audio_port_type_t; - -/* Each port has a unique ID or handle allocated by policy manager */ -typedef int audio_port_handle_t; -#define AUDIO_PORT_HANDLE_NONE 0 - - -/* maximum audio device address length */ -#define AUDIO_DEVICE_MAX_ADDRESS_LEN 32 - -/* extension for audio port configuration structure when the audio port is a - * hardware device */ -struct audio_port_config_device_ext { - audio_module_handle_t hw_module; /* module the device is attached to */ - audio_devices_t type; /* device type (e.g AUDIO_DEVICE_OUT_SPEAKER) */ - char address[AUDIO_DEVICE_MAX_ADDRESS_LEN]; /* device address. "" if N/A */ -}; - -/* extension for audio port configuration structure when the audio port is a - * sub mix */ -struct audio_port_config_mix_ext { - audio_module_handle_t hw_module; /* module the stream is attached to */ - audio_io_handle_t handle; /* I/O handle of the input/output stream */ - union { - //TODO: change use case for output streams: use strategy and mixer attributes - audio_stream_type_t stream; - audio_source_t source; - } usecase; -}; - -/* extension for audio port configuration structure when the audio port is an - * audio session */ -struct audio_port_config_session_ext { - audio_session_t session; /* audio session */ -}; - -/* Flags indicating which fields are to be considered in struct audio_port_config */ -#define AUDIO_PORT_CONFIG_SAMPLE_RATE 0x1 -#define AUDIO_PORT_CONFIG_CHANNEL_MASK 0x2 -#define AUDIO_PORT_CONFIG_FORMAT 0x4 -#define AUDIO_PORT_CONFIG_GAIN 0x8 -#define AUDIO_PORT_CONFIG_ALL (AUDIO_PORT_CONFIG_SAMPLE_RATE | \ - AUDIO_PORT_CONFIG_CHANNEL_MASK | \ - AUDIO_PORT_CONFIG_FORMAT | \ - AUDIO_PORT_CONFIG_GAIN) - -/* audio port configuration structure used to specify a particular configuration of - * an audio port */ -struct audio_port_config { - audio_port_handle_t id; /* port unique ID */ - audio_port_role_t role; /* sink or source */ - audio_port_type_t type; /* device, mix ... */ - unsigned int config_mask; /* e.g AUDIO_PORT_CONFIG_ALL */ - unsigned int sample_rate; /* sampling rate in Hz */ - audio_channel_mask_t channel_mask; /* channel mask if applicable */ - audio_format_t format; /* format if applicable */ - struct audio_gain_config gain; /* gain to apply if applicable */ - union { - struct audio_port_config_device_ext device; /* device specific info */ - struct audio_port_config_mix_ext mix; /* mix specific info */ - struct audio_port_config_session_ext session; /* session specific info */ - } ext; -}; - - -/* max number of sampling rates in audio port */ -#define AUDIO_PORT_MAX_SAMPLING_RATES 16 -/* max number of channel masks in audio port */ -#define AUDIO_PORT_MAX_CHANNEL_MASKS 16 -/* max number of audio formats in audio port */ -#define AUDIO_PORT_MAX_FORMATS 16 -/* max number of gain controls in audio port */ -#define AUDIO_PORT_MAX_GAINS 16 - -/* extension for audio port structure when the audio port is a hardware device */ -struct audio_port_device_ext { - audio_module_handle_t hw_module; /* module the device is attached to */ - audio_devices_t type; /* device type (e.g AUDIO_DEVICE_OUT_SPEAKER) */ - char address[AUDIO_DEVICE_MAX_ADDRESS_LEN]; -}; - -/* Latency class of the audio mix */ -typedef enum { - AUDIO_LATENCY_LOW, - AUDIO_LATENCY_NORMAL, -} audio_mix_latency_class_t; - -/* extension for audio port structure when the audio port is a sub mix */ -struct audio_port_mix_ext { - audio_module_handle_t hw_module; /* module the stream is attached to */ - audio_io_handle_t handle; /* I/O handle of the input.output stream */ - audio_mix_latency_class_t latency_class; /* latency class */ - // other attributes: routing strategies -}; - -/* extension for audio port structure when the audio port is an audio session */ -struct audio_port_session_ext { - audio_session_t session; /* audio session */ -}; - - -struct audio_port { - audio_port_handle_t id; /* port unique ID */ - audio_port_role_t role; /* sink or source */ - audio_port_type_t type; /* device, mix ... */ - unsigned int num_sample_rates; /* number of sampling rates in following array */ - unsigned int sample_rates[AUDIO_PORT_MAX_SAMPLING_RATES]; - unsigned int num_channel_masks; /* number of channel masks in following array */ - audio_channel_mask_t channel_masks[AUDIO_PORT_MAX_CHANNEL_MASKS]; - unsigned int num_formats; /* number of formats in following array */ - audio_format_t formats[AUDIO_PORT_MAX_FORMATS]; - unsigned int num_gains; /* number of gains in following array */ - struct audio_gain gains[AUDIO_PORT_MAX_GAINS]; - struct audio_port_config active_config; /* current audio port configuration */ - union { - struct audio_port_device_ext device; - struct audio_port_mix_ext mix; - struct audio_port_session_ext session; - } ext; -}; - -/* An audio patch represents a connection between one or more source ports and - * one or more sink ports. Patches are connected and disconnected by audio policy manager or by - * applications via framework APIs. - * Each patch is identified by a handle at the interface used to create that patch. For instance, - * when a patch is created by the audio HAL, the HAL allocates and returns a handle. - * This handle is unique to a given audio HAL hardware module. - * But the same patch receives another system wide unique handle allocated by the framework. - * This unique handle is used for all transactions inside the framework. - */ -typedef int audio_patch_handle_t; -#define AUDIO_PATCH_HANDLE_NONE 0 - -#define AUDIO_PATCH_PORTS_MAX 16 - -struct audio_patch { - audio_patch_handle_t id; /* patch unique ID */ - unsigned int num_sources; /* number of sources in following array */ - struct audio_port_config sources[AUDIO_PATCH_PORTS_MAX]; - unsigned int num_sinks; /* number of sinks in following array */ - struct audio_port_config sinks[AUDIO_PATCH_PORTS_MAX]; -}; - - - -/* a HW synchronization source returned by the audio HAL */ -typedef uint32_t audio_hw_sync_t; - -/* an invalid HW synchronization source indicating an error */ -#define AUDIO_HW_SYNC_INVALID 0 - -static inline bool audio_is_output_device(audio_devices_t device) -{ - if (((device & AUDIO_DEVICE_BIT_IN) == 0) && - (popcount(device) == 1) && ((device & ~AUDIO_DEVICE_OUT_ALL) == 0)) - return true; - else - return false; -} - -static inline bool audio_is_input_device(audio_devices_t device) -{ - if ((device & AUDIO_DEVICE_BIT_IN) != 0) { - device &= ~AUDIO_DEVICE_BIT_IN; - if ((popcount(device) == 1) && ((device & ~AUDIO_DEVICE_IN_ALL) == 0)) - return true; - } - return false; -} - -static inline bool audio_is_output_devices(audio_devices_t device) -{ - return (device & AUDIO_DEVICE_BIT_IN) == 0; -} - -static inline bool audio_is_a2dp_in_device(audio_devices_t device) -{ - if ((device & AUDIO_DEVICE_BIT_IN) != 0) { - device &= ~AUDIO_DEVICE_BIT_IN; - if ((popcount(device) == 1) && (device & AUDIO_DEVICE_IN_BLUETOOTH_A2DP)) - return true; - } - return false; -} - -static inline bool audio_is_a2dp_out_device(audio_devices_t device) -{ - if ((popcount(device) == 1) && (device & AUDIO_DEVICE_OUT_ALL_A2DP)) - return true; - else - return false; -} - -// Deprecated - use audio_is_a2dp_out_device() instead -static inline bool audio_is_a2dp_device(audio_devices_t device) -{ - return audio_is_a2dp_out_device(device); -} - -static inline bool audio_is_bluetooth_sco_device(audio_devices_t device) -{ - if ((device & AUDIO_DEVICE_BIT_IN) == 0) { - if ((popcount(device) == 1) && ((device & ~AUDIO_DEVICE_OUT_ALL_SCO) == 0)) - return true; - } else { - device &= ~AUDIO_DEVICE_BIT_IN; - if ((popcount(device) == 1) && ((device & ~AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET) == 0)) - return true; - } - - return false; -} - -static inline bool audio_is_usb_out_device(audio_devices_t device) -{ - return ((popcount(device) == 1) && (device & AUDIO_DEVICE_OUT_ALL_USB)); -} - -static inline bool audio_is_usb_in_device(audio_devices_t device) -{ - if ((device & AUDIO_DEVICE_BIT_IN) != 0) { - device &= ~AUDIO_DEVICE_BIT_IN; - if (popcount(device) == 1 && (device & AUDIO_DEVICE_IN_ALL_USB) != 0) - return true; - } - return false; -} - -/* OBSOLETE - use audio_is_usb_out_device() instead. */ -static inline bool audio_is_usb_device(audio_devices_t device) -{ - return audio_is_usb_out_device(device); -} - -static inline bool audio_is_remote_submix_device(audio_devices_t device) -{ - if ((device & AUDIO_DEVICE_OUT_REMOTE_SUBMIX) == AUDIO_DEVICE_OUT_REMOTE_SUBMIX - || (device & AUDIO_DEVICE_IN_REMOTE_SUBMIX) == AUDIO_DEVICE_IN_REMOTE_SUBMIX) - return true; - else - return false; -} - -/* Returns true if: - * representation is valid, and - * there is at least one channel bit set which _could_ correspond to an input channel, and - * there are no channel bits set which could _not_ correspond to an input channel. - * Otherwise returns false. - */ -static inline bool audio_is_input_channel(audio_channel_mask_t channel) -{ - uint32_t bits = audio_channel_mask_get_bits(channel); - switch (audio_channel_mask_get_representation(channel)) { - case AUDIO_CHANNEL_REPRESENTATION_POSITION: - if (bits & ~AUDIO_CHANNEL_IN_ALL) { - bits = 0; - } - // fall through - case AUDIO_CHANNEL_REPRESENTATION_INDEX: - return bits != 0; - default: - return false; - } -} - -/* Returns true if: - * representation is valid, and - * there is at least one channel bit set which _could_ correspond to an output channel, and - * there are no channel bits set which could _not_ correspond to an output channel. - * Otherwise returns false. - */ -static inline bool audio_is_output_channel(audio_channel_mask_t channel) -{ - uint32_t bits = audio_channel_mask_get_bits(channel); - switch (audio_channel_mask_get_representation(channel)) { - case AUDIO_CHANNEL_REPRESENTATION_POSITION: - if (bits & ~AUDIO_CHANNEL_OUT_ALL) { - bits = 0; - } - // fall through - case AUDIO_CHANNEL_REPRESENTATION_INDEX: - return bits != 0; - default: - return false; - } -} - -/* Returns the number of channels from an input channel mask, - * used in the context of audio input or recording. - * If a channel bit is set which could _not_ correspond to an input channel, - * it is excluded from the count. - * Returns zero if the representation is invalid. - */ -static inline uint32_t audio_channel_count_from_in_mask(audio_channel_mask_t channel) -{ - uint32_t bits = audio_channel_mask_get_bits(channel); - switch (audio_channel_mask_get_representation(channel)) { - case AUDIO_CHANNEL_REPRESENTATION_POSITION: - // TODO: We can now merge with from_out_mask and remove anding - bits &= AUDIO_CHANNEL_IN_ALL; - // fall through - case AUDIO_CHANNEL_REPRESENTATION_INDEX: - return popcount(bits); - default: - return 0; - } -} - -/* Returns the number of channels from an output channel mask, - * used in the context of audio output or playback. - * If a channel bit is set which could _not_ correspond to an output channel, - * it is excluded from the count. - * Returns zero if the representation is invalid. - */ -static inline uint32_t audio_channel_count_from_out_mask(audio_channel_mask_t channel) -{ - uint32_t bits = audio_channel_mask_get_bits(channel); - switch (audio_channel_mask_get_representation(channel)) { - case AUDIO_CHANNEL_REPRESENTATION_POSITION: - // TODO: We can now merge with from_in_mask and remove anding - bits &= AUDIO_CHANNEL_OUT_ALL; - // fall through - case AUDIO_CHANNEL_REPRESENTATION_INDEX: - return popcount(bits); - default: - return 0; - } -} - -/* Derive an output channel mask for position assignment from a channel count. - * This is to be used when the content channel mask is unknown. The 1, 2, 4, 5, 6, 7 and 8 channel - * cases are mapped to the standard game/home-theater layouts, but note that 4 is mapped to quad, - * and not stereo + FC + mono surround. A channel count of 3 is arbitrarily mapped to stereo + FC - * for continuity with stereo. - * Returns the matching channel mask, - * or AUDIO_CHANNEL_NONE if the channel count is zero, - * or AUDIO_CHANNEL_INVALID if the channel count exceeds that of the - * configurations for which a default output channel mask is defined. - */ -static inline audio_channel_mask_t audio_channel_out_mask_from_count(uint32_t channel_count) -{ - uint32_t bits; - switch (channel_count) { - case 0: - return AUDIO_CHANNEL_NONE; - case 1: - bits = AUDIO_CHANNEL_OUT_MONO; - break; - case 2: - bits = AUDIO_CHANNEL_OUT_STEREO; - break; - case 3: - bits = AUDIO_CHANNEL_OUT_STEREO | AUDIO_CHANNEL_OUT_FRONT_CENTER; - break; - case 4: // 4.0 - bits = AUDIO_CHANNEL_OUT_QUAD; - break; - case 5: // 5.0 - bits = AUDIO_CHANNEL_OUT_QUAD | AUDIO_CHANNEL_OUT_FRONT_CENTER; - break; - case 6: // 5.1 - bits = AUDIO_CHANNEL_OUT_5POINT1; - break; - case 7: // 6.1 - bits = AUDIO_CHANNEL_OUT_5POINT1 | AUDIO_CHANNEL_OUT_BACK_CENTER; - break; - case 8: - bits = AUDIO_CHANNEL_OUT_7POINT1; - break; - default: - return AUDIO_CHANNEL_INVALID; - } - return audio_channel_mask_from_representation_and_bits( - AUDIO_CHANNEL_REPRESENTATION_POSITION, bits); -} - -/* Derive an input channel mask for position assignment from a channel count. - * Currently handles only mono and stereo. - * Returns the matching channel mask, - * or AUDIO_CHANNEL_NONE if the channel count is zero, - * or AUDIO_CHANNEL_INVALID if the channel count exceeds that of the - * configurations for which a default input channel mask is defined. - */ -static inline audio_channel_mask_t audio_channel_in_mask_from_count(uint32_t channel_count) -{ - uint32_t bits; - switch (channel_count) { - case 0: - return AUDIO_CHANNEL_NONE; - case 1: - bits = AUDIO_CHANNEL_IN_MONO; - break; - case 2: - bits = AUDIO_CHANNEL_IN_STEREO; - break; - default: - return AUDIO_CHANNEL_INVALID; - } - return audio_channel_mask_from_representation_and_bits( - AUDIO_CHANNEL_REPRESENTATION_POSITION, bits); -} - -/* Derive a channel mask for index assignment from a channel count. - * Returns the matching channel mask, - * or AUDIO_CHANNEL_NONE if the channel count is zero, - * or AUDIO_CHANNEL_INVALID if the channel count exceeds AUDIO_CHANNEL_COUNT_MAX. - */ -static inline audio_channel_mask_t audio_channel_mask_for_index_assignment_from_count( - uint32_t channel_count) -{ - if (channel_count == 0) { - return AUDIO_CHANNEL_NONE; - } - if (channel_count > AUDIO_CHANNEL_COUNT_MAX) { - return AUDIO_CHANNEL_INVALID; - } - uint32_t bits = (1 << channel_count) - 1; - return audio_channel_mask_from_representation_and_bits( - AUDIO_CHANNEL_REPRESENTATION_INDEX, bits); -} - -static inline bool audio_is_valid_format(audio_format_t format) -{ - switch (format & AUDIO_FORMAT_MAIN_MASK) { - case AUDIO_FORMAT_PCM: - switch (format) { - case AUDIO_FORMAT_PCM_16_BIT: - case AUDIO_FORMAT_PCM_8_BIT: - case AUDIO_FORMAT_PCM_32_BIT: - case AUDIO_FORMAT_PCM_8_24_BIT: - case AUDIO_FORMAT_PCM_FLOAT: - case AUDIO_FORMAT_PCM_24_BIT_PACKED: - return true; - default: - return false; - } - /* not reached */ - case AUDIO_FORMAT_MP3: - case AUDIO_FORMAT_AMR_NB: - case AUDIO_FORMAT_AMR_WB: - case AUDIO_FORMAT_AAC: - case AUDIO_FORMAT_HE_AAC_V1: - case AUDIO_FORMAT_HE_AAC_V2: - case AUDIO_FORMAT_VORBIS: - case AUDIO_FORMAT_OPUS: - case AUDIO_FORMAT_AC3: - case AUDIO_FORMAT_E_AC3: - return true; - default: - return false; - } -} - -static inline bool audio_is_linear_pcm(audio_format_t format) -{ - return ((format & AUDIO_FORMAT_MAIN_MASK) == AUDIO_FORMAT_PCM); -} - -static inline size_t audio_bytes_per_sample(audio_format_t format) -{ - size_t size = 0; - - switch (format) { - case AUDIO_FORMAT_PCM_32_BIT: - case AUDIO_FORMAT_PCM_8_24_BIT: - size = sizeof(int32_t); - break; - case AUDIO_FORMAT_PCM_24_BIT_PACKED: - size = sizeof(uint8_t) * 3; - break; - case AUDIO_FORMAT_PCM_16_BIT: - size = sizeof(int16_t); - break; - case AUDIO_FORMAT_PCM_8_BIT: - size = sizeof(uint8_t); - break; - case AUDIO_FORMAT_PCM_FLOAT: - size = sizeof(float); - break; - default: - break; - } - return size; -} - -/* converts device address to string sent to audio HAL via set_parameters */ -static char *audio_device_address_to_parameter(audio_devices_t device, const char *address) -{ - const size_t kSize = AUDIO_DEVICE_MAX_ADDRESS_LEN + sizeof("a2dp_sink_address="); - char param[kSize]; - - if (device & AUDIO_DEVICE_OUT_ALL_A2DP) - snprintf(param, kSize, "%s=%s", "a2dp_sink_address", address); - else if (device & AUDIO_DEVICE_OUT_REMOTE_SUBMIX) - snprintf(param, kSize, "%s=%s", "mix", address); - else - snprintf(param, kSize, "%s", address); - - return strdup(param); -} - - -__END_DECLS - -#endif // ANDROID_AUDIO_CORE_H
diff --git a/include/system/audio_policy.h b/include/system/audio_policy.h deleted file mode 100644 index 2881104..0000000 --- a/include/system/audio_policy.h +++ /dev/null
@@ -1,103 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#ifndef ANDROID_AUDIO_POLICY_CORE_H -#define ANDROID_AUDIO_POLICY_CORE_H - -#include <stdint.h> -#include <sys/cdefs.h> -#include <sys/types.h> - -#include <cutils/bitops.h> - -__BEGIN_DECLS - -/* The enums were moved here mostly from - * frameworks/base/include/media/AudioSystem.h - */ - -/* device categories used for audio_policy->set_force_use() */ -typedef enum { - AUDIO_POLICY_FORCE_NONE, - AUDIO_POLICY_FORCE_SPEAKER, - AUDIO_POLICY_FORCE_HEADPHONES, - AUDIO_POLICY_FORCE_BT_SCO, - AUDIO_POLICY_FORCE_BT_A2DP, - AUDIO_POLICY_FORCE_WIRED_ACCESSORY, - AUDIO_POLICY_FORCE_BT_CAR_DOCK, - AUDIO_POLICY_FORCE_BT_DESK_DOCK, - AUDIO_POLICY_FORCE_ANALOG_DOCK, - AUDIO_POLICY_FORCE_DIGITAL_DOCK, - AUDIO_POLICY_FORCE_NO_BT_A2DP, /* A2DP sink is not preferred to speaker or wired HS */ - AUDIO_POLICY_FORCE_SYSTEM_ENFORCED, - AUDIO_POLICY_FORCE_HDMI_SYSTEM_AUDIO_ENFORCED, - - AUDIO_POLICY_FORCE_CFG_CNT, - AUDIO_POLICY_FORCE_CFG_MAX = AUDIO_POLICY_FORCE_CFG_CNT - 1, - - AUDIO_POLICY_FORCE_DEFAULT = AUDIO_POLICY_FORCE_NONE, -} audio_policy_forced_cfg_t; - -/* usages used for audio_policy->set_force_use() */ -typedef enum { - AUDIO_POLICY_FORCE_FOR_COMMUNICATION, - AUDIO_POLICY_FORCE_FOR_MEDIA, - AUDIO_POLICY_FORCE_FOR_RECORD, - AUDIO_POLICY_FORCE_FOR_DOCK, - AUDIO_POLICY_FORCE_FOR_SYSTEM, - AUDIO_POLICY_FORCE_FOR_HDMI_SYSTEM_AUDIO, - - AUDIO_POLICY_FORCE_USE_CNT, - AUDIO_POLICY_FORCE_USE_MAX = AUDIO_POLICY_FORCE_USE_CNT - 1, -} audio_policy_force_use_t; - -/* device connection states used for audio_policy->set_device_connection_state() - */ -typedef enum { - AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, - AUDIO_POLICY_DEVICE_STATE_AVAILABLE, - - AUDIO_POLICY_DEVICE_STATE_CNT, - AUDIO_POLICY_DEVICE_STATE_MAX = AUDIO_POLICY_DEVICE_STATE_CNT - 1, -} audio_policy_dev_state_t; - -typedef enum { - /* Used to generate a tone to notify the user of a - * notification/alarm/ringtone while they are in a call. */ - AUDIO_POLICY_TONE_IN_CALL_NOTIFICATION = 0, - - AUDIO_POLICY_TONE_CNT, - AUDIO_POLICY_TONE_MAX = AUDIO_POLICY_TONE_CNT - 1, -} audio_policy_tone_t; - - -static inline bool audio_is_low_visibility(audio_stream_type_t stream) -{ - switch (stream) { - case AUDIO_STREAM_SYSTEM: - case AUDIO_STREAM_NOTIFICATION: - case AUDIO_STREAM_RING: - return true; - default: - return false; - } -} - - -__END_DECLS - -#endif // ANDROID_AUDIO_POLICY_CORE_H
diff --git a/include/system/camera.h b/include/system/camera.h index 7a4dd53..5d0873a 100644 --- a/include/system/camera.h +++ b/include/system/camera.h
@@ -174,6 +174,22 @@ * count is non-positive or too big to be realized. */ CAMERA_CMD_SET_VIDEO_BUFFER_COUNT = 10, + + /** + * Configure an explicit format to use for video recording metadata mode. + * This can be used to switch the format from the + * default IMPLEMENTATION_DEFINED gralloc format to some other + * device-supported format, and the default dataspace from the BT_709 color + * space to some other device-supported dataspace. arg1 is the HAL pixel + * format, and arg2 is the HAL dataSpace. This command returns + * INVALID_OPERATION error if it is sent after video recording is started, + * or the command is not supported at all. + * + * If the gralloc format is set to a format other than + * IMPLEMENTATION_DEFINED, then HALv3 devices will use gralloc usage flags + * of SW_READ_OFTEN. + */ + CAMERA_CMD_SET_VIDEO_FORMAT = 11 }; /** camera fatal errors */ @@ -194,7 +210,12 @@ /** The facing of the camera is opposite to that of the screen. */ CAMERA_FACING_BACK = 0, /** The facing of the camera is the same as that of the screen. */ - CAMERA_FACING_FRONT = 1 + CAMERA_FACING_FRONT = 1, + /** + * The facing of the camera is not fixed relative to the screen. + * The cameras with this facing are external cameras, e.g. USB cameras. + */ + CAMERA_FACING_EXTERNAL = 2 }; enum {
diff --git a/include/system/graphics.h b/include/system/graphics.h index efd48cb..afd9f7b 100644 --- a/include/system/graphics.h +++ b/include/system/graphics.h
@@ -58,11 +58,6 @@ HAL_PIXEL_FORMAT_RGB_565 = 4, HAL_PIXEL_FORMAT_BGRA_8888 = 5, - // Deprecated sRGB formats for source code compatibility - // Not for use in new code - HAL_PIXEL_FORMAT_sRGB_A_8888 = 0xC, - HAL_PIXEL_FORMAT_sRGB_X_8888 = 0xD, - /* * 0x100 - 0x1FF * @@ -152,7 +147,8 @@ * When used with ANativeWindow, the dataSpace field describes the color * space of the buffer, except that dataSpace field * HAL_DATASPACE_DEPTH indicates that this buffer contains a depth - * image where each sample is a distance value measured by a depth camera. + * image where each sample is a distance value measured by a depth camera, + * plus an associated confidence value. */ HAL_PIXEL_FORMAT_Y16 = 0x20363159, @@ -194,9 +190,6 @@ */ HAL_PIXEL_FORMAT_RAW16 = 0x20, - // Temporary alias for source code compatibility; do not use in new code - HAL_PIXEL_FORMAT_RAW_SENSOR = HAL_PIXEL_FORMAT_RAW16, - /* * Android RAW10 format: * @@ -252,6 +245,56 @@ HAL_PIXEL_FORMAT_RAW10 = 0x25, /* + * Android RAW12 format: + * + * This format is exposed outside of camera HAL to applications. + * + * RAW12 is a single-channel, 12-bit per pixel, densely packed in each row, + * unprocessed format, usually representing raw Bayer-pattern images coming from + * an image sensor. + * + * In an image buffer with this format, starting from the first pixel of each + * row, each two consecutive pixels are packed into 3 bytes (24 bits). The first + * and second byte contains the top 8 bits of first and second pixel. The third + * byte contains the 4 least significant bits of the two pixels, the exact layout + * data for each two consecutive pixels is illustrated below (Pi[j] stands for + * the jth bit of the ith pixel): + * + * bit 7 bit 0 + * ======|======|======|======|======|======|======|======| + * Byte 0: |P0[11]|P0[10]|P0[ 9]|P0[ 8]|P0[ 7]|P0[ 6]|P0[ 5]|P0[ 4]| + * |------|------|------|------|------|------|------|------| + * Byte 1: |P1[11]|P1[10]|P1[ 9]|P1[ 8]|P1[ 7]|P1[ 6]|P1[ 5]|P1[ 4]| + * |------|------|------|------|------|------|------|------| + * Byte 2: |P1[ 3]|P1[ 2]|P1[ 1]|P1[ 0]|P0[ 3]|P0[ 2]|P0[ 1]|P0[ 0]| + * ======================================================= + * + * This format assumes: + * - a width multiple of 4 pixels + * - an even height + * - a vertical stride equal to the height + * - strides are specified in bytes, not in pixels + * + * size = stride * height + * + * When stride is equal to width * (12 / 8), there will be no padding bytes at + * the end of each row, the entire image data is densely packed. When stride is + * larger than width * (12 / 8), padding bytes will be present at the end of + * each row (including the last row). + * + * This format must be accepted by the gralloc module when used with the + * following usage flags: + * - GRALLOC_USAGE_HW_CAMERA_* + * - GRALLOC_USAGE_SW_* + * - GRALLOC_USAGE_RENDERSCRIPT + * + * When used with ANativeWindow, the dataSpace field should be + * HAL_DATASPACE_ARBITRARY, as raw image sensor buffers require substantial + * extra metadata to define. + */ + HAL_PIXEL_FORMAT_RAW12 = 0x26, + + /* * Android opaque RAW format: * * This format is exposed outside of the camera HAL to applications. @@ -316,18 +359,18 @@ HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED = 0x22, /* - * Android flexible YCbCr formats + * Android flexible YCbCr 4:2:0 formats * - * This format allows platforms to use an efficient YCbCr/YCrCb buffer - * layout, while still describing the buffer layout in a way accessible to - * the CPU in a device-independent manner. While called YCbCr, it can be + * This format allows platforms to use an efficient YCbCr/YCrCb 4:2:0 + * buffer layout, while still describing the general format in a + * layout-independent manner. While called YCbCr, it can be * used to describe formats with either chromatic ordering, as well as * whole planar or semiplanar layouts. * * struct android_ycbcr (below) is the the struct used to describe it. * * This format must be accepted by the gralloc module when - * USAGE_HW_CAMERA_WRITE and USAGE_SW_READ_* are set. + * USAGE_SW_WRITE_* or USAGE_SW_READ_* are set. * * This format is locked for use by gralloc's (*lock_ycbcr) method, and * locking with the (*lock) method will return an error. @@ -337,6 +380,62 @@ */ HAL_PIXEL_FORMAT_YCbCr_420_888 = 0x23, + /* + * Android flexible YCbCr 4:2:2 formats + * + * This format allows platforms to use an efficient YCbCr/YCrCb 4:2:2 + * buffer layout, while still describing the general format in a + * layout-independent manner. While called YCbCr, it can be + * used to describe formats with either chromatic ordering, as well as + * whole planar or semiplanar layouts. + * + * This format is currently only used by SW readable buffers + * produced by MediaCodecs, so the gralloc module can ignore this format. + */ + HAL_PIXEL_FORMAT_YCbCr_422_888 = 0x27, + + /* + * Android flexible YCbCr 4:4:4 formats + * + * This format allows platforms to use an efficient YCbCr/YCrCb 4:4:4 + * buffer layout, while still describing the general format in a + * layout-independent manner. While called YCbCr, it can be + * used to describe formats with either chromatic ordering, as well as + * whole planar or semiplanar layouts. + * + * This format is currently only used by SW readable buffers + * produced by MediaCodecs, so the gralloc module can ignore this format. + */ + HAL_PIXEL_FORMAT_YCbCr_444_888 = 0x28, + + /* + * Android flexible RGB 888 formats + * + * This format allows platforms to use an efficient RGB/BGR/RGBX/BGRX + * buffer layout, while still describing the general format in a + * layout-independent manner. While called RGB, it can be + * used to describe formats with either color ordering and optional + * padding, as well as whole planar layout. + * + * This format is currently only used by SW readable buffers + * produced by MediaCodecs, so the gralloc module can ignore this format. + */ + HAL_PIXEL_FORMAT_FLEX_RGB_888 = 0x29, + + /* + * Android flexible RGBA 8888 formats + * + * This format allows platforms to use an efficient RGBA/BGRA/ARGB/ABGR + * buffer layout, while still describing the general format in a + * layout-independent manner. While called RGBA, it can be + * used to describe formats with any of the component orderings, as + * well as whole planar layout. + * + * This format is currently only used by SW readable buffers + * produced by MediaCodecs, so the gralloc module can ignore this format. + */ + HAL_PIXEL_FORMAT_FLEX_RGBA_8888 = 0x2A, + /* Legacy formats (deprecated), used by ImageFormat.java */ HAL_PIXEL_FORMAT_YCbCr_422_SP = 0x10, // NV16 HAL_PIXEL_FORMAT_YCrCb_420_SP = 0x11, // NV21 @@ -383,25 +482,31 @@ * When locking a native buffer of the above format and dataSpace value, * the vaddr pointer can be cast to this structure. * - * A variable-length list of (x,y,z) 3D points, as floats. + * A variable-length list of (x,y,z, confidence) 3D points, as floats. (x, y, + * z) represents a measured point's position, with the coordinate system defined + * by the data source. Confidence represents the estimated likelihood that this + * measurement is correct. It is between 0.f and 1.f, inclusive, with 1.f == + * 100% confidence. * * @num_points is the number of points in the list * * @xyz_points is the flexible array of floating-point values. - * It contains (num_points) * 3 floats. + * It contains (num_points) * 4 floats. * * For example: * android_depth_points d = get_depth_buffer(); * struct { - * float x; float y; float z; + * float x; float y; float z; float confidence; * } firstPoint, lastPoint; * - * firstPoint.x = d.xyz_points[0]; - * firstPoint.y = d.xyz_points[1]; - * firstPoint.z = d.xyz_points[2]; - * lastPoint.x = d.xyz_points[(d.num_points - 1) * 3 + 0]; - * lastPoint.y = d.xyz_points[(d.num_points - 1) * 3 + 1]; - * lastPoint.z = d.xyz_points[(d.num_points - 1) * 3 + 2]; + * firstPoint.x = d.xyzc_points[0]; + * firstPoint.y = d.xyzc_points[1]; + * firstPoint.z = d.xyzc_points[2]; + * firstPoint.confidence = d.xyzc_points[3]; + * lastPoint.x = d.xyzc_points[(d.num_points - 1) * 4 + 0]; + * lastPoint.y = d.xyzc_points[(d.num_points - 1) * 4 + 1]; + * lastPoint.z = d.xyzc_points[(d.num_points - 1) * 4 + 2]; + * lastPoint.confidence = d.xyzc_points[(d.num_points - 1) * 4 + 3]; */ struct android_depth_points { @@ -410,7 +515,7 @@ /** reserved for future use, set to 0 by gralloc's (*lock)() */ uint32_t reserved[8]; - float xyz_points[]; + float xyzc_points[]; }; /** @@ -632,9 +737,18 @@ /* * The buffer contains depth ranging measurements from a depth camera. * This value is valid with formats: - * HAL_PIXEL_FORMAT_Y16: 16-bit single channel depth image. + * HAL_PIXEL_FORMAT_Y16: 16-bit samples, consisting of a depth measurement + * and an associated confidence value. The 3 MSBs of the sample make + * up the confidence value, and the low 13 LSBs of the sample make up + * the depth measurement. + * For the confidence section, 0 means 100% confidence, 1 means 0% + * confidence. The mapping to a linear float confidence value between + * 0.f and 1.f can be obtained with + * float confidence = (((depthSample >> 13) - 1) & 0x7) / 7.0f; + * The depth measurement can be extracted simply with + * uint16_t range = (depthSample & 0x1FFF); * HAL_PIXEL_FORMAT_BLOB: A depth point cloud, as - * a variable-length float (x,y,z) coordinate point list. + * a variable-length float (x,y,z, confidence) coordinate point list. * The point cloud will be represented with the android_depth_points * structure. */
diff --git a/include/system/radio.h b/include/system/radio.h new file mode 100644 index 0000000..a088526 --- /dev/null +++ b/include/system/radio.h
@@ -0,0 +1,247 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_RADIO_H +#define ANDROID_RADIO_H + +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <sys/cdefs.h> +#include <sys/types.h> + + +#define RADIO_NUM_BANDS_MAX 16 +#define RADIO_NUM_SPACINGS_MAX 16 +#define RADIO_STRING_LEN_MAX 128 + +/* + * Radio hardware module class. A given radio hardware module HAL is of one class + * only. The platform can not have more than one hardware module of each class. + * Current version of the framework only supports RADIO_CLASS_AM_FM. + */ +typedef enum { + RADIO_CLASS_AM_FM = 0, /* FM (including HD radio) and AM */ + RADIO_CLASS_SAT = 1, /* Satellite Radio */ + RADIO_CLASS_DT = 2, /* Digital Radio (DAB) */ +} radio_class_t; + +/* value for field "type" of radio band described in struct radio_hal_band_config */ +typedef enum { + RADIO_BAND_AM = 0, /* Amplitude Modulation band: LW, MW, SW */ + RADIO_BAND_FM = 1, /* Frequency Modulation band: FM */ + RADIO_BAND_FM_HD = 2, /* FM HD Radio / DRM (IBOC) */ + RADIO_BAND_AM_HD = 3, /* AM HD Radio / DRM (IBOC) */ +} radio_band_t; + +/* RDS variant implemented. A struct radio_hal_fm_band_config can list none or several. */ +enum { + RADIO_RDS_NONE = 0x0, + RADIO_RDS_WORLD = 0x01, + RADIO_RDS_US = 0x02, +}; +typedef unsigned int radio_rds_t; + +/* FM deemphasis variant implemented. A struct radio_hal_fm_band_config can list one or more. */ +enum { + RADIO_DEEMPHASIS_50 = 0x1, + RADIO_DEEMPHASIS_75 = 0x2, +}; +typedef unsigned int radio_deemphasis_t; + +/* Region a particular radio band configuration corresponds to. Not used at the HAL. + * Derived by the framework when converting the band descriptors retrieved from the HAL to + * individual band descriptors for each supported region. */ +typedef enum { + RADIO_REGION_NONE = -1, + RADIO_REGION_ITU_1 = 0, + RADIO_REGION_ITU_2 = 1, + RADIO_REGION_OIRT = 2, + RADIO_REGION_JAPAN = 3, + RADIO_REGION_KOREA = 4, +} radio_region_t; + +/* scanning direction for scan() and step() tuner APIs */ +typedef enum { + RADIO_DIRECTION_UP, + RADIO_DIRECTION_DOWN +} radio_direction_t; + +/* unique handle allocated to a radio module */ +typedef unsigned int radio_handle_t; + +/* Opaque meta data structure used by radio meta data API (see system/radio_metadata.h) */ +typedef struct radio_medtadata radio_metadata_t; + + +/* Additional attributes for an FM band configuration */ +typedef struct radio_hal_fm_band_config { + radio_deemphasis_t deemphasis; /* deemphasis variant */ + bool stereo; /* stereo supported */ + radio_rds_t rds; /* RDS variants supported */ + bool ta; /* Traffic Announcement supported */ + bool af; /* Alternate Frequency supported */ +} radio_hal_fm_band_config_t; + +/* Additional attributes for an AM band configuration */ +typedef struct radio_hal_am_band_config { + bool stereo; /* stereo supported */ +} radio_hal_am_band_config_t; + +/* Radio band configuration. Describes a given band supported by the radio module. + * The HAL can expose only one band per type with the the maximum range supported and all options. + * THe framework will derive the actual regions were this module can operate and expose separate + * band configurations for applications to chose from. */ +typedef struct radio_hal_band_config { + radio_band_t type; + bool antenna_connected; + unsigned int lower_limit; + unsigned int upper_limit; + unsigned int num_spacings; + unsigned int spacings[RADIO_NUM_SPACINGS_MAX]; + union { + radio_hal_fm_band_config_t fm; + radio_hal_am_band_config_t am; + }; +} radio_hal_band_config_t; + +/* Used internally by the framework to represent a band for s specific region */ +typedef struct radio_band_config { + radio_region_t region; + radio_hal_band_config_t band; +} radio_band_config_t; + + +/* Exposes properties of a given hardware radio module. + * NOTE: current framework implementation supports only one audio source (num_audio_sources = 1). + * The source corresponds to AUDIO_DEVICE_IN_FM_TUNER. + * If more than one tuner is supported (num_tuners > 1), only one can be connected to the audio + * source. */ +typedef struct radio_hal_properties { + radio_class_t class_id; /* Class of this module. E.g RADIO_CLASS_AM_FM */ + char implementor[RADIO_STRING_LEN_MAX]; /* implementor name */ + char product[RADIO_STRING_LEN_MAX]; /* product name */ + char version[RADIO_STRING_LEN_MAX]; /* product version */ + char serial[RADIO_STRING_LEN_MAX]; /* serial number (for subscription services) */ + unsigned int num_tuners; /* number of tuners controllable independently */ + unsigned int num_audio_sources; /* number of audio sources driven simultaneously */ + bool supports_capture; /* the hardware supports capture of audio source audio HAL */ + unsigned int num_bands; /* number of band descriptors */ + radio_hal_band_config_t bands[RADIO_NUM_BANDS_MAX]; /* band descriptors */ +} radio_hal_properties_t; + +/* Used internally by the framework. Same information as in struct radio_hal_properties plus a + * unique handle and one band configuration per region. */ +typedef struct radio_properties { + radio_handle_t handle; + radio_class_t class_id; + char implementor[RADIO_STRING_LEN_MAX]; + char product[RADIO_STRING_LEN_MAX]; + char version[RADIO_STRING_LEN_MAX]; + char serial[RADIO_STRING_LEN_MAX]; + unsigned int num_tuners; + unsigned int num_audio_sources; + bool supports_capture; + unsigned int num_bands; + radio_band_config_t bands[RADIO_NUM_BANDS_MAX]; +} radio_properties_t; + +/* Radio program information. Returned by the HAL with event RADIO_EVENT_TUNED. + * Contains information on currently tuned channel. + */ +typedef struct radio_program_info { + unsigned int channel; /* current channel. (e.g kHz for band type RADIO_BAND_FM) */ + unsigned int sub_channel; /* current sub channel. (used for RADIO_BAND_FM_HD) */ + bool tuned; /* tuned to a program or not */ + bool stereo; /* program is stereo or not */ + bool digital; /* digital program or not (e.g HD Radio program) */ + unsigned int signal_strength; /* signal strength from 0 to 100 */ + radio_metadata_t *metadata; /* non null if meta data are present (e.g PTY, song title ...) */ +} radio_program_info_t; + + +/* Events sent to the framework via the HAL callback. An event can notify the completion of an + * asynchronous command (configuration, tune, scan ...) or a spontaneous change (antenna connection, + * failure, AF switching, meta data reception... */ +enum { + RADIO_EVENT_HW_FAILURE = 0, /* hardware module failure. Requires reopening the tuner */ + RADIO_EVENT_CONFIG = 1, /* configuration change completed */ + RADIO_EVENT_ANTENNA = 2, /* Antenna connected, disconnected */ + RADIO_EVENT_TUNED = 3, /* tune, step, scan completed */ + RADIO_EVENT_METADATA = 4, /* New meta data received */ + RADIO_EVENT_TA = 5, /* Traffic announcement start or stop */ + RADIO_EVENT_AF_SWITCH = 6, /* Switch to Alternate Frequency */ + // begin framework only events + RADIO_EVENT_CONTROL = 100, /* loss/gain of tuner control */ + RADIO_EVENT_SERVER_DIED = 101, /* radio service died */ +}; +typedef unsigned int radio_event_type_t; + +/* Event passed to the framework by the HAL callback */ +typedef struct radio_hal_event { + radio_event_type_t type; /* event type */ + int status; /* used by RADIO_EVENT_CONFIG, RADIO_EVENT_TUNED */ + union { + bool on; /* RADIO_EVENT_ANTENNA, RADIO_EVENT_TA */ + radio_hal_band_config_t config; /* RADIO_EVENT_CONFIG */ + radio_program_info_t info; /* RADIO_EVENT_TUNED, RADIO_EVENT_AF_SWITCH */ + radio_metadata_t *metadata; /* RADIO_EVENT_METADATA */ + }; +} radio_hal_event_t; + +/* Used internally by the framework. Same information as in struct radio_hal_event */ +typedef struct radio_event { + radio_event_type_t type; + int status; + union { + bool on; + radio_band_config_t config; + radio_program_info_t info; + radio_metadata_t *metadata; /* offset from start of struct when in shared memory */ + }; +} radio_event_t; + + +static radio_rds_t radio_rds_for_region(bool rds, radio_region_t region) { + if (!rds) + return RADIO_RDS_NONE; + switch(region) { + case RADIO_REGION_ITU_1: + case RADIO_REGION_OIRT: + case RADIO_REGION_JAPAN: + case RADIO_REGION_KOREA: + return RADIO_RDS_WORLD; + case RADIO_REGION_ITU_2: + return RADIO_RDS_US; + default: + return RADIO_REGION_NONE; + } +} + +static radio_deemphasis_t radio_demephasis_for_region(radio_region_t region) { + switch(region) { + case RADIO_REGION_KOREA: + case RADIO_REGION_ITU_2: + return RADIO_DEEMPHASIS_75; + case RADIO_REGION_ITU_1: + case RADIO_REGION_OIRT: + case RADIO_REGION_JAPAN: + default: + return RADIO_DEEMPHASIS_50; + } +} + +#endif // ANDROID_RADIO_H
diff --git a/include/system/sound_trigger.h b/include/system/sound_trigger.h deleted file mode 100644 index 773e4f7..0000000 --- a/include/system/sound_trigger.h +++ /dev/null
@@ -1,223 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_SOUND_TRIGGER_H -#define ANDROID_SOUND_TRIGGER_H - -#include <stdbool.h> -#include <system/audio.h> - -#define SOUND_TRIGGER_MAX_STRING_LEN 64 /* max length of strings in properties or - descriptor structs */ -#define SOUND_TRIGGER_MAX_LOCALE_LEN 6 /* max length of locale string. e.g en_US */ -#define SOUND_TRIGGER_MAX_USERS 10 /* max number of concurrent users */ -#define SOUND_TRIGGER_MAX_PHRASES 10 /* max number of concurrent phrases */ - -typedef enum { - SOUND_TRIGGER_STATE_NO_INIT = -1, /* The sound trigger service is not initialized */ - SOUND_TRIGGER_STATE_ENABLED = 0, /* The sound trigger service is enabled */ - SOUND_TRIGGER_STATE_DISABLED = 1 /* The sound trigger service is disabled */ -} sound_trigger_service_state_t; - -#define RECOGNITION_MODE_VOICE_TRIGGER 0x1 /* simple voice trigger */ -#define RECOGNITION_MODE_USER_IDENTIFICATION 0x2 /* trigger only if one user in model identified */ -#define RECOGNITION_MODE_USER_AUTHENTICATION 0x4 /* trigger only if one user in mode - authenticated */ -#define RECOGNITION_STATUS_SUCCESS 0 -#define RECOGNITION_STATUS_ABORT 1 -#define RECOGNITION_STATUS_FAILURE 2 - -#define SOUND_MODEL_STATUS_UPDATED 0 - -typedef enum { - SOUND_MODEL_TYPE_UNKNOWN = -1, /* use for unspecified sound model type */ - SOUND_MODEL_TYPE_KEYPHRASE = 0 /* use for key phrase sound models */ -} sound_trigger_sound_model_type_t; - -typedef struct sound_trigger_uuid_s { - unsigned int timeLow; - unsigned short timeMid; - unsigned short timeHiAndVersion; - unsigned short clockSeq; - unsigned char node[6]; -} sound_trigger_uuid_t; - -/* - * sound trigger implementation descriptor read by the framework via get_properties(). - * Used by SoundTrigger service to report to applications and manage concurrency and policy. - */ -struct sound_trigger_properties { - char implementor[SOUND_TRIGGER_MAX_STRING_LEN]; /* implementor name */ - char description[SOUND_TRIGGER_MAX_STRING_LEN]; /* implementation description */ - unsigned int version; /* implementation version */ - sound_trigger_uuid_t uuid; /* unique implementation ID. - Must change with version each version */ - unsigned int max_sound_models; /* maximum number of concurrent sound models - loaded */ - unsigned int max_key_phrases; /* maximum number of key phrases */ - unsigned int max_users; /* maximum number of concurrent users detected */ - unsigned int recognition_modes; /* all supported modes. - e.g RECOGNITION_MODE_VOICE_TRIGGER */ - bool capture_transition; /* supports seamless transition from detection - to capture */ - unsigned int max_buffer_ms; /* maximum buffering capacity in ms if - capture_transition is true*/ - bool concurrent_capture; /* supports capture by other use cases while - detection is active */ - bool trigger_in_event; /* returns the trigger capture in event */ - unsigned int power_consumption_mw; /* Rated power consumption when detection is active - with TDB silence/sound/speech ratio */ -}; - -typedef int sound_trigger_module_handle_t; - -struct sound_trigger_module_descriptor { - sound_trigger_module_handle_t handle; - struct sound_trigger_properties properties; -}; - -typedef int sound_model_handle_t; - -/* - * Generic sound model descriptor. This struct is the header of a larger block passed to - * load_sound_model() and containing the binary data of the sound model. - * Proprietary representation of users in binary data must match information indicated - * by users field - */ -struct sound_trigger_sound_model { - sound_trigger_sound_model_type_t type; /* model type. e.g. SOUND_MODEL_TYPE_KEYPHRASE */ - sound_trigger_uuid_t uuid; /* unique sound model ID. */ - sound_trigger_uuid_t vendor_uuid; /* unique vendor ID. Identifies the engine the - sound model was build for */ - unsigned int data_size; /* size of opaque model data */ - unsigned int data_offset; /* offset of opaque data start from head of struct - (e.g sizeof struct sound_trigger_sound_model) */ -}; - -/* key phrase descriptor */ -struct sound_trigger_phrase { - unsigned int id; /* keyphrase ID */ - unsigned int recognition_mode; /* recognition modes supported by this key phrase */ - unsigned int num_users; /* number of users in the key phrase */ - unsigned int users[SOUND_TRIGGER_MAX_USERS]; /* users ids: (not uid_t but sound trigger - specific IDs */ - char locale[SOUND_TRIGGER_MAX_LOCALE_LEN]; /* locale - JAVA Locale style (e.g. en_US) */ - char text[SOUND_TRIGGER_MAX_STRING_LEN]; /* phrase text in UTF-8 format. */ -}; - -/* - * Specialized sound model for key phrase detection. - * Proprietary representation of key phrases in binary data must match information indicated - * by phrases field - */ -struct sound_trigger_phrase_sound_model { - struct sound_trigger_sound_model common; - unsigned int num_phrases; /* number of key phrases in model */ - struct sound_trigger_phrase phrases[SOUND_TRIGGER_MAX_PHRASES]; -}; - - -/* - * Generic recognition event sent via recognition callback - */ -struct sound_trigger_recognition_event { - int status; /* recognition status e.g. - RECOGNITION_STATUS_SUCCESS */ - sound_trigger_sound_model_type_t type; /* event type, same as sound model type. - e.g. SOUND_MODEL_TYPE_KEYPHRASE */ - sound_model_handle_t model; /* loaded sound model that triggered the - event */ - bool capture_available; /* it is possible to capture audio from this - utterance buffered by the - implementation */ - int capture_session; /* audio session ID. framework use */ - int capture_delay_ms; /* delay in ms between end of model - detection and start of audio available - for capture. A negative value is possible - (e.g. if key phrase is also available for - capture */ - int capture_preamble_ms; /* duration in ms of audio captured - before the start of the trigger. - 0 if none. */ - bool trigger_in_data; /* the opaque data is the capture of - the trigger sound */ - audio_config_t audio_config; /* audio format of either the trigger in - event data or to use for capture of the - rest of the utterance */ - unsigned int data_size; /* size of opaque event data */ - unsigned int data_offset; /* offset of opaque data start from start of - this struct (e.g sizeof struct - sound_trigger_phrase_recognition_event) */ -}; - -/* - * Confidence level for each user in struct sound_trigger_phrase_recognition_extra - */ -struct sound_trigger_confidence_level { - unsigned int user_id; /* user ID */ - unsigned int level; /* confidence level in percent (0 - 100). - - min level for recognition configuration - - detected level for recognition event */ -}; - -/* - * Specialized recognition event for key phrase detection - */ -struct sound_trigger_phrase_recognition_extra { - unsigned int id; /* keyphrase ID */ - unsigned int recognition_modes; /* recognition modes used for this keyphrase */ - unsigned int confidence_level; /* confidence level for mode RECOGNITION_MODE_VOICE_TRIGGER */ - unsigned int num_levels; /* number of user confidence levels */ - struct sound_trigger_confidence_level levels[SOUND_TRIGGER_MAX_USERS]; -}; - -struct sound_trigger_phrase_recognition_event { - struct sound_trigger_recognition_event common; - unsigned int num_phrases; - struct sound_trigger_phrase_recognition_extra phrase_extras[SOUND_TRIGGER_MAX_PHRASES]; -}; - -/* - * configuration for sound trigger capture session passed to start_recognition() - */ -struct sound_trigger_recognition_config { - audio_io_handle_t capture_handle; /* IO handle that will be used for capture. - N/A if capture_requested is false */ - audio_devices_t capture_device; /* input device requested for detection capture */ - bool capture_requested; /* capture and buffer audio for this recognition - instance */ - unsigned int num_phrases; /* number of key phrases recognition extras */ - struct sound_trigger_phrase_recognition_extra phrases[SOUND_TRIGGER_MAX_PHRASES]; - /* configuration for each key phrase */ - unsigned int data_size; /* size of opaque capture configuration data */ - unsigned int data_offset; /* offset of opaque data start from start of this struct - (e.g sizeof struct sound_trigger_recognition_config) */ -}; - -/* - * Event sent via load sound model callback - */ -struct sound_trigger_model_event { - int status; /* sound model status e.g. SOUND_MODEL_STATUS_UPDATED */ - sound_model_handle_t model; /* loaded sound model that triggered the event */ - unsigned int data_size; /* size of event data if any. Size of updated sound model if - status is SOUND_MODEL_STATUS_UPDATED */ - unsigned int data_offset; /* offset of data start from start of this struct - (e.g sizeof struct sound_trigger_model_event) */ -}; - - -#endif // ANDROID_SOUND_TRIGGER_H
diff --git a/include/system/window.h b/include/system/window.h index a875427..508ce00 100644 --- a/include/system/window.h +++ b/include/system/window.h
@@ -267,7 +267,16 @@ * The default data space for the buffers as set by the consumer. * The values are defined in graphics.h. */ - NATIVE_WINDOW_DEFAULT_DATASPACE = 12 + NATIVE_WINDOW_DEFAULT_DATASPACE = 12, + + /* + * Returns the age of the contents of the most recently dequeued buffer as + * the number of frames that have elapsed since it was last queued. For + * example, if the window is double-buffered, the age of any given buffer in + * steady state will be 2. If the dequeued buffer has never been queued, its + * age will be 0. + */ + NATIVE_WINDOW_BUFFER_AGE = 13, }; /* Valid operations for the (*perform)() hook.
diff --git a/include/sysutils/NetlinkEvent.h b/include/sysutils/NetlinkEvent.h index 4fa49c5..b80f3ea 100644 --- a/include/sysutils/NetlinkEvent.h +++ b/include/sysutils/NetlinkEvent.h
@@ -21,25 +21,29 @@ #define NL_PARAMS_MAX 32 class NetlinkEvent { +public: + enum class Action { + kUnknown = 0, + kAdd = 1, + kRemove = 2, + kChange = 3, + kLinkUp = 4, + kLinkDown = 5, + kAddressUpdated = 6, + kAddressRemoved = 7, + kRdnss = 8, + kRouteUpdated = 9, + kRouteRemoved = 10, + }; + +private: int mSeq; char *mPath; - int mAction; + Action mAction; char *mSubsystem; char *mParams[NL_PARAMS_MAX]; public: - const static int NlActionUnknown; - const static int NlActionAdd; - const static int NlActionRemove; - const static int NlActionChange; - const static int NlActionLinkDown; - const static int NlActionLinkUp; - const static int NlActionAddressUpdated; - const static int NlActionAddressRemoved; - const static int NlActionRdnss; - const static int NlActionRouteUpdated; - const static int NlActionRouteRemoved; - NetlinkEvent(); virtual ~NetlinkEvent(); @@ -47,7 +51,7 @@ const char *findParam(const char *paramName); const char *getSubsystem() { return mSubsystem; } - int getAction() { return mAction; } + Action getAction() { return mAction; } void dump();
diff --git a/include/usbhost/usbhost.h b/include/usbhost/usbhost.h index d26e931..4350ec1 100644 --- a/include/usbhost/usbhost.h +++ b/include/usbhost/usbhost.h
@@ -156,6 +156,10 @@ */ char* usb_device_get_product_name(struct usb_device *device); +/* Returns the version number for the USB device. + */ +int usb_device_get_version(struct usb_device *device); + /* Returns the USB serial number for the USB device. * Call free() to free the result when you are done with it. */
diff --git a/include/utils/BlobCache.h b/include/utils/BlobCache.h index 7d621e4..65dca9f 100644 --- a/include/utils/BlobCache.h +++ b/include/utils/BlobCache.h
@@ -185,6 +185,12 @@ // mNumEntries is number of cache entries following the header in the // data. size_t mNumEntries; + + // mBuildId is the build id of the device when the cache was created. + // When an update to the build happens (via an OTA or other update) this + // is used to invalidate the cache. + int mBuildIdLength; + char mBuildId[]; }; // An EntryHeader is the header for a serialized cache entry. No need to
diff --git a/include/utils/LinearAllocator.h b/include/utils/LinearAllocator.h deleted file mode 100644 index 4772bc8..0000000 --- a/include/utils/LinearAllocator.h +++ /dev/null
@@ -1,97 +0,0 @@ -/* - * Copyright 2012, The Android Open Source Project - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef ANDROID_LINEARALLOCATOR_H -#define ANDROID_LINEARALLOCATOR_H - -#include <stddef.h> - -namespace android { - -/** - * A memory manager that internally allocates multi-kbyte buffers for placing objects in. It avoids - * the overhead of malloc when many objects are allocated. It is most useful when creating many - * small objects with a similar lifetime, and doesn't add significant overhead for large - * allocations. - */ -class LinearAllocator { -public: - LinearAllocator(); - ~LinearAllocator(); - - /** - * Reserves and returns a region of memory of at least size 'size', aligning as needed. - * Typically this is used in an object's overridden new() method or as a replacement for malloc. - * - * The lifetime of the returned buffers is tied to that of the LinearAllocator. If calling - * delete() on an object stored in a buffer is needed, it should be overridden to use - * rewindIfLastAlloc() - */ - void* alloc(size_t size); - - /** - * Attempt to deallocate the given buffer, with the LinearAllocator attempting to rewind its - * state if possible. No destructors are called. - */ - void rewindIfLastAlloc(void* ptr, size_t allocSize); - - /** - * Dump memory usage statistics to the log (allocated and wasted space) - */ - void dumpMemoryStats(const char* prefix = ""); - - /** - * The number of bytes used for buffers allocated in the LinearAllocator (does not count space - * wasted) - */ - size_t usedSize() const { return mTotalAllocated - mWastedSpace; } - -private: - LinearAllocator(const LinearAllocator& other); - - class Page; - - Page* newPage(size_t pageSize); - bool fitsInCurrentPage(size_t size); - void ensureNext(size_t size); - void* start(Page *p); - void* end(Page* p); - - size_t mPageSize; - size_t mMaxAllocSize; - void* mNext; - Page* mCurrentPage; - Page* mPages; - - // Memory usage tracking - size_t mTotalAllocated; - size_t mWastedSpace; - size_t mPageCount; - size_t mDedicatedPageCount; -}; - -}; // namespace android - -#endif // ANDROID_LINEARALLOCATOR_H
diff --git a/include/utils/Looper.h b/include/utils/Looper.h index 15c9891..da2d5f2 100644 --- a/include/utils/Looper.h +++ b/include/utils/Looper.h
@@ -386,11 +386,12 @@ void removeMessages(const sp<MessageHandler>& handler, int what); /** - * Return whether this looper's thread is currently idling -- that is, whether it - * stopped waiting for more work to do. Note that this is intrinsically racy, since - * its state can change before you get the result back. + * Returns whether this looper's thread is currently polling for more work to do. + * This is a good signal that the loop is still alive rather than being stuck + * handling a callback. Note that this method is intrinsically racy, since the + * state of the loop can change before you get the result back. */ - bool isIdling() const; + bool isPolling() const; /** * Prepares a looper associated with the calling thread, and returns it. @@ -419,8 +420,12 @@ struct Request { int fd; int ident; + int events; + int seq; sp<LooperCallback> callback; void* data; + + void initEventItem(struct epoll_event* eventItem) const; }; struct Response { @@ -442,8 +447,7 @@ const bool mAllowNonCallbacks; // immutable - int mWakeReadPipeFd; // immutable - int mWakeWritePipeFd; // immutable + int mWakeEventFd; // immutable Mutex mLock; Vector<MessageEnvelope> mMessageEnvelopes; // guarded by mLock @@ -451,12 +455,14 @@ // Whether we are currently waiting for work. Not protected by a lock, // any use of it is racy anyway. - volatile bool mIdling; + volatile bool mPolling; - int mEpollFd; // immutable + int mEpollFd; // guarded by mLock but only modified on the looper thread + bool mEpollRebuildRequired; // guarded by mLock // Locked list of file descriptor monitoring requests. KeyedVector<int, Request> mRequests; // guarded by mLock + int mNextRequestSeq; // This state is only used privately by pollOnce and does not require a lock since // it runs on a single thread. @@ -465,11 +471,15 @@ nsecs_t mNextMessageUptime; // set to LLONG_MAX when none int pollInner(int timeoutMillis); + int removeFd(int fd, int seq); void awoken(); void pushResponse(int events, const Request& request); + void rebuildEpollLocked(); + void scheduleEpollRebuildLocked(); static void initTLSKey(); static void threadDestructor(void *st); + static void initEpollEvent(struct epoll_event* eventItem); }; } // namespace android
diff --git a/include/ziparchive/zip_archive.h b/include/ziparchive/zip_archive.h index 386a390..3b00683 100644 --- a/include/ziparchive/zip_archive.h +++ b/include/ziparchive/zip_archive.h
@@ -153,7 +153,9 @@ * Returns 0 on success and negative values on failure. */ int32_t StartIteration(ZipArchiveHandle handle, void** cookie_ptr, - const ZipEntryName* optional_prefix); + const ZipEntryName* optional_prefix, + // TODO: Remove the default parameter. + const ZipEntryName* optional_suffix = NULL); /* * Advance to the next element in the zipfile in iteration order.
diff --git a/init/Android.mk b/init/Android.mk index 31d2fcd..de065dc 100644 --- a/init/Android.mk +++ b/init/Android.mk
@@ -50,7 +50,10 @@ watchdogd.cpp \ LOCAL_MODULE:= init -LOCAL_C_INCLUDES += system/extras/ext4_utils +LOCAL_C_INCLUDES += \ + system/extras/ext4_utils \ + system/core/mkbootimg + LOCAL_FORCE_STATIC_EXECUTABLE := true LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT) LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED)
diff --git a/init/builtins.cpp b/init/builtins.cpp index 4567b04..9e5f9ff 100644 --- a/init/builtins.cpp +++ b/init/builtins.cpp
@@ -29,7 +29,7 @@ #include <sys/wait.h> #include <unistd.h> #include <linux/loop.h> -#include <ext4_crypt.h> +#include <ext4_crypt_init_extensions.h> #include <selinux/selinux.h> #include <selinux/label.h> @@ -57,8 +57,14 @@ static int insmod(const char *filename, char *options) { + char filename_val[PROP_VALUE_MAX]; + if (expand_props(filename_val, filename, sizeof(filename_val)) == -1) { + ERROR("insmod: cannot expand '%s'\n", filename); + return -EINVAL; + } + std::string module; - if (!read_file(filename, &module)) { + if (!read_file(filename_val, &module)) { return -1; } @@ -386,18 +392,6 @@ } /* - * Callback to make a directory from the ext4 code - */ -static int do_mount_alls_make_dir(const char* dir) -{ - if (make_dir(dir, 0700) && errno != EEXIST) { - return -1; - } - - return 0; -} - -/* * This function might request a reboot, in which case it will * not return. */ @@ -452,6 +446,7 @@ property_set("vold.decrypt", "trigger_encryption"); } else if (ret == FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED) { property_set("ro.crypto.state", "encrypted"); + property_set("ro.crypto.type", "block"); property_set("vold.decrypt", "trigger_default_encryption"); } else if (ret == FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) { property_set("ro.crypto.state", "unencrypted"); @@ -465,26 +460,11 @@ ret = wipe_data_via_recovery(); /* If reboot worked, there is no return. */ } else if (ret == FS_MGR_MNTALL_DEV_DEFAULT_FILE_ENCRYPTED) { - // We have to create the key files here. Only init can call make_dir, - // and we can't do it from fs_mgr as then fs_mgr would depend on - // make_dir creating a circular dependency. - fstab = fs_mgr_read_fstab(args[1]); - for (int i = 0; i < fstab->num_entries; ++i) { - if (fs_mgr_is_file_encrypted(&fstab->recs[i])) { - if (e4crypt_create_device_key(fstab->recs[i].mount_point, - do_mount_alls_make_dir)) { - ERROR("Could not create device key on %s" - " - continue unencrypted\n", - fstab->recs[i].mount_point); - } - } - } - fs_mgr_free_fstab(fstab); - if (e4crypt_install_keyring()) { return -1; } property_set("ro.crypto.state", "encrypted"); + property_set("ro.crypto.type", "file"); // Although encrypted, we have device key, so we do not need to // do anything different from the nonencrypted case. @@ -494,6 +474,7 @@ return -1; } property_set("ro.crypto.state", "encrypted"); + property_set("ro.crypto.type", "file"); property_set("vold.decrypt", "trigger_restart_min_framework"); } else if (ret > 0) { ERROR("fs_mgr_mount_all returned unexpected error %d\n", ret); @@ -840,11 +821,30 @@ return -1; } -int do_installkey(int nargs, char **args) +/* + * Callback to make a directory from the ext4 code + */ +static int do_installkeys_ensure_dir_exists(const char* dir) { - if (nargs == 2) { - return e4crypt_install_key(args[1]); + if (make_dir(dir, 0700) && errno != EEXIST) { + return -1; } - return -1; + return 0; +} + +int do_installkey(int nargs, char **args) +{ + if (nargs != 2) { + return -1; + } + + char prop_value[PROP_VALUE_MAX] = {0}; + property_get("ro.crypto.type", prop_value); + if (strcmp(prop_value, "file")) { + return 0; + } + + return e4crypt_create_device_key(args[1], + do_installkeys_ensure_dir_exists); }
diff --git a/init/init.cpp b/init/init.cpp index dd74538..93fe944 100644 --- a/init/init.cpp +++ b/init/init.cpp
@@ -290,6 +290,16 @@ freecon(scon); scon = NULL; + if (svc->writepid_files_) { + std::string pid_str = android::base::StringPrintf("%d", pid); + for (auto& file : *svc->writepid_files_) { + if (!android::base::WriteStringToFile(pid_str, file)) { + ERROR("couldn't write %s to %s: %s\n", + pid_str.c_str(), file.c_str(), strerror(errno)); + } + } + } + if (svc->ioprio_class != IoSchedClass_NONE) { if (android_set_ioprio(getpid(), svc->ioprio_class, svc->ioprio_pri)) { ERROR("Failed to set pid %d ioprio = %d,%d: %s\n", @@ -380,7 +390,8 @@ if ((svc->flags & SVC_EXEC) != 0) { INFO("SVC_EXEC pid %d (uid %d gid %d+%zu context %s) started; waiting...\n", - svc->pid, svc->uid, svc->gid, svc->nr_supp_gids, svc->seclabel); + svc->pid, svc->uid, svc->gid, svc->nr_supp_gids, + svc->seclabel ? : "default"); waiting_for_exec = true; } @@ -618,7 +629,10 @@ Timer t; NOTICE("Waiting for %s...\n", COLDBOOT_DONE); - if (wait_for_file(COLDBOOT_DONE, COMMAND_RETRY_TIMEOUT)) { + // Any longer than 1s is an unreasonable length of time to delay booting. + // If you're hitting this timeout, check that you didn't make your + // sepolicy regular expressions too expensive (http://b/19899875). + if (wait_for_file(COLDBOOT_DONE, 1)) { ERROR("Timed out waiting for %s\n", COLDBOOT_DONE); }
diff --git a/init/init.h b/init/init.h index 1cabb14..c166969 100644 --- a/init/init.h +++ b/init/init.h
@@ -19,6 +19,9 @@ #include <sys/types.h> +#include <string> +#include <vector> + #include <cutils/list.h> #include <cutils/iosched_policy.h> @@ -116,6 +119,8 @@ struct action onrestart; /* Actions to execute on restart. */ + std::vector<std::string>* writepid_files_; + /* keycodes for triggering this service via /dev/keychord */ int *keycodes; int nkeycodes;
diff --git a/init/init_parser.cpp b/init/init_parser.cpp index b76b04e..666a86e 100644 --- a/init/init_parser.cpp +++ b/init/init_parser.cpp
@@ -206,6 +206,7 @@ break; case 'w': if (!strcmp(s, "rite")) return K_write; + if (!strcmp(s, "ritepid")) return K_writepid; if (!strcmp(s, "ait")) return K_wait; break; } @@ -382,13 +383,13 @@ static void parse_config(const char *fn, const std::string& data) { - struct parse_state state; struct listnode import_list; struct listnode *node; char *args[INIT_PARSER_MAXARGS]; - int nargs; - nargs = 0; + int nargs = 0; + + parse_state state; state.filename = fn; state.line = 0; state.ptr = strdup(data.c_str()); // TODO: fix this code! @@ -444,6 +445,7 @@ return -1; } + data.push_back('\n'); // TODO: fix parse_config. parse_config(path, data); dump_parser_state(); @@ -665,6 +667,7 @@ service* make_exec_oneshot_service(int nargs, char** args) { // Parse the arguments: exec [SECLABEL [UID [GID]*] --] COMMAND ARGS... + // SECLABEL can be a - to denote default int command_arg = 1; for (int i = 1; i < nargs; ++i) { if (strcmp(args[i], "--") == 0) { @@ -690,7 +693,7 @@ return NULL; } - if (command_arg > 2) { + if ((command_arg > 2) && strcmp(args[1], "-")) { svc->seclabel = args[1]; } if (command_arg > 3) { @@ -924,6 +927,16 @@ svc->seclabel = args[1]; } break; + case K_writepid: + if (nargs < 2) { + parse_error(state, "writepid option requires at least one filename\n"); + break; + } + svc->writepid_files_ = new std::vector<std::string>; + for (int i = 1; i < nargs; ++i) { + svc->writepid_files_->push_back(args[i]); + } + break; default: parse_error(state, "invalid option '%s'\n", args[0]);
diff --git a/init/keywords.h b/init/keywords.h index 37f01b8..e637d7d 100644 --- a/init/keywords.h +++ b/init/keywords.h
@@ -43,11 +43,15 @@ enum { K_UNKNOWN, #endif + KEYWORD(bootchart_init, COMMAND, 0, do_bootchart_init) + KEYWORD(chmod, COMMAND, 2, do_chmod) + KEYWORD(chown, COMMAND, 2, do_chown) KEYWORD(class, OPTION, 0, 0) + KEYWORD(class_reset, COMMAND, 1, do_class_reset) KEYWORD(class_start, COMMAND, 1, do_class_start) KEYWORD(class_stop, COMMAND, 1, do_class_stop) - KEYWORD(class_reset, COMMAND, 1, do_class_reset) KEYWORD(console, OPTION, 0, 0) + KEYWORD(copy, COMMAND, 2, do_copy) KEYWORD(critical, OPTION, 0, 0) KEYWORD(disabled, OPTION, 0, 0) KEYWORD(domainname, COMMAND, 1, do_domainname) @@ -57,16 +61,20 @@ KEYWORD(group, OPTION, 0, 0) KEYWORD(hostname, COMMAND, 1, do_hostname) KEYWORD(ifup, COMMAND, 1, do_ifup) + KEYWORD(import, SECTION, 1, 0) KEYWORD(insmod, COMMAND, 1, do_insmod) KEYWORD(installkey, COMMAND, 1, do_installkey) - KEYWORD(import, SECTION, 1, 0) + KEYWORD(ioprio, OPTION, 0, 0) KEYWORD(keycodes, OPTION, 0, 0) + KEYWORD(load_all_props, COMMAND, 0, do_load_all_props) + KEYWORD(load_persist_props, COMMAND, 0, do_load_persist_props) + KEYWORD(loglevel, COMMAND, 1, do_loglevel) KEYWORD(mkdir, COMMAND, 1, do_mkdir) KEYWORD(mount_all, COMMAND, 1, do_mount_all) KEYWORD(mount, COMMAND, 3, do_mount) - KEYWORD(on, SECTION, 0, 0) KEYWORD(oneshot, OPTION, 0, 0) KEYWORD(onrestart, OPTION, 0, 0) + KEYWORD(on, SECTION, 0, 0) KEYWORD(powerctl, COMMAND, 1, do_powerctl) KEYWORD(restart, COMMAND, 1, do_restart) KEYWORD(restorecon, COMMAND, 1, do_restorecon) @@ -82,22 +90,15 @@ KEYWORD(start, COMMAND, 1, do_start) KEYWORD(stop, COMMAND, 1, do_stop) KEYWORD(swapon_all, COMMAND, 1, do_swapon_all) - KEYWORD(trigger, COMMAND, 1, do_trigger) KEYWORD(symlink, COMMAND, 1, do_symlink) KEYWORD(sysclktz, COMMAND, 1, do_sysclktz) + KEYWORD(trigger, COMMAND, 1, do_trigger) KEYWORD(user, OPTION, 0, 0) KEYWORD(verity_load_state, COMMAND, 0, do_verity_load_state) KEYWORD(verity_update_state, COMMAND, 0, do_verity_update_state) KEYWORD(wait, COMMAND, 1, do_wait) KEYWORD(write, COMMAND, 2, do_write) - KEYWORD(copy, COMMAND, 2, do_copy) - KEYWORD(chown, COMMAND, 2, do_chown) - KEYWORD(chmod, COMMAND, 2, do_chmod) - KEYWORD(loglevel, COMMAND, 1, do_loglevel) - KEYWORD(load_persist_props, COMMAND, 0, do_load_persist_props) - KEYWORD(load_all_props, COMMAND, 0, do_load_all_props) - KEYWORD(ioprio, OPTION, 0, 0) - KEYWORD(bootchart_init, COMMAND, 0, do_bootchart_init) + KEYWORD(writepid, OPTION, 0, 0) #ifdef __MAKE_KEYWORD_ENUM__ KEYWORD_COUNT, };
diff --git a/init/property_service.cpp b/init/property_service.cpp index 930ef82..c2881ae 100644 --- a/init/property_service.cpp +++ b/init/property_service.cpp
@@ -46,12 +46,18 @@ #include <selinux/selinux.h> #include <selinux/label.h> +#include <fs_mgr.h> +#include <base/file.h> +#include "bootimg.h" + #include "property_service.h" #include "init.h" #include "util.h" #include "log.h" #define PERSISTENT_PROPERTY_DIR "/data/property" +#define FSTAB_PREFIX "/fstab." +#define RECOVERY_MOUNT_POINT "/recovery" static int persistent_properties_loaded = 0; static bool property_area_initialized = false; @@ -199,6 +205,16 @@ if (!is_legal_property_name(name, namelen)) return -1; if (valuelen >= PROP_VALUE_MAX) return -1; + if (strcmp("selinux.reload_policy", name) == 0 && strcmp("1", value) == 0) { + if (selinux_reload_policy() != 0) { + ERROR("Failed to reload policy\n"); + } + } else if (strcmp("selinux.restorecon_recursive", name) == 0 && valuelen > 0) { + if (restorecon_recursive(value) != 0) { + ERROR("Failed to restorecon_recursive %s\n", value); + } + } + prop_info* pi = (prop_info*) __system_property_find(name); if(pi != 0) { @@ -230,9 +246,6 @@ * to prevent them from being overwritten by default values. */ write_persistent_property(name, value); - } else if (strcmp("selinux.reload_policy", name) == 0 && - strcmp("1", value) == 0) { - selinux_reload_policy(); } property_changed(name, value); return 0; @@ -414,6 +427,7 @@ Timer t; std::string data; if (read_file(filename, &data)) { + data.push_back('\n'); load_properties(&data[0], filter); } NOTICE("(Loading properties from %s took %.2fs.)\n", filename, t.duration()); @@ -506,16 +520,57 @@ load_persistent_properties(); } +void load_recovery_id_prop() { + char fstab_filename[PROP_VALUE_MAX + sizeof(FSTAB_PREFIX)]; + char propbuf[PROP_VALUE_MAX]; + int ret = property_get("ro.hardware", propbuf); + if (!ret) { + ERROR("ro.hardware not set - unable to load recovery id\n"); + return; + } + snprintf(fstab_filename, sizeof(fstab_filename), FSTAB_PREFIX "%s", propbuf); + + std::unique_ptr<fstab, void(*)(fstab*)> tab(fs_mgr_read_fstab(fstab_filename), + fs_mgr_free_fstab); + if (!tab) { + ERROR("unable to read fstab %s: %s\n", fstab_filename, strerror(errno)); + return; + } + + fstab_rec* rec = fs_mgr_get_entry_for_mount_point(tab.get(), RECOVERY_MOUNT_POINT); + if (rec == NULL) { + ERROR("/recovery not specified in fstab\n"); + return; + } + + int fd = open(rec->blk_device, O_RDONLY); + if (fd == -1) { + ERROR("error opening block device %s: %s\n", rec->blk_device, strerror(errno)); + return; + } + + boot_img_hdr hdr; + if (android::base::ReadFully(fd, &hdr, sizeof(hdr))) { + std::string hex = bytes_to_hex(reinterpret_cast<uint8_t*>(hdr.id), sizeof(hdr.id)); + property_set("ro.recovery_id", hex.c_str()); + } else { + ERROR("error reading /recovery: %s\n", strerror(errno)); + } + + close(fd); +} + void load_all_props() { load_properties_from_file(PROP_PATH_SYSTEM_BUILD, NULL); load_properties_from_file(PROP_PATH_VENDOR_BUILD, NULL); - load_properties_from_file(PROP_PATH_BOOTIMAGE_BUILD, NULL); load_properties_from_file(PROP_PATH_FACTORY, "ro.*"); load_override_properties(); /* Read persistent properties after all default values have been loaded. */ load_persistent_properties(); + + load_recovery_id_prop(); } void start_property_service() {
diff --git a/init/readme.txt b/init/readme.txt index 6b9c42d..9e3394e 100644 --- a/init/readme.txt +++ b/init/readme.txt
@@ -60,36 +60,36 @@ runs the service. critical - This is a device-critical service. If it exits more than four times in - four minutes, the device will reboot into recovery mode. + This is a device-critical service. If it exits more than four times in + four minutes, the device will reboot into recovery mode. disabled - This service will not automatically start with its class. - It must be explicitly started by name. + This service will not automatically start with its class. + It must be explicitly started by name. setenv <name> <value> - Set the environment variable <name> to <value> in the launched process. + Set the environment variable <name> to <value> in the launched process. socket <name> <type> <perm> [ <user> [ <group> [ <seclabel> ] ] ] - Create a unix domain socket named /dev/socket/<name> and pass - its fd to the launched process. <type> must be "dgram", "stream" or "seqpacket". - User and group default to 0. - 'seclabel' is the SELinux security context for the socket. - It defaults to the service security context, as specified by seclabel or - computed based on the service executable file security context. + Create a unix domain socket named /dev/socket/<name> and pass + its fd to the launched process. <type> must be "dgram", "stream" or "seqpacket". + User and group default to 0. + 'seclabel' is the SELinux security context for the socket. + It defaults to the service security context, as specified by seclabel or + computed based on the service executable file security context. user <username> - Change to username before exec'ing this service. - Currently defaults to root. (??? probably should default to nobody) - Currently, if your process requires linux capabilities then you cannot use - this command. You must instead request the capabilities in-process while - still root, and then drop to your desired uid. + Change to username before exec'ing this service. + Currently defaults to root. (??? probably should default to nobody) + Currently, if your process requires linux capabilities then you cannot use + this command. You must instead request the capabilities in-process while + still root, and then drop to your desired uid. group <groupname> [ <groupname> ]* - Change to groupname before exec'ing this service. Additional - groupnames beyond the (required) first one are used to set the - supplemental groups of the process (via setgroups()). - Currently defaults to root. (??? probably should default to nobody) + Change to groupname before exec'ing this service. Additional + groupnames beyond the (required) first one are used to set the + supplemental groups of the process (via setgroups()). + Currently defaults to root. (??? probably should default to nobody) seclabel <seclabel> Change to 'seclabel' before exec'ing this service. @@ -99,22 +99,26 @@ If not specified and no transition is defined in policy, defaults to the init context. oneshot - Do not restart the service when it exits. + Do not restart the service when it exits. class <name> - Specify a class name for the service. All services in a - named class may be started or stopped together. A service - is in the class "default" if one is not specified via the - class option. + Specify a class name for the service. All services in a + named class may be started or stopped together. A service + is in the class "default" if one is not specified via the + class option. onrestart - Execute a Command (see below) when service restarts. + Execute a Command (see below) when service restarts. + +writepid <file...> + Write the child's pid to the given files when it forks. Meant for + cgroup/cpuset usage. Triggers -------- - Triggers are strings which can be used to match certain kinds - of events and used to cause an action to occur. +Triggers are strings which can be used to match certain kinds +of events and used to cause an action to occur. boot This is the first trigger that will occur when init starts @@ -180,7 +184,7 @@ Fork and execute command with the given arguments. The command starts after "--" so that an optional security context, user, and supplementary groups can be provided. No other commands will be run until this one - finishes. + finishes. <seclabel> can be a - to denote default. export <name> <value> Set the environment variable <name> equal to <value> in the
diff --git a/init/ueventd_parser.cpp b/init/ueventd_parser.cpp index 7a4841f..497c606 100644 --- a/init/ueventd_parser.cpp +++ b/init/ueventd_parser.cpp
@@ -193,10 +193,10 @@ static void parse_config(const char *fn, const std::string& data) { - struct parse_state state; char *args[UEVENTD_PARSER_MAXARGS]; - int nargs; - nargs = 0; + + int nargs = 0; + parse_state state; state.filename = fn; state.line = 1; state.ptr = strdup(data.c_str()); // TODO: fix this code! @@ -231,6 +231,7 @@ return -1; } + data.push_back('\n'); // TODO: fix parse_config. parse_config(fn, data); dump_parser_state(); return 0;
diff --git a/init/util.cpp b/init/util.cpp index 20ce806..a5392c6 100644 --- a/init/util.cpp +++ b/init/util.cpp
@@ -36,6 +36,7 @@ /* for ANDROID_SOCKET_* */ #include <cutils/sockets.h> +#include <base/stringprintf.h> #include <private/android_filesystem_config.h> @@ -169,10 +170,7 @@ } bool okay = android::base::ReadFdToString(fd, content); - TEMP_FAILURE_RETRY(close(fd)); - if (okay) { - content->append("\n", 1); - } + close(fd); return okay; } @@ -186,7 +184,7 @@ if (result == -1) { NOTICE("write_file: Unable to write to '%s': %s\n", path, strerror(errno)); } - TEMP_FAILURE_RETRY(close(fd)); + close(fd); return result; } @@ -368,10 +366,10 @@ int wait_for_file(const char *filename, int timeout) { struct stat info; - time_t timeout_time = gettime() + timeout; + uint64_t timeout_time_ns = gettime_ns() + timeout * UINT64_C(1000000000); int ret = -1; - while (gettime() < timeout_time && ((ret = stat(filename, &info)) < 0)) + while (gettime_ns() < timeout_time_ns && ((ret = stat(filename, &info)) < 0)) usleep(10000); return ret; @@ -464,3 +462,13 @@ { return selinux_android_restorecon(pathname, SELINUX_ANDROID_RESTORECON_RECURSE); } + +/* + * Writes hex_len hex characters (1/2 byte) to hex from bytes. + */ +std::string bytes_to_hex(const uint8_t* bytes, size_t bytes_len) { + std::string hex("0x"); + for (size_t i = 0; i < bytes_len; i++) + android::base::StringAppendF(&hex, "%02x", bytes[i]); + return hex; +}
diff --git a/init/util.h b/init/util.h index 1c947ec..09d64cd 100644 --- a/init/util.h +++ b/init/util.h
@@ -62,4 +62,5 @@ int make_dir(const char *path, mode_t mode); int restorecon(const char *pathname); int restorecon_recursive(const char *pathname); +std::string bytes_to_hex(const uint8_t *bytes, size_t bytes_len); #endif
diff --git a/libbacktrace/Android.mk b/libbacktrace/Android.mk index 54cace9..6a689a6 100644 --- a/libbacktrace/Android.mk +++ b/libbacktrace/Android.mk
@@ -118,12 +118,14 @@ backtrace_test_shared_libraries := \ libbacktrace_test \ libbacktrace \ - -backtrace_test_shared_libraries_target := \ + libbase \ libcutils \ -backtrace_test_static_libraries_host := \ - libcutils \ +backtrace_test_shared_libraries_target += \ + libdl \ + +backtrace_test_ldlibs_host += \ + -ldl \ module := backtrace_test module_tag := debug
diff --git a/libbacktrace/Backtrace.cpp b/libbacktrace/Backtrace.cpp index 91ca8b7..97f0ef4 100644 --- a/libbacktrace/Backtrace.cpp +++ b/libbacktrace/Backtrace.cpp
@@ -27,6 +27,8 @@ #include <backtrace/Backtrace.h> #include <backtrace/BacktraceMap.h> +#include <cutils/threads.h> + #include "BacktraceLog.h" #include "thread_utils.h" #include "UnwindCurrent.h" @@ -97,14 +99,14 @@ map_name = "<unknown>"; } - uintptr_t relative_pc; - if (BacktraceMap::IsValid(frame->map)) { - relative_pc = frame->pc - frame->map.start; - } else { - relative_pc = frame->pc; - } + uintptr_t relative_pc = BacktraceMap::GetRelativePc(frame->map, frame->pc); std::string line(StringPrintf("#%02zu pc %" PRIPTR " %s", frame->num, relative_pc, map_name)); + // Special handling for non-zero offset maps, we need to print that + // information. + if (frame->map.offset != 0) { + line += " (offset " + StringPrintf("0x%" PRIxPTR, frame->map.offset) + ")"; + } if (!frame->func_name.empty()) { line += " (" + frame->func_name; if (frame->func_offset) { @@ -117,7 +119,9 @@ } void Backtrace::FillInMap(uintptr_t pc, backtrace_map_t* map) { - map_->FillIn(pc, map); + if (map_ != nullptr) { + map_->FillIn(pc, map); + } } Backtrace* Backtrace::Create(pid_t pid, pid_t tid, BacktraceMap* map) {
diff --git a/libbacktrace/BacktraceCurrent.cpp b/libbacktrace/BacktraceCurrent.cpp index fd1f4da..2714d93 100644 --- a/libbacktrace/BacktraceCurrent.cpp +++ b/libbacktrace/BacktraceCurrent.cpp
@@ -29,6 +29,8 @@ #include <backtrace/Backtrace.h> #include <backtrace/BacktraceMap.h> +#include <cutils/threads.h> + #include "BacktraceCurrent.h" #include "BacktraceLog.h" #include "ThreadEntry.h" @@ -63,6 +65,11 @@ } bool BacktraceCurrent::Unwind(size_t num_ignore_frames, ucontext_t* ucontext) { + if (GetMap() == nullptr) { + // Without a map object, we can't do anything. + return false; + } + if (ucontext) { return UnwindFromContext(num_ignore_frames, ucontext); } @@ -89,7 +96,7 @@ static void SignalHandler(int, siginfo_t*, void* sigcontext) { ThreadEntry* entry = ThreadEntry::Get(getpid(), gettid(), false); if (!entry) { - BACK_LOGW("Unable to find pid %d tid %d information", getpid(), gettid()); + BACK_LOGE("pid %d, tid %d entry not found", getpid(), gettid()); return; } @@ -102,9 +109,14 @@ // the thread run ahead causing problems. // The number indicates that we are waiting for the second Wake() call // overall which is made by the thread requesting an unwind. - entry->Wait(2); - - ThreadEntry::Remove(entry); + if (entry->Wait(2)) { + // Do not remove the entry here because that can result in a deadlock + // if the code cannot properly send a signal to the thread under test. + entry->Wake(); + } else { + // At this point, it is possible that entry has been freed, so just exit. + BACK_LOGE("Timed out waiting for unwind thread to indicate it completed."); + } } bool BacktraceCurrent::UnwindThread(size_t num_ignore_frames) { @@ -121,17 +133,15 @@ act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK; sigemptyset(&act.sa_mask); if (sigaction(THREAD_SIGNAL, &act, &oldact) != 0) { - BACK_LOGW("sigaction failed %s", strerror(errno)); - entry->Unlock(); + BACK_LOGE("sigaction failed: %s", strerror(errno)); ThreadEntry::Remove(entry); pthread_mutex_unlock(&g_sigaction_mutex); return false; } if (tgkill(Pid(), Tid(), THREAD_SIGNAL) != 0) { - BACK_LOGW("tgkill %d failed: %s", Tid(), strerror(errno)); + BACK_LOGE("tgkill %d failed: %s", Tid(), strerror(errno)); sigaction(THREAD_SIGNAL, &oldact, nullptr); - entry->Unlock(); ThreadEntry::Remove(entry); pthread_mutex_unlock(&g_sigaction_mutex); return false; @@ -139,17 +149,30 @@ // Wait for the thread to get the ucontext. The number indicates // that we are waiting for the first Wake() call made by the thread. - entry->Wait(1); + bool wait_completed = entry->Wait(1); // After the thread has received the signal, allow other unwinders to // continue. sigaction(THREAD_SIGNAL, &oldact, nullptr); pthread_mutex_unlock(&g_sigaction_mutex); - bool unwind_done = UnwindFromContext(num_ignore_frames, entry->GetUcontext()); + bool unwind_done = false; + if (wait_completed) { + unwind_done = UnwindFromContext(num_ignore_frames, entry->GetUcontext()); - // Tell the signal handler to exit and release the entry. - entry->Wake(); + // Tell the signal handler to exit and release the entry. + entry->Wake(); + + // Wait for the thread to indicate it is done with the ThreadEntry. + if (!entry->Wait(3)) { + // Send a warning, but do not mark as a failure to unwind. + BACK_LOGW("Timed out waiting for signal handler to indicate it finished."); + } + } else { + BACK_LOGE("Timed out waiting for signal handler to get ucontext data."); + } + + ThreadEntry::Remove(entry); return unwind_done; }
diff --git a/libbacktrace/BacktraceLog.h b/libbacktrace/BacktraceLog.h index 1632ec2..5c39f1c 100644 --- a/libbacktrace/BacktraceLog.h +++ b/libbacktrace/BacktraceLog.h
@@ -25,4 +25,7 @@ #define BACK_LOGW(format, ...) \ ALOGW("%s: " format, __PRETTY_FUNCTION__, ##__VA_ARGS__) +#define BACK_LOGE(format, ...) \ + ALOGE("%s: " format, __PRETTY_FUNCTION__, ##__VA_ARGS__) + #endif // _LIBBACKTRACE_BACKTRACE_LOG_H
diff --git a/libbacktrace/BacktracePtrace.cpp b/libbacktrace/BacktracePtrace.cpp index 6134438..fd8b713 100644 --- a/libbacktrace/BacktracePtrace.cpp +++ b/libbacktrace/BacktracePtrace.cpp
@@ -37,8 +37,6 @@ errno = 0; *out_value = ptrace(PTRACE_PEEKTEXT, tid, reinterpret_cast<void*>(addr), nullptr); if (*out_value == static_cast<word_t>(-1) && errno) { - BACK_LOGW("invalid pointer %p reading from tid %d, ptrace() strerror(errno)=%s", - reinterpret_cast<void*>(addr), tid, strerror(errno)); return false; } return true; @@ -83,13 +81,12 @@ if (!PtraceRead(Tid(), addr & ~(sizeof(word_t) - 1), &data_word)) { return 0; } - align_bytes = sizeof(word_t) - align_bytes; - memcpy(buffer, reinterpret_cast<uint8_t*>(&data_word) + sizeof(word_t) - align_bytes, - align_bytes); - addr += align_bytes; - buffer += align_bytes; - bytes -= align_bytes; - bytes_read += align_bytes; + size_t copy_bytes = MIN(sizeof(word_t) - align_bytes, bytes); + memcpy(buffer, reinterpret_cast<uint8_t*>(&data_word) + align_bytes, copy_bytes); + addr += copy_bytes; + buffer += copy_bytes; + bytes -= copy_bytes; + bytes_read += copy_bytes; } size_t num_words = bytes / sizeof(word_t);
diff --git a/libbacktrace/ThreadEntry.cpp b/libbacktrace/ThreadEntry.cpp index e8b60c8..084c1aa 100644 --- a/libbacktrace/ThreadEntry.cpp +++ b/libbacktrace/ThreadEntry.cpp
@@ -69,7 +69,7 @@ } void ThreadEntry::Remove(ThreadEntry* entry) { - pthread_mutex_unlock(&entry->mutex_); + entry->Unlock(); pthread_mutex_lock(&ThreadEntry::list_mutex_); if (--entry->ref_count_ == 0) { @@ -96,20 +96,24 @@ pthread_cond_destroy(&wait_cond_); } -void ThreadEntry::Wait(int value) { +bool ThreadEntry::Wait(int value) { timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); - ts.tv_sec += 10; + ts.tv_sec += 5; + bool wait_completed = true; pthread_mutex_lock(&wait_mutex_); while (wait_value_ != value) { int ret = pthread_cond_timedwait(&wait_cond_, &wait_mutex_, &ts); if (ret != 0) { - BACK_LOGW("pthread_cond_timedwait failed: %s", strerror(ret)); + BACK_LOGW("pthread_cond_timedwait for value %d failed: %s", value, strerror(ret)); + wait_completed = false; break; } } pthread_mutex_unlock(&wait_mutex_); + + return wait_completed; } void ThreadEntry::Wake() {
diff --git a/libbacktrace/ThreadEntry.h b/libbacktrace/ThreadEntry.h index 94becf2..11924a3 100644 --- a/libbacktrace/ThreadEntry.h +++ b/libbacktrace/ThreadEntry.h
@@ -29,7 +29,7 @@ void Wake(); - void Wait(int); + bool Wait(int); void CopyUcontextFromSigcontext(void*);
diff --git a/libbacktrace/UnwindMap.cpp b/libbacktrace/UnwindMap.cpp index fa59d07..879fea5 100644 --- a/libbacktrace/UnwindMap.cpp +++ b/libbacktrace/UnwindMap.cpp
@@ -51,6 +51,8 @@ map.start = unw_map.start; map.end = unw_map.end; + map.offset = unw_map.offset; + map.load_base = unw_map.load_base; map.flags = unw_map.flags; map.name = unw_map.path; @@ -91,6 +93,8 @@ map.start = unw_map.start; map.end = unw_map.end; + map.offset = unw_map.offset; + map.load_base = unw_map.load_base; map.flags = unw_map.flags; map.name = unw_map.path;
diff --git a/libbacktrace/UnwindPtrace.cpp b/libbacktrace/UnwindPtrace.cpp index a7c3de5..07c2430 100644 --- a/libbacktrace/UnwindPtrace.cpp +++ b/libbacktrace/UnwindPtrace.cpp
@@ -48,6 +48,11 @@ } bool UnwindPtrace::Unwind(size_t num_ignore_frames, ucontext_t* ucontext) { + if (GetMap() == nullptr) { + // Without a map object, we can't do anything. + return false; + } + if (ucontext) { BACK_LOGW("Unwinding from a specified context not supported yet."); return false;
diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp index 4af6592..c650755 100644 --- a/libbacktrace/backtrace_test.cpp +++ b/libbacktrace/backtrace_test.cpp
@@ -16,7 +16,9 @@ #define _GNU_SOURCE 1 #include <dirent.h> +#include <dlfcn.h> #include <errno.h> +#include <fcntl.h> #include <inttypes.h> #include <pthread.h> #include <signal.h> @@ -25,25 +27,29 @@ #include <stdlib.h> #include <string.h> #include <sys/ptrace.h> +#include <sys/stat.h> #include <sys/types.h> #include <sys/wait.h> #include <time.h> #include <unistd.h> -#include <backtrace/Backtrace.h> -#include <backtrace/BacktraceMap.h> - -// For the THREAD_SIGNAL definition. -#include "BacktraceCurrent.h" - -#include <cutils/atomic.h> -#include <gtest/gtest.h> - #include <algorithm> +#include <list> #include <memory> #include <string> #include <vector> +#include <backtrace/Backtrace.h> +#include <backtrace/BacktraceMap.h> + +#include <base/stringprintf.h> +#include <cutils/atomic.h> +#include <cutils/threads.h> + +#include <gtest/gtest.h> + +// For the THREAD_SIGNAL definition. +#include "BacktraceCurrent.h" #include "thread_utils.h" // Number of microseconds per milliseconds. @@ -771,6 +777,7 @@ // Check map name empty, but exists. frame.map.start = 1; frame.map.end = 1; + frame.map.load_base = 0; #if defined(__LP64__) EXPECT_EQ("#01 pc 0000000000000001 <unknown>", #else @@ -808,6 +815,25 @@ EXPECT_EQ("#01 pc 12345678 MapFake (ProcFake+645)", #endif backtrace->FormatFrameData(&frame)); + + // Check func_name is set, func offset is non-zero, and load_base is non-zero. + frame.func_offset = 645; + frame.map.load_base = 100; +#if defined(__LP64__) + EXPECT_EQ("#01 pc 00000000123456dc MapFake (ProcFake+645)", +#else + EXPECT_EQ("#01 pc 123456dc MapFake (ProcFake+645)", +#endif + backtrace->FormatFrameData(&frame)); + + // Check a non-zero map offset. + frame.map.offset = 0x1000; +#if defined(__LP64__) + EXPECT_EQ("#01 pc 00000000123456dc MapFake (offset 0x1000) (ProcFake+645)", +#else + EXPECT_EQ("#01 pc 123456dc MapFake (offset 0x1000) (ProcFake+645)", +#endif + backtrace->FormatFrameData(&frame)); } struct map_test_t { @@ -871,6 +897,17 @@ ASSERT_EQ(waitpid(pid, nullptr, 0), pid); } +void InitMemory(uint8_t* memory, size_t bytes) { + for (size_t i = 0; i < bytes; i++) { + memory[i] = i; + if (memory[i] == '\0') { + // Don't use '\0' in our data so we can verify that an overread doesn't + // occur by using a '\0' as the character after the read data. + memory[i] = 23; + } + } +} + void* ThreadReadTest(void* data) { thread_t* thread_data = reinterpret_cast<thread_t*>(data); @@ -889,9 +926,7 @@ } // Set up a simple pattern in memory. - for (size_t i = 0; i < pagesize; i++) { - memory[i] = i; - } + InitMemory(memory, pagesize); thread_data->data = memory; @@ -919,9 +954,8 @@ // Create a page of data to use to do quick compares. uint8_t* expected = new uint8_t[pagesize]; - for (size_t i = 0; i < pagesize; i++) { - expected[i] = i; - } + InitMemory(expected, pagesize); + uint8_t* data = new uint8_t[2*pagesize]; // Verify that we can only read one page worth of data. size_t bytes_read = backtrace->Read(read_addr, data, 2 * pagesize); @@ -935,6 +969,20 @@ ASSERT_TRUE(memcmp(data, &expected[i], 2 * sizeof(word_t)) == 0) << "Offset at " << i << " failed"; } + + // Verify small unaligned reads. + for (size_t i = 1; i < sizeof(word_t); i++) { + for (size_t j = 1; j < sizeof(word_t); j++) { + // Set one byte past what we expect to read, to guarantee we don't overread. + data[j] = '\0'; + bytes_read = backtrace->Read(read_addr + i, data, j); + ASSERT_EQ(j, bytes_read); + ASSERT_TRUE(memcmp(data, &expected[i], j) == 0) + << "Offset at " << i << " length " << j << " miscompared"; + ASSERT_EQ('\0', data[j]) + << "Offset at " << i << " length " << j << " wrote too much data"; + } + } delete data; delete expected; } @@ -978,9 +1026,7 @@ } // Set up a simple pattern in memory. - for (size_t i = 0; i < pagesize; i++) { - memory[i] = i; - } + InitMemory(memory, pagesize); g_addr = reinterpret_cast<uintptr_t>(memory); g_ready = 1; @@ -991,6 +1037,7 @@ } TEST(libbacktrace, process_read) { + g_ready = 0; pid_t pid; if ((pid = fork()) == 0) { ForkedReadTest(); @@ -1037,6 +1084,297 @@ ASSERT_TRUE(test_executed); } +void VerifyFunctionsFound(const std::vector<std::string>& found_functions) { + // We expect to find these functions in libbacktrace_test. If we don't + // find them, that's a bug in the memory read handling code in libunwind. + std::list<std::string> expected_functions; + expected_functions.push_back("test_recursive_call"); + expected_functions.push_back("test_level_one"); + expected_functions.push_back("test_level_two"); + expected_functions.push_back("test_level_three"); + expected_functions.push_back("test_level_four"); + for (const auto& found_function : found_functions) { + for (const auto& expected_function : expected_functions) { + if (found_function == expected_function) { + expected_functions.remove(found_function); + break; + } + } + } + ASSERT_TRUE(expected_functions.empty()) << "Not all functions found in shared library."; +} + +const char* CopySharedLibrary() { +#if defined(__LP64__) + const char* lib_name = "lib64"; +#else + const char* lib_name = "lib"; +#endif + +#if defined(__BIONIC__) + const char* tmp_so_name = "/data/local/tmp/libbacktrace_test.so"; + std::string cp_cmd = android::base::StringPrintf("cp /system/%s/libbacktrace_test.so %s", + lib_name, tmp_so_name); +#else + const char* tmp_so_name = "/tmp/libbacktrace_test.so"; + if (getenv("ANDROID_HOST_OUT") == NULL) { + fprintf(stderr, "ANDROID_HOST_OUT not set, make sure you run lunch."); + return nullptr; + } + std::string cp_cmd = android::base::StringPrintf("cp %s/%s/libbacktrace_test.so %s", + getenv("ANDROID_HOST_OUT"), lib_name, + tmp_so_name); +#endif + + // Copy the shared so to a tempory directory. + system(cp_cmd.c_str()); + + return tmp_so_name; +} + +TEST(libbacktrace, check_unreadable_elf_local) { + const char* tmp_so_name = CopySharedLibrary(); + ASSERT_TRUE(tmp_so_name != nullptr); + + struct stat buf; + ASSERT_TRUE(stat(tmp_so_name, &buf) != -1); + uintptr_t map_size = buf.st_size; + + int fd = open(tmp_so_name, O_RDONLY); + ASSERT_TRUE(fd != -1); + + void* map = mmap(NULL, map_size, PROT_READ, MAP_PRIVATE, fd, 0); + ASSERT_TRUE(map != MAP_FAILED); + close(fd); + ASSERT_TRUE(unlink(tmp_so_name) != -1); + + std::vector<std::string> found_functions; + std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, + BACKTRACE_CURRENT_THREAD)); + ASSERT_TRUE(backtrace.get() != nullptr); + + // Needed before GetFunctionName will work. + backtrace->Unwind(0); + + // Loop through the entire map, and get every function we can find. + map_size += reinterpret_cast<uintptr_t>(map); + std::string last_func; + for (uintptr_t read_addr = reinterpret_cast<uintptr_t>(map); + read_addr < map_size; read_addr += 4) { + uintptr_t offset; + std::string func_name = backtrace->GetFunctionName(read_addr, &offset); + if (!func_name.empty() && last_func != func_name) { + found_functions.push_back(func_name); + } + last_func = func_name; + } + + ASSERT_TRUE(munmap(map, map_size - reinterpret_cast<uintptr_t>(map)) == 0); + + VerifyFunctionsFound(found_functions); +} + +TEST(libbacktrace, check_unreadable_elf_remote) { + const char* tmp_so_name = CopySharedLibrary(); + ASSERT_TRUE(tmp_so_name != nullptr); + + g_ready = 0; + + struct stat buf; + ASSERT_TRUE(stat(tmp_so_name, &buf) != -1); + uintptr_t map_size = buf.st_size; + + pid_t pid; + if ((pid = fork()) == 0) { + int fd = open(tmp_so_name, O_RDONLY); + if (fd == -1) { + fprintf(stderr, "Failed to open file %s: %s\n", tmp_so_name, strerror(errno)); + unlink(tmp_so_name); + exit(0); + } + + void* map = mmap(NULL, map_size, PROT_READ, MAP_PRIVATE, fd, 0); + if (map == MAP_FAILED) { + fprintf(stderr, "Failed to map in memory: %s\n", strerror(errno)); + unlink(tmp_so_name); + exit(0); + } + close(fd); + if (unlink(tmp_so_name) == -1) { + fprintf(stderr, "Failed to unlink: %s\n", strerror(errno)); + exit(0); + } + + g_addr = reinterpret_cast<uintptr_t>(map); + g_ready = 1; + while (true) { + usleep(US_PER_MSEC); + } + exit(0); + } + ASSERT_TRUE(pid > 0); + + std::vector<std::string> found_functions; + uint64_t start = NanoTime(); + while (true) { + ASSERT_TRUE(ptrace(PTRACE_ATTACH, pid, 0, 0) == 0); + + // Wait for the process to get to a stopping point. + WaitForStop(pid); + + std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, BACKTRACE_CURRENT_THREAD)); + ASSERT_TRUE(backtrace.get() != nullptr); + + uintptr_t read_addr; + ASSERT_EQ(sizeof(uintptr_t), backtrace->Read(reinterpret_cast<uintptr_t>(&g_ready), reinterpret_cast<uint8_t*>(&read_addr), sizeof(uintptr_t))); + if (read_addr) { + ASSERT_EQ(sizeof(uintptr_t), backtrace->Read(reinterpret_cast<uintptr_t>(&g_addr), reinterpret_cast<uint8_t*>(&read_addr), sizeof(uintptr_t))); + + // Needed before GetFunctionName will work. + backtrace->Unwind(0); + + // Loop through the entire map, and get every function we can find. + map_size += read_addr; + std::string last_func; + for (; read_addr < map_size; read_addr += 4) { + uintptr_t offset; + std::string func_name = backtrace->GetFunctionName(read_addr, &offset); + if (!func_name.empty() && last_func != func_name) { + found_functions.push_back(func_name); + } + last_func = func_name; + } + break; + } + ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0); + + if ((NanoTime() - start) > 5 * NS_PER_SEC) { + break; + } + usleep(US_PER_MSEC); + } + + kill(pid, SIGKILL); + ASSERT_EQ(waitpid(pid, nullptr, 0), pid); + + VerifyFunctionsFound(found_functions); +} + +bool FindFuncFrameInBacktrace(Backtrace* backtrace, uintptr_t test_func, size_t* frame_num) { + backtrace_map_t map; + backtrace->FillInMap(test_func, &map); + if (!BacktraceMap::IsValid(map)) { + return false; + } + + // Loop through the frames, and find the one that is in the map. + *frame_num = 0; + for (Backtrace::const_iterator it = backtrace->begin(); it != backtrace->end(); ++it) { + if (BacktraceMap::IsValid(it->map) && map.start == it->map.start && + it->pc >= test_func) { + *frame_num = it->num; + return true; + } + } + return false; +} + +void VerifyUnreadableElfFrame(Backtrace* backtrace, uintptr_t test_func, size_t frame_num) { + ASSERT_LT(backtrace->NumFrames(), static_cast<size_t>(MAX_BACKTRACE_FRAMES)) + << DumpFrames(backtrace); + + ASSERT_TRUE(frame_num != 0) << DumpFrames(backtrace); + // Make sure that there is at least one more frame above the test func call. + ASSERT_LT(frame_num, backtrace->NumFrames()) << DumpFrames(backtrace); + + uintptr_t diff = backtrace->GetFrame(frame_num)->pc - test_func; + ASSERT_LT(diff, 200U) << DumpFrames(backtrace); +} + +void VerifyUnreadableElfBacktrace(uintptr_t test_func) { + std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, + BACKTRACE_CURRENT_THREAD)); + ASSERT_TRUE(backtrace.get() != nullptr); + ASSERT_TRUE(backtrace->Unwind(0)); + + size_t frame_num; + ASSERT_TRUE(FindFuncFrameInBacktrace(backtrace.get(), test_func, &frame_num)); + + VerifyUnreadableElfFrame(backtrace.get(), test_func, frame_num); +} + +typedef int (*test_func_t)(int, int, int, int, void (*)(uintptr_t), uintptr_t); + +TEST(libbacktrace, unwind_through_unreadable_elf_local) { + const char* tmp_so_name = CopySharedLibrary(); + ASSERT_TRUE(tmp_so_name != nullptr); + void* lib_handle = dlopen(tmp_so_name, RTLD_NOW); + ASSERT_TRUE(lib_handle != nullptr); + ASSERT_TRUE(unlink(tmp_so_name) != -1); + + test_func_t test_func; + test_func = reinterpret_cast<test_func_t>(dlsym(lib_handle, "test_level_one")); + ASSERT_TRUE(test_func != nullptr); + + ASSERT_NE(test_func(1, 2, 3, 4, VerifyUnreadableElfBacktrace, + reinterpret_cast<uintptr_t>(test_func)), 0); + + ASSERT_TRUE(dlclose(lib_handle) == 0); +} + +TEST(libbacktrace, unwind_through_unreadable_elf_remote) { + const char* tmp_so_name = CopySharedLibrary(); + ASSERT_TRUE(tmp_so_name != nullptr); + void* lib_handle = dlopen(tmp_so_name, RTLD_NOW); + ASSERT_TRUE(lib_handle != nullptr); + ASSERT_TRUE(unlink(tmp_so_name) != -1); + + test_func_t test_func; + test_func = reinterpret_cast<test_func_t>(dlsym(lib_handle, "test_level_one")); + ASSERT_TRUE(test_func != nullptr); + + pid_t pid; + if ((pid = fork()) == 0) { + test_func(1, 2, 3, 4, 0, 0); + exit(0); + } + ASSERT_TRUE(pid > 0); + ASSERT_TRUE(dlclose(lib_handle) == 0); + + uint64_t start = NanoTime(); + bool done = false; + while (!done) { + ASSERT_TRUE(ptrace(PTRACE_ATTACH, pid, 0, 0) == 0); + + // Wait for the process to get to a stopping point. + WaitForStop(pid); + + std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, BACKTRACE_CURRENT_THREAD)); + ASSERT_TRUE(backtrace.get() != nullptr); + ASSERT_TRUE(backtrace->Unwind(0)); + + size_t frame_num; + if (FindFuncFrameInBacktrace(backtrace.get(), + reinterpret_cast<uintptr_t>(test_func), &frame_num)) { + + VerifyUnreadableElfFrame(backtrace.get(), reinterpret_cast<uintptr_t>(test_func), frame_num); + done = true; + } + + ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0); + + if ((NanoTime() - start) > 5 * NS_PER_SEC) { + break; + } + usleep(US_PER_MSEC); + } + + kill(pid, SIGKILL); + ASSERT_EQ(waitpid(pid, nullptr, 0), pid); + + ASSERT_TRUE(done) << "Test function never found in unwind."; +} + #if defined(ENABLE_PSS_TESTS) #include "GetPss.h" @@ -1110,3 +1448,4 @@ ASSERT_EQ(waitpid(pid, nullptr, 0), pid); } #endif +
diff --git a/libbacktrace/thread_utils.c b/libbacktrace/thread_utils.c index 6f4cd3c..e75f56e 100644 --- a/libbacktrace/thread_utils.c +++ b/libbacktrace/thread_utils.c
@@ -16,25 +16,12 @@ #include "thread_utils.h" -#if defined(__APPLE__) +#if !defined(__BIONIC__) -#include <sys/syscall.h> - -// Mac OS >= 10.6 has a system call equivalent to Linux's gettid(). -pid_t gettid() { - return syscall(SYS_thread_selfid); -} - -#elif !defined(__BIONIC__) - -// glibc doesn't implement or export either gettid or tgkill. +// glibc doesn't implement or export tgkill. #include <unistd.h> #include <sys/syscall.h> -pid_t gettid() { - return syscall(__NR_gettid); -} - int tgkill(int tgid, int tid, int sig) { return syscall(__NR_tgkill, tgid, tid, sig); }
diff --git a/libbacktrace/thread_utils.h b/libbacktrace/thread_utils.h index ae4c929..df83581 100644 --- a/libbacktrace/thread_utils.h +++ b/libbacktrace/thread_utils.h
@@ -23,8 +23,6 @@ int tgkill(int tgid, int tid, int sig); -pid_t gettid(); - __END_DECLS #endif /* _LIBBACKTRACE_THREAD_UTILS_H */
diff --git a/libcutils/Android.mk b/libcutils/Android.mk index 9dc15d1..0963076 100644 --- a/libcutils/Android.mk +++ b/libcutils/Android.mk
@@ -125,6 +125,9 @@ LOCAL_C_INCLUDES := $(libcutils_c_includes) LOCAL_STATIC_LIBRARIES := liblog +ifneq ($(ENABLE_CPUSETS),) +LOCAL_CFLAGS += -DUSE_CPUSETS +endif LOCAL_CFLAGS += -Werror -std=gnu90 include $(BUILD_STATIC_LIBRARY) @@ -134,6 +137,9 @@ # liblog symbols present in libcutils. LOCAL_WHOLE_STATIC_LIBRARIES := libcutils liblog LOCAL_SHARED_LIBRARIES := liblog +ifneq ($(ENABLE_CPUSETS),) +LOCAL_CFLAGS += -DUSE_CPUSETS +endif LOCAL_CFLAGS += -Werror LOCAL_C_INCLUDES := $(libcutils_c_includes) include $(BUILD_SHARED_LIBRARY)
diff --git a/libcutils/debugger.c b/libcutils/debugger.c index 4558719..3407ec3 100644 --- a/libcutils/debugger.c +++ b/libcutils/debugger.c
@@ -68,7 +68,7 @@ } if (send_request(sock_fd, &msg, sizeof(msg)) < 0) { - TEMP_FAILURE_RETRY(close(sock_fd)); + close(sock_fd); return -1; } @@ -95,7 +95,7 @@ break; } } - TEMP_FAILURE_RETRY(close(sock_fd)); + close(sock_fd); return result; } @@ -124,6 +124,6 @@ memcpy(pathbuf, buffer, n + 1); } } - TEMP_FAILURE_RETRY(close(sock_fd)); + close(sock_fd); return result; }
diff --git a/libcutils/fs_config.c b/libcutils/fs_config.c index 9f8023e..9a1ad19 100644 --- a/libcutils/fs_config.c +++ b/libcutils/fs_config.c
@@ -149,14 +149,21 @@ { 00644, AID_ROOT, AID_ROOT, 0, 0 }, }; -static int fs_config_open(int dir) +static int fs_config_open(int dir, const char *target_out_path) { int fd = -1; - const char *out = getenv("OUT"); - if (out && *out) { + if (target_out_path && *target_out_path) { + /* target_out_path is the path to the directory holding content of system partition + but as we cannot guaranty it ends with '/system' we need this below skip_len logic */ char *name = NULL; - asprintf(&name, "%s%s", out, dir ? conf_dir : conf_file); + int target_out_path_len = strlen(target_out_path); + int skip_len = strlen("/system"); + + if (target_out_path[target_out_path_len] == '/') { + skip_len++; + } + asprintf(&name, "%s%s", target_out_path, (dir ? conf_dir : conf_file) + skip_len); if (name) { fd = TEMP_FAILURE_RETRY(open(name, O_RDONLY | O_BINARY)); free(name); @@ -187,7 +194,7 @@ return !strncmp(prefix, path, len); } -void fs_config(const char *path, int dir, +void fs_config(const char *path, int dir, const char *target_out_path, unsigned *uid, unsigned *gid, unsigned *mode, uint64_t *capabilities) { const struct fs_path_config *pc; @@ -199,7 +206,7 @@ plen = strlen(path); - fd = fs_config_open(dir); + fd = fs_config_open(dir, target_out_path); if (fd >= 0) { struct fs_path_config_from_file header;
diff --git a/libcutils/klog.c b/libcutils/klog.c index f574f08..710dc66 100644 --- a/libcutils/klog.c +++ b/libcutils/klog.c
@@ -40,6 +40,11 @@ void klog_init(void) { if (klog_fd >= 0) return; /* Already initialized */ + klog_fd = open("/dev/kmsg", O_WRONLY | O_CLOEXEC); + if (klog_fd >= 0) { + return; + } + static const char* name = "/dev/__kmsg__"; if (mknod(name, S_IFCHR | 0600, (1 << 8) | 11) == 0) { klog_fd = open(name, O_WRONLY | O_CLOEXEC);
diff --git a/libcutils/sched_policy.c b/libcutils/sched_policy.c index dfc8777..83222f4 100644 --- a/libcutils/sched_policy.c +++ b/libcutils/sched_policy.c
@@ -1,16 +1,16 @@ /* ** Copyright 2007, The Android Open Source Project ** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at ** -** http://www.apache.org/licenses/LICENSE-2.0 +** http://www.apache.org/licenses/LICENSE-2.0 ** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and ** limitations under the License. */ @@ -50,6 +50,7 @@ // timer slack value in nS enforced when the thread moves to background #define TIMER_SLACK_BG 40000000 +#define TIMER_SLACK_FG 50000 static pthread_once_t the_once = PTHREAD_ONCE_INIT; @@ -59,27 +60,16 @@ static int bg_cgroup_fd = -1; static int fg_cgroup_fd = -1; +// File descriptors open to /dev/cpuset/../tasks, setup by initialize, or -1 on error +static int bg_cpuset_fd = -1; +static int fg_cpuset_fd = -1; + /* Add tid to the scheduling group defined by the policy */ -static int add_tid_to_cgroup(int tid, SchedPolicy policy) +static int add_tid_to_cgroup(int tid, int fd) { - int fd; - - switch (policy) { - case SP_BACKGROUND: - fd = bg_cgroup_fd; - break; - case SP_FOREGROUND: - case SP_AUDIO_APP: - case SP_AUDIO_SYS: - fd = fg_cgroup_fd; - break; - default: - fd = -1; - break; - } - if (fd < 0) { - SLOGE("add_tid_to_cgroup failed; policy=%d\n", policy); + SLOGE("add_tid_to_cgroup failed; fd=%d\n", fd); + errno = EINVAL; return -1; } @@ -100,8 +90,9 @@ */ if (errno == ESRCH) return 0; - SLOGW("add_tid_to_cgroup failed to write '%s' (%s); policy=%d\n", - ptr, strerror(errno), policy); + SLOGW("add_tid_to_cgroup failed to write '%s' (%s); fd=%d\n", + ptr, strerror(errno), fd); + errno = EINVAL; return -1; } @@ -127,6 +118,17 @@ } else { __sys_supports_schedgroups = 0; } + +#ifdef USE_CPUSETS + if (!access("/dev/cpuset/tasks", F_OK)) { + + filename = "/dev/cpuset/foreground/tasks"; + fg_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC); + filename = "/dev/cpuset/background/tasks"; + bg_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC); + } +#endif + } /* @@ -236,6 +238,42 @@ return 0; } +int set_cpuset_policy(int tid, SchedPolicy policy) +{ + // in the absence of cpusets, use the old sched policy +#ifndef USE_CPUSETS + return set_sched_policy(tid, policy); +#else + if (tid == 0) { + tid = gettid(); + } + policy = _policy(policy); + pthread_once(&the_once, __initialize); + + int fd; + switch (policy) { + case SP_BACKGROUND: + fd = bg_cpuset_fd; + break; + case SP_FOREGROUND: + case SP_AUDIO_APP: + case SP_AUDIO_SYS: + fd = fg_cpuset_fd; + break; + default: + fd = -1; + break; + } + + if (add_tid_to_cgroup(tid, fd) != 0) { + if (errno != ESRCH && errno != ENOENT) + return -errno; + } + + return 0; +#endif +} + int set_sched_policy(int tid, SchedPolicy policy) { if (tid == 0) { @@ -286,7 +324,23 @@ #endif if (__sys_supports_schedgroups) { - if (add_tid_to_cgroup(tid, policy)) { + int fd; + switch (policy) { + case SP_BACKGROUND: + fd = bg_cgroup_fd; + break; + case SP_FOREGROUND: + case SP_AUDIO_APP: + case SP_AUDIO_SYS: + fd = fg_cgroup_fd; + break; + default: + fd = -1; + break; + } + + + if (add_tid_to_cgroup(tid, fd) != 0) { if (errno != ESRCH && errno != ENOENT) return -errno; } @@ -296,11 +350,12 @@ param.sched_priority = 0; sched_setscheduler(tid, (policy == SP_BACKGROUND) ? - SCHED_BATCH : SCHED_NORMAL, + SCHED_BATCH : SCHED_NORMAL, ¶m); } - prctl(PR_SET_TIMERSLACK_PID, policy == SP_BACKGROUND ? TIMER_SLACK_BG : 0, tid); + prctl(PR_SET_TIMERSLACK_PID, + policy == SP_BACKGROUND ? TIMER_SLACK_BG : TIMER_SLACK_FG, tid); return 0; } @@ -337,4 +392,3 @@ else return "error"; } -
diff --git a/libcutils/threads.c b/libcutils/threads.c index 5f5577b..036f8c5 100644 --- a/libcutils/threads.c +++ b/libcutils/threads.c
@@ -16,8 +16,6 @@ #include "cutils/threads.h" -#if !defined(_WIN32) - // For gettid. #if defined(__APPLE__) #include "AvailabilityMacros.h" // For MAC_OS_X_VERSION_MAX_ALLOWED @@ -30,9 +28,24 @@ #include <syscall.h> #include <unistd.h> #elif defined(_WIN32) -#include <Windows.h> +#include <windows.h> #endif +// No definition needed for Android because we'll just pick up bionic's copy. +#ifndef __ANDROID__ +pid_t gettid() { +#if defined(__APPLE__) + return syscall(SYS_thread_selfid); +#elif defined(__linux__) + return syscall(__NR_gettid); +#elif defined(_WIN32) + return GetCurrentThreadId(); +#endif +} +#endif // __ANDROID__ + +#if !defined(_WIN32) + void* thread_store_get( thread_store_t* store ) { if (!store->has_tls) @@ -58,24 +71,6 @@ pthread_setspecific( store->tls, value ); } -// No definition needed for Android because we'll just pick up bionic's copy. -#ifndef __ANDROID__ -pid_t gettid() { -#if defined(__APPLE__) - uint64_t owner; - int rc = pthread_threadid_np(NULL, &owner); - if (rc != 0) { - abort(); - } - return owner; -#elif defined(__linux__) - return syscall(__NR_gettid); -#elif defined(_WIN32) - return (pid_t)GetCurrentThreadId(); -#endif -} -#endif // __ANDROID__ - #else /* !defined(_WIN32) */ void* thread_store_get( thread_store_t* store ) {
diff --git a/libion/ion.c b/libion/ion.c index 4908932..d1984bd 100644 --- a/libion/ion.c +++ b/libion/ion.c
@@ -91,6 +91,7 @@ int flags, off_t offset, unsigned char **ptr, int *map_fd) { int ret; + unsigned char *tmp_ptr; struct ion_fd_data data = { .handle = handle, }; @@ -103,16 +104,17 @@ ret = ion_ioctl(fd, ION_IOC_MAP, &data); if (ret < 0) return ret; - *map_fd = data.fd; - if (*map_fd < 0) { + if (data.fd < 0) { ALOGE("map ioctl returned negative fd\n"); return -EINVAL; } - *ptr = mmap(NULL, length, prot, flags, *map_fd, offset); - if (*ptr == MAP_FAILED) { + tmp_ptr = mmap(NULL, length, prot, flags, data.fd, offset); + if (tmp_ptr == MAP_FAILED) { ALOGE("mmap failed: %s\n", strerror(errno)); return -errno; } + *map_fd = data.fd; + *ptr = tmp_ptr; return ret; } @@ -129,11 +131,11 @@ ret = ion_ioctl(fd, ION_IOC_SHARE, &data); if (ret < 0) return ret; - *share_fd = data.fd; - if (*share_fd < 0) { + if (data.fd < 0) { ALOGE("share ioctl returned negative fd\n"); return -EINVAL; } + *share_fd = data.fd; return ret; }
diff --git a/liblog/Android.mk b/liblog/Android.mk index 70aff83..d7766f5 100644 --- a/liblog/Android.mk +++ b/liblog/Android.mk
@@ -85,7 +85,7 @@ LOCAL_CFLAGS := -Werror $(liblog_cflags) # TODO: This is to work around b/19059885. Remove after root cause is fixed -LOCAL_LDFLAGS_arm := -Wl,--hash-style=sysv +LOCAL_LDFLAGS_arm := -Wl,--hash-style=both include $(BUILD_SHARED_LIBRARY)
diff --git a/liblog/log_is_loggable.c b/liblog/log_is_loggable.c index 2e09192..7a8e33f 100644 --- a/liblog/log_is_loggable.c +++ b/liblog/log_is_loggable.c
@@ -15,41 +15,158 @@ */ #include <ctype.h> +#include <pthread.h> +#include <stdlib.h> #include <string.h> -#include <sys/system_properties.h> +#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_ +#include <sys/_system_properties.h> #include <android/log.h> -static int __android_log_level(const char *tag, int def) +struct cache { + const prop_info *pinfo; + uint32_t serial; + char c; +}; + +static void refresh_cache(struct cache *cache, const char *key) { + uint32_t serial; char buf[PROP_VALUE_MAX]; - if (!tag || !*tag) { - return def; + if (!cache->pinfo) { + cache->pinfo = __system_property_find(key); + if (!cache->pinfo) { + return; + } } - { - static const char log_namespace[] = "persist.log.tag."; - char key[sizeof(log_namespace) + strlen(tag)]; + serial = __system_property_serial(cache->pinfo); + if (serial == cache->serial) { + return; + } + cache->serial = serial; + __system_property_read(cache->pinfo, 0, buf); + cache->c = buf[0]; +} - strcpy(key, log_namespace); +static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; + +static int __android_log_level(const char *tag, int def) +{ + /* sizeof() is used on this array below */ + static const char log_namespace[] = "persist.log.tag."; + static const size_t base_offset = 8; /* skip "persist." */ + /* calculate the size of our key temporary buffer */ + const size_t taglen = (tag && *tag) ? strlen(tag) : 0; + /* sizeof(log_namespace) = strlen(log_namespace) + 1 */ + char key[sizeof(log_namespace) + taglen]; + char *kp; + size_t i; + char c = 0; + /* + * Single layer cache of four properties. Priorities are: + * log.tag.<tag> + * persist.log.tag.<tag> + * log.tag + * persist.log.tag + * Where the missing tag matches all tags and becomes the + * system global default. We do not support ro.log.tag* . + */ + static char *last_tag; + static uint32_t global_serial; + uint32_t current_global_serial; + static struct cache tag_cache[2] = { + { NULL, -1, 0 }, + { NULL, -1, 0 } + }; + static struct cache global_cache[2] = { + { NULL, -1, 0 }, + { NULL, -1, 0 } + }; + + strcpy(key, log_namespace); + + pthread_mutex_lock(&lock); + + current_global_serial = __system_property_area_serial(); + + if (taglen) { + uint32_t current_local_serial = current_global_serial; + + if (!last_tag || strcmp(last_tag, tag)) { + /* invalidate log.tag.<tag> cache */ + for(i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) { + tag_cache[i].pinfo = NULL; + tag_cache[i].serial = -1; + tag_cache[i].c = '\0'; + } + free(last_tag); + last_tag = NULL; + current_global_serial = -1; + } + if (!last_tag) { + last_tag = strdup(tag); + } strcpy(key + sizeof(log_namespace) - 1, tag); - if (__system_property_get(key + 8, buf) <= 0) { - buf[0] = '\0'; - } - if (!buf[0] && __system_property_get(key, buf) <= 0) { - buf[0] = '\0'; + kp = key; + for(i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) { + if (current_local_serial != global_serial) { + refresh_cache(&tag_cache[i], kp); + } + + if (tag_cache[i].c) { + c = tag_cache[i].c; + break; + } + + kp = key + base_offset; } } - switch (toupper(buf[0])) { - case 'V': return ANDROID_LOG_VERBOSE; - case 'D': return ANDROID_LOG_DEBUG; - case 'I': return ANDROID_LOG_INFO; - case 'W': return ANDROID_LOG_WARN; - case 'E': return ANDROID_LOG_ERROR; - case 'F': /* FALLTHRU */ /* Not officially supported */ - case 'A': return ANDROID_LOG_FATAL; - case 'S': return -1; /* ANDROID_LOG_SUPPRESS */ + + switch (toupper(c)) { /* if invalid, resort to global */ + case 'V': + case 'D': + case 'I': + case 'W': + case 'E': + case 'F': /* Not officially supported */ + case 'A': + case 'S': + break; + default: + /* clear '.' after log.tag */ + key[sizeof(log_namespace) - 2] = '\0'; + + kp = key; + for(i = 0; i < (sizeof(global_cache) / sizeof(global_cache[0])); ++i) { + if (current_global_serial != global_serial) { + refresh_cache(&global_cache[i], kp); + } + + if (global_cache[i].c) { + c = global_cache[i].c; + break; + } + + kp = key + base_offset; + } + break; + } + + global_serial = current_global_serial; + + pthread_mutex_unlock(&lock); + + switch (toupper(c)) { + case 'V': return ANDROID_LOG_VERBOSE; + case 'D': return ANDROID_LOG_DEBUG; + case 'I': return ANDROID_LOG_INFO; + case 'W': return ANDROID_LOG_WARN; + case 'E': return ANDROID_LOG_ERROR; + case 'F': /* FALLTHRU */ /* Not officially supported */ + case 'A': return ANDROID_LOG_FATAL; + case 'S': return -1; /* ANDROID_LOG_SUPPRESS */ } return def; }
diff --git a/liblog/log_read.c b/liblog/log_read.c index 5364e4f..9c4af30 100644 --- a/liblog/log_read.c +++ b/liblog/log_read.c
@@ -208,6 +208,7 @@ [LOG_ID_EVENTS] = "events", [LOG_ID_SYSTEM] = "system", [LOG_ID_CRASH] = "crash", + [LOG_ID_KERNEL] = "kernel", }; const char *android_log_id_to_name(log_id_t log_id)
diff --git a/liblog/log_read_kern.c b/liblog/log_read_kern.c index bdc7b18..69b405c 100644 --- a/liblog/log_read_kern.c +++ b/liblog/log_read_kern.c
@@ -62,7 +62,8 @@ [LOG_ID_RADIO] = "radio", [LOG_ID_EVENTS] = "events", [LOG_ID_SYSTEM] = "system", - [LOG_ID_CRASH] = "crash" + [LOG_ID_CRASH] = "crash", + [LOG_ID_KERNEL] = "kernel", }; const char *android_log_id_to_name(log_id_t log_id)
diff --git a/liblog/log_time.cpp b/liblog/log_time.cpp index 50742df..9d5ea0e 100644 --- a/liblog/log_time.cpp +++ b/liblog/log_time.cpp
@@ -22,7 +22,7 @@ #include <log/log_read.h> -const char log_time::default_format[] = "%m-%d %H:%M:%S.%3q"; +const char log_time::default_format[] = "%m-%d %H:%M:%S.%q"; const timespec log_time::EPOCH = { 0, 0 }; // Add %#q for fractional seconds to standard strptime function
diff --git a/liblog/logd_write.c b/liblog/logd_write.c index c62a246..bdee28f 100644 --- a/liblog/logd_write.c +++ b/liblog/logd_write.c
@@ -310,7 +310,8 @@ [LOG_ID_RADIO] = "radio", [LOG_ID_EVENTS] = "events", [LOG_ID_SYSTEM] = "system", - [LOG_ID_CRASH] = "crash" + [LOG_ID_CRASH] = "crash", + [LOG_ID_KERNEL] = "kernel", }; const char *android_log_id_to_name(log_id_t log_id)
diff --git a/liblog/logprint.c b/liblog/logprint.c index 7ba4c8e..c2f1545 100644 --- a/liblog/logprint.c +++ b/liblog/logprint.c
@@ -26,11 +26,15 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <inttypes.h> #include <sys/param.h> #include <log/logd.h> #include <log/logprint.h> +/* open coded fragment, prevent circular dependencies */ +#define WEAK static + typedef struct FilterInfo_t { char *mTag; android_LogPriority mPri; @@ -42,6 +46,8 @@ FilterInfo *filters; AndroidLogPrintFormat format; bool colored_output; + bool usec_time_output; + bool printable_output; }; /* @@ -184,6 +190,8 @@ p_ret->global_pri = ANDROID_LOG_VERBOSE; p_ret->format = FORMAT_BRIEF; p_ret->colored_output = false; + p_ret->usec_time_output = false; + p_ret->printable_output = false; return p_ret; } @@ -206,13 +214,24 @@ -void android_log_setPrintFormat(AndroidLogFormat *p_format, +int android_log_setPrintFormat(AndroidLogFormat *p_format, AndroidLogPrintFormat format) { - if (format == FORMAT_COLOR) + switch (format) { + case FORMAT_MODIFIER_COLOR: p_format->colored_output = true; - else - p_format->format = format; + return 0; + case FORMAT_MODIFIER_TIME_USEC: + p_format->usec_time_output = true; + return 0; + case FORMAT_MODIFIER_PRINTABLE: + p_format->printable_output = true; + return 0; + default: + break; + } + p_format->format = format; + return 1; } /** @@ -230,7 +249,9 @@ else if (strcmp(formatString, "time") == 0) format = FORMAT_TIME; else if (strcmp(formatString, "threadtime") == 0) format = FORMAT_THREADTIME; else if (strcmp(formatString, "long") == 0) format = FORMAT_LONG; - else if (strcmp(formatString, "color") == 0) format = FORMAT_COLOR; + else if (strcmp(formatString, "color") == 0) format = FORMAT_MODIFIER_COLOR; + else if (strcmp(formatString, "usec") == 0) format = FORMAT_MODIFIER_TIME_USEC; + else if (strcmp(formatString, "printable") == 0) format = FORMAT_MODIFIER_PRINTABLE; else format = FORMAT_OFF; return format; @@ -266,29 +287,35 @@ } if(0 == strncmp("*", filterExpression, tagNameLength)) { - // This filter expression refers to the global filter - // The default level for this is DEBUG if the priority - // is unspecified + /* + * This filter expression refers to the global filter + * The default level for this is DEBUG if the priority + * is unspecified + */ if (pri == ANDROID_LOG_DEFAULT) { pri = ANDROID_LOG_DEBUG; } p_format->global_pri = pri; } else { - // for filter expressions that don't refer to the global - // filter, the default is verbose if the priority is unspecified + /* + * for filter expressions that don't refer to the global + * filter, the default is verbose if the priority is unspecified + */ if (pri == ANDROID_LOG_DEFAULT) { pri = ANDROID_LOG_VERBOSE; } char *tagName; -// Presently HAVE_STRNDUP is never defined, so the second case is always taken -// Darwin doesn't have strnup, everything else does +/* + * Presently HAVE_STRNDUP is never defined, so the second case is always taken + * Darwin doesn't have strnup, everything else does + */ #ifdef HAVE_STRNDUP tagName = strndup(filterExpression, tagNameLength); #else - //a few extra bytes copied... + /* a few extra bytes copied... */ tagName = strdup(filterExpression); tagName[tagNameLength] = '\0'; #endif /*HAVE_STRNDUP*/ @@ -325,9 +352,9 @@ char *p_ret; int err; - // Yes, I'm using strsep + /* Yes, I'm using strsep */ while (NULL != (p_ret = strsep(&p_cur, " \t,"))) { - // ignore whitespace-only entries + /* ignore whitespace-only entries */ if(p_ret[0] != '\0') { err = android_log_addFilterRule(p_format, p_ret); @@ -371,8 +398,10 @@ * When that happens, we must null-terminate the message ourselves. */ if (buf->len < 3) { - // An well-formed entry must consist of at least a priority - // and two null characters + /* + * An well-formed entry must consist of at least a priority + * and two null characters + */ fprintf(stderr, "+++ LOG: entry too small\n"); return -1; } @@ -402,7 +431,7 @@ return -1; } if (msgEnd == -1) { - // incoming message not null-terminated; force it + /* incoming message not null-terminated; force it */ msgEnd = buf->len - 1; msg[msgEnd] = '\0'; } @@ -432,7 +461,7 @@ low = src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24); high = src[4] | (src[5] << 8) | (src[6] << 16) | (src[7] << 24); - return ((long long) high << 32) | (long long) low; + return ((uint64_t) high << 32) | (uint64_t) low; } @@ -463,8 +492,6 @@ type = *eventData++; eventDataLen--; - //fprintf(stderr, "--- type=%d (rem len=%d)\n", type, eventDataLen); - switch (type) { case EVENT_TYPE_INT: /* 32-bit signed int */ @@ -490,7 +517,7 @@ case EVENT_TYPE_LONG: /* 64-bit signed long */ { - long long lval; + uint64_t lval; if (eventDataLen < 8) return -1; @@ -498,7 +525,30 @@ eventData += 8; eventDataLen -= 8; - outCount = snprintf(outBuf, outBufLen, "%lld", lval); + outCount = snprintf(outBuf, outBufLen, "%" PRId64, lval); + if (outCount < outBufLen) { + outBuf += outCount; + outBufLen -= outCount; + } else { + /* halt output */ + goto no_room; + } + } + break; + case EVENT_TYPE_FLOAT: + /* float */ + { + uint32_t ival; + float fval; + + if (eventDataLen < 4) + return -1; + ival = get4LE(eventData); + fval = *(float*)&ival; + eventData += 4; + eventDataLen -= 4; + + outCount = snprintf(outBuf, outBufLen, "%f", fval); if (outCount < outBufLen) { outBuf += outCount; outBufLen -= outCount; @@ -702,6 +752,122 @@ return 0; } +/* + * One utf8 character at a time + * + * Returns the length of the utf8 character in the buffer, + * or -1 if illegal or truncated + * + * Open coded from libutils/Unicode.cpp, borrowed from utf8_length(), + * can not remove from here because of library circular dependencies. + * Expect one-day utf8_character_length with the same signature could + * _also_ be part of libutils/Unicode.cpp if its usefullness needs to + * propagate globally. + */ +WEAK ssize_t utf8_character_length(const char *src, size_t len) +{ + const char *cur = src; + const char first_char = *cur++; + static const uint32_t kUnicodeMaxCodepoint = 0x0010FFFF; + int32_t mask, to_ignore_mask; + size_t num_to_read; + uint32_t utf32; + + if ((first_char & 0x80) == 0) { /* ASCII */ + return 1; + } + + /* + * (UTF-8's character must not be like 10xxxxxx, + * but 110xxxxx, 1110xxxx, ... or 1111110x) + */ + if ((first_char & 0x40) == 0) { + return -1; + } + + for (utf32 = 1, num_to_read = 1, mask = 0x40, to_ignore_mask = 0x80; + num_to_read < 5 && (first_char & mask); + num_to_read++, to_ignore_mask |= mask, mask >>= 1) { + if (num_to_read > len) { + return -1; + } + if ((*cur & 0xC0) != 0x80) { /* can not be 10xxxxxx? */ + return -1; + } + utf32 = (utf32 << 6) + (*cur++ & 0b00111111); + } + /* "first_char" must be (110xxxxx - 11110xxx) */ + if (num_to_read >= 5) { + return -1; + } + to_ignore_mask |= mask; + utf32 |= ((~to_ignore_mask) & first_char) << (6 * (num_to_read - 1)); + if (utf32 > kUnicodeMaxCodepoint) { + return -1; + } + return num_to_read; +} + +/* + * Convert to printable from message to p buffer, return string length. If p is + * NULL, do not copy, but still return the expected string length. + */ +static size_t convertPrintable(char *p, const char *message, size_t messageLen) +{ + char *begin = p; + bool print = p != NULL; + + while (messageLen) { + char buf[6]; + ssize_t len = sizeof(buf) - 1; + if ((size_t)len > messageLen) { + len = messageLen; + } + len = utf8_character_length(message, len); + + if (len < 0) { + snprintf(buf, sizeof(buf), + ((messageLen > 1) && isdigit(message[1])) + ? "\\%03o" + : "\\%o", + *message & 0377); + len = 1; + } else { + buf[0] = '\0'; + if (len == 1) { + if (*message == '\a') { + strcpy(buf, "\\a"); + } else if (*message == '\b') { + strcpy(buf, "\\b"); + } else if (*message == '\t') { + strcpy(buf, "\\t"); + } else if (*message == '\v') { + strcpy(buf, "\\v"); + } else if (*message == '\f') { + strcpy(buf, "\\f"); + } else if (*message == '\r') { + strcpy(buf, "\\r"); + } else if (*message == '\\') { + strcpy(buf, "\\\\"); + } else if ((*message < ' ') || (*message & 0x80)) { + snprintf(buf, sizeof(buf), "\\%o", *message & 0377); + } + } + if (!buf[0]) { + strncpy(buf, message, len); + buf[len] = '\0'; + } + } + if (print) { + strcpy(p, buf); + } + p += strlen(buf); + message += len; + messageLen -= len; + } + return p - begin; +} + /** * Formats a log message into a buffer * @@ -721,7 +887,7 @@ struct tm tmBuf; #endif struct tm* ptm; - char timeBuf[32]; + char timeBuf[32]; /* good margin, 23+nul for msec, 26+nul for usec */ char prefixBuf[128], suffixBuf[128]; char priChar; int prefixSuffixIsHeaderFooter = 0; @@ -745,8 +911,16 @@ #else ptm = localtime(&(entry->tv_sec)); #endif - //strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", ptm); + /* strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", ptm); */ strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm); + len = strlen(timeBuf); + if (p_format->usec_time_output) { + snprintf(timeBuf + len, sizeof(timeBuf) - len, + ".%06ld", entry->tv_nsec / 1000); + } else { + snprintf(timeBuf + len, sizeof(timeBuf) - len, + ".%03ld", entry->tv_nsec / 1000000); + } /* * Construct a buffer containing the log header and log message. @@ -787,23 +961,21 @@ break; case FORMAT_TIME: len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen, - "%s.%03ld %c/%-8s(%5d): ", timeBuf, entry->tv_nsec / 1000000, - priChar, entry->tag, entry->pid); + "%s %c/%-8s(%5d): ", timeBuf, priChar, entry->tag, entry->pid); strcpy(suffixBuf + suffixLen, "\n"); ++suffixLen; break; case FORMAT_THREADTIME: len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen, - "%s.%03ld %5d %5d %c %-8s: ", timeBuf, entry->tv_nsec / 1000000, + "%s %5d %5d %c %-8s: ", timeBuf, entry->pid, entry->tid, priChar, entry->tag); strcpy(suffixBuf + suffixLen, "\n"); ++suffixLen; break; case FORMAT_LONG: len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen, - "[ %s.%03ld %5d:%5d %c/%-8s ]\n", - timeBuf, entry->tv_nsec / 1000000, entry->pid, - entry->tid, priChar, entry->tag); + "[ %s %5d:%5d %c/%-8s ]\n", + timeBuf, entry->pid, entry->tid, priChar, entry->tag); strcpy(suffixBuf + suffixLen, "\n\n"); suffixLen += 2; prefixSuffixIsHeaderFooter = 1; @@ -834,24 +1006,34 @@ const char *pm; if (prefixSuffixIsHeaderFooter) { - // we're just wrapping message with a header/footer + /* we're just wrapping message with a header/footer */ numLines = 1; } else { pm = entry->message; numLines = 0; - // The line-end finding here must match the line-end finding - // in for ( ... numLines...) loop below + /* + * The line-end finding here must match the line-end finding + * in for ( ... numLines...) loop below + */ while (pm < (entry->message + entry->messageLen)) { if (*pm++ == '\n') numLines++; } - // plus one line for anything not newline-terminated at the end + /* plus one line for anything not newline-terminated at the end */ if (pm > entry->message && *(pm-1) != '\n') numLines++; } - // this is an upper bound--newlines in message may be counted - // extraneously - bufferSize = (numLines * (prefixLen + suffixLen)) + entry->messageLen + 1; + /* + * this is an upper bound--newlines in message may be counted + * extraneously + */ + bufferSize = (numLines * (prefixLen + suffixLen)) + 1; + if (p_format->printable_output) { + /* Calculate extra length to convert non-printable to printable */ + bufferSize += convertPrintable(NULL, entry->message, entry->messageLen); + } else { + bufferSize += entry->messageLen; + } if (defaultBufferSize >= bufferSize) { ret = defaultBuffer; @@ -871,8 +1053,12 @@ if (prefixSuffixIsHeaderFooter) { strcat(p, prefixBuf); p += prefixLen; - strncat(p, entry->message, entry->messageLen); - p += entry->messageLen; + if (p_format->printable_output) { + p += convertPrintable(p, entry->message, entry->messageLen); + } else { + strncat(p, entry->message, entry->messageLen); + p += entry->messageLen; + } strcat(p, suffixBuf); p += suffixLen; } else { @@ -881,15 +1067,19 @@ size_t lineLen; lineStart = pm; - // Find the next end-of-line in message + /* Find the next end-of-line in message */ while (pm < (entry->message + entry->messageLen) && *pm != '\n') pm++; lineLen = pm - lineStart; strcat(p, prefixBuf); p += prefixLen; - strncat(p, lineStart, lineLen); - p += lineLen; + if (p_format->printable_output) { + p += convertPrintable(p, lineStart, lineLen); + } else { + strncat(p, lineStart, lineLen); + p += lineLen; + } strcat(p, suffixBuf); p += suffixLen;
diff --git a/liblog/tests/Android.mk b/liblog/tests/Android.mk index d75bbc9..a407c50 100644 --- a/liblog/tests/Android.mk +++ b/liblog/tests/Android.mk
@@ -77,6 +77,6 @@ LOCAL_MODULE := $(test_module_prefix)unit-tests LOCAL_MODULE_TAGS := $(test_tags) LOCAL_CFLAGS += $(test_c_flags) -LOCAL_SHARED_LIBRARIES := liblog +LOCAL_SHARED_LIBRARIES := liblog libcutils LOCAL_SRC_FILES := $(test_src_files) include $(BUILD_NATIVE_TEST)
diff --git a/liblog/tests/libc_test.cpp b/liblog/tests/libc_test.cpp index 29501be..0e84f4e 100644 --- a/liblog/tests/libc_test.cpp +++ b/liblog/tests/libc_test.cpp
@@ -136,3 +136,12 @@ android_logger_list_close(logger_list); } + +TEST(libc, __pstore_append) { + FILE *fp; + ASSERT_TRUE(NULL != (fp = fopen("/dev/pmsg0", "a"))); + static const char message[] = "libc.__pstore_append\n"; + ASSERT_EQ((size_t)1, fwrite(message, sizeof(message), 1, fp)); + ASSERT_EQ(0, fclose(fp)); + fprintf(stderr, "Reboot, ensure string libc.__pstore_append is in /sys/fs/pstore/pmsg-ramoops-0\n"); +}
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp index 33f6481..abe0239 100644 --- a/liblog/tests/liblog_test.cpp +++ b/liblog/tests/liblog_test.cpp
@@ -17,6 +17,8 @@ #include <fcntl.h> #include <inttypes.h> #include <signal.h> + +#include <cutils/properties.h> #include <gtest/gtest.h> #include <log/log.h> #include <log/logger.h> @@ -439,6 +441,7 @@ LOG_FAILURE_RETRY(__android_log_buf_write(LOG_ID_SYSTEM, ANDROID_LOG_INFO, tag, max_payload_buf)); + sleep(2); struct logger_list *logger_list; @@ -603,10 +606,14 @@ if (id != android_name_to_log_id(name)) { continue; } + fprintf(stderr, "log buffer %s\r", name); struct logger * logger; EXPECT_TRUE(NULL != (logger = android_logger_open(logger_list, id))); EXPECT_EQ(id, android_logger_get_id(logger)); - EXPECT_LT(0, android_logger_get_log_size(logger)); + /* crash buffer is allowed to be empty, that is actually healthy! */ + if (android_logger_get_log_size(logger) || strcmp("crash", name)) { + EXPECT_LT(0, android_logger_get_log_size(logger)); + } EXPECT_LT(0, android_logger_get_log_readable_size(logger)); EXPECT_LT(0, android_logger_get_log_version(logger)); } @@ -682,3 +689,190 @@ android_log_format_free(p_format); } + +TEST(liblog, is_loggable) { + static const char tag[] = "is_loggable"; + static const char log_namespace[] = "persist.log.tag."; + static const size_t base_offset = 8; /* skip "persist." */ + // sizeof("string") = strlen("string") + 1 + char key[sizeof(log_namespace) + sizeof(tag) - 1]; + char hold[4][PROP_VALUE_MAX]; + static const struct { + int level; + char type; + } levels[] = { + { ANDROID_LOG_VERBOSE, 'v' }, + { ANDROID_LOG_DEBUG , 'd' }, + { ANDROID_LOG_INFO , 'i' }, + { ANDROID_LOG_WARN , 'w' }, + { ANDROID_LOG_ERROR , 'e' }, + { ANDROID_LOG_FATAL , 'a' }, + { -1 , 's' }, + { -2 , 'g' }, // Illegal value, resort to default + }; + + // Set up initial test condition + memset(hold, 0, sizeof(hold)); + snprintf(key, sizeof(key), "%s%s", log_namespace, tag); + property_get(key, hold[0], ""); + property_set(key, ""); + property_get(key + base_offset, hold[1], ""); + property_set(key + base_offset, ""); + strcpy(key, log_namespace); + key[sizeof(log_namespace) - 2] = '\0'; + property_get(key, hold[2], ""); + property_set(key, ""); + property_get(key, hold[3], ""); + property_set(key + base_offset, ""); + + // All combinations of level and defaults + for(size_t i = 0; i < (sizeof(levels) / sizeof(levels[0])); ++i) { + if (levels[i].level == -2) { + continue; + } + for(size_t j = 0; j < (sizeof(levels) / sizeof(levels[0])); ++j) { + if (levels[j].level == -2) { + continue; + } + fprintf(stderr, "i=%zu j=%zu\r", i, j); + if ((levels[i].level < levels[j].level) + || (levels[j].level == -1)) { + EXPECT_FALSE(__android_log_is_loggable(levels[i].level, tag, + levels[j].level)); + } else { + EXPECT_TRUE(__android_log_is_loggable(levels[i].level, tag, + levels[j].level)); + } + } + } + + // All combinations of level and tag and global properties + for(size_t i = 0; i < (sizeof(levels) / sizeof(levels[0])); ++i) { + if (levels[i].level == -2) { + continue; + } + for(size_t j = 0; j < (sizeof(levels) / sizeof(levels[0])); ++j) { + char buf[2]; + buf[0] = levels[j].type; + buf[1] = '\0'; + + snprintf(key, sizeof(key), "%s%s", log_namespace, tag); + fprintf(stderr, "i=%zu j=%zu property_set(\"%s\",\"%s\")\r", + i, j, key, buf); + property_set(key, buf); + if ((levels[i].level < levels[j].level) + || (levels[j].level == -1) + || ((levels[i].level < ANDROID_LOG_DEBUG) + && (levels[j].level == -2))) { + EXPECT_FALSE(__android_log_is_loggable(levels[i].level, tag, + ANDROID_LOG_DEBUG)); + } else { + EXPECT_TRUE(__android_log_is_loggable(levels[i].level, tag, + ANDROID_LOG_DEBUG)); + } + property_set(key, ""); + + fprintf(stderr, "i=%zu j=%zu property_set(\"%s\",\"%s\")\r", + i, j, key + base_offset, buf); + property_set(key + base_offset, buf); + if ((levels[i].level < levels[j].level) + || (levels[j].level == -1) + || ((levels[i].level < ANDROID_LOG_DEBUG) + && (levels[j].level == -2))) { + EXPECT_FALSE(__android_log_is_loggable(levels[i].level, tag, + ANDROID_LOG_DEBUG)); + } else { + EXPECT_TRUE(__android_log_is_loggable(levels[i].level, tag, + ANDROID_LOG_DEBUG)); + } + property_set(key + base_offset, ""); + + strcpy(key, log_namespace); + key[sizeof(log_namespace) - 2] = '\0'; + fprintf(stderr, "i=%zu j=%zu property_set(\"%s\",\"%s\")\r", + i, j, key, buf); + property_set(key, buf); + if ((levels[i].level < levels[j].level) + || (levels[j].level == -1) + || ((levels[i].level < ANDROID_LOG_DEBUG) + && (levels[j].level == -2))) { + EXPECT_FALSE(__android_log_is_loggable(levels[i].level, tag, + ANDROID_LOG_DEBUG)); + } else { + EXPECT_TRUE(__android_log_is_loggable(levels[i].level, tag, + ANDROID_LOG_DEBUG)); + } + property_set(key, ""); + + fprintf(stderr, "i=%zu j=%zu property_set(\"%s\",\"%s\")\r", + i, j, key + base_offset, buf); + property_set(key + base_offset, buf); + if ((levels[i].level < levels[j].level) + || (levels[j].level == -1) + || ((levels[i].level < ANDROID_LOG_DEBUG) + && (levels[j].level == -2))) { + EXPECT_FALSE(__android_log_is_loggable(levels[i].level, tag, + ANDROID_LOG_DEBUG)); + } else { + EXPECT_TRUE(__android_log_is_loggable(levels[i].level, tag, + ANDROID_LOG_DEBUG)); + } + property_set(key + base_offset, ""); + } + } + + // All combinations of level and tag properties, but with global set to INFO + strcpy(key, log_namespace); + key[sizeof(log_namespace) - 2] = '\0'; + property_set(key, "I"); + snprintf(key, sizeof(key), "%s%s", log_namespace, tag); + for(size_t i = 0; i < (sizeof(levels) / sizeof(levels[0])); ++i) { + if (levels[i].level == -2) { + continue; + } + for(size_t j = 0; j < (sizeof(levels) / sizeof(levels[0])); ++j) { + char buf[2]; + buf[0] = levels[j].type; + buf[1] = '\0'; + + fprintf(stderr, "i=%zu j=%zu property_set(\"%s\",\"%s\")\r", + i, j, key, buf); + property_set(key, buf); + if ((levels[i].level < levels[j].level) + || (levels[j].level == -1) + || ((levels[i].level < ANDROID_LOG_INFO) // Yes INFO + && (levels[j].level == -2))) { + EXPECT_FALSE(__android_log_is_loggable(levels[i].level, tag, + ANDROID_LOG_DEBUG)); + } else { + EXPECT_TRUE(__android_log_is_loggable(levels[i].level, tag, + ANDROID_LOG_DEBUG)); + } + property_set(key, ""); + + fprintf(stderr, "i=%zu j=%zu property_set(\"%s\",\"%s\")\r", + i, j, key + base_offset, buf); + property_set(key + base_offset, buf); + if ((levels[i].level < levels[j].level) + || (levels[j].level == -1) + || ((levels[i].level < ANDROID_LOG_INFO) // Yes INFO + && (levels[j].level == -2))) { + EXPECT_FALSE(__android_log_is_loggable(levels[i].level, tag, + ANDROID_LOG_DEBUG)); + } else { + EXPECT_TRUE(__android_log_is_loggable(levels[i].level, tag, + ANDROID_LOG_DEBUG)); + } + property_set(key + base_offset, ""); + } + } + + // reset parms + snprintf(key, sizeof(key), "%s%s", log_namespace, tag); + property_set(key, hold[0]); + property_set(key + base_offset, hold[1]); + strcpy(key, log_namespace); + key[sizeof(log_namespace) - 2] = '\0'; + property_set(key, hold[2]); + property_set(key + base_offset, hold[3]); +}
diff --git a/libmincrypt/Android.mk b/libmincrypt/Android.mk index 503bcb4..7906986 100644 --- a/libmincrypt/Android.mk +++ b/libmincrypt/Android.mk
@@ -6,8 +6,6 @@ LOCAL_MODULE := libmincrypt LOCAL_SRC_FILES := dsa_sig.c p256.c p256_ec.c p256_ecdsa.c rsa.c sha.c sha256.c LOCAL_CFLAGS := -Wall -Werror -# Clang's slp-vectorize phase has segmentation fault when compiling p256_ec.c. -LOCAL_CLANG_CFLAGS += -fno-slp-vectorize include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS)
diff --git a/libnativebridge/native_bridge.cc b/libnativebridge/native_bridge.cc index 6fa4b39..f63497b 100644 --- a/libnativebridge/native_bridge.cc +++ b/libnativebridge/native_bridge.cc
@@ -83,7 +83,7 @@ static void* native_bridge_handle = nullptr; // Pointer to the callbacks. Available as soon as LoadNativeBridge succeeds, but only initialized // later. -static NativeBridgeCallbacks* callbacks = nullptr; +static const NativeBridgeCallbacks* callbacks = nullptr; // Callbacks provided by the environment to the bridge. Passed to LoadNativeBridge. static const NativeBridgeRuntimeCallbacks* runtime_callbacks = nullptr; @@ -96,7 +96,7 @@ // and hard code the directory name again here. static constexpr const char* kCodeCacheDir = "code_cache"; -static constexpr uint32_t kNativeBridgeCallbackVersion = 1; +static constexpr uint32_t kLibNativeBridgeVersion = 2; // Characters allowed in a native bridge filename. The first character must // be in [a-zA-Z] (expected 'l' for "libx"). The rest must be in [a-zA-Z0-9._-]. @@ -121,7 +121,9 @@ // First character must be [a-zA-Z]. if (!CharacterAllowed(*ptr, true)) { // Found an invalid fist character, don't accept. - ALOGE("Native bridge library %s has been rejected for first character %c", nb_library_filename, *ptr); + ALOGE("Native bridge library %s has been rejected for first character %c", + nb_library_filename, + *ptr); return false; } else { // For the rest, be more liberal. @@ -139,8 +141,22 @@ } } -static bool VersionCheck(NativeBridgeCallbacks* cb) { - return cb != nullptr && cb->version == kNativeBridgeCallbackVersion; +static bool VersionCheck(const NativeBridgeCallbacks* cb) { + // Libnativebridge is now designed to be forward-compatible. So only "0" is an unsupported + // version. + if (cb == nullptr || cb->version == 0) { + return false; + } + + // If this is a v2+ bridge, it may not be forwards- or backwards-compatible. Check. + if (cb->version >= 2) { + if (!callbacks->isCompatibleWith(kLibNativeBridgeVersion)) { + // TODO: Scan which version is supported, and fall back to handle it. + return false; + } + } + + return true; } static void CloseNativeBridge(bool with_error) { @@ -321,7 +337,7 @@ } // Set up the environment for the bridged app. -static void SetupEnvironment(NativeBridgeCallbacks* callbacks, JNIEnv* env, const char* isa) { +static void SetupEnvironment(const NativeBridgeCallbacks* callbacks, JNIEnv* env, const char* isa) { // Need a JNIEnv* to do anything. if (env == nullptr) { ALOGW("No JNIEnv* to set up app environment."); @@ -485,4 +501,18 @@ return false; } +uint32_t NativeBridgeGetVersion() { + if (NativeBridgeAvailable()) { + return callbacks->version; + } + return 0; +} + +NativeBridgeSignalHandlerFn NativeBridgeGetSignalHandler(int signal) { + if (NativeBridgeInitialized() && callbacks->version >= 2) { + return callbacks->getSignalHandler(signal); + } + return nullptr; +} + }; // namespace android
diff --git a/libnativebridge/tests/Android.mk b/libnativebridge/tests/Android.mk index f28c490..285e8c2 100644 --- a/libnativebridge/tests/Android.mk +++ b/libnativebridge/tests/Android.mk
@@ -11,6 +11,8 @@ CodeCacheExists_test.cpp \ CompleteFlow_test.cpp \ InvalidCharsNativeBridge_test.cpp \ + NativeBridge2Signal_test.cpp \ + NativeBridgeVersion_test.cpp \ NeedsNativeBridge_test.cpp \ PreInitializeNativeBridge_test.cpp \ PreInitializeNativeBridgeFail1_test.cpp \
diff --git a/libnativebridge/tests/Android.nativebridge-dummy.mk b/libnativebridge/tests/Android.nativebridge-dummy.mk index 1caf50a..2efc176 100644 --- a/libnativebridge/tests/Android.nativebridge-dummy.mk +++ b/libnativebridge/tests/Android.nativebridge-dummy.mk
@@ -32,3 +32,39 @@ LOCAL_MULTILIB := both include $(BUILD_HOST_SHARED_LIBRARY) + + +# v2. + +NATIVE_BRIDGE2_COMMON_SRC_FILES := \ + DummyNativeBridge2.cpp + +# Shared library for target +# ======================================================== +include $(CLEAR_VARS) + +LOCAL_MODULE:= libnativebridge2-dummy + +LOCAL_SRC_FILES:= $(NATIVE_BRIDGE2_COMMON_SRC_FILES) +LOCAL_CLANG := true +LOCAL_CFLAGS += -Werror -Wall +LOCAL_CPPFLAGS := -std=gnu++11 -fvisibility=protected +LOCAL_LDFLAGS := -ldl +LOCAL_MULTILIB := both + +include $(BUILD_SHARED_LIBRARY) + +# Shared library for host +# ======================================================== +include $(CLEAR_VARS) + +LOCAL_MODULE:= libnativebridge2-dummy + +LOCAL_SRC_FILES:= $(NATIVE_BRIDGE2_COMMON_SRC_FILES) +LOCAL_CLANG := true +LOCAL_CFLAGS += -Werror -Wall +LOCAL_CPPFLAGS := -std=gnu++11 -fvisibility=protected +LOCAL_LDFLAGS := -ldl +LOCAL_MULTILIB := both + +include $(BUILD_HOST_SHARED_LIBRARY)
diff --git a/libnativebridge/tests/DummyNativeBridge2.cpp b/libnativebridge/tests/DummyNativeBridge2.cpp new file mode 100644 index 0000000..6920c74 --- /dev/null +++ b/libnativebridge/tests/DummyNativeBridge2.cpp
@@ -0,0 +1,76 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// A dummy implementation of the native-bridge interface. + +#include "nativebridge/native_bridge.h" + +#include <signal.h> + +// NativeBridgeCallbacks implementations +extern "C" bool native_bridge2_initialize(const android::NativeBridgeRuntimeCallbacks* /* art_cbs */, + const char* /* app_code_cache_dir */, + const char* /* isa */) { + return true; +} + +extern "C" void* native_bridge2_loadLibrary(const char* /* libpath */, int /* flag */) { + return nullptr; +} + +extern "C" void* native_bridge2_getTrampoline(void* /* handle */, const char* /* name */, + const char* /* shorty */, uint32_t /* len */) { + return nullptr; +} + +extern "C" bool native_bridge2_isSupported(const char* /* libpath */) { + return false; +} + +extern "C" const struct android::NativeBridgeRuntimeValues* native_bridge2_getAppEnv( + const char* /* abi */) { + return nullptr; +} + +extern "C" bool native_bridge2_is_compatible_compatible_with(uint32_t version) { + // For testing, allow 1 and 2, but disallow 3+. + return version <= 2; +} + +static bool native_bridge2_dummy_signal_handler(int, siginfo_t*, void*) { + // TODO: Implement something here. We'd either have to have a death test with a log here, or + // we'd have to be able to resume after the faulting instruction... + return true; +} + +extern "C" android::NativeBridgeSignalHandlerFn native_bridge2_get_signal_handler(int signal) { + if (signal == SIGSEGV) { + return &native_bridge2_dummy_signal_handler; + } + return nullptr; +} + +android::NativeBridgeCallbacks NativeBridgeItf { + .version = 2, + .initialize = &native_bridge2_initialize, + .loadLibrary = &native_bridge2_loadLibrary, + .getTrampoline = &native_bridge2_getTrampoline, + .isSupported = &native_bridge2_isSupported, + .getAppEnv = &native_bridge2_getAppEnv, + .isCompatibleWith = &native_bridge2_is_compatible_compatible_with, + .getSignalHandler = &native_bridge2_get_signal_handler +}; +
diff --git a/libnativebridge/tests/NativeBridge2Signal_test.cpp b/libnativebridge/tests/NativeBridge2Signal_test.cpp new file mode 100644 index 0000000..44e45e3 --- /dev/null +++ b/libnativebridge/tests/NativeBridge2Signal_test.cpp
@@ -0,0 +1,42 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NativeBridgeTest.h" + +#include <signal.h> +#include <unistd.h> + +namespace android { + +constexpr const char* kNativeBridgeLibrary2 = "libnativebridge2-dummy.so"; + +TEST_F(NativeBridgeTest, V2_Signal) { + // Init + ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary2, nullptr)); + ASSERT_TRUE(NativeBridgeAvailable()); + ASSERT_TRUE(PreInitializeNativeBridge(".", "isa")); + ASSERT_TRUE(NativeBridgeAvailable()); + ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr)); + ASSERT_TRUE(NativeBridgeAvailable()); + + ASSERT_EQ(2U, NativeBridgeGetVersion()); + ASSERT_NE(nullptr, NativeBridgeGetSignalHandler(SIGSEGV)); + + // Clean-up code_cache + ASSERT_EQ(0, rmdir(kCodeCache)); +} + +} // namespace android
diff --git a/libnativebridge/tests/NativeBridgeVersion_test.cpp b/libnativebridge/tests/NativeBridgeVersion_test.cpp new file mode 100644 index 0000000..d3f9a80 --- /dev/null +++ b/libnativebridge/tests/NativeBridgeVersion_test.cpp
@@ -0,0 +1,38 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NativeBridgeTest.h" + +#include <unistd.h> + +namespace android { + +TEST_F(NativeBridgeTest, Version) { + // When a bridge isn't loaded, we expect 0. + EXPECT_EQ(NativeBridgeGetVersion(), 0U); + + // After our dummy bridge has been loaded, we expect 1. + ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary, nullptr)); + EXPECT_EQ(NativeBridgeGetVersion(), 1U); + + // Unload + UnloadNativeBridge(); + + // Version information is gone. + EXPECT_EQ(NativeBridgeGetVersion(), 0U); +} + +} // namespace android
diff --git a/libnetutils/dhcp_utils.c b/libnetutils/dhcp_utils.c index 70e37c6..c6b9fe4 100644 --- a/libnetutils/dhcp_utils.c +++ b/libnetutils/dhcp_utils.c
@@ -1,16 +1,16 @@ /* * Copyright 2008, The Android Open Source Project * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and * limitations under the License. */ @@ -33,7 +33,7 @@ static const int NAP_TIME = 200; /* wait for 200ms at a time */ /* when polling for property values */ static const char DAEMON_NAME_RENEW[] = "iprenew"; -static char errmsg[100]; +static char errmsg[100] = "\0"; /* interface length for dhcpcd daemon start (dhcpcd_<interface> as defined in init.rc file) * or for filling up system properties dhcpcd.<interface>.ipaddress, dhcpcd.<interface>.dns1 * and other properties on a successful bind @@ -74,7 +74,7 @@ while (maxnaps-- >= 0) { if (property_get(name, value, NULL)) { - if (desired_value == NULL || + if (desired_value == NULL || strcmp(value, desired_value) == 0) { return 0; } @@ -169,6 +169,47 @@ } /* + * Get any available DHCP results. + */ +int dhcp_get_results(const char *interface, + char *ipaddr, + char *gateway, + uint32_t *prefixLength, + char *dns[], + char *server, + uint32_t *lease, + char *vendorInfo, + char *domain, + char *mtu) +{ + char result_prop_name[PROPERTY_KEY_MAX]; + char prop_value[PROPERTY_VALUE_MAX]; + + /* Interface name after converting p2p0-p2p0-X to p2p to reuse system properties */ + char p2p_interface[MAX_INTERFACE_LENGTH]; + get_p2p_interface_replacement(interface, p2p_interface); + snprintf(result_prop_name, sizeof(result_prop_name), "%s.%s.result", + DHCP_PROP_NAME_PREFIX, + p2p_interface); + + memset(prop_value, '\0', PROPERTY_VALUE_MAX); + if (!property_get(result_prop_name, prop_value, NULL)) { + snprintf(errmsg, sizeof(errmsg), "%s", "DHCP result property was not set"); + return -1; + } + if (strcmp(prop_value, "ok") == 0) { + if (fill_ip_info(interface, ipaddr, gateway, prefixLength, dns, + server, lease, vendorInfo, domain, mtu) == -1) { + return -1; + } + return 0; + } else { + snprintf(errmsg, sizeof(errmsg), "DHCP result was %s", prop_value); + return -1; + } +} + +/* * Start the dhcp client daemon, and wait for it to finish * configuring the interface. * @@ -177,16 +218,7 @@ * Example: * service dhcpcd_<interface> /system/bin/dhcpcd -ABKL -f dhcpcd.conf */ -int dhcp_do_request(const char *interface, - char *ipaddr, - char *gateway, - uint32_t *prefixLength, - char *dns[], - char *server, - uint32_t *lease, - char *vendorInfo, - char *domain, - char *mtu) +int dhcp_start(const char *interface) { char result_prop_name[PROPERTY_KEY_MAX]; char daemon_prop_name[PROPERTY_KEY_MAX]; @@ -230,21 +262,7 @@ return -1; } - if (!property_get(result_prop_name, prop_value, NULL)) { - /* shouldn't ever happen, given the success of wait_for_property() */ - snprintf(errmsg, sizeof(errmsg), "%s", "DHCP result property was not set"); - return -1; - } - if (strcmp(prop_value, "ok") == 0) { - if (fill_ip_info(interface, ipaddr, gateway, prefixLength, dns, - server, lease, vendorInfo, domain, mtu) == -1) { - return -1; - } - return 0; - } else { - snprintf(errmsg, sizeof(errmsg), "DHCP result was %s", prop_value); - return -1; - } + return 0; } /** @@ -320,16 +338,7 @@ * service iprenew_<interface> /system/bin/dhcpcd -n * */ -int dhcp_do_request_renew(const char *interface, - char *ipaddr, - char *gateway, - uint32_t *prefixLength, - char *dns[], - char *server, - uint32_t *lease, - char *vendorInfo, - char *domain, - char *mtu) +int dhcp_start_renew(const char *interface) { char result_prop_name[PROPERTY_KEY_MAX]; char prop_value[PROPERTY_VALUE_MAX] = {'\0'}; @@ -359,16 +368,5 @@ return -1; } - if (!property_get(result_prop_name, prop_value, NULL)) { - /* shouldn't ever happen, given the success of wait_for_property() */ - snprintf(errmsg, sizeof(errmsg), "%s", "DHCP Renew result property was not set"); - return -1; - } - if (strcmp(prop_value, "ok") == 0) { - return fill_ip_info(interface, ipaddr, gateway, prefixLength, dns, - server, lease, vendorInfo, domain, mtu); - } else { - snprintf(errmsg, sizeof(errmsg), "DHCP Renew result was %s", prop_value); - return -1; - } + return 0; }
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp index a80965f..ad0500d 100644 --- a/libprocessgroup/processgroup.cpp +++ b/libprocessgroup/processgroup.cpp
@@ -252,14 +252,15 @@ int killProcessGroup(uid_t uid, int initialPid, int signal) { int processes; - int sleep_us = 100; + const int sleep_us = 5 * 1000; // 5ms int64_t startTime = android::uptimeMillis(); + int retry = 40; while ((processes = killProcessGroupOnce(uid, initialPid, signal)) > 0) { SLOGV("killed %d processes for processgroup %d\n", processes, initialPid); - if (sleep_us < 128000) { + if (retry > 0) { usleep(sleep_us); - sleep_us *= 2; + --retry; } else { SLOGE("failed to kill %d processes for processgroup %d\n", processes, initialPid);
diff --git a/libsuspend/autosuspend_autosleep.c b/libsuspend/autosuspend_autosleep.c index 0d31e74..7262cc7 100644 --- a/libsuspend/autosuspend_autosleep.c +++ b/libsuspend/autosuspend_autosleep.c
@@ -40,7 +40,7 @@ ALOGV("autosuspend_autosleep_enable\n"); - ret = write(autosleep_fd, sleep_state, strlen(sleep_state)); + ret = TEMP_FAILURE_RETRY(write(autosleep_fd, sleep_state, strlen(sleep_state))); if (ret < 0) { strerror_r(errno, buf, sizeof(buf)); ALOGE("Error writing to %s: %s\n", SYS_POWER_AUTOSLEEP, buf); @@ -62,7 +62,7 @@ ALOGV("autosuspend_autosleep_disable\n"); - ret = write(autosleep_fd, on_state, strlen(on_state)); + ret = TEMP_FAILURE_RETRY(write(autosleep_fd, on_state, strlen(on_state))); if (ret < 0) { strerror_r(errno, buf, sizeof(buf)); ALOGE("Error writing to %s: %s\n", SYS_POWER_AUTOSLEEP, buf); @@ -86,7 +86,7 @@ { char buf[80]; - autosleep_fd = open(SYS_POWER_AUTOSLEEP, O_WRONLY); + autosleep_fd = TEMP_FAILURE_RETRY(open(SYS_POWER_AUTOSLEEP, O_WRONLY)); if (autosleep_fd < 0) { strerror_r(errno, buf, sizeof(buf)); ALOGE("Error opening %s: %s\n", SYS_POWER_AUTOSLEEP, buf);
diff --git a/libsuspend/autosuspend_earlysuspend.c b/libsuspend/autosuspend_earlysuspend.c index 2bece4c..3793a69 100644 --- a/libsuspend/autosuspend_earlysuspend.c +++ b/libsuspend/autosuspend_earlysuspend.c
@@ -49,11 +49,9 @@ { int err = 0; char buf; - int fd = open(EARLYSUSPEND_WAIT_FOR_FB_WAKE, O_RDONLY, 0); + int fd = TEMP_FAILURE_RETRY(open(EARLYSUSPEND_WAIT_FOR_FB_WAKE, O_RDONLY, 0)); // if the file doesn't exist, the error will be caught in read() below - do { - err = read(fd, &buf, 1); - } while (err < 0 && errno == EINTR); + err = TEMP_FAILURE_RETRY(read(fd, &buf, 1)); ALOGE_IF(err < 0, "*** ANDROID_WAIT_FOR_FB_WAKE failed (%s)", strerror(errno)); close(fd); @@ -64,11 +62,9 @@ { int err = 0; char buf; - int fd = open(EARLYSUSPEND_WAIT_FOR_FB_SLEEP, O_RDONLY, 0); + int fd = TEMP_FAILURE_RETRY(open(EARLYSUSPEND_WAIT_FOR_FB_SLEEP, O_RDONLY, 0)); // if the file doesn't exist, the error will be caught in read() below - do { - err = read(fd, &buf, 1); - } while (err < 0 && errno == EINTR); + err = TEMP_FAILURE_RETRY(read(fd, &buf, 1)); ALOGE_IF(err < 0, "*** ANDROID_WAIT_FOR_FB_SLEEP failed (%s)", strerror(errno)); close(fd); @@ -134,7 +130,7 @@ ALOGV("autosuspend_earlysuspend_disable\n"); - ret = write(sPowerStatefd, pwr_state_on, strlen(pwr_state_on)); + ret = TEMP_FAILURE_RETRY(write(sPowerStatefd, pwr_state_on, strlen(pwr_state_on))); if (ret < 0) { strerror_r(errno, buf, sizeof(buf)); ALOGE("Error writing to %s: %s\n", EARLYSUSPEND_SYS_POWER_STATE, buf); @@ -195,7 +191,7 @@ char buf[80]; int ret; - sPowerStatefd = open(EARLYSUSPEND_SYS_POWER_STATE, O_RDWR); + sPowerStatefd = TEMP_FAILURE_RETRY(open(EARLYSUSPEND_SYS_POWER_STATE, O_RDWR)); if (sPowerStatefd < 0) { strerror_r(errno, buf, sizeof(buf)); @@ -203,7 +199,7 @@ return NULL; } - ret = write(sPowerStatefd, "on", 2); + ret = TEMP_FAILURE_RETRY(write(sPowerStatefd, "on", 2)); if (ret < 0) { strerror_r(errno, buf, sizeof(buf)); ALOGW("Error writing 'on' to %s: %s\n", EARLYSUSPEND_SYS_POWER_STATE, buf);
diff --git a/libsuspend/autosuspend_wakeup_count.c b/libsuspend/autosuspend_wakeup_count.c index 7483a8f..23a0290 100644 --- a/libsuspend/autosuspend_wakeup_count.c +++ b/libsuspend/autosuspend_wakeup_count.c
@@ -19,6 +19,7 @@ #include <pthread.h> #include <semaphore.h> #include <stddef.h> +#include <stdbool.h> #include <string.h> #include <sys/stat.h> #include <sys/types.h> @@ -38,7 +39,7 @@ static pthread_t suspend_thread; static sem_t suspend_lockout; static const char *sleep_state = "mem"; -static void (*wakeup_func)(void) = NULL; +static void (*wakeup_func)(bool success) = NULL; static void *suspend_thread_func(void *arg __attribute__((unused))) { @@ -46,12 +47,14 @@ char wakeup_count[20]; int wakeup_count_len; int ret; + bool success; while (1) { usleep(100000); ALOGV("%s: read wakeup_count\n", __func__); lseek(wakeup_count_fd, 0, SEEK_SET); - wakeup_count_len = read(wakeup_count_fd, wakeup_count, sizeof(wakeup_count)); + wakeup_count_len = TEMP_FAILURE_RETRY(read(wakeup_count_fd, wakeup_count, + sizeof(wakeup_count))); if (wakeup_count_len < 0) { strerror_r(errno, buf, sizeof(buf)); ALOGE("Error reading from %s: %s\n", SYS_POWER_WAKEUP_COUNT, buf); @@ -71,22 +74,21 @@ continue; } + success = true; ALOGV("%s: write %*s to wakeup_count\n", __func__, wakeup_count_len, wakeup_count); - ret = write(wakeup_count_fd, wakeup_count, wakeup_count_len); + ret = TEMP_FAILURE_RETRY(write(wakeup_count_fd, wakeup_count, wakeup_count_len)); if (ret < 0) { strerror_r(errno, buf, sizeof(buf)); ALOGE("Error writing to %s: %s\n", SYS_POWER_WAKEUP_COUNT, buf); } else { ALOGV("%s: write %s to %s\n", __func__, sleep_state, SYS_POWER_STATE); - ret = write(state_fd, sleep_state, strlen(sleep_state)); + ret = TEMP_FAILURE_RETRY(write(state_fd, sleep_state, strlen(sleep_state))); if (ret < 0) { - strerror_r(errno, buf, sizeof(buf)); - ALOGE("Error writing to %s: %s\n", SYS_POWER_STATE, buf); - } else { - void (*func)(void) = wakeup_func; - if (func != NULL) { - (*func)(); - } + success = false; + } + void (*func)(bool success) = wakeup_func; + if (func != NULL) { + (*func)(success); } } @@ -138,7 +140,7 @@ return ret; } -void set_wakeup_callback(void (*func)(void)) +void set_wakeup_callback(void (*func)(bool success)) { if (wakeup_func != NULL) { ALOGE("Duplicate wakeup callback applied, keeping original"); @@ -157,14 +159,14 @@ int ret; char buf[80]; - state_fd = open(SYS_POWER_STATE, O_RDWR); + state_fd = TEMP_FAILURE_RETRY(open(SYS_POWER_STATE, O_RDWR)); if (state_fd < 0) { strerror_r(errno, buf, sizeof(buf)); ALOGE("Error opening %s: %s\n", SYS_POWER_STATE, buf); goto err_open_state; } - wakeup_count_fd = open(SYS_POWER_WAKEUP_COUNT, O_RDWR); + wakeup_count_fd = TEMP_FAILURE_RETRY(open(SYS_POWER_WAKEUP_COUNT, O_RDWR)); if (wakeup_count_fd < 0) { strerror_r(errno, buf, sizeof(buf)); ALOGE("Error opening %s: %s\n", SYS_POWER_WAKEUP_COUNT, buf);
diff --git a/libsuspend/include/suspend/autosuspend.h b/libsuspend/include/suspend/autosuspend.h index 10e3d27..59188a8 100644 --- a/libsuspend/include/suspend/autosuspend.h +++ b/libsuspend/include/suspend/autosuspend.h
@@ -18,6 +18,7 @@ #define _LIBSUSPEND_AUTOSUSPEND_H_ #include <sys/cdefs.h> +#include <stdbool.h> __BEGIN_DECLS @@ -46,9 +47,11 @@ /* * set_wakeup_callback * - * Set a function to be called each time the device wakes up from suspend. + * Set a function to be called each time the device returns from suspend. + * success is true if the suspend was sucessful and false if the suspend + * aborted due to some reason. */ -void set_wakeup_callback(void (*func)(void)); +void set_wakeup_callback(void (*func)(bool success)); __END_DECLS
diff --git a/libsysutils/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp index 909df86..23dcd62 100644 --- a/libsysutils/src/NetlinkEvent.cpp +++ b/libsysutils/src/NetlinkEvent.cpp
@@ -47,20 +47,8 @@ #include <netlink/handlers.h> #include <netlink/msg.h> -const int NetlinkEvent::NlActionUnknown = 0; -const int NetlinkEvent::NlActionAdd = 1; -const int NetlinkEvent::NlActionRemove = 2; -const int NetlinkEvent::NlActionChange = 3; -const int NetlinkEvent::NlActionLinkUp = 4; -const int NetlinkEvent::NlActionLinkDown = 5; -const int NetlinkEvent::NlActionAddressUpdated = 6; -const int NetlinkEvent::NlActionAddressRemoved = 7; -const int NetlinkEvent::NlActionRdnss = 8; -const int NetlinkEvent::NlActionRouteUpdated = 9; -const int NetlinkEvent::NlActionRouteRemoved = 10; - NetlinkEvent::NetlinkEvent() { - mAction = NlActionUnknown; + mAction = Action::kUnknown; memset(mParams, 0, sizeof(mParams)); mPath = NULL; mSubsystem = NULL; @@ -154,8 +142,8 @@ switch(rta->rta_type) { case IFLA_IFNAME: asprintf(&mParams[0], "INTERFACE=%s", (char *) RTA_DATA(rta)); - mAction = (ifi->ifi_flags & IFF_LOWER_UP) ? NlActionLinkUp : - NlActionLinkDown; + mAction = (ifi->ifi_flags & IFF_LOWER_UP) ? Action::kLinkUp : + Action::kLinkDown; mSubsystem = strdup("net"); return true; } @@ -244,8 +232,8 @@ } // Fill in netlink event information. - mAction = (type == RTM_NEWADDR) ? NlActionAddressUpdated : - NlActionAddressRemoved; + mAction = (type == RTM_NEWADDR) ? Action::kAddressUpdated : + Action::kAddressRemoved; mSubsystem = strdup("net"); asprintf(&mParams[0], "ADDRESS=%s/%d", addrstr, ifaddr->ifa_prefixlen); @@ -276,7 +264,7 @@ asprintf(&mParams[0], "ALERT_NAME=%s", pm->prefix); asprintf(&mParams[1], "INTERFACE=%s", devname); mSubsystem = strdup("qlog"); - mAction = NlActionChange; + mAction = Action::kChange; return true; } @@ -311,7 +299,7 @@ asprintf(&mParams[0], "UID=%d", uid); mParams[1] = hex; mSubsystem = strdup("strict"); - mAction = NlActionChange; + mAction = Action::kChange; return true; } @@ -397,8 +385,8 @@ return false; // Fill in netlink event information. - mAction = (type == RTM_NEWROUTE) ? NlActionRouteUpdated : - NlActionRouteRemoved; + mAction = (type == RTM_NEWROUTE) ? Action::kRouteUpdated : + Action::kRouteRemoved; mSubsystem = strdup("net"); asprintf(&mParams[0], "ROUTE=%s/%d", dst, prefixLength); asprintf(&mParams[1], "GATEWAY=%s", (*gw) ? gw : ""); @@ -467,25 +455,27 @@ SLOGE("Invalid optlen %d for RDNSS option\n", optlen); return false; } - int numaddrs = (optlen - 1) / 2; + const int numaddrs = (optlen - 1) / 2; // Find the lifetime. struct nd_opt_rdnss *rndss_opt = (struct nd_opt_rdnss *) opthdr; - uint32_t lifetime = ntohl(rndss_opt->nd_opt_rdnss_lifetime); + const uint32_t lifetime = ntohl(rndss_opt->nd_opt_rdnss_lifetime); // Construct "SERVERS=<comma-separated string of DNS addresses>". - // Reserve (INET6_ADDRSTRLEN + 1) chars for each address: all but the - // the last address are followed by ','; the last is followed by '\0'. static const char kServerTag[] = "SERVERS="; - static const int kTagLength = sizeof(kServerTag) - 1; - int bufsize = kTagLength + numaddrs * (INET6_ADDRSTRLEN + 1); + static const size_t kTagLength = strlen(kServerTag); + // Reserve sufficient space for an IPv6 link-local address: all but the + // last address are followed by ','; the last is followed by '\0'. + static const size_t kMaxSingleAddressLength = + INET6_ADDRSTRLEN + strlen("%") + IFNAMSIZ + strlen(","); + const size_t bufsize = kTagLength + numaddrs * kMaxSingleAddressLength; char *buf = (char *) malloc(bufsize); if (!buf) { SLOGE("RDNSS option: out of memory\n"); return false; } strcpy(buf, kServerTag); - int pos = kTagLength; + size_t pos = kTagLength; struct in6_addr *addrs = (struct in6_addr *) (rndss_opt + 1); for (int i = 0; i < numaddrs; i++) { @@ -494,10 +484,14 @@ } inet_ntop(AF_INET6, addrs + i, buf + pos, bufsize - pos); pos += strlen(buf + pos); + if (IN6_IS_ADDR_LINKLOCAL(addrs + i)) { + buf[pos++] = '%'; + pos += strlcpy(buf + pos, ifname, bufsize - pos); + } } buf[pos] = '\0'; - mAction = NlActionRdnss; + mAction = Action::kRdnss; mSubsystem = strdup("net"); asprintf(&mParams[0], "INTERFACE=%s", ifname); asprintf(&mParams[1], "LIFETIME=%u", lifetime); @@ -617,11 +611,11 @@ const char* a; if ((a = HAS_CONST_PREFIX(s, end, "ACTION=")) != NULL) { if (!strcmp(a, "add")) - mAction = NlActionAdd; + mAction = Action::kAdd; else if (!strcmp(a, "remove")) - mAction = NlActionRemove; + mAction = Action::kRemove; else if (!strcmp(a, "change")) - mAction = NlActionChange; + mAction = Action::kChange; } else if ((a = HAS_CONST_PREFIX(s, end, "SEQNUM=")) != NULL) { mSeq = atoi(a); } else if ((a = HAS_CONST_PREFIX(s, end, "SUBSYSTEM=")) != NULL) {
diff --git a/libsysutils/src/SocketListener.cpp b/libsysutils/src/SocketListener.cpp index 527a6a0..3011ed7 100644 --- a/libsysutils/src/SocketListener.cpp +++ b/libsysutils/src/SocketListener.cpp
@@ -86,6 +86,7 @@ return -1; } SLOGV("got mSock = %d for %s", mSock, mSocketName); + fcntl(mSock, F_SETFD, FD_CLOEXEC); } if (mListen && listen(mSock, backlog) < 0) { @@ -212,6 +213,7 @@ sleep(1); continue; } + fcntl(c, F_SETFD, FD_CLOEXEC); pthread_mutex_lock(&mClientsLock); mClients->push_back(new SocketClient(c, true, mUseCmdNum)); pthread_mutex_unlock(&mClientsLock);
diff --git a/libusbhost/usbhost.c b/libusbhost/usbhost.c index 684f401..b8e3215 100644 --- a/libusbhost/usbhost.c +++ b/libusbhost/usbhost.c
@@ -56,6 +56,9 @@ #define USB_FS_ID_SCANNER USB_FS_DIR "/%d/%d" #define USB_FS_ID_FORMAT USB_FS_DIR "/%03d/%03d" +// Some devices fail to send string descriptors if we attempt reading > 255 bytes +#define MAX_STRING_DESCRIPTOR_LENGTH 255 + // From drivers/usb/core/devio.c // I don't know why this isn't in a kernel header #define MAX_USBFS_BUFFER_SIZE 16384 @@ -449,8 +452,8 @@ char* usb_device_get_string(struct usb_device *device, int id) { char string[256]; - __u16 buffer[128]; - __u16 languages[128]; + __u16 buffer[MAX_STRING_DESCRIPTOR_LENGTH / sizeof(__u16)]; + __u16 languages[MAX_STRING_DESCRIPTOR_LENGTH / sizeof(__u16)]; int i, result; int languageCount = 0; @@ -498,6 +501,12 @@ return usb_device_get_string(device, desc->iProduct); } +int usb_device_get_version(struct usb_device *device) +{ + struct usb_device_descriptor *desc = (struct usb_device_descriptor *)device->desc; + return desc->bcdUSB; +} + char* usb_device_get_serial(struct usb_device *device) { struct usb_device_descriptor *desc = (struct usb_device_descriptor *)device->desc;
diff --git a/libutils/Android.mk b/libutils/Android.mk index d3b2284..d1ed997 100644 --- a/libutils/Android.mk +++ b/libutils/Android.mk
@@ -16,11 +16,9 @@ commonSources:= \ BasicHashtable.cpp \ - BlobCache.cpp \ CallStack.cpp \ FileMap.cpp \ JenkinsHash.cpp \ - LinearAllocator.cpp \ LinearTransform.cpp \ Log.cpp \ NativeHandle.cpp \ @@ -75,6 +73,7 @@ # we have the common sources, plus some device-specific stuff LOCAL_SRC_FILES:= \ $(commonSources) \ + BlobCache.cpp \ Looper.cpp \ Trace.cpp @@ -84,7 +83,8 @@ LOCAL_CFLAGS += -Werror LOCAL_STATIC_LIBRARIES := \ - libcutils + libcutils \ + libc LOCAL_SHARED_LIBRARIES := \ libbacktrace \ @@ -110,6 +110,7 @@ # Include subdirectory makefiles # ============================================================ + include $(CLEAR_VARS) LOCAL_MODULE := SharedBufferTest LOCAL_STATIC_LIBRARIES := libutils libcutils
diff --git a/libutils/BlobCache.cpp b/libutils/BlobCache.cpp index 0ea09cf..126995b 100644 --- a/libutils/BlobCache.cpp +++ b/libutils/BlobCache.cpp
@@ -25,13 +25,15 @@ #include <utils/Errors.h> #include <utils/Log.h> +#include <cutils/properties.h> + namespace android { // BlobCache::Header::mMagicNumber value static const uint32_t blobCacheMagic = ('_' << 24) + ('B' << 16) + ('b' << 8) + '$'; // BlobCache::Header::mBlobCacheVersion value -static const uint32_t blobCacheVersion = 2; +static const uint32_t blobCacheVersion = 3; // BlobCache::Header::mDeviceVersion value static const uint32_t blobCacheDeviceVersion = 1; @@ -165,7 +167,7 @@ } size_t BlobCache::getFlattenedSize() const { - size_t size = align4(sizeof(Header)); + size_t size = align4(sizeof(Header) + PROPERTY_VALUE_MAX); for (size_t i = 0; i < mCacheEntries.size(); i++) { const CacheEntry& e(mCacheEntries[i]); sp<Blob> keyBlob = e.getKey(); @@ -187,10 +189,13 @@ header->mBlobCacheVersion = blobCacheVersion; header->mDeviceVersion = blobCacheDeviceVersion; header->mNumEntries = mCacheEntries.size(); + char buildId[PROPERTY_VALUE_MAX]; + header->mBuildIdLength = property_get("ro.build.id", buildId, ""); + memcpy(header->mBuildId, buildId, header->mBuildIdLength); // Write cache entries uint8_t* byteBuffer = reinterpret_cast<uint8_t*>(buffer); - off_t byteOffset = align4(sizeof(Header)); + off_t byteOffset = align4(sizeof(Header) + header->mBuildIdLength); for (size_t i = 0; i < mCacheEntries.size(); i++) { const CacheEntry& e(mCacheEntries[i]); sp<Blob> keyBlob = e.getKey(); @@ -239,15 +244,19 @@ ALOGE("unflatten: bad magic number: %" PRIu32, header->mMagicNumber); return BAD_VALUE; } + char buildId[PROPERTY_VALUE_MAX]; + int len = property_get("ro.build.id", buildId, ""); if (header->mBlobCacheVersion != blobCacheVersion || - header->mDeviceVersion != blobCacheDeviceVersion) { + header->mDeviceVersion != blobCacheDeviceVersion || + len != header->mBuildIdLength || + strncmp(buildId, header->mBuildId, len)) { // We treat version mismatches as an empty cache. return OK; } // Read cache entries const uint8_t* byteBuffer = reinterpret_cast<const uint8_t*>(buffer); - off_t byteOffset = align4(sizeof(Header)); + off_t byteOffset = align4(sizeof(Header) + header->mBuildIdLength); size_t numEntries = header->mNumEntries; for (size_t i = 0; i < numEntries; i++) { if (byteOffset + sizeof(EntryHeader) > size) {
diff --git a/libutils/LinearAllocator.cpp b/libutils/LinearAllocator.cpp deleted file mode 100644 index 8b90696..0000000 --- a/libutils/LinearAllocator.cpp +++ /dev/null
@@ -1,227 +0,0 @@ -/* - * Copyright 2012, The Android Open Source Project - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#define LOG_TAG "LinearAllocator" -#define LOG_NDEBUG 1 - -#include <stdlib.h> -#include <utils/LinearAllocator.h> -#include <utils/Log.h> - - -// The ideal size of a page allocation (these need to be multiples of 8) -#define INITIAL_PAGE_SIZE ((size_t)4096) // 4kb -#define MAX_PAGE_SIZE ((size_t)131072) // 128kb - -// The maximum amount of wasted space we can have per page -// Allocations exceeding this will have their own dedicated page -// If this is too low, we will malloc too much -// Too high, and we may waste too much space -// Must be smaller than INITIAL_PAGE_SIZE -#define MAX_WASTE_SIZE ((size_t)1024) - -#if ALIGN_DOUBLE -#define ALIGN_SZ (sizeof(double)) -#else -#define ALIGN_SZ (sizeof(int)) -#endif - -#define ALIGN(x) ((x + ALIGN_SZ - 1 ) & ~(ALIGN_SZ - 1)) -#define ALIGN_PTR(p) ((void*)(ALIGN((size_t)p))) - -#if LOG_NDEBUG -#define ADD_ALLOCATION(size) -#define RM_ALLOCATION(size) -#else -#include <utils/Thread.h> -#include <utils/Timers.h> -static size_t s_totalAllocations = 0; -static nsecs_t s_nextLog = 0; -static android::Mutex s_mutex; - -static void _logUsageLocked() { - nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); - if (now > s_nextLog) { - s_nextLog = now + milliseconds_to_nanoseconds(10); - ALOGV("Total memory usage: %zu kb", s_totalAllocations / 1024); - } -} - -static void _addAllocation(size_t size) { - android::AutoMutex lock(s_mutex); - s_totalAllocations += size; - _logUsageLocked(); -} - -#define ADD_ALLOCATION(size) _addAllocation(size); -#define RM_ALLOCATION(size) _addAllocation(-size); -#endif - -#define min(x,y) (((x) < (y)) ? (x) : (y)) - -namespace android { - -class LinearAllocator::Page { -public: - Page* next() { return mNextPage; } - void setNext(Page* next) { mNextPage = next; } - - Page() - : mNextPage(0) - {} - - void* operator new(size_t /*size*/, void* buf) { return buf; } - - void* start() { - return (void*) (((size_t)this) + sizeof(Page)); - } - - void* end(int pageSize) { - return (void*) (((size_t)start()) + pageSize); - } - -private: - Page(const Page& /*other*/) {} - Page* mNextPage; -}; - -LinearAllocator::LinearAllocator() - : mPageSize(INITIAL_PAGE_SIZE) - , mMaxAllocSize(MAX_WASTE_SIZE) - , mNext(0) - , mCurrentPage(0) - , mPages(0) - , mTotalAllocated(0) - , mWastedSpace(0) - , mPageCount(0) - , mDedicatedPageCount(0) {} - -LinearAllocator::~LinearAllocator(void) { - Page* p = mPages; - while (p) { - Page* next = p->next(); - p->~Page(); - free(p); - RM_ALLOCATION(mPageSize); - p = next; - } -} - -void* LinearAllocator::start(Page* p) { - return ALIGN_PTR(((size_t*)p) + sizeof(Page)); -} - -void* LinearAllocator::end(Page* p) { - return ((char*)p) + mPageSize; -} - -bool LinearAllocator::fitsInCurrentPage(size_t size) { - return mNext && ((char*)mNext + size) <= end(mCurrentPage); -} - -void LinearAllocator::ensureNext(size_t size) { - if (fitsInCurrentPage(size)) return; - - if (mCurrentPage && mPageSize < MAX_PAGE_SIZE) { - mPageSize = min(MAX_PAGE_SIZE, mPageSize * 2); - mPageSize = ALIGN(mPageSize); - } - mWastedSpace += mPageSize; - Page* p = newPage(mPageSize); - if (mCurrentPage) { - mCurrentPage->setNext(p); - } - mCurrentPage = p; - if (!mPages) { - mPages = mCurrentPage; - } - mNext = start(mCurrentPage); -} - -void* LinearAllocator::alloc(size_t size) { - size = ALIGN(size); - if (size > mMaxAllocSize && !fitsInCurrentPage(size)) { - ALOGV("Exceeded max size %zu > %zu", size, mMaxAllocSize); - // Allocation is too large, create a dedicated page for the allocation - Page* page = newPage(size); - mDedicatedPageCount++; - page->setNext(mPages); - mPages = page; - if (!mCurrentPage) - mCurrentPage = mPages; - return start(page); - } - ensureNext(size); - void* ptr = mNext; - mNext = ((char*)mNext) + size; - mWastedSpace -= size; - return ptr; -} - -void LinearAllocator::rewindIfLastAlloc(void* ptr, size_t allocSize) { - // Don't bother rewinding across pages - allocSize = ALIGN(allocSize); - if (ptr >= start(mCurrentPage) && ptr < end(mCurrentPage) - && ptr == ((char*)mNext - allocSize)) { - mTotalAllocated -= allocSize; - mWastedSpace += allocSize; - mNext = ptr; - } -} - -LinearAllocator::Page* LinearAllocator::newPage(size_t pageSize) { - pageSize = ALIGN(pageSize + sizeof(LinearAllocator::Page)); - ADD_ALLOCATION(pageSize); - mTotalAllocated += pageSize; - mPageCount++; - void* buf = malloc(pageSize); - return new (buf) Page(); -} - -static const char* toSize(size_t value, float& result) { - if (value < 2000) { - result = value; - return "B"; - } - if (value < 2000000) { - result = value / 1024.0f; - return "KB"; - } - result = value / 1048576.0f; - return "MB"; -} - -void LinearAllocator::dumpMemoryStats(const char* prefix) { - float prettySize; - const char* prettySuffix; - prettySuffix = toSize(mTotalAllocated, prettySize); - ALOGD("%sTotal allocated: %.2f%s", prefix, prettySize, prettySuffix); - prettySuffix = toSize(mWastedSpace, prettySize); - ALOGD("%sWasted space: %.2f%s (%.1f%%)", prefix, prettySize, prettySuffix, - (float) mWastedSpace / (float) mTotalAllocated * 100.0f); - ALOGD("%sPages %zu (dedicated %zu)", prefix, mPageCount, mDedicatedPageCount); -} - -}; // namespace android
diff --git a/libutils/Looper.cpp b/libutils/Looper.cpp index 9a2dd6c..5b0ff3a 100644 --- a/libutils/Looper.cpp +++ b/libutils/Looper.cpp
@@ -20,6 +20,8 @@ #include <unistd.h> #include <fcntl.h> #include <limits.h> +#include <inttypes.h> +#include <sys/eventfd.h> namespace android { @@ -68,41 +70,20 @@ Looper::Looper(bool allowNonCallbacks) : mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false), - mResponseIndex(0), mNextMessageUptime(LLONG_MAX) { - int wakeFds[2]; - int result = pipe(wakeFds); - LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe. errno=%d", errno); + mPolling(false), mEpollFd(-1), mEpollRebuildRequired(false), + mNextRequestSeq(0), mResponseIndex(0), mNextMessageUptime(LLONG_MAX) { + mWakeEventFd = eventfd(0, EFD_NONBLOCK); + LOG_ALWAYS_FATAL_IF(mWakeEventFd < 0, "Could not make wake event fd. errno=%d", errno); - mWakeReadPipeFd = wakeFds[0]; - mWakeWritePipeFd = wakeFds[1]; - - result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK); - LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking. errno=%d", - errno); - - result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK); - LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking. errno=%d", - errno); - - mIdling = false; - - // Allocate the epoll instance and register the wake pipe. - mEpollFd = epoll_create(EPOLL_SIZE_HINT); - LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance. errno=%d", errno); - - struct epoll_event eventItem; - memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union - eventItem.events = EPOLLIN; - eventItem.data.fd = mWakeReadPipeFd; - result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, & eventItem); - LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance. errno=%d", - errno); + AutoMutex _l(mLock); + rebuildEpollLocked(); } Looper::~Looper() { - close(mWakeReadPipeFd); - close(mWakeWritePipeFd); - close(mEpollFd); + close(mWakeEventFd); + if (mEpollFd >= 0) { + close(mEpollFd); + } } void Looper::initTLSKey() { @@ -156,6 +137,50 @@ return mAllowNonCallbacks; } +void Looper::rebuildEpollLocked() { + // Close old epoll instance if we have one. + if (mEpollFd >= 0) { +#if DEBUG_CALLBACKS + ALOGD("%p ~ rebuildEpollLocked - rebuilding epoll set", this); +#endif + close(mEpollFd); + } + + // Allocate the new epoll instance and register the wake pipe. + mEpollFd = epoll_create(EPOLL_SIZE_HINT); + LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance. errno=%d", errno); + + struct epoll_event eventItem; + memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union + eventItem.events = EPOLLIN; + eventItem.data.fd = mWakeEventFd; + int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem); + LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake event fd to epoll instance. errno=%d", + errno); + + for (size_t i = 0; i < mRequests.size(); i++) { + const Request& request = mRequests.valueAt(i); + struct epoll_event eventItem; + request.initEventItem(&eventItem); + + int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, request.fd, & eventItem); + if (epollResult < 0) { + ALOGE("Error adding epoll events for fd %d while rebuilding epoll set, errno=%d", + request.fd, errno); + } + } +} + +void Looper::scheduleEpollRebuildLocked() { + if (!mEpollRebuildRequired) { +#if DEBUG_CALLBACKS + ALOGD("%p ~ scheduleEpollRebuildLocked - scheduling epoll set rebuild", this); +#endif + mEpollRebuildRequired = true; + wake(); + } +} + int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) { int result = 0; for (;;) { @@ -206,7 +231,7 @@ timeoutMillis = messageTimeoutMillis; } #if DEBUG_POLL_AND_WAKE - ALOGD("%p ~ pollOnce - next message in %lldns, adjusted timeout: timeoutMillis=%d", + ALOGD("%p ~ pollOnce - next message in %" PRId64 "ns, adjusted timeout: timeoutMillis=%d", this, mNextMessageUptime - now, timeoutMillis); #endif } @@ -217,17 +242,24 @@ mResponseIndex = 0; // We are about to idle. - mIdling = true; + mPolling = true; struct epoll_event eventItems[EPOLL_MAX_EVENTS]; int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis); // No longer idling. - mIdling = false; + mPolling = false; // Acquire lock. mLock.lock(); + // Rebuild epoll set if needed. + if (mEpollRebuildRequired) { + mEpollRebuildRequired = false; + rebuildEpollLocked(); + goto Done; + } + // Check for poll error. if (eventCount < 0) { if (errno == EINTR) { @@ -255,11 +287,11 @@ for (int i = 0; i < eventCount; i++) { int fd = eventItems[i].data.fd; uint32_t epollEvents = eventItems[i].events; - if (fd == mWakeReadPipeFd) { + if (fd == mWakeEventFd) { if (epollEvents & EPOLLIN) { awoken(); } else { - ALOGW("Ignoring unexpected epoll events 0x%x on wake read pipe.", epollEvents); + ALOGW("Ignoring unexpected epoll events 0x%x on wake event fd.", epollEvents); } } else { ssize_t requestIndex = mRequests.indexOfKey(fd); @@ -326,10 +358,14 @@ ALOGD("%p ~ pollOnce - invoking fd event callback %p: fd=%d, events=0x%x, data=%p", this, response.request.callback.get(), fd, events, data); #endif + // Invoke the callback. Note that the file descriptor may be closed by + // the callback (and potentially even reused) before the function returns so + // we need to be a little careful when removing the file descriptor afterwards. int callbackResult = response.request.callback->handleEvent(fd, events, data); if (callbackResult == 0) { - removeFd(fd); + removeFd(fd, response.request.seq); } + // Clear the callback reference in the response structure promptly because we // will not clear the response vector itself until the next poll. response.request.callback.clear(); @@ -370,12 +406,9 @@ ALOGD("%p ~ wake", this); #endif - ssize_t nWrite; - do { - nWrite = write(mWakeWritePipeFd, "W", 1); - } while (nWrite == -1 && errno == EINTR); - - if (nWrite != 1) { + uint64_t inc = 1; + ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t))); + if (nWrite != sizeof(uint64_t)) { if (errno != EAGAIN) { ALOGW("Could not write wake signal, errno=%d", errno); } @@ -387,11 +420,8 @@ ALOGD("%p ~ awoken", this); #endif - char buffer[16]; - ssize_t nRead; - do { - nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer)); - } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer)); + uint64_t counter; + TEMP_FAILURE_RETRY(read(mWakeEventFd, &counter, sizeof(uint64_t))); } void Looper::pushResponse(int events, const Request& request) { @@ -425,23 +455,20 @@ ident = POLL_CALLBACK; } - int epollEvents = 0; - if (events & EVENT_INPUT) epollEvents |= EPOLLIN; - if (events & EVENT_OUTPUT) epollEvents |= EPOLLOUT; - { // acquire lock AutoMutex _l(mLock); Request request; request.fd = fd; request.ident = ident; + request.events = events; + request.seq = mNextRequestSeq++; request.callback = callback; request.data = data; + if (mNextRequestSeq == -1) mNextRequestSeq = 0; // reserve sequence number -1 struct epoll_event eventItem; - memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union - eventItem.events = epollEvents; - eventItem.data.fd = fd; + request.initEventItem(&eventItem); ssize_t requestIndex = mRequests.indexOfKey(fd); if (requestIndex < 0) { @@ -454,8 +481,36 @@ } else { int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_MOD, fd, & eventItem); if (epollResult < 0) { - ALOGE("Error modifying epoll events for fd %d, errno=%d", fd, errno); - return -1; + if (errno == ENOENT) { + // Tolerate ENOENT because it means that an older file descriptor was + // closed before its callback was unregistered and meanwhile a new + // file descriptor with the same number has been created and is now + // being registered for the first time. This error may occur naturally + // when a callback has the side-effect of closing the file descriptor + // before returning and unregistering itself. Callback sequence number + // checks further ensure that the race is benign. + // + // Unfortunately due to kernel limitations we need to rebuild the epoll + // set from scratch because it may contain an old file handle that we are + // now unable to remove since its file descriptor is no longer valid. + // No such problem would have occurred if we were using the poll system + // call instead, but that approach carries others disadvantages. +#if DEBUG_CALLBACKS + ALOGD("%p ~ addFd - EPOLL_CTL_MOD failed due to file descriptor " + "being recycled, falling back on EPOLL_CTL_ADD, errno=%d", + this, errno); +#endif + epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem); + if (epollResult < 0) { + ALOGE("Error modifying or adding epoll events for fd %d, errno=%d", + fd, errno); + return -1; + } + scheduleEpollRebuildLocked(); + } else { + ALOGE("Error modifying epoll events for fd %d, errno=%d", fd, errno); + return -1; + } } mRequests.replaceValueAt(requestIndex, request); } @@ -464,8 +519,12 @@ } int Looper::removeFd(int fd) { + return removeFd(fd, -1); +} + +int Looper::removeFd(int fd, int seq) { #if DEBUG_CALLBACKS - ALOGD("%p ~ removeFd - fd=%d", this, fd); + ALOGD("%p ~ removeFd - fd=%d, seq=%d", this, fd, seq); #endif { // acquire lock @@ -475,13 +534,48 @@ return 0; } - int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_DEL, fd, NULL); - if (epollResult < 0) { - ALOGE("Error removing epoll events for fd %d, errno=%d", fd, errno); - return -1; + // Check the sequence number if one was given. + if (seq != -1 && mRequests.valueAt(requestIndex).seq != seq) { +#if DEBUG_CALLBACKS + ALOGD("%p ~ removeFd - sequence number mismatch, oldSeq=%d", + this, mRequests.valueAt(requestIndex).seq); +#endif + return 0; } + // Always remove the FD from the request map even if an error occurs while + // updating the epoll set so that we avoid accidentally leaking callbacks. mRequests.removeItemsAt(requestIndex); + + int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_DEL, fd, NULL); + if (epollResult < 0) { + if (seq != -1 && (errno == EBADF || errno == ENOENT)) { + // Tolerate EBADF or ENOENT when the sequence number is known because it + // means that the file descriptor was closed before its callback was + // unregistered. This error may occur naturally when a callback has the + // side-effect of closing the file descriptor before returning and + // unregistering itself. + // + // Unfortunately due to kernel limitations we need to rebuild the epoll + // set from scratch because it may contain an old file handle that we are + // now unable to remove since its file descriptor is no longer valid. + // No such problem would have occurred if we were using the poll system + // call instead, but that approach carries others disadvantages. +#if DEBUG_CALLBACKS + ALOGD("%p ~ removeFd - EPOLL_CTL_DEL failed due to file descriptor " + "being closed, errno=%d", this, errno); +#endif + scheduleEpollRebuildLocked(); + } else { + // Some other error occurred. This is really weird because it means + // our list of callbacks got out of sync with the epoll set somehow. + // We defensively rebuild the epoll set to avoid getting spurious + // notifications with nowhere to go. + ALOGE("Error removing epoll events for fd %d, errno=%d", fd, errno); + scheduleEpollRebuildLocked(); + return -1; + } + } } // release lock return 1; } @@ -500,7 +594,7 @@ void Looper::sendMessageAtTime(nsecs_t uptime, const sp<MessageHandler>& handler, const Message& message) { #if DEBUG_CALLBACKS - ALOGD("%p ~ sendMessageAtTime - uptime=%lld, handler=%p, what=%d", + ALOGD("%p ~ sendMessageAtTime - uptime=%" PRId64 ", handler=%p, what=%d", this, uptime, handler.get(), message.what); #endif @@ -566,8 +660,18 @@ } // release lock } -bool Looper::isIdling() const { - return mIdling; +bool Looper::isPolling() const { + return mPolling; +} + +void Looper::Request::initEventItem(struct epoll_event* eventItem) const { + int epollEvents = 0; + if (events & EVENT_INPUT) epollEvents |= EPOLLIN; + if (events & EVENT_OUTPUT) epollEvents |= EPOLLOUT; + + memset(eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union + eventItem->events = epollEvents; + eventItem->data.fd = fd; } } // namespace android
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc index 8582344..cc39aa5 100644 --- a/libziparchive/zip_archive.cc +++ b/libziparchive/zip_archive.cc
@@ -30,6 +30,7 @@ #include <memory> #include <vector> +#include "base/file.h" #include "base/macros.h" // TEMP_FAILURE_RETRY may or may not be in unistd #include "base/memory.h" #include "log/log.h" @@ -85,7 +86,8 @@ // Length of the central directory comment. uint16_t comment_length; private: - DISALLOW_IMPLICIT_CONSTRUCTORS(EocdRecord); + EocdRecord() = default; + DISALLOW_COPY_AND_ASSIGN(EocdRecord); } __attribute__((packed)); // A structure representing the fixed length fields for a single @@ -138,7 +140,8 @@ // beginning of this archive. uint32_t local_file_header_offset; private: - DISALLOW_IMPLICIT_CONSTRUCTORS(CentralDirectoryRecord); + CentralDirectoryRecord() = default; + DISALLOW_COPY_AND_ASSIGN(CentralDirectoryRecord); } __attribute__((packed)); // The local file header for a given entry. This duplicates information @@ -175,7 +178,8 @@ // will appear immediately after the entry file name. uint16_t extra_field_length; private: - DISALLOW_IMPLICIT_CONSTRUCTORS(LocalFileHeader); + LocalFileHeader() = default; + DISALLOW_COPY_AND_ASSIGN(LocalFileHeader); } __attribute__((packed)); struct DataDescriptor { @@ -189,10 +193,10 @@ // Uncompressed size of the entry. uint32_t uncompressed_size; private: - DISALLOW_IMPLICIT_CONSTRUCTORS(DataDescriptor); + DataDescriptor() = default; + DISALLOW_COPY_AND_ASSIGN(DataDescriptor); } __attribute__((packed)); -#undef DISALLOW_IMPLICIT_CONSTRUCTORS static const uint32_t kGPBDDFlagMask = 0x0008; // mask value that signifies that the entry has a DD @@ -265,8 +269,6 @@ static const int32_t kErrorMessageLowerBound = -13; -static const char kTempMappingFileName[] = "zip: ExtractFileToFile"; - /* * A Read-only Zip archive. * @@ -324,35 +326,6 @@ } }; -static int32_t CopyFileToFile(int fd, uint8_t* begin, const uint32_t length, uint64_t *crc_out) { - static const uint32_t kBufSize = 32768; - uint8_t buf[kBufSize]; - - uint32_t count = 0; - uint64_t crc = 0; - while (count < length) { - uint32_t remaining = length - count; - - // Safe conversion because kBufSize is narrow enough for a 32 bit signed - // value. - ssize_t get_size = (remaining > kBufSize) ? kBufSize : remaining; - ssize_t actual = TEMP_FAILURE_RETRY(read(fd, buf, get_size)); - - if (actual != get_size) { - ALOGW("CopyFileToFile: copy read failed (" ZD " vs " ZD ")", actual, get_size); - return kIoError; - } - - memcpy(begin + count, buf, get_size); - crc = crc32(crc, buf, get_size); - count += get_size; - } - - *crc_out = crc; - - return 0; -} - /* * Round up to the next highest power of 2. * @@ -879,25 +852,38 @@ // We're not using vector here because this code is used in the Windows SDK // where the STL is not available. const uint8_t* prefix; - uint16_t prefix_len; + const uint16_t prefix_len; + const uint8_t* suffix; + const uint16_t suffix_len; ZipArchive* archive; - IterationHandle() : prefix(NULL), prefix_len(0) {} - - IterationHandle(const ZipEntryName& prefix_name) - : prefix_len(prefix_name.name_length) { - uint8_t* prefix_copy = new uint8_t[prefix_len]; - memcpy(prefix_copy, prefix_name.name, prefix_len); - prefix = prefix_copy; + IterationHandle(const ZipEntryName* prefix_name, + const ZipEntryName* suffix_name) + : prefix(NULL), + prefix_len(prefix_name ? prefix_name->name_length : 0), + suffix(NULL), + suffix_len(suffix_name ? suffix_name->name_length : 0) { + if (prefix_name) { + uint8_t* prefix_copy = new uint8_t[prefix_len]; + memcpy(prefix_copy, prefix_name->name, prefix_len); + prefix = prefix_copy; + } + if (suffix_name) { + uint8_t* suffix_copy = new uint8_t[suffix_len]; + memcpy(suffix_copy, suffix_name->name, suffix_len); + suffix = suffix_copy; + } } ~IterationHandle() { delete[] prefix; + delete[] suffix; } }; int32_t StartIteration(ZipArchiveHandle handle, void** cookie_ptr, - const ZipEntryName* optional_prefix) { + const ZipEntryName* optional_prefix, + const ZipEntryName* optional_suffix) { ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle); if (archive == NULL || archive->hash_table == NULL) { @@ -905,8 +891,7 @@ return kInvalidHandle; } - IterationHandle* cookie = - optional_prefix != NULL ? new IterationHandle(*optional_prefix) : new IterationHandle(); + IterationHandle* cookie = new IterationHandle(optional_prefix, optional_suffix); cookie->position = 0; cookie->archive = archive; @@ -956,7 +941,13 @@ for (uint32_t i = currentOffset; i < hash_table_length; ++i) { if (hash_table[i].name != NULL && (handle->prefix_len == 0 || - (memcmp(handle->prefix, hash_table[i].name, handle->prefix_len) == 0))) { + (hash_table[i].name_length >= handle->prefix_len && + memcmp(handle->prefix, hash_table[i].name, handle->prefix_len) == 0)) && + (handle->suffix_len == 0 || + (hash_table[i].name_length >= handle->suffix_len && + memcmp(handle->suffix, + hash_table[i].name + hash_table[i].name_length - handle->suffix_len, + handle->suffix_len) == 0))) { handle->position = (i + 1); const int error = FindEntry(archive, i, data); if (!error) { @@ -972,6 +963,122 @@ return kIterationEnd; } +class Writer { + public: + virtual bool Append(uint8_t* buf, size_t buf_size) = 0; + virtual ~Writer() {} + protected: + Writer() = default; + private: + DISALLOW_COPY_AND_ASSIGN(Writer); +}; + +// A Writer that writes data to a fixed size memory region. +// The size of the memory region must be equal to the total size of +// the data appended to it. +class MemoryWriter : public Writer { + public: + MemoryWriter(uint8_t* buf, size_t size) : Writer(), + buf_(buf), size_(size), bytes_written_(0) { + } + + virtual bool Append(uint8_t* buf, size_t buf_size) override { + if (bytes_written_ + buf_size > size_) { + ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)", + size_, bytes_written_ + buf_size); + return false; + } + + memcpy(buf_ + bytes_written_, buf, buf_size); + bytes_written_ += buf_size; + return true; + } + + private: + uint8_t* const buf_; + const size_t size_; + size_t bytes_written_; +}; + +// A Writer that appends data to a file |fd| at its current position. +// The file will be truncated to the end of the written data. +class FileWriter : public Writer { + public: + + // Creates a FileWriter for |fd| and prepare to write |entry| to it, + // guaranteeing that the file descriptor is valid and that there's enough + // space on the volume to write out the entry completely and that the file + // is truncated to the correct length. + // + // Returns a valid FileWriter on success, |nullptr| if an error occurred. + static std::unique_ptr<FileWriter> Create(int fd, const ZipEntry* entry) { + const uint32_t declared_length = entry->uncompressed_length; + const off64_t current_offset = lseek64(fd, 0, SEEK_CUR); + if (current_offset == -1) { + ALOGW("Zip: unable to seek to current location on fd %d: %s", fd, strerror(errno)); + return nullptr; + } + + int result = 0; +#if defined(__linux__) + if (declared_length > 0) { + // Make sure we have enough space on the volume to extract the compressed + // entry. Note that the call to ftruncate below will change the file size but + // will not allocate space on disk and this call to fallocate will not + // change the file size. + // Note: fallocate is only supported by the following filesystems - + // btrfs, ext4, ocfs2, and xfs. Therefore fallocate might fail with + // EOPNOTSUPP error when issued in other filesystems. + // Hence, check for the return error code before concluding that the + // disk does not have enough space. + result = TEMP_FAILURE_RETRY(fallocate(fd, 0, current_offset, declared_length)); + if (result == -1 && errno == ENOSPC) { + ALOGW("Zip: unable to allocate space for file to %" PRId64 ": %s", + static_cast<int64_t>(declared_length + current_offset), strerror(errno)); + return std::unique_ptr<FileWriter>(nullptr); + } + } +#endif // __linux__ + + result = TEMP_FAILURE_RETRY(ftruncate(fd, declared_length + current_offset)); + if (result == -1) { + ALOGW("Zip: unable to truncate file to %" PRId64 ": %s", + static_cast<int64_t>(declared_length + current_offset), strerror(errno)); + return std::unique_ptr<FileWriter>(nullptr); + } + + return std::unique_ptr<FileWriter>(new FileWriter(fd, declared_length)); + } + + virtual bool Append(uint8_t* buf, size_t buf_size) override { + if (total_bytes_written_ + buf_size > declared_length_) { + ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)", + declared_length_, total_bytes_written_ + buf_size); + return false; + } + + const bool result = android::base::WriteFully(fd_, buf, buf_size); + if (result) { + total_bytes_written_ += buf_size; + } else { + ALOGW("Zip: unable to write " ZD " bytes to file; %s", buf_size, strerror(errno)); + } + + return result; + } + private: + FileWriter(const int fd, const size_t declared_length) : + Writer(), + fd_(fd), + declared_length_(declared_length), + total_bytes_written_(0) { + } + + const int fd_; + const size_t declared_length_; + size_t total_bytes_written_; +}; + // This method is using libz macros with old-style-casts #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wold-style-cast" @@ -980,9 +1087,8 @@ } #pragma GCC diagnostic pop -static int32_t InflateToFile(int fd, const ZipEntry* entry, - uint8_t* begin, uint32_t length, - uint64_t* crc_out) { +static int32_t InflateEntryToWriter(int fd, const ZipEntry* entry, + Writer* writer, uint64_t* crc_out) { const size_t kBufSize = 32768; std::vector<uint8_t> read_buf(kBufSize); std::vector<uint8_t> write_buf(kBufSize); @@ -1027,7 +1133,6 @@ const uint32_t uncompressed_length = entry->uncompressed_length; uint32_t compressed_length = entry->compressed_length; - uint32_t write_count = 0; do { /* read as much as we can */ if (zstream.avail_in == 0) { @@ -1057,12 +1162,10 @@ if (zstream.avail_out == 0 || (zerr == Z_STREAM_END && zstream.avail_out != kBufSize)) { const size_t write_size = zstream.next_out - &write_buf[0]; - // The file might have declared a bogus length. - if (write_size + write_count > length) { - return -1; + if (!writer->Append(&write_buf[0], write_size)) { + // The file might have declared a bogus length. + return kInconsistentInformation; } - memcpy(begin + write_count, &write_buf[0], write_size); - write_count += write_size; zstream.next_out = &write_buf[0]; zstream.avail_out = kBufSize; @@ -1083,8 +1186,41 @@ return 0; } -int32_t ExtractToMemory(ZipArchiveHandle handle, - ZipEntry* entry, uint8_t* begin, uint32_t size) { +static int32_t CopyEntryToWriter(int fd, const ZipEntry* entry, Writer* writer, + uint64_t *crc_out) { + static const uint32_t kBufSize = 32768; + std::vector<uint8_t> buf(kBufSize); + + const uint32_t length = entry->uncompressed_length; + uint32_t count = 0; + uint64_t crc = 0; + while (count < length) { + uint32_t remaining = length - count; + + // Safe conversion because kBufSize is narrow enough for a 32 bit signed + // value. + const ssize_t block_size = (remaining > kBufSize) ? kBufSize : remaining; + const ssize_t actual = TEMP_FAILURE_RETRY(read(fd, &buf[0], block_size)); + + if (actual != block_size) { + ALOGW("CopyFileToFile: copy read failed (" ZD " vs " ZD ")", actual, block_size); + return kIoError; + } + + if (!writer->Append(&buf[0], block_size)) { + return kIoError; + } + crc = crc32(crc, &buf[0], block_size); + count += block_size; + } + + *crc_out = crc; + + return 0; +} + +int32_t ExtractToWriter(ZipArchiveHandle handle, + ZipEntry* entry, Writer* writer) { ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle); const uint16_t method = entry->method; off64_t data_offset = entry->offset; @@ -1098,9 +1234,9 @@ int32_t return_value = -1; uint64_t crc = 0; if (method == kCompressStored) { - return_value = CopyFileToFile(archive->fd, begin, size, &crc); + return_value = CopyEntryToWriter(archive->fd, entry, writer, &crc); } else if (method == kCompressDeflated) { - return_value = InflateToFile(archive->fd, entry, begin, size, &crc); + return_value = InflateEntryToWriter(archive->fd, entry, writer, &crc); } if (!return_value && entry->has_data_descriptor) { @@ -1120,55 +1256,20 @@ return return_value; } +int32_t ExtractToMemory(ZipArchiveHandle handle, ZipEntry* entry, + uint8_t* begin, uint32_t size) { + std::unique_ptr<Writer> writer(new MemoryWriter(begin, size)); + return ExtractToWriter(handle, entry, writer.get()); +} + int32_t ExtractEntryToFile(ZipArchiveHandle handle, ZipEntry* entry, int fd) { - const uint32_t declared_length = entry->uncompressed_length; - - const off64_t current_offset = lseek64(fd, 0, SEEK_CUR); - if (current_offset == -1) { - ALOGW("Zip: unable to seek to current location on fd %d: %s", fd, - strerror(errno)); + std::unique_ptr<Writer> writer(FileWriter::Create(fd, entry)); + if (writer.get() == nullptr) { return kIoError; } - int result = 0; -#if defined(__linux__) - // Make sure we have enough space on the volume to extract the compressed - // entry. Note that the call to ftruncate below will change the file size but - // will not allocate space on disk. - if (declared_length > 0) { - result = TEMP_FAILURE_RETRY(fallocate(fd, 0, current_offset, declared_length)); - if (result == -1) { - ALOGW("Zip: unable to allocate space for file to %" PRId64 ": %s", - static_cast<int64_t>(declared_length + current_offset), strerror(errno)); - return kIoError; - } - } -#endif // defined(__linux__) - - result = TEMP_FAILURE_RETRY(ftruncate(fd, declared_length + current_offset)); - if (result == -1) { - ALOGW("Zip: unable to truncate file to %" PRId64 ": %s", - static_cast<int64_t>(declared_length + current_offset), strerror(errno)); - return kIoError; - } - - // Don't attempt to map a region of length 0. We still need the - // ftruncate() though, since the API guarantees that we will truncate - // the file to the end of the uncompressed output. - if (declared_length == 0) { - return 0; - } - - android::FileMap map; - if (!map.create(kTempMappingFileName, fd, current_offset, declared_length, false)) { - return kMmapFailed; - } - - const int32_t error = ExtractToMemory(handle, entry, - reinterpret_cast<uint8_t*>(map.getDataPtr()), - map.getDataLength()); - return error; + return ExtractToWriter(handle, entry, writer.get()); } const char* ErrorCodeString(int32_t error_code) { @@ -1182,4 +1283,3 @@ int GetFileDescriptor(const ZipArchiveHandle handle) { return reinterpret_cast<ZipArchive*>(handle)->fd; } -
diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc index 64faa6d..c799869 100644 --- a/libziparchive/zip_archive_test.cc +++ b/libziparchive/zip_archive_test.cc
@@ -23,6 +23,7 @@ #include <unistd.h> #include <vector> +#include <base/file.h> #include <gtest/gtest.h> static std::string test_data_dir; @@ -114,7 +115,7 @@ ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle)); void* iteration_cookie; - ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, NULL)); + ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, NULL, NULL)); ZipEntry data; ZipEntryName name; @@ -145,6 +146,116 @@ CloseArchive(handle); } +TEST(ziparchive, IterationWithPrefix) { + ZipArchiveHandle handle; + ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle)); + + void* iteration_cookie; + ZipEntryName prefix("b/"); + ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, &prefix, NULL)); + + ZipEntry data; + ZipEntryName name; + + // b/c.txt + ASSERT_EQ(0, Next(iteration_cookie, &data, &name)); + AssertNameEquals("b/c.txt", name); + + // b/d.txt + ASSERT_EQ(0, Next(iteration_cookie, &data, &name)); + AssertNameEquals("b/d.txt", name); + + // b/ + ASSERT_EQ(0, Next(iteration_cookie, &data, &name)); + AssertNameEquals("b/", name); + + // End of iteration. + ASSERT_EQ(-1, Next(iteration_cookie, &data, &name)); + + CloseArchive(handle); +} + +TEST(ziparchive, IterationWithSuffix) { + ZipArchiveHandle handle; + ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle)); + + void* iteration_cookie; + ZipEntryName suffix(".txt"); + ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, NULL, &suffix)); + + ZipEntry data; + ZipEntryName name; + + // b/c.txt + ASSERT_EQ(0, Next(iteration_cookie, &data, &name)); + AssertNameEquals("b/c.txt", name); + + // b/d.txt + ASSERT_EQ(0, Next(iteration_cookie, &data, &name)); + AssertNameEquals("b/d.txt", name); + + // a.txt + ASSERT_EQ(0, Next(iteration_cookie, &data, &name)); + AssertNameEquals("a.txt", name); + + // b.txt + ASSERT_EQ(0, Next(iteration_cookie, &data, &name)); + AssertNameEquals("b.txt", name); + + // End of iteration. + ASSERT_EQ(-1, Next(iteration_cookie, &data, &name)); + + CloseArchive(handle); +} + +TEST(ziparchive, IterationWithPrefixAndSuffix) { + ZipArchiveHandle handle; + ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle)); + + void* iteration_cookie; + ZipEntryName prefix("b"); + ZipEntryName suffix(".txt"); + ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, &prefix, &suffix)); + + ZipEntry data; + ZipEntryName name; + + // b/c.txt + ASSERT_EQ(0, Next(iteration_cookie, &data, &name)); + AssertNameEquals("b/c.txt", name); + + // b/d.txt + ASSERT_EQ(0, Next(iteration_cookie, &data, &name)); + AssertNameEquals("b/d.txt", name); + + // b.txt + ASSERT_EQ(0, Next(iteration_cookie, &data, &name)); + AssertNameEquals("b.txt", name); + + // End of iteration. + ASSERT_EQ(-1, Next(iteration_cookie, &data, &name)); + + CloseArchive(handle); +} + +TEST(ziparchive, IterationWithBadPrefixAndSuffix) { + ZipArchiveHandle handle; + ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle)); + + void* iteration_cookie; + ZipEntryName prefix("x"); + ZipEntryName suffix("y"); + ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, &prefix, &suffix)); + + ZipEntry data; + ZipEntryName name; + + // End of iteration. + ASSERT_EQ(-1, Next(iteration_cookie, &data, &name)); + + CloseArchive(handle); +} + TEST(ziparchive, FindEntry) { ZipArchiveHandle handle; ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle)); @@ -228,6 +339,44 @@ 0x54557478, 0x13030005, 0x7552e25c, 0x01000b78, 0x00428904, 0x13880400, 0x4b500000, 0x00000605, 0x00010000, 0x004f0001, 0x00430000, 0x00000000 }; +// This is a zip file containing a single entry (ab.txt) that contains +// 90072 repetitions of the string "ab\n" and has an uncompressed length +// of 270216 bytes. +static const uint16_t kAbZip[] = { + 0x4b50, 0x0403, 0x0014, 0x0000, 0x0008, 0x51d2, 0x4698, 0xc4b0, + 0x2cda, 0x011b, 0x0000, 0x1f88, 0x0004, 0x0006, 0x001c, 0x6261, + 0x742e, 0x7478, 0x5455, 0x0009, 0x7c03, 0x3a09, 0x7c55, 0x3a09, + 0x7555, 0x0b78, 0x0100, 0x8904, 0x0042, 0x0400, 0x1388, 0x0000, + 0xc2ed, 0x0d31, 0x0000, 0x030c, 0x7fa0, 0x3b2e, 0x22ff, 0xa2aa, + 0x841f, 0x45fc, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, + 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, + 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, + 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, + 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, + 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, + 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, + 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, + 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, + 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, + 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, + 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, + 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, + 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, + 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, + 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, + 0x5555, 0x5555, 0x5555, 0x5555, 0xdd55, 0x502c, 0x014b, 0x1e02, + 0x1403, 0x0000, 0x0800, 0xd200, 0x9851, 0xb046, 0xdac4, 0x1b2c, + 0x0001, 0x8800, 0x041f, 0x0600, 0x1800, 0x0000, 0x0000, 0x0100, + 0x0000, 0xa000, 0x0081, 0x0000, 0x6100, 0x2e62, 0x7874, 0x5574, + 0x0554, 0x0300, 0x097c, 0x553a, 0x7875, 0x000b, 0x0401, 0x4289, + 0x0000, 0x8804, 0x0013, 0x5000, 0x054b, 0x0006, 0x0000, 0x0100, + 0x0100, 0x4c00, 0x0000, 0x5b00, 0x0001, 0x0000, 0x0000 +}; + +static const uint8_t kAbTxtName[] = { 'a', 'b', '.', 't', 'x', 't' }; +static const uint16_t kAbTxtNameLength = sizeof(kAbTxtName); +static const size_t kAbUncompressedSize = 270216; + static int make_temporary_file(const char* file_name_pattern) { char full_path[1024]; // Account for differences between the host and the target. @@ -275,6 +424,55 @@ close(output_fd); } +TEST(ziparchive, EntryLargerThan32K) { + char temp_file_pattern[] = "entry_larger_than_32k_test_XXXXXX"; + int fd = make_temporary_file(temp_file_pattern); + ASSERT_NE(-1, fd); + ASSERT_TRUE(android::base::WriteFully(fd, reinterpret_cast<const uint8_t*>(kAbZip), + sizeof(kAbZip) - 1)); + ZipArchiveHandle handle; + ASSERT_EQ(0, OpenArchiveFd(fd, "EntryLargerThan32KTest", &handle)); + + ZipEntry entry; + ZipEntryName ab_name; + ab_name.name = kAbTxtName; + ab_name.name_length = kAbTxtNameLength; + ASSERT_EQ(0, FindEntry(handle, ab_name, &entry)); + ASSERT_EQ(kAbUncompressedSize, entry.uncompressed_length); + + // Extract the entry to memory. + std::vector<uint8_t> buffer(kAbUncompressedSize); + ASSERT_EQ(0, ExtractToMemory(handle, &entry, &buffer[0], buffer.size())); + + // Extract the entry to a file. + char output_file_pattern[] = "entry_larger_than_32k_test_output_XXXXXX"; + int output_fd = make_temporary_file(output_file_pattern); + ASSERT_NE(-1, output_fd); + ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, output_fd)); + + // Make sure the extracted file size is as expected. + struct stat stat_buf; + ASSERT_EQ(0, fstat(output_fd, &stat_buf)); + ASSERT_EQ(kAbUncompressedSize, static_cast<size_t>(stat_buf.st_size)); + + // Read the file back to a buffer and make sure the contents are + // the same as the memory buffer we extracted directly to. + std::vector<uint8_t> file_contents(kAbUncompressedSize); + ASSERT_EQ(0, lseek64(output_fd, 0, SEEK_SET)); + ASSERT_TRUE(android::base::ReadFully(output_fd, &file_contents[0], file_contents.size())); + ASSERT_EQ(file_contents, buffer); + + for (int i = 0; i < 90072; ++i) { + const uint8_t* line = &file_contents[0] + (3 * i); + ASSERT_EQ('a', line[0]); + ASSERT_EQ('b', line[1]); + ASSERT_EQ('\n', line[2]); + } + + close(fd); + close(output_fd); +} + TEST(ziparchive, TrailerAfterEOCD) { char temp_file_pattern[] = "trailer_after_eocd_test_XXXXXX"; int fd = make_temporary_file(temp_file_pattern);
diff --git a/logcat/Android.mk b/logcat/Android.mk index f46a4de..7115f9b 100644 --- a/logcat/Android.mk +++ b/logcat/Android.mk
@@ -5,7 +5,7 @@ LOCAL_SRC_FILES:= logcat.cpp event.logtags -LOCAL_SHARED_LIBRARIES := liblog +LOCAL_SHARED_LIBRARIES := liblog libbase libcutils LOCAL_MODULE := logcat
diff --git a/logcat/event.logtags b/logcat/event.logtags index 1b5c6f4..909f8e2 100644 --- a/logcat/event.logtags +++ b/logcat/event.logtags
@@ -21,6 +21,7 @@ # 2: long # 3: string # 4: list +# 5: float # # The data unit is a number taken from the following list: # 1: Number of objects
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp index 2b19b93..e598bb8 100644 --- a/logcat/logcat.cpp +++ b/logcat/logcat.cpp
@@ -1,29 +1,40 @@ // Copyright 2006-2015 The Android Open Source Project +#include <arpa/inet.h> #include <assert.h> #include <ctype.h> +#include <dirent.h> #include <errno.h> #include <fcntl.h> #include <math.h> +#include <sched.h> +#include <signal.h> +#include <stdarg.h> #include <stdio.h> #include <stdlib.h> -#include <stdarg.h> #include <string.h> -#include <signal.h> -#include <time.h> -#include <unistd.h> #include <sys/cdefs.h> +#include <sys/resource.h> #include <sys/socket.h> #include <sys/stat.h> -#include <arpa/inet.h> +#include <sys/types.h> +#include <time.h> +#include <unistd.h> +#include <memory> +#include <string> + +#include <base/file.h> +#include <base/strings.h> +#include <cutils/sched_policy.h> #include <cutils/sockets.h> +#include <log/event_tag_map.h> #include <log/log.h> #include <log/log_read.h> -#include <log/logger.h> #include <log/logd.h> +#include <log/logger.h> #include <log/logprint.h> -#include <log/event_tag_map.h> +#include <utils/threads.h> #define DEFAULT_MAX_ROTATED_LOGS 4 @@ -202,7 +213,19 @@ g_outFD = STDOUT_FILENO; } else { - struct stat statbuf; + if (set_sched_policy(0, SP_BACKGROUND) < 0) { + fprintf(stderr, "failed to set background scheduling policy\n"); + } + + struct sched_param param; + memset(¶m, 0, sizeof(param)); + if (sched_setscheduler((pid_t) 0, SCHED_BATCH, ¶m) < 0) { + fprintf(stderr, "failed to set to batch scheduler\n"); + } + + if (setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND) < 0) { + fprintf(stderr, "failed set to priority\n"); + } g_outFD = openLogFile (g_outputFileName); @@ -210,6 +233,7 @@ logcat_panic(false, "couldn't open output file"); } + struct stat statbuf; if (fstat(g_outFD, &statbuf) == -1) { close(g_outFD); logcat_panic(false, "couldn't get output file stat\n"); @@ -231,11 +255,12 @@ fprintf(stderr, "options include:\n" " -s Set default filter to silent.\n" " Like specifying filterspec '*:S'\n" - " -f <filename> Log to file. Default to stdout\n" + " -f <filename> Log to file. Default is stdout\n" " -r <kbytes> Rotate log every kbytes. Requires -f\n" " -n <count> Sets max number of rotated logs to <count>, default 4\n" " -v <format> Sets the log print format, where <format> is:\n\n" - " brief color long process raw tag thread threadtime time\n\n" + " brief color long printable process raw tag thread\n" + " threadtime time usec\n\n" " -D print dividers between each log buffer\n" " -c clear (flush) the entire log and exit\n" " -d dump the log and then exit (don't block)\n" @@ -291,9 +316,7 @@ return -1; } - android_log_setPrintFormat(g_logformat, format); - - return 0; + return android_log_setPrintFormat(g_logformat, format); } static const char multipliers[][2] = { @@ -354,6 +377,86 @@ exit(EXIT_FAILURE); } +static const char g_defaultTimeFormat[] = "%m-%d %H:%M:%S.%q"; + +// Find last logged line in gestalt of all matching existing output files +static log_time lastLogTime(char *outputFileName) { + log_time retval(log_time::EPOCH); + if (!outputFileName) { + return retval; + } + + log_time now(CLOCK_REALTIME); + + std::string directory; + char *file = strrchr(outputFileName, '/'); + if (!file) { + directory = "."; + file = outputFileName; + } else { + *file = '\0'; + directory = outputFileName; + *file = '/'; + ++file; + } + size_t len = strlen(file); + log_time modulo(0, NS_PER_SEC); + std::unique_ptr<DIR, int(*)(DIR*)>dir(opendir(directory.c_str()), closedir); + struct dirent *dp; + while ((dp = readdir(dir.get())) != NULL) { + if ((dp->d_type != DT_REG) + || strncmp(dp->d_name, file, len) + || (dp->d_name[len] + && ((dp->d_name[len] != '.') + || !isdigit(dp->d_name[len+1])))) { + continue; + } + + std::string file_name = directory; + file_name += "/"; + file_name += dp->d_name; + std::string file; + if (!android::base::ReadFileToString(file_name, &file)) { + continue; + } + + bool found = false; + for (const auto& line : android::base::Split(file, "\n")) { + log_time t(log_time::EPOCH); + char *ep = t.strptime(line.c_str(), g_defaultTimeFormat); + if (!ep || (*ep != ' ')) { + continue; + } + // determine the time precision of the logs (eg: msec or usec) + for (unsigned long mod = 1UL; mod < modulo.tv_nsec; mod *= 10) { + if (t.tv_nsec % (mod * 10)) { + modulo.tv_nsec = mod; + break; + } + } + // We filter any times later than current as we may not have the + // year stored with each log entry. Also, since it is possible for + // entries to be recorded out of order (very rare) we select the + // maximum we find just in case. + if ((t < now) && (t > retval)) { + retval = t; + found = true; + } + } + // We count on the basename file to be the definitive end, so stop here. + if (!dp->d_name[len] && found) { + break; + } + } + if (retval == log_time::EPOCH) { + return retval; + } + // tail_time prints matching or higher, round up by the modulo to prevent + // a replay of the last entry we have just checked. + retval += modulo; + return retval; +} + } /* namespace android */ @@ -419,12 +522,11 @@ /* FALLTHRU */ case 'T': if (strspn(optarg, "0123456789") != strlen(optarg)) { - char *cp = tail_time.strptime(optarg, - log_time::default_format); + char *cp = tail_time.strptime(optarg, g_defaultTimeFormat); if (!cp) { logcat_panic(false, "-%c \"%s\" not in \"%s\" time format\n", - ret, optarg, log_time::default_format); + ret, optarg, g_defaultTimeFormat); } if (*cp) { char c = *cp; @@ -547,9 +649,11 @@ break; case 'f': + if ((tail_time == log_time::EPOCH) && (tail_lines != 0)) { + tail_time = lastLogTime(optarg); + } // redirect output to a file g_outputFileName = optarg; - break; case 'r': @@ -569,10 +673,7 @@ if (err < 0) { logcat_panic(true, "Invalid parameter %s to -v\n", optarg); } - - if (strcmp("color", optarg)) { // exception for modifiers - hasSetLogFormat = 1; - } + hasSetLogFormat |= err; break; case 'Q':
diff --git a/logd/Android.mk b/logd/Android.mk index e65e9ff..615d030 100644 --- a/logd/Android.mk +++ b/logd/Android.mk
@@ -18,6 +18,7 @@ LogWhiteBlackList.cpp \ libaudit.c \ LogAudit.cpp \ + LogKlog.cpp \ event.logtags LOCAL_SHARED_LIBRARIES := \ @@ -40,4 +41,15 @@ include $(BUILD_EXECUTABLE) +include $(CLEAR_VARS) + +LOCAL_MODULE := logpersist.start +LOCAL_MODULE_TAGS := debug +LOCAL_MODULE_CLASS := EXECUTABLES +LOCAL_MODULE_PATH := $(bin_dir) +LOCAL_SRC_FILES := logpersist +ALL_TOOLS := logpersist.start logpersist.stop logpersist.cat +LOCAL_POST_INSTALL_CMD := $(hide) $(foreach t,$(filter-out $(LOCAL_MODULE),$(ALL_TOOLS)),ln -sf $(LOCAL_MODULE) $(TARGET_OUT)/bin/$(t);) +include $(BUILD_PREBUILT) + include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/logd/CommandListener.cpp b/logd/CommandListener.cpp index bdfed3b..5489cc9 100644 --- a/logd/CommandListener.cpp +++ b/logd/CommandListener.cpp
@@ -33,9 +33,9 @@ #include "LogCommand.h" CommandListener::CommandListener(LogBuffer *buf, LogReader * /*reader*/, - LogListener * /*swl*/) - : FrameworkListener(getLogSocket()) - , mBuf(*buf) { + LogListener * /*swl*/) : + FrameworkListener(getLogSocket()), + mBuf(*buf) { // registerCmd(new ShutdownCmd(buf, writer, swl)); registerCmd(new ClearCmd(buf)); registerCmd(new GetBufSizeCmd(buf)); @@ -48,12 +48,12 @@ } CommandListener::ShutdownCmd::ShutdownCmd(LogBuffer *buf, LogReader *reader, - LogListener *swl) - : LogCommand("shutdown") - , mBuf(*buf) - , mReader(*reader) - , mSwl(*swl) -{ } + LogListener *swl) : + LogCommand("shutdown"), + mBuf(*buf), + mReader(*reader), + mSwl(*swl) { +} int CommandListener::ShutdownCmd::runCommand(SocketClient * /*cli*/, int /*argc*/, @@ -63,10 +63,10 @@ exit(0); } -CommandListener::ClearCmd::ClearCmd(LogBuffer *buf) - : LogCommand("clear") - , mBuf(*buf) -{ } +CommandListener::ClearCmd::ClearCmd(LogBuffer *buf) : + LogCommand("clear"), + mBuf(*buf) { +} static void setname() { static bool name_set; @@ -100,10 +100,10 @@ return 0; } -CommandListener::GetBufSizeCmd::GetBufSizeCmd(LogBuffer *buf) - : LogCommand("getLogSize") - , mBuf(*buf) -{ } +CommandListener::GetBufSizeCmd::GetBufSizeCmd(LogBuffer *buf) : + LogCommand("getLogSize"), + mBuf(*buf) { +} int CommandListener::GetBufSizeCmd::runCommand(SocketClient *cli, int argc, char **argv) { @@ -126,10 +126,10 @@ return 0; } -CommandListener::SetBufSizeCmd::SetBufSizeCmd(LogBuffer *buf) - : LogCommand("setLogSize") - , mBuf(*buf) -{ } +CommandListener::SetBufSizeCmd::SetBufSizeCmd(LogBuffer *buf) : + LogCommand("setLogSize"), + mBuf(*buf) { +} int CommandListener::SetBufSizeCmd::runCommand(SocketClient *cli, int argc, char **argv) { @@ -160,10 +160,10 @@ return 0; } -CommandListener::GetBufSizeUsedCmd::GetBufSizeUsedCmd(LogBuffer *buf) - : LogCommand("getLogSizeUsed") - , mBuf(*buf) -{ } +CommandListener::GetBufSizeUsedCmd::GetBufSizeUsedCmd(LogBuffer *buf) : + LogCommand("getLogSizeUsed"), + mBuf(*buf) { +} int CommandListener::GetBufSizeUsedCmd::runCommand(SocketClient *cli, int argc, char **argv) { @@ -186,10 +186,10 @@ return 0; } -CommandListener::GetStatisticsCmd::GetStatisticsCmd(LogBuffer *buf) - : LogCommand("getStatistics") - , mBuf(*buf) -{ } +CommandListener::GetStatisticsCmd::GetStatisticsCmd(LogBuffer *buf) : + LogCommand("getStatistics"), + mBuf(*buf) { +} static void package_string(char **strp) { const char *a = *strp; @@ -243,10 +243,10 @@ return 0; } -CommandListener::GetPruneListCmd::GetPruneListCmd(LogBuffer *buf) - : LogCommand("getPruneList") - , mBuf(*buf) -{ } +CommandListener::GetPruneListCmd::GetPruneListCmd(LogBuffer *buf) : + LogCommand("getPruneList"), + mBuf(*buf) { +} int CommandListener::GetPruneListCmd::runCommand(SocketClient *cli, int /*argc*/, char ** /*argv*/) { @@ -263,10 +263,10 @@ return 0; } -CommandListener::SetPruneListCmd::SetPruneListCmd(LogBuffer *buf) - : LogCommand("setPruneList") - , mBuf(*buf) -{ } +CommandListener::SetPruneListCmd::SetPruneListCmd(LogBuffer *buf) : + LogCommand("setPruneList"), + mBuf(*buf) { +} int CommandListener::SetPruneListCmd::runCommand(SocketClient *cli, int argc, char **argv) { @@ -301,9 +301,8 @@ return 0; } -CommandListener::ReinitCmd::ReinitCmd() - : LogCommand("reinit") -{ } +CommandListener::ReinitCmd::ReinitCmd() : LogCommand("reinit") { +} int CommandListener::ReinitCmd::runCommand(SocketClient *cli, int /*argc*/, char ** /*argv*/) {
diff --git a/logd/FlushCommand.cpp b/logd/FlushCommand.cpp index 26a1861..d584925 100644 --- a/logd/FlushCommand.cpp +++ b/logd/FlushCommand.cpp
@@ -27,14 +27,14 @@ unsigned long tail, unsigned int logMask, pid_t pid, - uint64_t start) - : mReader(reader) - , mNonBlock(nonBlock) - , mTail(tail) - , mLogMask(logMask) - , mPid(pid) - , mStart(start) -{ } + uint64_t start) : + mReader(reader), + mNonBlock(nonBlock), + mTail(tail), + mLogMask(logMask), + mPid(pid), + mStart(start) { +} // runSocketCommand is called once for every open client on the // log reader socket. Here we manage and associated the reader
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp index caae54b..4b3547c 100644 --- a/logd/LogAudit.cpp +++ b/logd/LogAudit.cpp
@@ -29,6 +29,7 @@ #include "libaudit.h" #include "LogAudit.h" +#include "LogKlog.h" #define KMSG_PRIORITY(PRI) \ '<', \ @@ -36,12 +37,12 @@ '0' + LOG_MAKEPRI(LOG_AUTH, LOG_PRI(PRI)) % 10, \ '>' -LogAudit::LogAudit(LogBuffer *buf, LogReader *reader, int fdDmesg) - : SocketListener(getLogSocket(), false) - , logbuf(buf) - , reader(reader) - , fdDmesg(fdDmesg) - , initialized(false) { +LogAudit::LogAudit(LogBuffer *buf, LogReader *reader, int fdDmesg) : + SocketListener(getLogSocket(), false), + logbuf(buf), + reader(reader), + fdDmesg(fdDmesg), + initialized(false) { static const char auditd_message[] = { KMSG_PRIORITY(LOG_INFO), 'l', 'o', 'g', 'd', '.', 'a', 'u', 'd', 'i', 't', 'd', ':', ' ', 's', 't', 'a', 'r', 't', '\n' }; @@ -121,6 +122,15 @@ && (*cp == ':')) { memcpy(timeptr + sizeof(audit_str) - 1, "0.0", 3); memmove(timeptr + sizeof(audit_str) - 1 + 3, cp, strlen(cp) + 1); + // + // We are either in 1970ish (MONOTONIC) or 2015+ish (REALTIME) so to + // differentiate without prejudice, we use 1980 to delineate, earlier + // is monotonic, later is real. + // +# define EPOCH_PLUS_10_YEARS (10 * 1461 / 4 * 24 * 60 * 60) + if (now.tv_sec < EPOCH_PLUS_10_YEARS) { + LogKlog::convertMonotonicToReal(now); + } } else { now.strptime("", ""); // side effect of setting CLOCK_REALTIME } @@ -135,7 +145,9 @@ ++cp; } tid = pid; + logbuf->lock(); uid = logbuf->pidToUid(pid); + logbuf->unlock(); memmove(pidptr, cp, strlen(cp) + 1); } @@ -170,14 +182,20 @@ static const char comm_str[] = " comm=\""; const char *comm = strstr(str, comm_str); const char *estr = str + strlen(str); + char *commfree = NULL; if (comm) { estr = comm; comm += sizeof(comm_str) - 1; } else if (pid == getpid()) { pid = tid; comm = "auditd"; - } else if (!(comm = logbuf->pidToName(pid))) { - comm = "unknown"; + } else { + logbuf->lock(); + comm = commfree = logbuf->pidToName(pid); + logbuf->unlock(); + if (!comm) { + comm = "unknown"; + } } const char *ecomm = strchr(comm, '"'); @@ -208,6 +226,7 @@ } } + free(commfree); free(str); if (notify) { @@ -223,7 +242,7 @@ int LogAudit::log(char *buf) { char *audit = strstr(buf, " audit("); if (!audit) { - return -EXDEV; + return 0; } *audit = '\0';
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp index 1dced11..0f5071b 100644 --- a/logd/LogBuffer.cpp +++ b/logd/LogBuffer.cpp
@@ -22,6 +22,8 @@ #include <time.h> #include <unistd.h> +#include <unordered_map> + #include <cutils/properties.h> #include <log/logger.h> @@ -126,8 +128,7 @@ } } -LogBuffer::LogBuffer(LastLogTimes *times) - : mTimes(*times) { +LogBuffer::LogBuffer(LastLogTimes *times) : mTimes(*times) { pthread_mutex_init(&mLogElementsLock, NULL); init(); @@ -139,8 +140,26 @@ if ((log_id >= LOG_ID_MAX) || (log_id < 0)) { return -EINVAL; } + LogBufferElement *elem = new LogBufferElement(log_id, realtime, uid, pid, tid, msg, len); + int prio = ANDROID_LOG_INFO; + const char *tag = NULL; + if (log_id == LOG_ID_EVENTS) { + tag = android::tagToName(elem->getTag()); + } else { + prio = *msg; + tag = msg + 1; + } + if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) { + // Log traffic received to total + pthread_mutex_lock(&mLogElementsLock); + stats.add(elem); + stats.subtract(elem); + pthread_mutex_unlock(&mLogElementsLock); + delete elem; + return -EACCES; + } pthread_mutex_lock(&mLogElementsLock); @@ -247,31 +266,21 @@ uint64_t getKey() { return value; } }; -class LogBufferElementEntry { - const uint64_t key; - LogBufferElement *last; +class LogBufferElementLast { + + typedef std::unordered_map<uint64_t, LogBufferElement *> LogBufferElementMap; + LogBufferElementMap map; public: - LogBufferElementEntry(const uint64_t &k, LogBufferElement *e):key(k),last(e) { } - const uint64_t&getKey() const { return key; } - - LogBufferElement *getLast() { return last; } -}; - -class LogBufferElementLast : public android::BasicHashtable<uint64_t, LogBufferElementEntry> { - -public: bool merge(LogBufferElement *e, unsigned short dropped) { LogBufferElementKey key(e->getUid(), e->getPid(), e->getTid()); - android::hash_t hash = android::hash_type(key.getKey()); - ssize_t index = find(-1, hash, key.getKey()); - if (index != -1) { - LogBufferElementEntry &entry = editEntryAt(index); - LogBufferElement *l = entry.getLast(); + LogBufferElementMap::iterator it = map.find(key.getKey()); + if (it != map.end()) { + LogBufferElement *l = it->second; unsigned short d = l->getDropped(); if ((dropped + d) > USHRT_MAX) { - removeAt(index); + map.erase(it); } else { l->setDropped(dropped + d); return true; @@ -280,24 +289,25 @@ return false; } - size_t add(LogBufferElement *e) { + void add(LogBufferElement *e) { LogBufferElementKey key(e->getUid(), e->getPid(), e->getTid()); - android::hash_t hash = android::hash_type(key.getKey()); - return android::BasicHashtable<uint64_t, LogBufferElementEntry>:: - add(hash, LogBufferElementEntry(key.getKey(), e)); + map[key.getKey()] = e; } inline void clear() { - android::BasicHashtable<uint64_t, LogBufferElementEntry>::clear(); + map.clear(); } void clear(LogBufferElement *e) { - uint64_t current = e->getRealTime().nsec() - NS_PER_SEC; - ssize_t index = -1; - while((index = next(index)) >= 0) { - if (current > editEntryAt(index).getLast()->getRealTime().nsec()) { - removeAt(index); - index = -1; + uint64_t current = e->getRealTime().nsec() + - (EXPIRE_RATELIMIT * NS_PER_SEC); + for(LogBufferElementMap::iterator it = map.begin(); it != map.end();) { + LogBufferElement *l = it->second; + if ((l->getDropped() >= EXPIRE_THRESHOLD) + && (current > l->getRealTime().nsec())) { + it = map.erase(it); + } else { + ++it; } } } @@ -415,8 +425,6 @@ continue; } - leading = false; - if (hasBlacklist && mPrune.naughty(e)) { last.clear(e); it = erase(it); @@ -446,6 +454,19 @@ } if (e->getUid() != worst) { + if (leading) { + static const timespec too_old = { + EXPIRE_HOUR_THRESHOLD * 60 * 60, 0 + }; + LogBufferElementCollection::iterator last; + last = mLogElements.end(); + --last; + if ((e->getRealTime() < ((*last)->getRealTime() - too_old)) + || (e->getRealTime() > (*last)->getRealTime())) { + break; + } + } + leading = false; last.clear(e); ++it; continue; @@ -459,15 +480,21 @@ kick = true; unsigned short len = e->getMsgLen(); - stats.drop(e); - e->setDropped(1); - if (last.merge(e, 1)) { - it = mLogElements.erase(it); - stats.erase(e); - delete e; + + // do not create any leading drops + if (leading) { + it = erase(it); } else { - last.add(e); - ++it; + stats.drop(e); + e->setDropped(1); + if (last.merge(e, 1)) { + it = mLogElements.erase(it); + stats.erase(e); + delete e; + } else { + last.add(e); + ++it; + } } if (worst_sizes < second_worst_sizes) { break; @@ -506,7 +533,7 @@ break; } - if (hasWhitelist && mPrune.nice(e)) { // WhiteListed + if (hasWhitelist && !e->getDropped() && mPrune.nice(e)) { // WhiteListed whitelist = true; it++; continue; @@ -630,7 +657,7 @@ pthread_mutex_unlock(&mLogElementsLock); // range locking in LastLogTimes looks after us - max = element->flushTo(reader); + max = element->flushTo(reader, this); if (max == element->FLUSH_ERROR) { return max;
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h index 9ee243d..a13fded 100644 --- a/logd/LogBuffer.h +++ b/logd/LogBuffer.h
@@ -71,9 +71,12 @@ // *strp uses malloc, use free to release. void formatPrune(char **strp) { mPrune.format(strp); } - // helper + // helper must be protected directly or implicitly by lock()/unlock() char *pidToName(pid_t pid) { return stats.pidToName(pid); } uid_t pidToUid(pid_t pid) { return stats.pidToUid(pid); } + char *uidToName(uid_t uid) { return stats.uidToName(uid); } + void lock() { pthread_mutex_lock(&mLogElementsLock); } + void unlock() { pthread_mutex_unlock(&mLogElementsLock); } private: void maybePrune(log_id_t id);
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp index a173e63..9fb1439 100644 --- a/logd/LogBufferElement.cpp +++ b/logd/LogBufferElement.cpp
@@ -14,7 +14,9 @@ * limitations under the License. */ +#include <ctype.h> #include <endian.h> +#include <fcntl.h> #include <stdio.h> #include <string.h> #include <time.h> @@ -28,18 +30,18 @@ #include "LogReader.h" const uint64_t LogBufferElement::FLUSH_ERROR(0); -atomic_int_fast64_t LogBufferElement::sequence; +atomic_int_fast64_t LogBufferElement::sequence(1); LogBufferElement::LogBufferElement(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid, pid_t tid, - const char *msg, unsigned short len) - : mLogId(log_id) - , mUid(uid) - , mPid(pid) - , mTid(tid) - , mMsgLen(len) - , mSequence(sequence.fetch_add(1, memory_order_relaxed)) - , mRealTime(realtime) { + const char *msg, unsigned short len) : + mLogId(log_id), + mUid(uid), + mPid(pid), + mTid(tid), + mMsgLen(len), + mSequence(sequence.fetch_add(1, memory_order_relaxed)), + mRealTime(realtime) { mMsg = new char[len]; memcpy(mMsg, msg, len); } @@ -48,18 +50,109 @@ delete [] mMsg; } -// assumption: mMsg == NULL -size_t LogBufferElement::populateDroppedMessage(char *&buffer, bool privileged) { - static const char format_uid[] = "uid=%u dropped=%u"; - static const size_t unprivileged_offset = 7; - static const char tag[] = "logd"; - - size_t len; - if (privileged) { - len = snprintf(NULL, 0, format_uid, mUid, mDropped); - } else { - len = snprintf(NULL, 0, format_uid + unprivileged_offset, mDropped); +uint32_t LogBufferElement::getTag() const { + if ((mLogId != LOG_ID_EVENTS) || !mMsg || (mMsgLen < sizeof(uint32_t))) { + return 0; } + return le32toh(reinterpret_cast<android_event_header_t *>(mMsg)->tag); +} + +// caller must own and free character string +char *android::tidToName(pid_t tid) { + char *retval = NULL; + char buffer[256]; + snprintf(buffer, sizeof(buffer), "/proc/%u/comm", tid); + int fd = open(buffer, O_RDONLY); + if (fd >= 0) { + ssize_t ret = read(fd, buffer, sizeof(buffer)); + if (ret >= (ssize_t)sizeof(buffer)) { + ret = sizeof(buffer) - 1; + } + while ((ret > 0) && isspace(buffer[ret - 1])) { + --ret; + } + if (ret > 0) { + buffer[ret] = '\0'; + retval = strdup(buffer); + } + close(fd); + } + + // if nothing for comm, check out cmdline + char *name = android::pidToName(tid); + if (!retval) { + retval = name; + name = NULL; + } + + // check if comm is truncated, see if cmdline has full representation + if (name) { + // impossible for retval to be NULL if name not NULL + size_t retval_len = strlen(retval); + size_t name_len = strlen(name); + // KISS: ToDo: Only checks prefix truncated, not suffix, or both + if ((retval_len < name_len) && !strcmp(retval, name + name_len - retval_len)) { + free(retval); + retval = name; + } else { + free(name); + } + } + return retval; +} + +// assumption: mMsg == NULL +size_t LogBufferElement::populateDroppedMessage(char *&buffer, + LogBuffer *parent) { + static const char tag[] = "chatty"; + + if (!__android_log_is_loggable(ANDROID_LOG_INFO, tag, ANDROID_LOG_VERBOSE)) { + return 0; + } + + static const char format_uid[] = "uid=%u%s%s expire %u line%s"; + parent->lock(); + char *name = parent->uidToName(mUid); + parent->unlock(); + char *commName = android::tidToName(mTid); + if (!commName && (mTid != mPid)) { + commName = android::tidToName(mPid); + } + if (!commName) { + parent->lock(); + commName = parent->pidToName(mPid); + parent->unlock(); + } + size_t len = name ? strlen(name) : 0; + if (len && commName && !strncmp(name, commName, len)) { + if (commName[len] == '\0') { + free(commName); + commName = NULL; + } else { + free(name); + name = NULL; + } + } + if (name) { + char *p = NULL; + asprintf(&p, "(%s)", name); + if (p) { + free(name); + name = p; + } + } + if (commName) { + char *p = NULL; + asprintf(&p, " %s", commName); + if (p) { + free(commName); + commName = p; + } + } + // identical to below to calculate the buffer size required + len = snprintf(NULL, 0, format_uid, mUid, name ? name : "", + commName ? commName : "", + mDropped, (mDropped > 1) ? "s" : ""); size_t hdrLen; if (mLogId == LOG_ID_EVENTS) { @@ -70,6 +163,8 @@ buffer = static_cast<char *>(calloc(1, hdrLen + len + 1)); if (!buffer) { + free(name); + free(commName); return 0; } @@ -86,16 +181,16 @@ strcpy(buffer + 1, tag); } - if (privileged) { - snprintf(buffer + hdrLen, len + 1, format_uid, mUid, mDropped); - } else { - snprintf(buffer + hdrLen, len + 1, format_uid + unprivileged_offset, mDropped); - } + snprintf(buffer + hdrLen, len + 1, format_uid, mUid, name ? name : "", + commName ? commName : "", + mDropped, (mDropped > 1) ? "s" : ""); + free(name); + free(commName); return retval; } -uint64_t LogBufferElement::flushTo(SocketClient *reader) { +uint64_t LogBufferElement::flushTo(SocketClient *reader, LogBuffer *parent) { struct logger_entry_v3 entry; memset(&entry, 0, sizeof(struct logger_entry_v3)); @@ -114,7 +209,7 @@ char *buffer = NULL; if (!mMsg) { - entry.len = populateDroppedMessage(buffer, clientHasLogCredentials(reader)); + entry.len = populateDroppedMessage(buffer, parent); if (!entry.len) { return mSequence; }
diff --git a/logd/LogBufferElement.h b/logd/LogBufferElement.h index 7b6456d..ca2c3a6 100644 --- a/logd/LogBufferElement.h +++ b/logd/LogBufferElement.h
@@ -25,18 +25,35 @@ #include <log/log.h> #include <log/log_read.h> +// Hijack this header as a common include file used by most all sources +// to report some utilities defined here and there. + namespace android { // Furnished in main.cpp. Caller must own and free returned value -// This function is designed for a single caller and is NOT thread-safe char *uidToName(uid_t uid); +// Furnished in LogStatistics.cpp. Caller must own and free returned value +char *pidToName(pid_t pid); +char *tidToName(pid_t tid); + +// Furnished in main.cpp. Thread safe. +const char *tagToName(uint32_t tag); + } static inline bool worstUidEnabledForLogid(log_id_t id) { - return (id != LOG_ID_CRASH) && (id != LOG_ID_EVENTS); + return (id != LOG_ID_CRASH) && (id != LOG_ID_KERNEL) && (id != LOG_ID_EVENTS); } +class LogBuffer; + +#define EXPIRE_HOUR_THRESHOLD 24 // Only expire chatty UID logs to preserve + // non-chatty UIDs less than this age in hours +#define EXPIRE_THRESHOLD 10 // A smaller expire count is considered too + // chatty for the temporal expire messages +#define EXPIRE_RATELIMIT 10 // maximum rate in seconds to report expiration + class LogBufferElement { const log_id_t mLogId; const uid_t mUid; @@ -52,7 +69,8 @@ static atomic_int_fast64_t sequence; // assumption: mMsg == NULL - size_t populateDroppedMessage(char *&buffer, bool privileged); + size_t populateDroppedMessage(char *&buffer, + LogBuffer *parent); public: LogBufferElement(log_id_t log_id, log_time realtime, @@ -77,8 +95,10 @@ static uint64_t getCurrentSequence(void) { return sequence.load(memory_order_relaxed); } log_time getRealTime(void) const { return mRealTime; } + uint32_t getTag(void) const; + static const uint64_t FLUSH_ERROR; - uint64_t flushTo(SocketClient *writer); + uint64_t flushTo(SocketClient *writer, LogBuffer *parent); }; #endif
diff --git a/logd/LogCommand.cpp b/logd/LogCommand.cpp index b78c0e0..06d865c 100644 --- a/logd/LogCommand.cpp +++ b/logd/LogCommand.cpp
@@ -23,8 +23,7 @@ #include "LogCommand.h" -LogCommand::LogCommand(const char *cmd) - : FrameworkCommand(cmd) { +LogCommand::LogCommand(const char *cmd) : FrameworkCommand(cmd) { } // gets a list of supplementary group IDs associated with
diff --git a/logd/LogKlog.cpp b/logd/LogKlog.cpp new file mode 100644 index 0000000..d578c04 --- /dev/null +++ b/logd/LogKlog.cpp
@@ -0,0 +1,598 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <ctype.h> +#include <errno.h> +#include <inttypes.h> +#include <limits.h> +#include <stdarg.h> +#include <stdlib.h> +#include <sys/prctl.h> +#include <sys/uio.h> +#include <syslog.h> + +#include <log/logger.h> + +#include "LogKlog.h" + +#define KMSG_PRIORITY(PRI) \ + '<', \ + '0' + (LOG_SYSLOG | (PRI)) / 10, \ + '0' + (LOG_SYSLOG | (PRI)) % 10, \ + '>' + +static const char priority_message[] = { KMSG_PRIORITY(LOG_INFO), '\0' }; + +// Parsing is hard + +// called if we see a '<', s is the next character, returns pointer after '>' +static char *is_prio(char *s) { + if (!isdigit(*s++)) { + return NULL; + } + char c; + while ((c = *s++)) { + if (!isdigit(c)) { + return (c == '>') ? s : NULL; + } + } + return NULL; +} + +// called if we see a '[', s is the next character, returns pointer after ']' +static char *is_timestamp(char *s) { + while (*s == ' ') { + ++s; + } + if (!isdigit(*s++)) { + return NULL; + } + bool first_period = true; + char c; + while ((c = *s++)) { + if ((c == '.') && first_period) { + first_period = false; + } else if (!isdigit(c)) { + return ((c == ']') && !first_period && (*s == ' ')) ? s : NULL; + } + } + return NULL; +} + +// Like strtok_r with "\r\n" except that we look for log signatures (regex) +// \(\(<[0-9]+>\)\([[] *[0-9]+[.][0-9]+[]] \)\{0,1\}\|[[] *[0-9]+[.][0-9]+[]] \) +// and split if we see a second one without a newline. + +#define SIGNATURE_MASK 0xF0 +// <digit> following ('0' to '9' masked with ~SIGNATURE_MASK) added to signature +#define LESS_THAN_SIG SIGNATURE_MASK +#define OPEN_BRACKET_SIG ((SIGNATURE_MASK << 1) & SIGNATURE_MASK) +// space is one more than <digit> of 9 +#define OPEN_BRACKET_SPACE ((char)(OPEN_BRACKET_SIG | 10)) + +char *log_strtok_r(char *s, char **last) { + if (!s) { + if (!(s = *last)) { + return NULL; + } + // fixup for log signature split <, + // LESS_THAN_SIG + <digit> + if ((*s & SIGNATURE_MASK) == LESS_THAN_SIG) { + *s = (*s & ~SIGNATURE_MASK) + '0'; + *--s = '<'; + } + // fixup for log signature split [, + // OPEN_BRACKET_SPACE is space, OPEN_BRACKET_SIG + <digit> + if ((*s & SIGNATURE_MASK) == OPEN_BRACKET_SIG) { + if (*s == OPEN_BRACKET_SPACE) { + *s = ' '; + } else { + *s = (*s & ~SIGNATURE_MASK) + '0'; + } + *--s = '['; + } + } + + s += strspn(s, "\r\n"); + + if (!*s) { // no non-delimiter characters + *last = NULL; + return NULL; + } + char *peek, *tok = s; + + for (;;) { + char c = *s++; + switch (c) { + case '\0': + *last = NULL; + return tok; + + case '\r': + case '\n': + s[-1] = '\0'; + *last = s; + return tok; + + case '<': + peek = is_prio(s); + if (!peek) { + break; + } + if (s != (tok + 1)) { // not first? + s[-1] = '\0'; + *s &= ~SIGNATURE_MASK; + *s |= LESS_THAN_SIG; // signature for '<' + *last = s; + return tok; + } + s = peek; + if ((*s == '[') && ((peek = is_timestamp(s + 1)))) { + s = peek; + } + break; + + case '[': + peek = is_timestamp(s); + if (!peek) { + break; + } + if (s != (tok + 1)) { // not first? + s[-1] = '\0'; + if (*s == ' ') { + *s = OPEN_BRACKET_SPACE; + } else { + *s &= ~SIGNATURE_MASK; + *s |= OPEN_BRACKET_SIG; // signature for '[' + } + *last = s; + return tok; + } + s = peek; + break; + } + } + /* NOTREACHED */ +} + +log_time LogKlog::correction = log_time(CLOCK_REALTIME) - log_time(CLOCK_MONOTONIC); + +LogKlog::LogKlog(LogBuffer *buf, LogReader *reader, int fdWrite, int fdRead, bool auditd) : + SocketListener(fdRead, false), + logbuf(buf), + reader(reader), + signature(CLOCK_MONOTONIC), + fdWrite(fdWrite), + fdRead(fdRead), + initialized(false), + enableLogging(true), + auditd(auditd) { + static const char klogd_message[] = "%slogd.klogd: %" PRIu64 "\n"; + char buffer[sizeof(priority_message) + sizeof(klogd_message) + 20 - 4]; + snprintf(buffer, sizeof(buffer), klogd_message, priority_message, + signature.nsec()); + write(fdWrite, buffer, strlen(buffer)); +} + +bool LogKlog::onDataAvailable(SocketClient *cli) { + if (!initialized) { + prctl(PR_SET_NAME, "logd.klogd"); + initialized = true; + enableLogging = false; + } + + char buffer[LOGGER_ENTRY_MAX_PAYLOAD]; + size_t len = 0; + + for(;;) { + ssize_t retval = 0; + if ((sizeof(buffer) - 1 - len) > 0) { + retval = read(cli->getSocket(), buffer + len, sizeof(buffer) - 1 - len); + } + if ((retval == 0) && (len == 0)) { + break; + } + if (retval < 0) { + return false; + } + len += retval; + bool full = len == (sizeof(buffer) - 1); + char *ep = buffer + len; + *ep = '\0'; + len = 0; + for(char *ptr = NULL, *tok = buffer; + ((tok = log_strtok_r(tok, &ptr))); + tok = NULL) { + if (((tok + strlen(tok)) == ep) && (retval != 0) && full) { + len = strlen(tok); + memmove(buffer, tok, len); + break; + } + if (*tok) { + log(tok); + } + } + } + + return true; +} + + +void LogKlog::calculateCorrection(const log_time &monotonic, + const char *real_string) { + log_time real; + if (!real.strptime(real_string, "%Y-%m-%d %H:%M:%S.%09q UTC")) { + return; + } + // kernel report UTC, log_time::strptime is localtime from calendar. + // Bionic and liblog strptime does not support %z or %Z to pick up + // timezone so we are calculating our own correction. + time_t now = real.tv_sec; + struct tm tm; + memset(&tm, 0, sizeof(tm)); + tm.tm_isdst = -1; + localtime_r(&now, &tm); + real.tv_sec += tm.tm_gmtoff; + correction = real - monotonic; +} + +void LogKlog::sniffTime(log_time &now, const char **buf, bool reverse) { + const char *cp; + if ((cp = now.strptime(*buf, "[ %s.%q]"))) { + static const char suspend[] = "PM: suspend entry "; + static const char resume[] = "PM: suspend exit "; + static const char suspended[] = "Suspended for "; + + if (isspace(*cp)) { + ++cp; + } + if (!strncmp(cp, suspend, sizeof(suspend) - 1)) { + calculateCorrection(now, cp + sizeof(suspend) - 1); + } else if (!strncmp(cp, resume, sizeof(resume) - 1)) { + calculateCorrection(now, cp + sizeof(resume) - 1); + } else if (!strncmp(cp, suspended, sizeof(suspended) - 1)) { + log_time real; + char *endp; + real.tv_sec = strtol(cp + sizeof(suspended) - 1, &endp, 10); + if (*endp == '.') { + real.tv_nsec = strtol(endp + 1, &endp, 10) * 1000000L; + if (reverse) { + correction -= real; + } else { + correction += real; + } + } + } + + convertMonotonicToReal(now); + *buf = cp; + } else { + now = log_time(CLOCK_REALTIME); + } +} + +// Passed the entire SYSLOG_ACTION_READ_ALL buffer and interpret a +// compensated start time. +void LogKlog::synchronize(const char *buf) { + const char *cp = strstr(buf, "] PM: suspend e"); + if (!cp) { + return; + } + + do { + --cp; + } while ((cp > buf) && (isdigit(*cp) || isspace(*cp) || (*cp == '.'))); + + log_time now; + sniffTime(now, &cp, true); + + char *suspended = strstr(buf, "] Suspended for "); + if (!suspended || (suspended > cp)) { + return; + } + cp = suspended; + + do { + --cp; + } while ((cp > buf) && (isdigit(*cp) || isspace(*cp) || (*cp == '.'))); + + sniffTime(now, &cp, true); +} + +// kernel log prefix, convert to a kernel log priority number +static int parseKernelPrio(const char **buf) { + int pri = LOG_USER | LOG_INFO; + const char *cp = *buf; + if (*cp == '<') { + pri = 0; + while(isdigit(*++cp)) { + pri = (pri * 10) + *cp - '0'; + } + if (*cp == '>') { + ++cp; + } else { + cp = *buf; + pri = LOG_USER | LOG_INFO; + } + *buf = cp; + } + return pri; +} + +// Convert kernel log priority number into an Android Logger priority number +static int convertKernelPrioToAndroidPrio(int pri) { + switch(pri & LOG_PRIMASK) { + case LOG_EMERG: + // FALLTHRU + case LOG_ALERT: + // FALLTHRU + case LOG_CRIT: + return ANDROID_LOG_FATAL; + + case LOG_ERR: + return ANDROID_LOG_ERROR; + + case LOG_WARNING: + return ANDROID_LOG_WARN; + + default: + // FALLTHRU + case LOG_NOTICE: + // FALLTHRU + case LOG_INFO: + break; + + case LOG_DEBUG: + return ANDROID_LOG_DEBUG; + } + + return ANDROID_LOG_INFO; +} + +// +// log a message into the kernel log buffer +// +// Filter rules to parse <PRI> <TIME> <tag> and <message> in order for +// them to appear correct in the logcat output: +// +// LOG_KERN (0): +// <PRI>[<TIME>] <tag> ":" <message> +// <PRI>[<TIME>] <tag> <tag> ":" <message> +// <PRI>[<TIME>] <tag> <tag>_work ":" <message> +// <PRI>[<TIME>] <tag> '<tag>.<num>' ":" <message> +// <PRI>[<TIME>] <tag> '<tag><num>' ":" <message> +// <PRI>[<TIME>] <tag>_host '<tag>.<num>' ":" <message> +// (unimplemented) <PRI>[<TIME>] <tag> '<num>.<tag>' ":" <message> +// <PRI>[<TIME>] "[INFO]"<tag> : <message> +// <PRI>[<TIME>] "------------[ cut here ]------------" (?) +// <PRI>[<TIME>] "---[ end trace 3225a3070ca3e4ac ]---" (?) +// LOG_USER, LOG_MAIL, LOG_DAEMON, LOG_AUTH, LOG_SYSLOG, LOG_LPR, LOG_NEWS +// LOG_UUCP, LOG_CRON, LOG_AUTHPRIV, LOG_FTP: +// <PRI+TAG>[<TIME>] (see sys/syslog.h) +// Observe: +// Minimum tag length = 3 NB: drops things like r5:c00bbadf, but allow PM: +// Maximum tag words = 2 +// Maximum tag length = 16 NB: we are thinking of how ugly logcat can get. +// Not a Tag if there is no message content. +// leading additional spaces means no tag, inherit last tag. +// Not a Tag if <tag>: is "ERROR:", "WARNING:", "INFO:" or "CPU:" +// Drop: +// empty messages +// messages with ' audit(' in them if auditd is running +// logd.klogd: +// return -1 if message logd.klogd: <signature> +// +int LogKlog::log(const char *buf) { + if (auditd && strstr(buf, " audit(")) { + return 0; + } + + int pri = parseKernelPrio(&buf); + + log_time now; + sniffTime(now, &buf, false); + + // sniff for start marker + const char klogd_message[] = "logd.klogd: "; + if (!strncmp(buf, klogd_message, sizeof(klogd_message) - 1)) { + char *endp; + uint64_t sig = strtoll(buf + sizeof(klogd_message) - 1, &endp, 10); + if (sig == signature.nsec()) { + if (initialized) { + enableLogging = true; + } else { + enableLogging = false; + } + return -1; + } + return 0; + } + + if (!enableLogging) { + return 0; + } + + // Parse pid, tid and uid (not possible) + const pid_t pid = 0; + const pid_t tid = 0; + const uid_t uid = 0; + + // Parse (rules at top) to pull out a tag from the incoming kernel message. + // Some may view the following as an ugly heuristic, the desire is to + // beautify the kernel logs into an Android Logging format; the goal is + // admirable but costly. + while (isspace(*buf)) { + ++buf; + } + if (!*buf) { + return 0; + } + const char *start = buf; + const char *tag = ""; + const char *etag = tag; + if (!isspace(*buf)) { + const char *bt, *et, *cp; + + bt = buf; + if (!strncmp(buf, "[INFO]", 6)) { + // <PRI>[<TIME>] "[INFO]"<tag> ":" message + bt = buf + 6; + } + for(et = bt; *et && (*et != ':') && !isspace(*et); ++et); + for(cp = et; isspace(*cp); ++cp); + size_t size; + + if (*cp == ':') { + // One Word + tag = bt; + etag = et; + buf = cp + 1; + } else { + size = et - bt; + if (strncmp(bt, cp, size)) { + // <PRI>[<TIME>] <tag>_host '<tag>.<num>' : message + if (!strncmp(bt + size - 5, "_host", 5) + && !strncmp(bt, cp, size - 5)) { + const char *b = cp; + cp += size - 5; + if (*cp == '.') { + while (!isspace(*++cp) && (*cp != ':')); + const char *e; + for(e = cp; isspace(*cp); ++cp); + if (*cp == ':') { + tag = b; + etag = e; + buf = cp + 1; + } + } + } else { + while (!isspace(*++cp) && (*cp != ':')); + const char *e; + for(e = cp; isspace(*cp); ++cp); + // Two words + if (*cp == ':') { + tag = bt; + etag = e; + buf = cp + 1; + } + } + } else if (isspace(cp[size])) { + const char *b = cp; + cp += size; + while (isspace(*++cp)); + // <PRI>[<TIME>] <tag> <tag> : message + if (*cp == ':') { + tag = bt; + etag = et; + buf = cp + 1; + } + } else if (cp[size] == ':') { + // <PRI>[<TIME>] <tag> <tag> : message + tag = bt; + etag = et; + buf = cp + size + 1; + } else if ((cp[size] == '.') || isdigit(cp[size])) { + // <PRI>[<TIME>] <tag> '<tag>.<num>' : message + // <PRI>[<TIME>] <tag> '<tag><num>' : message + const char *b = cp; + cp += size; + while (!isspace(*++cp) && (*cp != ':')); + const char *e = cp; + while (isspace(*cp)) { + ++cp; + } + if (*cp == ':') { + tag = b; + etag = e; + buf = cp + 1; + } + } else { + while (!isspace(*++cp) && (*cp != ':')); + const char *e = cp; + while (isspace(*cp)) { + ++cp; + } + // Two words + if (*cp == ':') { + tag = bt; + etag = e; + buf = cp + 1; + } + } + } + size = etag - tag; + if ((size <= 1) + || ((size == 2) && (isdigit(tag[0]) || isdigit(tag[1]))) + || ((size == 3) && !strncmp(tag, "CPU", 3)) + || ((size == 7) && !strncmp(tag, "WARNING", 7)) + || ((size == 5) && !strncmp(tag, "ERROR", 5)) + || ((size == 4) && !strncmp(tag, "INFO", 4))) { + buf = start; + etag = tag = ""; + } + } + size_t l = etag - tag; + // skip leading space + while (isspace(*buf)) { + ++buf; + } + // truncate trailing space + size_t b = strlen(buf); + while (b && isspace(buf[b-1])) { + --b; + } + // trick ... allow tag with empty content to be logged. log() drops empty + if (!b && l) { + buf = " "; + b = 1; + } + size_t n = 1 + l + 1 + b + 1; + + // Allocate a buffer to hold the interpreted log message + int rc = n; + char *newstr = reinterpret_cast<char *>(malloc(n)); + if (!newstr) { + rc = -ENOMEM; + return rc; + } + char *np = newstr; + + // Convert priority into single-byte Android logger priority + *np = convertKernelPrioToAndroidPrio(pri); + ++np; + + // Copy parsed tag following priority + strncpy(np, tag, l); + np += l; + *np = '\0'; + ++np; + + // Copy main message to the remainder + strncpy(np, buf, b); + np[b] = '\0'; + + // Log message + rc = logbuf->log(LOG_ID_KERNEL, now, uid, pid, tid, newstr, + (n <= USHRT_MAX) ? (unsigned short) n : USHRT_MAX); + free(newstr); + + // notify readers + if (!rc) { + reader->notifyNewLog(); + } + + return rc; +}
diff --git a/logd/LogKlog.h b/logd/LogKlog.h new file mode 100644 index 0000000..a898c63 --- /dev/null +++ b/logd/LogKlog.h
@@ -0,0 +1,57 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _LOGD_LOG_KLOG_H__ +#define _LOGD_LOG_KLOG_H__ + +#include <sysutils/SocketListener.h> +#include <log/log_read.h> +#include "LogReader.h" + +char *log_strtok_r(char *str, char **saveptr); + +class LogKlog : public SocketListener { + LogBuffer *logbuf; + LogReader *reader; + const log_time signature; + const int fdWrite; // /dev/kmsg + const int fdRead; // /proc/kmsg + // Set once thread is started, separates KLOG_ACTION_READ_ALL + // and KLOG_ACTION_READ phases. + bool initialized; + // Used during each of the above phases to control logging. + bool enableLogging; + // set if we are also running auditd, to filter out audit reports from + // our copy of the kernel log + bool auditd; + + static log_time correction; + +public: + LogKlog(LogBuffer *buf, LogReader *reader, int fdWrite, int fdRead, bool auditd); + int log(const char *buf); + void synchronize(const char *buf); + + static void convertMonotonicToReal(log_time &real) { real += correction; } + +protected: + void sniffTime(log_time &now, const char **buf, bool reverse); + void calculateCorrection(const log_time &monotonic, const char *real_string); + virtual bool onDataAvailable(SocketClient *cli); + +}; + +#endif
diff --git a/logd/LogListener.cpp b/logd/LogListener.cpp index 05ced06..b29f5ab 100644 --- a/logd/LogListener.cpp +++ b/logd/LogListener.cpp
@@ -28,11 +28,11 @@ #include "LogListener.h" -LogListener::LogListener(LogBuffer *buf, LogReader *reader) - : SocketListener(getLogSocket(), false) - , logbuf(buf) - , reader(reader) -{ } +LogListener::LogListener(LogBuffer *buf, LogReader *reader) : + SocketListener(getLogSocket(), false), + logbuf(buf), + reader(reader) { +} bool LogListener::onDataAvailable(SocketClient *cli) { static bool name_set; @@ -88,7 +88,7 @@ } android_log_header_t *header = reinterpret_cast<android_log_header_t *>(buffer); - if (/* header->id < LOG_ID_MIN || */ header->id >= LOG_ID_MAX) { + if (/* header->id < LOG_ID_MIN || */ header->id >= LOG_ID_MAX || header->id == LOG_ID_KERNEL) { return false; }
diff --git a/logd/LogReader.cpp b/logd/LogReader.cpp index 745e847..c7deec0 100644 --- a/logd/LogReader.cpp +++ b/logd/LogReader.cpp
@@ -24,10 +24,10 @@ #include "LogReader.h" #include "FlushCommand.h" -LogReader::LogReader(LogBuffer *logbuf) - : SocketListener(getLogSocket(), true) - , mLogbuf(*logbuf) -{ } +LogReader::LogReader(LogBuffer *logbuf) : + SocketListener(getLogSocket(), true), + mLogbuf(*logbuf) { +} // When we are notified a new log entry is available, inform // all of our listening sockets. @@ -116,14 +116,14 @@ uint64_t last; public: - LogFindStart(unsigned logMask, pid_t pid, log_time &start, uint64_t &sequence) - : mPid(pid) - , mLogMask(logMask) - , startTimeSet(false) - , start(start) - , sequence(sequence) - , last(sequence) - { } + LogFindStart(unsigned logMask, pid_t pid, log_time &start, uint64_t &sequence) : + mPid(pid), + mLogMask(logMask), + startTimeSet(false), + start(start), + sequence(sequence), + last(sequence) { + } static int callback(const LogBufferElement *element, void *obj) { LogFindStart *me = reinterpret_cast<LogFindStart *>(obj);
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp index eadc4dd..48c2fe6 100644 --- a/logd/LogStatistics.cpp +++ b/logd/LogStatistics.cpp
@@ -26,8 +26,7 @@ #include "LogStatistics.h" -LogStatistics::LogStatistics() - : enable(false) { +LogStatistics::LogStatistics() : enable(false) { log_id_for_each(id) { mSizes[id] = 0; mElements[id] = 0; @@ -39,10 +38,10 @@ namespace android { // caller must own and free character string -static char *pidToName(pid_t pid) { +char *pidToName(pid_t pid) { char *retval = NULL; - if (pid == 0) { // special case from auditd for kernel - retval = strdup("logd.auditd"); + if (pid == 0) { // special case from auditd/klogd for kernel + retval = strdup("logd"); } else { char buffer[512]; snprintf(buffer, sizeof(buffer), "/proc/%u/cmdline", pid); @@ -70,50 +69,25 @@ mSizes[log_id] += size; ++mElements[log_id]; - uid_t uid = e->getUid(); - unsigned short dropped = e->getDropped(); - android::hash_t hash = android::hash_type(uid); - uidTable_t &table = uidTable[log_id]; - ssize_t index = table.find(-1, hash, uid); - if (index == -1) { - UidEntry initEntry(uid); - initEntry.add(size); - initEntry.add_dropped(dropped); - table.add(hash, initEntry); - } else { - UidEntry &entry = table.editEntryAt(index); - entry.add(size); - entry.add_dropped(dropped); - } - mSizesTotal[log_id] += size; ++mElementsTotal[log_id]; + if (log_id == LOG_ID_KERNEL) { + return; + } + + uidTable[log_id].add(e->getUid(), e); + if (!enable) { return; } - pid_t pid = e->getPid(); - hash = android::hash_type(pid); - index = pidTable.find(-1, hash, pid); - if (index == -1) { - PidEntry initEntry(pid, uid, android::pidToName(pid)); - initEntry.add(size); - initEntry.add_dropped(dropped); - pidTable.add(hash, initEntry); - } else { - PidEntry &entry = pidTable.editEntryAt(index); - if (entry.getUid() != uid) { - entry.setUid(uid); - entry.setName(android::pidToName(pid)); - } else if (!entry.getName()) { - char *name = android::pidToName(pid); - if (name) { - entry.setName(name); - } - } - entry.add(size); - entry.add_dropped(dropped); + pidTable.add(e->getPid(), e); + tidTable.add(e->getTid(), e); + + uint32_t tag = e->getTag(); + if (tag) { + tagTable.add(tag, e); } } @@ -123,30 +97,22 @@ mSizes[log_id] -= size; --mElements[log_id]; - uid_t uid = e->getUid(); - unsigned short dropped = e->getDropped(); - android::hash_t hash = android::hash_type(uid); - uidTable_t &table = uidTable[log_id]; - ssize_t index = table.find(-1, hash, uid); - if (index != -1) { - UidEntry &entry = table.editEntryAt(index); - if (entry.subtract(size) || entry.subtract_dropped(dropped)) { - table.removeAt(index); - } + if (log_id == LOG_ID_KERNEL) { + return; } + uidTable[log_id].subtract(e->getUid(), e); + if (!enable) { return; } - pid_t pid = e->getPid(); - hash = android::hash_type(pid); - index = pidTable.find(-1, hash, pid); - if (index != -1) { - PidEntry &entry = pidTable.editEntryAt(index); - if (entry.subtract(size) || entry.subtract_dropped(dropped)) { - pidTable.removeAt(index); - } + pidTable.subtract(e->getPid(), e); + tidTable.subtract(e->getTid(), e); + + uint32_t tag = e->getTag(); + if (tag) { + tagTable.subtract(tag, e); } } @@ -157,28 +123,14 @@ unsigned short size = e->getMsgLen(); mSizes[log_id] -= size; - uid_t uid = e->getUid(); - android::hash_t hash = android::hash_type(uid); - typeof uidTable[0] &table = uidTable[log_id]; - ssize_t index = table.find(-1, hash, uid); - if (index != -1) { - UidEntry &entry = table.editEntryAt(index); - entry.subtract(size); - entry.add_dropped(1); - } + uidTable[log_id].drop(e->getUid(), e); if (!enable) { return; } - pid_t pid = e->getPid(); - hash = android::hash_type(pid); - index = pidTable.find(-1, hash, pid); - if (index != -1) { - PidEntry &entry = pidTable.editEntryAt(index); - entry.subtract(size); - entry.add_dropped(1); - } + pidTable.drop(e->getPid(), e); + tidTable.drop(e->getTid(), e); } // caller must own and free character string @@ -199,15 +151,18 @@ } // Parse /data/system/packages.list - char *name = android::uidToName(uid); + uid_t userId = uid % AID_USER; + char *name = android::uidToName(userId); + if (!name && (userId > (AID_SHARED_GID_START - AID_APP))) { + name = android::uidToName(userId - (AID_SHARED_GID_START - AID_APP)); + } if (name) { return name; } // report uid -> pid(s) -> pidToName if unique - ssize_t index = -1; - while ((index = pidTable.next(index)) != -1) { - const PidEntry &entry = pidTable.entryAt(index); + for(pidTable_t::iterator it = pidTable.begin(); it != pidTable.end(); ++it) { + const PidEntry &entry = it->second; if (entry.getUid() == uid) { const char *n = entry.getName(); @@ -217,7 +172,8 @@ name = strdup(n); } else if (strcmp(name, n)) { free(name); - return NULL; + name = NULL; + break; } } } @@ -379,6 +335,7 @@ } if (enable) { + // Pid table bool headerPrinted = false; std::unique_ptr<const PidEntry *[]> sorted = pidTable.sort(maximum_sorted_entries); ssize_t index = -1; @@ -435,6 +392,108 @@ } } + if (enable) { + // Tid table + bool headerPrinted = false; + // sort() returns list of references, unique_ptr makes sure self-delete + std::unique_ptr<const TidEntry *[]> sorted = tidTable.sort(maximum_sorted_entries); + ssize_t index = -1; + while ((index = tidTable.next(index, sorted, maximum_sorted_entries)) >= 0) { + const TidEntry *entry = sorted[index]; + uid_t u = entry->getUid(); + if ((uid != AID_ROOT) && (u != uid)) { + continue; + } + + if (!headerPrinted) { // Only print header if we have table to print + output.appendFormat("\n\n"); + android::String8 name("Chattiest TIDs:"); + android::String8 size("Size"); + android::String8 pruned("Pruned"); + format_line(output, name, size, pruned); + + name.setTo(" TID/UID COMM"); + size.setTo("BYTES"); + pruned.setTo("LINES"); + format_line(output, name, size, pruned); + + headerPrinted = true; + } + + android::String8 name(""); + name.appendFormat("%5u/%u", entry->getKey(), u); + const char *n = entry->getName(); + if (n) { + name.appendFormat("%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", n); + } else { + // if we do not have a PID name, lets punt to try UID name? + char *un = uidToName(u); + if (un) { + name.appendFormat("%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", un); + free(un); + } + // We tried, better to not have a name at all, we still + // have TID/UID by number to report in any case. + } + + android::String8 size(""); + size.appendFormat("%zu", entry->getSizes()); + + android::String8 pruned(""); + size_t dropped = entry->getDropped(); + if (dropped) { + pruned.appendFormat("%zu", dropped); + } + + format_line(output, name, size, pruned); + } + } + + if (enable && (logMask & (1 << LOG_ID_EVENTS))) { + // Tag table + bool headerPrinted = false; + std::unique_ptr<const TagEntry *[]> sorted = tagTable.sort(maximum_sorted_entries); + ssize_t index = -1; + while ((index = tagTable.next(index, sorted, maximum_sorted_entries)) >= 0) { + const TagEntry *entry = sorted[index]; + uid_t u = entry->getUid(); + if ((uid != AID_ROOT) && (u != uid)) { + continue; + } + + android::String8 pruned(""); + + if (!headerPrinted) { + output.appendFormat("\n\n"); + android::String8 name("Chattiest events log buffer TAGs:"); + android::String8 size("Size"); + format_line(output, name, size, pruned); + + name.setTo(" TAG/UID TAGNAME"); + size.setTo("BYTES"); + format_line(output, name, size, pruned); + + headerPrinted = true; + } + + android::String8 name(""); + if (u == (uid_t)-1) { + name.appendFormat("%7u", entry->getKey()); + } else { + name.appendFormat("%7u/%u", entry->getKey(), u); + } + const char *n = entry->getName(); + if (n) { + name.appendFormat("%*s%s", (int)std::max(14 - name.length(), (size_t)1), "", n); + } + + android::String8 size(""); + size.appendFormat("%zu", entry->getSizes()); + + format_line(output, name, size, pruned); + } + } + *buf = strdup(output.string()); } @@ -460,48 +519,14 @@ } uid_t LogStatistics::pidToUid(pid_t pid) { - uid_t uid; - android::hash_t hash = android::hash_type(pid); - ssize_t index = pidTable.find(-1, hash, pid); - if (index == -1) { - uid = android::pidToUid(pid); - PidEntry initEntry(pid, uid, android::pidToName(pid)); - pidTable.add(hash, initEntry); - } else { - PidEntry &entry = pidTable.editEntryAt(index); - if (!entry.getName()) { - char *name = android::pidToName(pid); - if (name) { - entry.setName(name); - } - } - uid = entry.getUid(); - } - return uid; + return pidTable.add(pid)->second.getUid(); } // caller must free character string char *LogStatistics::pidToName(pid_t pid) { - char *name; - - android::hash_t hash = android::hash_type(pid); - ssize_t index = pidTable.find(-1, hash, pid); - if (index == -1) { - name = android::pidToName(pid); - PidEntry initEntry(pid, android::pidToUid(pid), name ? strdup(name) : NULL); - pidTable.add(hash, initEntry); - } else { - PidEntry &entry = pidTable.editEntryAt(index); - const char *n = entry.getName(); - if (n) { - name = strdup(n); - } else { - name = android::pidToName(pid); - if (name) { - entry.setName(strdup(name)); - } - } + const char *name = pidTable.add(pid)->second.getName(); + if (!name) { + return NULL; } - - return name; + return strdup(name); }
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h index f3110d7..760d6b2 100644 --- a/logd/LogStatistics.h +++ b/logd/LogStatistics.h
@@ -21,8 +21,9 @@ #include <stdlib.h> #include <sys/types.h> +#include <unordered_map> + #include <log/log.h> -#include <utils/BasicHashtable.h> #include "LogBufferElement.h" @@ -30,8 +31,14 @@ for (log_id_t i = LOG_ID_MIN; i < LOG_ID_MAX; i = (log_id_t) (i + 1)) template <typename TKey, typename TEntry> -class LogHashtable : public android::BasicHashtable<TKey, TEntry> { +class LogHashtable { + + std::unordered_map<TKey, TEntry> map; + public: + + typedef typename std::unordered_map<TKey, TEntry>::iterator iterator; + std::unique_ptr<const TEntry *[]> sort(size_t n) { if (!n) { std::unique_ptr<const TEntry *[]> sorted(NULL); @@ -41,9 +48,8 @@ const TEntry **retval = new const TEntry* [n]; memset(retval, 0, sizeof(*retval) * n); - ssize_t index = -1; - while ((index = android::BasicHashtable<TKey, TEntry>::next(index)) >= 0) { - const TEntry &entry = android::BasicHashtable<TKey, TEntry>::entryAt(index); + for(iterator it = map.begin(); it != map.end(); ++it) { + const TEntry &entry = it->second; size_t s = entry.getSizes(); ssize_t i = n - 1; while ((!retval[i] || (s > retval[i]->getSizes())) && (--i >= 0)) @@ -70,55 +76,215 @@ return index; } - ssize_t next(ssize_t index) { - return android::BasicHashtable<TKey, TEntry>::next(index); + inline iterator add(TKey key, LogBufferElement *e) { + iterator it = map.find(key); + if (it == map.end()) { + it = map.insert(std::make_pair(key, TEntry(e))).first; + } else { + it->second.add(e); + } + return it; + } + + inline iterator add(TKey key) { + iterator it = map.find(key); + if (it == map.end()) { + it = map.insert(std::make_pair(key, TEntry(key))).first; + } else { + it->second.add(key); + } + return it; + } + + void subtract(TKey key, LogBufferElement *e) { + iterator it = map.find(key); + if ((it != map.end()) && it->second.subtract(e)) { + map.erase(it); + } + } + + inline void drop(TKey key, LogBufferElement *e) { + iterator it = map.find(key); + if (it != map.end()) { + it->second.drop(e); + } + } + + inline iterator begin() { return map.begin(); } + inline iterator end() { return map.end(); } + +}; + +struct EntryBase { + size_t size; + + EntryBase():size(0) { } + EntryBase(LogBufferElement *e):size(e->getMsgLen()) { } + + size_t getSizes() const { return size; } + + inline void add(LogBufferElement *e) { size += e->getMsgLen(); } + inline bool subtract(LogBufferElement *e) { size -= e->getMsgLen(); return !size; } +}; + +struct EntryBaseDropped : public EntryBase { + size_t dropped; + + EntryBaseDropped():dropped(0) { } + EntryBaseDropped(LogBufferElement *e):EntryBase(e),dropped(e->getDropped()){ } + + size_t getDropped() const { return dropped; } + + inline void add(LogBufferElement *e) { + dropped += e->getDropped(); + EntryBase::add(e); + } + inline bool subtract(LogBufferElement *e) { + dropped -= e->getDropped(); + return EntryBase::subtract(e) && !dropped; + } + inline void drop(LogBufferElement *e) { + dropped += 1; + EntryBase::subtract(e); } }; -struct UidEntry { +struct UidEntry : public EntryBaseDropped { const uid_t uid; - size_t size; - size_t dropped; - UidEntry(uid_t uid):uid(uid),size(0),dropped(0) { } + UidEntry(LogBufferElement *e):EntryBaseDropped(e),uid(e->getUid()) { } inline const uid_t&getKey() const { return uid; } - size_t getSizes() const { return size; } - size_t getDropped() const { return dropped; } - - inline void add(size_t s) { size += s; } - inline void add_dropped(size_t d) { dropped += d; } - inline bool subtract(size_t s) { size -= s; return !dropped && !size; } - inline bool subtract_dropped(size_t d) { dropped -= d; return !dropped && !size; } }; -struct PidEntry { +namespace android { +uid_t pidToUid(pid_t pid); +} + +struct PidEntry : public EntryBaseDropped { const pid_t pid; uid_t uid; char *name; - size_t size; - size_t dropped; - PidEntry(pid_t p, uid_t u, char *n):pid(p),uid(u),name(n),size(0),dropped(0) { } + PidEntry(pid_t p): + EntryBaseDropped(), + pid(p), + uid(android::pidToUid(p)), + name(android::pidToName(pid)) { } + PidEntry(LogBufferElement *e): + EntryBaseDropped(e), + pid(e->getPid()), + uid(e->getUid()), + name(android::pidToName(e->getPid())) { } PidEntry(const PidEntry &c): + EntryBaseDropped(c), pid(c.pid), uid(c.uid), - name(c.name ? strdup(c.name) : NULL), - size(c.size), - dropped(c.dropped) { } + name(c.name ? strdup(c.name) : NULL) { } ~PidEntry() { free(name); } const pid_t&getKey() const { return pid; } const uid_t&getUid() const { return uid; } - uid_t&setUid(uid_t u) { return uid = u; } const char*getName() const { return name; } - char *setName(char *n) { free(name); return name = n; } - size_t getSizes() const { return size; } - size_t getDropped() const { return dropped; } - inline void add(size_t s) { size += s; } - inline void add_dropped(size_t d) { dropped += d; } - inline bool subtract(size_t s) { size -= s; return !dropped && !size; } - inline bool subtract_dropped(size_t d) { dropped -= d; return !dropped && !size; } + + inline void add(pid_t p) { + if (name && !strncmp(name, "zygote", 6)) { + free(name); + name = NULL; + } + if (!name) { + char *n = android::pidToName(p); + if (n) { + name = n; + } + } + } + + inline void add(LogBufferElement *e) { + uid_t u = e->getUid(); + if (getUid() != u) { + uid = u; + free(name); + name = android::pidToName(e->getPid()); + } else { + add(e->getPid()); + } + EntryBaseDropped::add(e); + } +}; + +struct TidEntry : public EntryBaseDropped { + const pid_t tid; + uid_t uid; + char *name; + + TidEntry(pid_t t): + EntryBaseDropped(), + tid(t), + uid(android::pidToUid(t)), + name(android::tidToName(tid)) { } + TidEntry(LogBufferElement *e): + EntryBaseDropped(e), + tid(e->getTid()), + uid(e->getUid()), + name(android::tidToName(e->getTid())) { } + TidEntry(const TidEntry &c): + EntryBaseDropped(c), + tid(c.tid), + uid(c.uid), + name(c.name ? strdup(c.name) : NULL) { } + ~TidEntry() { free(name); } + + const pid_t&getKey() const { return tid; } + const uid_t&getUid() const { return uid; } + const char*getName() const { return name; } + + inline void add(pid_t t) { + if (name && !strncmp(name, "zygote", 6)) { + free(name); + name = NULL; + } + if (!name) { + char *n = android::tidToName(t); + if (n) { + name = n; + } + } + } + + inline void add(LogBufferElement *e) { + uid_t u = e->getUid(); + if (getUid() != u) { + uid = u; + free(name); + name = android::tidToName(e->getTid()); + } else { + add(e->getTid()); + } + EntryBaseDropped::add(e); + } +}; + +struct TagEntry : public EntryBase { + const uint32_t tag; + uid_t uid; + + TagEntry(LogBufferElement *e): + EntryBase(e), + tag(e->getTag()), + uid(e->getUid()) { } + + const uint32_t&getKey() const { return tag; } + const uid_t&getUid() const { return uid; } + const char*getName() const { return android::tagToName(tag); } + + inline void add(LogBufferElement *e) { + uid_t u = e->getUid(); + if (uid != u) { + uid = -1; + } + EntryBase::add(e); + } }; // Log Statistics @@ -137,6 +303,14 @@ typedef LogHashtable<pid_t, PidEntry> pidTable_t; pidTable_t pidTable; + // tid to uid list + typedef LogHashtable<pid_t, TidEntry> tidTable_t; + tidTable_t tidTable; + + // tag list + typedef LogHashtable<uint32_t, TagEntry> tagTable_t; + tagTable_t tagTable; + public: LogStatistics(); @@ -160,7 +334,7 @@ // *strp = malloc, balance with free void format(char **strp, uid_t uid, unsigned int logMask); - // helper + // helper (must be locked directly or implicitly by mLogElementsLock) char *pidToName(pid_t pid); uid_t pidToUid(pid_t pid); char *uidToName(uid_t uid);
diff --git a/logd/LogTimes.cpp b/logd/LogTimes.cpp index 1b60b7e..68a0680 100644 --- a/logd/LogTimes.cpp +++ b/logd/LogTimes.cpp
@@ -26,24 +26,24 @@ LogTimeEntry::LogTimeEntry(LogReader &reader, SocketClient *client, bool nonBlock, unsigned long tail, unsigned int logMask, pid_t pid, - uint64_t start) - : mRefCount(1) - , mRelease(false) - , mError(false) - , threadRunning(false) - , mReader(reader) - , mLogMask(logMask) - , mPid(pid) - , mCount(0) - , mTail(tail) - , mIndex(0) - , mClient(client) - , mStart(start) - , mNonBlock(nonBlock) - , mEnd(LogBufferElement::getCurrentSequence()) -{ - pthread_cond_init(&threadTriggeredCondition, NULL); - cleanSkip_Locked(); + uint64_t start) : + mRefCount(1), + mRelease(false), + mError(false), + threadRunning(false), + leadingDropped(false), + mReader(reader), + mLogMask(logMask), + mPid(pid), + mCount(0), + mTail(tail), + mIndex(0), + mClient(client), + mStart(start), + mNonBlock(nonBlock), + mEnd(LogBufferElement::getCurrentSequence()) { + pthread_cond_init(&threadTriggeredCondition, NULL); + cleanSkip_Locked(); } void LogTimeEntry::startReader_Locked(void) { @@ -124,6 +124,8 @@ bool privileged = FlushCommand::hasReadLogs(client); + me->leadingDropped = true; + lock(); while (me->threadRunning && !me->isError_Locked()) { @@ -133,6 +135,7 @@ if (me->mTail) { logbuf.flushTo(client, start, privileged, FilterFirstPass, me); + me->leadingDropped = true; } start = logbuf.flushTo(client, start, privileged, FilterSecondPass, me); @@ -164,6 +167,14 @@ LogTimeEntry::lock(); + if (me->leadingDropped) { + if (element->getDropped()) { + LogTimeEntry::unlock(); + return false; + } + me->leadingDropped = false; + } + if (me->mCount == 0) { me->mStart = element->getSequence(); } @@ -191,6 +202,13 @@ goto skip; } + if (me->leadingDropped) { + if (element->getDropped()) { + goto skip; + } + me->leadingDropped = false; + } + // Truncate to close race between first and second pass if (me->mNonBlock && me->mTail && (me->mIndex >= me->mCount)) { goto stop;
diff --git a/logd/LogTimes.h b/logd/LogTimes.h index ae2f92b..783bce6 100644 --- a/logd/LogTimes.h +++ b/logd/LogTimes.h
@@ -32,6 +32,7 @@ bool mRelease; bool mError; bool threadRunning; + bool leadingDropped; pthread_cond_t threadTriggeredCondition; pthread_t mThread; LogReader &mReader;
diff --git a/logd/LogWhiteBlackList.cpp b/logd/LogWhiteBlackList.cpp index bee940d..277b3ca 100644 --- a/logd/LogWhiteBlackList.cpp +++ b/logd/LogWhiteBlackList.cpp
@@ -22,10 +22,8 @@ // White and Black list -Prune::Prune(uid_t uid, pid_t pid) - : mUid(uid) - , mPid(pid) -{ } +Prune::Prune(uid_t uid, pid_t pid) : mUid(uid), mPid(pid) { +} int Prune::cmp(uid_t uid, pid_t pid) const { if ((mUid == uid_all) || (mUid == uid)) { @@ -51,8 +49,7 @@ } } -PruneList::PruneList() - : mWorstUidEnabled(true) { +PruneList::PruneList() : mWorstUidEnabled(true) { mNaughty.clear(); mNice.clear(); }
diff --git a/logd/README.property b/logd/README.property index 60542b2..a472efd 100644 --- a/logd/README.property +++ b/logd/README.property
@@ -4,9 +4,14 @@ logd.auditd bool true Enable selinux audit daemon logd.auditd.dmesg bool true selinux audit messages duplicated and sent on to dmesg log +logd.klogd bool depends Enable klogd daemon logd.statistics bool depends Enable logcat -S statistics. -ro.config.low_ram bool false if true, logd.statistics default false -ro.build.type string if user, logd.statistics default false +ro.config.low_ram bool false if true, logd.statistics & logd.klogd + default false +ro.build.type string if user, logd.statistics & logd.klogd + default false +persist.logd.logpersistd string Enable logpersist daemon, "logcatd" + turns on logcat -f in logd context persist.logd.size number 256K default size of the buffer for all log ids at initial startup, at runtime use: logcat -b all -G <value>
diff --git a/logd/libaudit.c b/logd/libaudit.c index cf76305..d00d579 100644 --- a/logd/libaudit.c +++ b/logd/libaudit.c
@@ -177,7 +177,7 @@ */ status.pid = pid; status.mask = AUDIT_STATUS_PID | AUDIT_STATUS_RATE_LIMIT; - status.rate_limit = 5; // audit entries per second + status.rate_limit = 20; // audit entries per second /* Let the kernel know this pid will be registering for audit events */ rc = audit_send(fd, AUDIT_SET, &status, sizeof(status));
diff --git a/logd/logpersist b/logd/logpersist new file mode 100755 index 0000000..215e1e2 --- /dev/null +++ b/logd/logpersist
@@ -0,0 +1,36 @@ +#! /system/bin/sh +# logpersist cat start and stop handlers +data=/data/misc/logd +property=persist.logd.logpersistd +service=logcatd +progname="${0##*/}" +if [ X"${1}" = "-h" -o X"${1}" = X"--help" ]; then + echo "${progname%.*}.cat - dump current ${service%d} logs" + echo "${progname%.*}.start - start ${service} service" + echo "${progname%.*}.stop [--clear] - stop ${service} service" + exit 0 +fi +case ${progname} in +*.cat) + su 1036 ls "${data}" | + tr -d '\r' | + sort -ru | + sed "s#^#${data}/#" | + su 1036 xargs cat + ;; +*.start) + su 0 setprop ${property} ${service} + getprop ${property} + sleep 1 + ps -t | grep "${data##*/}.*${service%d}" + ;; +*.stop) + su 0 stop ${service} + su 0 setprop ${property} "" + [ X"${1}" != X"-c" -a X"${1}" != X"--clear" ] || + ( sleep 1 ; su 1036,9998 rm -rf "${data}" ) + ;; +*) + echo "Unexpected command ${0##*/} ${@}" >&2 + exit 1 +esac
diff --git a/logd/main.cpp b/logd/main.cpp index eb29596..a3241d0 100644 --- a/logd/main.cpp +++ b/logd/main.cpp
@@ -27,20 +27,26 @@ #include <sys/capability.h> #include <sys/klog.h> #include <sys/prctl.h> +#include <sys/resource.h> #include <sys/stat.h> #include <sys/types.h> #include <syslog.h> #include <unistd.h> +#include <memory> + #include <cutils/properties.h> #include <cutils/sched_policy.h> #include <cutils/sockets.h> +#include <log/event_tag_map.h> #include <private/android_filesystem_config.h> +#include <utils/threads.h> #include "CommandListener.h" #include "LogBuffer.h" #include "LogListener.h" #include "LogAudit.h" +#include "LogKlog.h" #define KMSG_PRIORITY(PRI) \ '<', \ @@ -89,10 +95,18 @@ return -1; } + if (setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND) < 0) { + return -1; + } + if (prctl(PR_SET_KEEPCAPS, 1) < 0) { return -1; } + if (setgroups(0, NULL) == -1) { + return -1; + } + if (setgid(AID_LOGD) != 0) { return -1; } @@ -154,6 +168,7 @@ static void *reinit_thread_start(void * /*obj*/) { prctl(PR_SET_NAME, "logd.daemon"); set_sched_policy(0, SP_BACKGROUND); + setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND); setgid(AID_SYSTEM); setuid(AID_SYSTEM); @@ -210,18 +225,26 @@ return NULL; } +static sem_t sem_name; + char *android::uidToName(uid_t u) { if (!u || !reinit_running) { return NULL; } - // Not multi-thread safe, we know there is only one caller + sem_wait(&sem_name); + + // Not multi-thread safe, we use sem_name to protect uid = u; name = NULL; sem_post(&reinit); sem_wait(&uidName); - return name; + char *ret = name; + + sem_post(&sem_name); + + return ret; } // Serves as a global method to trigger reinitialization @@ -230,6 +253,73 @@ sem_post(&reinit); } +// tagToName converts an events tag into a name +const char *android::tagToName(uint32_t tag) { + static const EventTagMap *map; + + if (!map) { + sem_wait(&sem_name); + if (!map) { + map = android_openEventTagMap(EVENT_TAG_MAP_FILE); + } + sem_post(&sem_name); + if (!map) { + return NULL; + } + } + return android_lookupEventTag(map, tag); +} + +static bool property_get_bool_svelte(const char *key) { + bool not_user; + { + char property[PROPERTY_VALUE_MAX]; + property_get("ro.build.type", property, ""); + not_user = !!strcmp(property, "user"); + } + return property_get_bool(key, not_user + && !property_get_bool("ro.config.low_ram", false)); +} + +static void readDmesg(LogAudit *al, LogKlog *kl) { + if (!al && !kl) { + return; + } + + int len = klogctl(KLOG_SIZE_BUFFER, NULL, 0); + if (len <= 0) { + return; + } + + len += 1024; // Margin for additional input race or trailing nul + std::unique_ptr<char []> buf(new char[len]); + + int rc = klogctl(KLOG_READ_ALL, buf.get(), len); + if (rc <= 0) { + return; + } + + if (rc < len) { + len = rc + 1; + } + buf[len - 1] = '\0'; + + if (kl) { + kl->synchronize(buf.get()); + } + + for (char *ptr = NULL, *tok = buf.get(); + (rc >= 0) && ((tok = log_strtok_r(tok, &ptr))); + tok = NULL) { + if (al) { + rc = al->log(tok); + } + if (kl) { + rc = kl->log(tok); + } + } +} + // Foreground waits for exit of the main persistent threads // that are started here. The threads are created to manage // UNIX domain client sockets for writing, reading and @@ -237,6 +327,11 @@ // logging plugins like auditd and restart control. Additional // transitory per-client threads are created for each reader. int main(int argc, char *argv[]) { + int fdPmesg = -1; + bool klogd = property_get_bool_svelte("logd.klogd"); + if (klogd) { + fdPmesg = open("/proc/kmsg", O_RDONLY | O_NDELAY); + } fdDmesg = open("/dev/kmsg", O_WRONLY); // issue reinit command. KISS argument parsing. @@ -277,6 +372,7 @@ // Reinit Thread sem_init(&reinit, 0, 0); sem_init(&uidName, 0, 0); + sem_init(&sem_name, 0, 1); pthread_attr_t attr; if (!pthread_attr_init(&attr)) { struct sched_param param; @@ -312,14 +408,8 @@ signal(SIGHUP, reinit_signal_handler); - { - char property[PROPERTY_VALUE_MAX]; - property_get("ro.build.type", property, ""); - if (property_get_bool("logd.statistics", - !!strcmp(property, "user") - && !property_get_bool("ro.config.low_ram", false))) { - logBuf->enableStatistics(); - } + if (property_get_bool_svelte("logd.statistics")) { + logBuf->enableStatistics(); } // LogReader listens on /dev/socket/logdr. When a client @@ -354,31 +444,27 @@ bool auditd = property_get_bool("logd.auditd", true); + LogAudit *al = NULL; if (auditd) { bool dmesg = property_get_bool("logd.auditd.dmesg", true); + al = new LogAudit(logBuf, reader, dmesg ? fdDmesg : -1); + } - // failure is an option ... messages are in dmesg (required by standard) - LogAudit *al = new LogAudit(logBuf, reader, dmesg ? fdDmesg : -1); + LogKlog *kl = NULL; + if (klogd) { + kl = new LogKlog(logBuf, reader, fdDmesg, fdPmesg, al != NULL); + } - int len = klogctl(KLOG_SIZE_BUFFER, NULL, 0); - if (len > 0) { - len++; - char buf[len]; + readDmesg(al, kl); - int rc = klogctl(KLOG_READ_ALL, buf, len); + // failure is an option ... messages are in dmesg (required by standard) - if (rc >= 0) { - buf[len - 1] = '\0'; + if (kl && kl->startListener()) { + delete kl; + } - for (char *ptr, *tok = buf; (tok = strtok_r(tok, "\r\n", &ptr)); tok = NULL) { - al->log(tok); - } - } - } - - if (al->startListener()) { - delete al; - } + if (al && al->startListener()) { + delete al; } TEMP_FAILURE_RETRY(pause());
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp index 46bd9c0..3266360 100644 --- a/logd/tests/logd_test.cpp +++ b/logd/tests/logd_test.cpp
@@ -93,53 +93,42 @@ static char *find_benchmark_spam(char *cp) { // liblog_benchmarks has been run designed to SPAM. The signature of - // a noisiest UID statistics is one of the following: + // a noisiest UID statistics is: // - // main: UID/PID Total size/num Now UID/PID[?] Total - // 0 7500306/304207 71608/3183 0/4225? 7454388/303656 - // <wrap> 93432/1012 - // -or- - // 0/gone 7454388/303656 93432/1012 + // Chattiest UIDs in main log buffer: Size Pruned + // UID PACKAGE BYTES LINES + // 0 root 54164 147569 // - // basically if we see a *large* number of 0/????? entries - unsigned long value; + char *benchmark = NULL; do { - char *benchmark = strstr(cp, " 0/"); - char *benchmark_newline = strstr(cp, "\n0/"); + static const char signature[] = "\n0 root "; + + benchmark = strstr(cp, signature); if (!benchmark) { - benchmark = benchmark_newline; - } - if (benchmark_newline && (benchmark > benchmark_newline)) { - benchmark = benchmark_newline; - } - cp = benchmark; - if (!cp) { break; } - cp += 3; - while (isdigit(*cp) || (*cp == 'g') || (*cp == 'o') || (*cp == 'n')) { + cp = benchmark + sizeof(signature); + while (isspace(*cp)) { ++cp; } - value = 0; - // ###? or gone - if ((*cp == '?') || (*cp == 'e')) { - while (*++cp == ' '); - while (isdigit(*cp)) { - value = value * 10ULL + *cp - '0'; - ++cp; - } - if (*cp != '/') { - value = 0; - continue; - } - while (isdigit(*++cp)); - while (*cp == ' ') ++cp; - if (!isdigit(*cp)) { - value = 0; - } + benchmark = cp; + while (isdigit(*cp)) { + ++cp; } - } while ((value < 900000ULL) && *cp); - return cp; + while (isspace(*cp)) { + ++cp; + } + unsigned long value = 0; + while (isdigit(*cp)) { + value = value * 10ULL + *cp - '0'; + ++cp; + } + if (value > 100000UL) { + break; + } + benchmark = NULL; + } while (*cp); + return benchmark; } TEST(logd, statistics) { @@ -179,16 +168,16 @@ EXPECT_EQ(0, truncated); #ifdef TARGET_USES_LOGD - char *main_logs = strstr(cp, "\nmain:"); + char *main_logs = strstr(cp, "\nChattiest UIDs in main "); EXPECT_TRUE(NULL != main_logs); - char *radio_logs = strstr(cp, "\nradio:"); + char *radio_logs = strstr(cp, "\nChattiest UIDs in radio "); EXPECT_TRUE(NULL != radio_logs); - char *system_logs = strstr(cp, "\nsystem:"); + char *system_logs = strstr(cp, "\nChattiest UIDs in system "); EXPECT_TRUE(NULL != system_logs); - char *events_logs = strstr(cp, "\nevents:"); + char *events_logs = strstr(cp, "\nChattiest UIDs in events "); EXPECT_TRUE(NULL != events_logs); #endif @@ -431,13 +420,13 @@ } #ifdef TARGET_USES_LOGD - EXPECT_GE(100000UL, ns[log_maximum_retry]); // 42777 user + EXPECT_GE(200000UL, ns[log_maximum_retry]); // 104734 user #else EXPECT_GE(10000UL, ns[log_maximum_retry]); // 5636 kernel #endif #ifdef TARGET_USES_LOGD - EXPECT_GE(30000UL, ns[log_maximum]); // 27305 user + EXPECT_GE(90000UL, ns[log_maximum]); // 46913 user #else EXPECT_GE(10000UL, ns[log_maximum]); // 5637 kernel #endif @@ -445,13 +434,13 @@ EXPECT_GE(4096UL, ns[clock_overhead]); // 4095 #ifdef TARGET_USES_LOGD - EXPECT_GE(250000UL, ns[log_overhead]); // 121876 user + EXPECT_GE(250000UL, ns[log_overhead]); // 126886 user #else EXPECT_GE(100000UL, ns[log_overhead]); // 50945 kernel #endif #ifdef TARGET_USES_LOGD - EXPECT_GE(7500UL, ns[log_latency]); // 3718 user space + EXPECT_GE(10000UL, ns[log_latency]); // 5669 user space #else EXPECT_GE(500000UL, ns[log_latency]); // 254200 kernel #endif @@ -483,8 +472,7 @@ ASSERT_TRUE(benchmark_statistics_found != NULL); // Check how effective the SPAM filter is, parse out Now size. - // Total Now - // 0/4225? 7454388/303656 31488/755 + // 0 root 54164 147569 // ^-- benchmark_statistics_found unsigned long nowSpamSize = atol(benchmark_statistics_found);
diff --git a/logwrapper/logwrap.c b/logwrapper/logwrap.c index 3a6276e..83576fb 100644 --- a/logwrapper/logwrap.c +++ b/logwrapper/logwrap.c
@@ -325,7 +325,7 @@ if (log_target & LOG_KLOG) { snprintf(log_info.klog_fmt, sizeof(log_info.klog_fmt), - "<6>%.*s: %%s", MAX_KLOG_TAG, log_info.btag); + "<6>%.*s: %%s\n", MAX_KLOG_TAG, log_info.btag); } if ((log_target & LOG_FILE) && !file_path) {
diff --git a/mkbootimg/bootimg.h b/mkbootimg/bootimg.h index 9171d85..5ab6195 100644 --- a/mkbootimg/bootimg.h +++ b/mkbootimg/bootimg.h
@@ -15,6 +15,8 @@ ** limitations under the License. */ +#include <stdint.h> + #ifndef _BOOT_IMAGE_H_ #define _BOOT_IMAGE_H_ @@ -28,31 +30,31 @@ struct boot_img_hdr { - unsigned char magic[BOOT_MAGIC_SIZE]; + uint8_t magic[BOOT_MAGIC_SIZE]; - unsigned kernel_size; /* size in bytes */ - unsigned kernel_addr; /* physical load addr */ + uint32_t kernel_size; /* size in bytes */ + uint32_t kernel_addr; /* physical load addr */ - unsigned ramdisk_size; /* size in bytes */ - unsigned ramdisk_addr; /* physical load addr */ + uint32_t ramdisk_size; /* size in bytes */ + uint32_t ramdisk_addr; /* physical load addr */ - unsigned second_size; /* size in bytes */ - unsigned second_addr; /* physical load addr */ + uint32_t second_size; /* size in bytes */ + uint32_t second_addr; /* physical load addr */ - unsigned tags_addr; /* physical addr for kernel tags */ - unsigned page_size; /* flash page size we assume */ - unsigned unused[2]; /* future expansion: should be 0 */ + uint32_t tags_addr; /* physical addr for kernel tags */ + uint32_t page_size; /* flash page size we assume */ + uint32_t unused[2]; /* future expansion: should be 0 */ - unsigned char name[BOOT_NAME_SIZE]; /* asciiz product name */ + uint8_t name[BOOT_NAME_SIZE]; /* asciiz product name */ - unsigned char cmdline[BOOT_ARGS_SIZE]; + uint8_t cmdline[BOOT_ARGS_SIZE]; - unsigned id[8]; /* timestamp / checksum / sha1 / etc */ + uint32_t id[8]; /* timestamp / checksum / sha1 / etc */ /* Supplemental command line data; kept here to maintain * binary compatibility with older versions of mkbootimg */ - unsigned char extra_cmdline[BOOT_EXTRA_ARGS_SIZE]; -}; + uint8_t extra_cmdline[BOOT_EXTRA_ARGS_SIZE]; +} __attribute__((packed)); /* ** +-----------------+
diff --git a/mkbootimg/mkbootimg.c b/mkbootimg/mkbootimg.c index fc92b4d..b6a2801 100644 --- a/mkbootimg/mkbootimg.c +++ b/mkbootimg/mkbootimg.c
@@ -21,6 +21,7 @@ #include <unistd.h> #include <fcntl.h> #include <errno.h> +#include <stdbool.h> #include "mincrypt/sha.h" #include "bootimg.h" @@ -59,12 +60,13 @@ { fprintf(stderr,"usage: mkbootimg\n" " --kernel <filename>\n" - " --ramdisk <filename>\n" + " [ --ramdisk <filename> ]\n" " [ --second <2ndbootloader-filename> ]\n" " [ --cmdline <kernel-commandline> ]\n" " [ --board <boardname> ]\n" " [ --base <address> ]\n" " [ --pagesize <pagesize> ]\n" + " [ --id ]\n" " -o|--output <filename>\n" ); return 1; @@ -74,6 +76,14 @@ static unsigned char padding[16384] = { 0, }; +static void print_id(const uint8_t *id, size_t id_len) { + printf("0x"); + for (unsigned i = 0; i < id_len; i++) { + printf("%02x", id[i]); + } + printf("\n"); +} + int write_padding(int fd, unsigned pagesize, unsigned itemsize) { unsigned pagemask = pagesize - 1; @@ -96,24 +106,24 @@ { boot_img_hdr hdr; - char *kernel_fn = 0; - void *kernel_data = 0; - char *ramdisk_fn = 0; - void *ramdisk_data = 0; - char *second_fn = 0; - void *second_data = 0; + char *kernel_fn = NULL; + void *kernel_data = NULL; + char *ramdisk_fn = NULL; + void *ramdisk_data = NULL; + char *second_fn = NULL; + void *second_data = NULL; char *cmdline = ""; - char *bootimg = 0; + char *bootimg = NULL; char *board = ""; - unsigned pagesize = 2048; + uint32_t pagesize = 2048; int fd; SHA_CTX ctx; const uint8_t* sha; - unsigned base = 0x10000000; - unsigned kernel_offset = 0x00008000; - unsigned ramdisk_offset = 0x01000000; - unsigned second_offset = 0x00f00000; - unsigned tags_offset = 0x00000100; + uint32_t base = 0x10000000U; + uint32_t kernel_offset = 0x00008000U; + uint32_t ramdisk_offset = 0x01000000U; + uint32_t second_offset = 0x00f00000U; + uint32_t tags_offset = 0x00000100U; size_t cmdlen; argc--; @@ -121,42 +131,48 @@ memset(&hdr, 0, sizeof(hdr)); + bool get_id = false; while(argc > 0){ char *arg = argv[0]; - char *val = argv[1]; - if(argc < 2) { - return usage(); - } - argc -= 2; - argv += 2; - if(!strcmp(arg, "--output") || !strcmp(arg, "-o")) { - bootimg = val; - } else if(!strcmp(arg, "--kernel")) { - kernel_fn = val; - } else if(!strcmp(arg, "--ramdisk")) { - ramdisk_fn = val; - } else if(!strcmp(arg, "--second")) { - second_fn = val; - } else if(!strcmp(arg, "--cmdline")) { - cmdline = val; - } else if(!strcmp(arg, "--base")) { - base = strtoul(val, 0, 16); - } else if(!strcmp(arg, "--kernel_offset")) { - kernel_offset = strtoul(val, 0, 16); - } else if(!strcmp(arg, "--ramdisk_offset")) { - ramdisk_offset = strtoul(val, 0, 16); - } else if(!strcmp(arg, "--second_offset")) { - second_offset = strtoul(val, 0, 16); - } else if(!strcmp(arg, "--tags_offset")) { - tags_offset = strtoul(val, 0, 16); - } else if(!strcmp(arg, "--board")) { - board = val; - } else if(!strcmp(arg,"--pagesize")) { - pagesize = strtoul(val, 0, 10); - if ((pagesize != 2048) && (pagesize != 4096) - && (pagesize != 8192) && (pagesize != 16384)) { - fprintf(stderr,"error: unsupported page size %d\n", pagesize); - return -1; + if (!strcmp(arg, "--id")) { + get_id = true; + argc -= 1; + argv += 1; + } else if(argc >= 2) { + char *val = argv[1]; + argc -= 2; + argv += 2; + if(!strcmp(arg, "--output") || !strcmp(arg, "-o")) { + bootimg = val; + } else if(!strcmp(arg, "--kernel")) { + kernel_fn = val; + } else if(!strcmp(arg, "--ramdisk")) { + ramdisk_fn = val; + } else if(!strcmp(arg, "--second")) { + second_fn = val; + } else if(!strcmp(arg, "--cmdline")) { + cmdline = val; + } else if(!strcmp(arg, "--base")) { + base = strtoul(val, 0, 16); + } else if(!strcmp(arg, "--kernel_offset")) { + kernel_offset = strtoul(val, 0, 16); + } else if(!strcmp(arg, "--ramdisk_offset")) { + ramdisk_offset = strtoul(val, 0, 16); + } else if(!strcmp(arg, "--second_offset")) { + second_offset = strtoul(val, 0, 16); + } else if(!strcmp(arg, "--tags_offset")) { + tags_offset = strtoul(val, 0, 16); + } else if(!strcmp(arg, "--board")) { + board = val; + } else if(!strcmp(arg,"--pagesize")) { + pagesize = strtoul(val, 0, 10); + if ((pagesize != 2048) && (pagesize != 4096) + && (pagesize != 8192) && (pagesize != 16384)) { + fprintf(stderr,"error: unsupported page size %d\n", pagesize); + return -1; + } + } else { + return usage(); } } else { return usage(); @@ -179,11 +195,6 @@ return usage(); } - if(ramdisk_fn == 0) { - fprintf(stderr,"error: no ramdisk image specified\n"); - return usage(); - } - if(strlen(board) >= BOOT_NAME_SIZE) { fprintf(stderr,"error: board name too large\n"); return usage(); @@ -213,7 +224,7 @@ return 1; } - if(!strcmp(ramdisk_fn,"NONE")) { + if(ramdisk_fn == 0) { ramdisk_data = 0; hdr.ramdisk_size = 0; } else { @@ -266,6 +277,10 @@ if(write_padding(fd, pagesize, hdr.second_size)) goto fail; } + if (get_id) { + print_id((uint8_t *) hdr.id, sizeof(hdr.id)); + } + return 0; fail:
diff --git a/rootdir/Android.mk b/rootdir/Android.mk index 3ecb1db..7ab76b8 100644 --- a/rootdir/Android.mk +++ b/rootdir/Android.mk
@@ -26,7 +26,7 @@ # # create some directories (some are mount points) LOCAL_POST_INSTALL_CMD := mkdir -p $(addprefix $(TARGET_ROOT_OUT)/, \ - sbin dev proc sys system data) + sbin dev proc sys system data oem) include $(BUILD_SYSTEM)/base_rules.mk
diff --git a/rootdir/init.environ.rc.in b/rootdir/init.environ.rc.in index 0064790..b34ea01 100644 --- a/rootdir/init.environ.rc.in +++ b/rootdir/init.environ.rc.in
@@ -5,7 +5,7 @@ export ANDROID_ASSETS /system/app export ANDROID_DATA /data export ANDROID_STORAGE /storage + export EXTERNAL_STORAGE /sdcard export ASEC_MOUNTPOINT /mnt/asec - export LOOP_MOUNTPOINT /mnt/obb export BOOTCLASSPATH %BOOTCLASSPATH% export SYSTEMSERVERCLASSPATH %SYSTEMSERVERCLASSPATH%
diff --git a/rootdir/init.rc b/rootdir/init.rc index c00c590..b71908c 100644 --- a/rootdir/init.rc +++ b/rootdir/init.rc
@@ -19,9 +19,6 @@ start ueventd - # create mountpoints - mkdir /mnt 0775 root system - on init sysclktz 0 @@ -55,28 +52,34 @@ mkdir /cache 0770 system cache mkdir /config 0500 root root + # Mount staging areas for devices managed by vold # See storage config details at http://source.android.com/tech/storage/ - mkdir /mnt/shell 0700 shell shell - mkdir /mnt/media_rw 0700 media_rw media_rw - mkdir /storage 0751 root sdcard_r + mkdir /mnt 0755 root system + mount tmpfs tmpfs /mnt mode=0755,uid=0,gid=1000 + restorecon_recursive /mnt - # Directory for putting things only root should see. mkdir /mnt/secure 0700 root root + mkdir /mnt/secure/asec 0700 root root + mkdir /mnt/asec 0755 root system + mkdir /mnt/obb 0755 root system + mkdir /mnt/media_rw 0750 root media_rw + mkdir /mnt/user 0755 root root + mkdir /mnt/user/0 0755 root root + mkdir /mnt/expand 0771 system system - # Directory for staging bindmounts - mkdir /mnt/secure/staging 0700 root root + # Storage views to support runtime permissions + mkdir /storage 0755 root root + mkdir /mnt/runtime 0700 root root + mkdir /mnt/runtime/default 0755 root root + mkdir /mnt/runtime/default/self 0755 root root + mkdir /mnt/runtime/read 0755 root root + mkdir /mnt/runtime/read/self 0755 root root + mkdir /mnt/runtime/write 0755 root root + mkdir /mnt/runtime/write/self 0755 root root - # Directory-target for where the secure container - # imagefile directory will be bind-mounted - mkdir /mnt/secure/asec 0700 root root - - # Secure container public mount points. - mkdir /mnt/asec 0700 root system - mount tmpfs tmpfs /mnt/asec mode=0755,gid=1000 - - # Filesystem image public mount points. - mkdir /mnt/obb 0700 root system - mount tmpfs tmpfs /mnt/obb mode=0755,gid=1000 + # Symlink to keep legacy apps working in multi-user world + symlink /storage/self/primary /sdcard + symlink /mnt/user/0/primary /mnt/runtime/default/self/primary # memory control cgroup mkdir /dev/memcg 0700 root system @@ -104,6 +107,10 @@ # set fwmark on accepted sockets write /proc/sys/net/ipv4/tcp_fwmark_accept 1 + # disable icmp redirects + write /proc/sys/net/ipv4/conf/all/accept_redirects 0 + write /proc/sys/net/ipv6/conf/all/accept_redirects 0 + # Create cgroup mount points for process groups mkdir /dev/cpuctl mount cgroup none /dev/cpuctl cpu @@ -122,6 +129,28 @@ write /dev/cpuctl/bg_non_interactive/cpu.rt_runtime_us 700000 write /dev/cpuctl/bg_non_interactive/cpu.rt_period_us 1000000 + # sets up initial cpusets for ActivityManager + mkdir /dev/cpuset + mount cpuset none /dev/cpuset + mkdir /dev/cpuset/foreground + mkdir /dev/cpuset/background + # this ensures that the cpusets are present and usable, but the device's + # init.rc must actually set the correct cpus + write /dev/cpuset/foreground/cpus 0 + write /dev/cpuset/background/cpus 0 + write /dev/cpuset/foreground/mems 0 + write /dev/cpuset/background/mems 0 + chown system system /dev/cpuset + chown system system /dev/cpuset/foreground + chown system system /dev/cpuset/background + chown system system /dev/cpuset/tasks + chown system system /dev/cpuset/foreground/tasks + chown system system /dev/cpuset/background/tasks + chmod 0664 /dev/cpuset/foreground/tasks + chmod 0664 /dev/cpuset/background/tasks + chmod 0664 /dev/cpuset/tasks + + # qtaguid will limit access to specific data based on group memberships. # net_bw_acct grants impersonation of socket owners. # net_bw_stats grants access to other apps' detailed tagged-socket stats. @@ -185,8 +214,10 @@ start logd # once everything is setup, no need to modify / mount rootfs rootfs / ro remount - # mount shared so changes propagate into child namespaces + # Mount shared so changes propagate into child namespaces mount rootfs rootfs / shared rec + # Mount default storage into root namespace + mount none /mnt/runtime/default /storage slave bind rec # We chown/chmod /cache again so because mount is run as root + defaults chown system cache /cache @@ -194,9 +225,9 @@ # We restorecon /cache in case the cache partition has been reset. restorecon_recursive /cache - # This may have been created by the recovery system with odd permissions - chown system cache /cache/recovery - chmod 0770 /cache/recovery + # Create /cache/recovery in case it's not there. It'll also fix the odd + # permissions if created by the recovery system. + mkdir /cache/recovery 0770 system cache #change permissions on vmallocinfo so we can grab it from bugreports chown root log /proc/vmallocinfo @@ -220,14 +251,20 @@ mkdir /cache/lost+found 0770 root root on post-fs-data - installkey /data - # We chown/chmod /data again so because mount is run as root + defaults chown system system /data chmod 0771 /data # We restorecon /data in case the userdata partition has been reset. restorecon /data + # Emulated internal storage area + mkdir /data/media 0770 media_rw media_rw + + # Make sure we have the device encryption key + start logd + start vold + installkey /data + # Start bootcharting as soon as possible after the data partition is # mounted to collect more data. mkdir /data/bootchart 0755 shell shell @@ -239,7 +276,10 @@ # create basic filesystem structure mkdir /data/misc 01771 system misc mkdir /data/misc/adb 02750 system shell - mkdir /data/misc/bluedroid 0770 bluetooth net_bt_stack + mkdir /data/misc/bluedroid 02770 bluetooth net_bt_stack + # Fix the access permissions and group ownership for 'bt_config.conf' + chmod 0660 /data/misc/bluedroid/bt_config.conf + chown bluetooth net_bt_stack /data/misc/bluedroid/bt_config.conf mkdir /data/misc/bluetooth 0770 system system mkdir /data/misc/keystore 0700 keystore keystore mkdir /data/misc/gatekeeper 0700 system system @@ -257,10 +297,12 @@ mkdir /data/misc/ethernet 0770 system system mkdir /data/misc/dhcp 0770 dhcp dhcp mkdir /data/misc/user 0771 root root + mkdir /data/misc/perfprofd 0775 root root # give system access to wpa_supplicant.conf for backup and restore chmod 0660 /data/misc/wifi/wpa_supplicant.conf mkdir /data/local 0751 root root mkdir /data/misc/media 0700 media media + mkdir /data/misc/vold 0700 root root # For security reasons, /data/local/tmp should always be empty. # Do not place files or directories in /data/local/tmp @@ -317,7 +359,7 @@ restorecon_recursive /data # Check any timezone data in /data is newer than the copy in /system, delete if not. - exec u:r:tzdatacheck:s0 system system -- /system/bin/tzdatacheck /system/usr/share/zoneinfo /data/misc/zoneinfo + exec - system system -- /system/bin/tzdatacheck /system/usr/share/zoneinfo /data/misc/zoneinfo # If there is no fs-post-data action in the init.<device>.rc file, you # must uncomment this line, otherwise encrypted filesystems @@ -340,9 +382,9 @@ write /proc/sys/vm/overcommit_memory 1 write /proc/sys/vm/min_free_order_shift 4 chown root system /sys/module/lowmemorykiller/parameters/adj - chmod 0220 /sys/module/lowmemorykiller/parameters/adj + chmod 0664 /sys/module/lowmemorykiller/parameters/adj chown root system /sys/module/lowmemorykiller/parameters/minfree - chmod 0220 /sys/module/lowmemorykiller/parameters/minfree + chmod 0664 /sys/module/lowmemorykiller/parameters/minfree # Tweak background writeout write /proc/sys/vm/dirty_expire_centisecs 200 @@ -449,7 +491,6 @@ class_start main on property:vold.decrypt=trigger_restart_framework - installkey /data class_start main class_start late_start @@ -483,6 +524,7 @@ socket logd stream 0666 logd logd socket logdr seqpacket 0666 logd logd socket logdw dgram 0222 logd logd + group root system service logd-reinit /system/bin/logd --reinit oneshot @@ -492,6 +534,7 @@ class core critical seclabel u:r:healthd:s0 + group root system service console /system/bin/sh class core @@ -531,9 +574,12 @@ onrestart restart surfaceflinger onrestart restart drm -service vold /system/bin/vold +service vold /system/bin/vold \ + --blkid_context=u:r:blkid:s0 --blkid_untrusted_context=u:r:blkid_untrusted:s0 \ + --fsck_context=u:r:fsck:s0 --fsck_untrusted_context=u:r:fsck_untrusted:s0 class core socket vold stream 0660 root mount + socket cryptd stream 0660 root mount ioprio be 2 service netd /system/bin/netd @@ -552,6 +598,7 @@ service ril-daemon /system/bin/rild class main socket rild stream 660 root radio + socket sap_uim_socket1 stream 660 bluetooth bluetooth socket rild-debug stream 660 radio system user root group radio cache inet misc audio log @@ -594,6 +641,10 @@ disabled oneshot +service gatekeeperd /system/bin/gatekeeperd /data/misc/gatekeeper + class late_start + user system + service installd /system/bin/installd class main socket installd stream 600 system system @@ -637,7 +688,31 @@ disabled oneshot -service pre-recovery /system/bin/uncrypt +service uncrypt /system/bin/uncrypt class main disabled oneshot + +service pre-recovery /system/bin/uncrypt --reboot + class main + disabled + oneshot + +service perfprofd /system/xbin/perfprofd + class late_start + user root + oneshot + +on property:persist.logd.logpersistd=logcatd + # all exec/services are called with umask(077), so no gain beyond 0700 + mkdir /data/misc/logd 0700 logd log + # logd for write to /data/misc/logd, log group for read from pstore (-L) + exec - logd log -- /system/bin/logcat -L -b all -v threadtime -v usec -v printable -D -f /data/misc/logd/logcat -r 64 -n 256 + start logcatd + +service logcatd /system/bin/logcat -b all -v threadtime -v usec -v printable -D -f /data/misc/logd/logcat -r 64 -n 256 + class late_start + disabled + # logd for write to /data/misc/logd, log group for read from log daemon + user logd + group log
diff --git a/rootdir/init.trace.rc b/rootdir/init.trace.rc index cd8d350..50944e6 100644 --- a/rootdir/init.trace.rc +++ b/rootdir/init.trace.rc
@@ -1,6 +1,6 @@ ## Permissions to allow system-wide tracing to the kernel trace buffer. ## -on early-boot +on boot # Allow writing to the kernel trace log. chmod 0222 /sys/kernel/debug/tracing/trace_marker
diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc index 9cf9ed9..b735dc3 100644 --- a/rootdir/ueventd.rc +++ b/rootdir/ueventd.rc
@@ -95,3 +95,6 @@ /sys/devices/virtual/usb_composite/* enable 0664 root system /sys/devices/system/cpu/cpu* cpufreq/scaling_max_freq 0664 system system /sys/devices/system/cpu/cpu* cpufreq/scaling_min_freq 0664 system system + +# DVB API device nodes +/dev/dvb* 0660 root system
diff --git a/run-as/package.c b/run-as/package.c index 9e1f5bb..aea89e5 100644 --- a/run-as/package.c +++ b/run-as/package.c
@@ -16,6 +16,7 @@ */ #include <errno.h> #include <fcntl.h> +#include <stdio.h> #include <string.h> #include <sys/mman.h> #include <sys/stat.h> @@ -421,7 +422,7 @@ * If the package database is corrupted, return -1 and set errno to EINVAL */ int -get_package_info(const char* pkgName, PackageInfo *info) +get_package_info(const char* pkgName, uid_t userId, PackageInfo *info) { char* buffer; size_t buffer_len; @@ -506,7 +507,20 @@ if (q == p) goto BAD_FORMAT; - p = string_copy(info->dataDir, sizeof info->dataDir, p, q - p); + /* If userId == 0 (i.e. user is device owner) we can use dataDir value + * from packages.list, otherwise compose data directory as + * /data/user/$uid/$packageId + */ + if (userId == 0) { + p = string_copy(info->dataDir, sizeof info->dataDir, p, q - p); + } else { + snprintf(info->dataDir, + sizeof info->dataDir, + "/data/user/%d/%s", + userId, + pkgName); + p = q; + } /* skip spaces */ if (parse_spaces(&p, end) < 0)
diff --git a/run-as/package.h b/run-as/package.h index 34603c0..eeb5913 100644 --- a/run-as/package.h +++ b/run-as/package.h
@@ -33,9 +33,11 @@ char seinfo[PATH_MAX]; } PackageInfo; -/* see documentation in package.c for these functiosn */ +/* see documentation in package.c for these functions */ -extern int get_package_info(const char* packageName, PackageInfo* info); +extern int get_package_info(const char* packageName, + uid_t userId, + PackageInfo* info); extern int check_data_path(const char* dataDir, uid_t uid);
diff --git a/run-as/run-as.c b/run-as/run-as.c index 368b8f1..3f32e7d 100644 --- a/run-as/run-as.c +++ b/run-as/run-as.c
@@ -102,13 +102,14 @@ static void usage(void) { - panic("Usage:\n " PROGNAME " <package-name> <command> [<args>]\n"); + panic("Usage:\n " PROGNAME " <package-name> [--user <uid>] <command> [<args>]\n"); } int main(int argc, char **argv) { const char* pkgname; - int myuid, uid, gid; + uid_t myuid, uid, gid, userAppId = 0; + int commandArgvOfs = 2, userId = 0; PackageInfo info; struct __user_cap_header_struct capheader; struct __user_cap_data_struct capdata[2]; @@ -136,14 +137,31 @@ panic("Could not set capabilities: %s\n", strerror(errno)); } - /* retrieve package information from system (does setegid) */ pkgname = argv[1]; - if (get_package_info(pkgname, &info) < 0) { + + /* get user_id from command line if provided */ + if ((argc >= 4) && !strcmp(argv[2], "--user")) { + userId = atoi(argv[3]); + if (userId < 0) + panic("Negative user id %d is provided\n", userId); + commandArgvOfs += 2; + } + + /* retrieve package information from system (does setegid) */ + if (get_package_info(pkgname, userId, &info) < 0) { panic("Package '%s' is unknown\n", pkgname); } + /* verify that user id is not too big. */ + if ((UID_MAX - info.uid) / AID_USER < (uid_t)userId) { + panic("User id %d is too big\n", userId); + } + + /* calculate user app ID. */ + userAppId = (AID_USER * userId) + info.uid; + /* reject system packages */ - if (info.uid < AID_APP) { + if (userAppId < AID_APP) { panic("Package '%s' is not an application\n", pkgname); } @@ -153,14 +171,14 @@ } /* check that the data directory path is valid */ - if (check_data_path(info.dataDir, info.uid) < 0) { + if (check_data_path(info.dataDir, userAppId) < 0) { panic("Package '%s' has corrupt installation\n", pkgname); } /* Ensure that we change all real/effective/saved IDs at the * same time to avoid nasty surprises. */ - uid = gid = info.uid; + uid = gid = userAppId; if(setresgid(gid,gid,gid) || setresuid(uid,uid,uid)) { panic("Permission denied\n"); } @@ -181,8 +199,9 @@ } /* User specified command for exec. */ - if ((argc >= 3) && (execvp(argv[2], argv+2) < 0)) { - panic("exec failed for %s: %s\n", argv[2], strerror(errno)); + if ((argc >= commandArgvOfs + 1) && + (execvp(argv[commandArgvOfs], argv+commandArgvOfs) < 0)) { + panic("exec failed for %s: %s\n", argv[commandArgvOfs], strerror(errno)); } /* Default exec shell. */
diff --git a/sdcard/sdcard.c b/sdcard/sdcard.c index 893c0dc..a79e2dd 100644 --- a/sdcard/sdcard.c +++ b/sdcard/sdcard.c
@@ -74,22 +74,6 @@ * requiring any additional GIDs. * - Separate permissions for protecting directories like Pictures and Music. * - Multi-user separation on the same physical device. - * - * The derived permissions look like this: - * - * rwxrwx--x root:sdcard_rw / - * rwxrwx--- root:sdcard_pics /Pictures - * rwxrwx--- root:sdcard_av /Music - * - * rwxrwx--x root:sdcard_rw /Android - * rwxrwx--x root:sdcard_rw /Android/data - * rwxrwx--- u0_a12:sdcard_rw /Android/data/com.example - * rwxrwx--x root:sdcard_rw /Android/obb/ - * rwxrwx--- u0_a12:sdcard_rw /Android/obb/com.example - * - * rwxrwx--- root:sdcard_all /Android/user - * rwxrwx--x root:sdcard_rw /Android/user/10 - * rwxrwx--- u10_a12:sdcard_rw /Android/user/10/Android/data/com.example */ #define FUSE_TRACE 0 @@ -115,9 +99,6 @@ * the largest possible data payload. */ #define MAX_REQUEST_SIZE (sizeof(struct fuse_in_header) + sizeof(struct fuse_write_in) + MAX_WRITE) -/* Default number of threads. */ -#define DEFAULT_NUM_THREADS 2 - /* Pseudo-error constant used to indicate that no fuse status is needed * or that a reply has already been written. */ #define NO_STATUS 1 @@ -135,7 +116,7 @@ PERM_INHERIT, /* This node is one level above a normal root; used for legacy layouts * which use the first level to represent user_id. */ - PERM_LEGACY_PRE_ROOT, + PERM_PRE_ROOT, /* This node is "/" */ PERM_ROOT, /* This node is "/Android" */ @@ -146,17 +127,8 @@ PERM_ANDROID_OBB, /* This node is "/Android/media" */ PERM_ANDROID_MEDIA, - /* This node is "/Android/user" */ - PERM_ANDROID_USER, } perm_t; -/* Permissions structure to derive */ -typedef enum { - DERIVE_NONE, - DERIVE_LEGACY, - DERIVE_UNIFIED, -} derive_t; - struct handle { int fd; }; @@ -179,8 +151,7 @@ perm_t perm; userid_t userid; uid_t uid; - gid_t gid; - mode_t mode; + bool under_android; struct node *next; /* per-dir sibling list */ struct node *child; /* first contained file by this dir */ @@ -212,25 +183,21 @@ return strcasecmp(keyA, keyB) == 0; } -static int int_hash(void *key) { - return (int) (uintptr_t) key; -} - -static bool int_equals(void *keyA, void *keyB) { - return keyA == keyB; -} - -/* Global data structure shared by all fuse handlers. */ -struct fuse { +/* Global data for all FUSE mounts */ +struct fuse_global { pthread_mutex_t lock; + uid_t uid; + gid_t gid; + bool multi_user; + + char source_path[PATH_MAX]; + char obb_path[PATH_MAX]; + + Hashmap* package_to_appid; + __u64 next_generation; - int fd; - derive_t derive; - bool split_perms; - gid_t write_gid; struct node root; - char obbpath[PATH_MAX]; /* Used to allocate unique inode numbers for fuse nodes. We use * a simple counter based scheme where inode numbers from deleted @@ -251,11 +218,24 @@ */ __u32 inode_ctr; - Hashmap* package_to_appid; - Hashmap* appid_with_rw; + struct fuse* fuse_default; + struct fuse* fuse_read; + struct fuse* fuse_write; }; -/* Private data used by a single fuse handler. */ +/* Single FUSE mount */ +struct fuse { + struct fuse_global* global; + + char dest_path[PATH_MAX]; + + int fd; + + gid_t gid; + mode_t mask; +}; + +/* Private data used by a single FUSE handler */ struct fuse_handler { struct fuse* fuse; int token; @@ -412,8 +392,8 @@ return actual; } -static void attr_from_stat(struct fuse_attr *attr, const struct stat *s, const struct node* node) -{ +static void attr_from_stat(struct fuse* fuse, struct fuse_attr *attr, + const struct stat *s, const struct node* node) { attr->ino = node->ino; attr->size = s->st_size; attr->blocks = s->st_blocks; @@ -427,12 +407,35 @@ attr->nlink = s->st_nlink; attr->uid = node->uid; - attr->gid = node->gid; - /* Filter requested mode based on underlying file, and - * pass through file type. */ + if (fuse->gid == AID_SDCARD_RW) { + /* As an optimization, certain trusted system components only run + * as owner but operate across all users. Since we're now handing + * out the sdcard_rw GID only to trusted apps, we're okay relaxing + * the user boundary enforcement for the default view. The UIDs + * assigned to app directories are still multiuser aware. */ + attr->gid = AID_SDCARD_RW; + } else { + attr->gid = multiuser_get_uid(node->userid, fuse->gid); + } + + int visible_mode = 0775 & ~fuse->mask; + if (node->perm == PERM_PRE_ROOT) { + /* Top of multi-user view should always be visible to ensure + * secondary users can traverse inside. */ + visible_mode = 0711; + } else if (node->under_android) { + /* Block "other" access to Android directories, since only apps + * belonging to a specific user should be in there; we still + * leave +x open for the default view. */ + if (fuse->gid == AID_SDCARD_RW) { + visible_mode = visible_mode & ~0006; + } else { + visible_mode = visible_mode & ~0007; + } + } int owner_mode = s->st_mode & 0700; - int filtered_mode = node->mode & (owner_mode | (owner_mode >> 3) | (owner_mode >> 6)); + int filtered_mode = visible_mode & (owner_mode | (owner_mode >> 3) | (owner_mode >> 6)); attr->mode = (attr->mode & S_IFMT) | filtered_mode; } @@ -458,105 +461,58 @@ node->perm = PERM_INHERIT; node->userid = parent->userid; node->uid = parent->uid; - node->gid = parent->gid; - node->mode = parent->mode; - - if (fuse->derive == DERIVE_NONE) { - return; - } + node->under_android = parent->under_android; /* Derive custom permissions based on parent and current node */ switch (parent->perm) { case PERM_INHERIT: /* Already inherited above */ break; - case PERM_LEGACY_PRE_ROOT: + case PERM_PRE_ROOT: /* Legacy internal layout places users at top level */ node->perm = PERM_ROOT; node->userid = strtoul(node->name, NULL, 10); break; case PERM_ROOT: /* Assume masked off by default. */ - node->mode = 0770; if (!strcasecmp(node->name, "Android")) { /* App-specific directories inside; let anyone traverse */ node->perm = PERM_ANDROID; - node->mode = 0771; - } else if (fuse->split_perms) { - if (!strcasecmp(node->name, "DCIM") - || !strcasecmp(node->name, "Pictures")) { - node->gid = AID_SDCARD_PICS; - } else if (!strcasecmp(node->name, "Alarms") - || !strcasecmp(node->name, "Movies") - || !strcasecmp(node->name, "Music") - || !strcasecmp(node->name, "Notifications") - || !strcasecmp(node->name, "Podcasts") - || !strcasecmp(node->name, "Ringtones")) { - node->gid = AID_SDCARD_AV; - } + node->under_android = true; } break; case PERM_ANDROID: if (!strcasecmp(node->name, "data")) { /* App-specific directories inside; let anyone traverse */ node->perm = PERM_ANDROID_DATA; - node->mode = 0771; } else if (!strcasecmp(node->name, "obb")) { /* App-specific directories inside; let anyone traverse */ node->perm = PERM_ANDROID_OBB; - node->mode = 0771; /* Single OBB directory is always shared */ - node->graft_path = fuse->obbpath; - node->graft_pathlen = strlen(fuse->obbpath); + node->graft_path = fuse->global->obb_path; + node->graft_pathlen = strlen(fuse->global->obb_path); } else if (!strcasecmp(node->name, "media")) { /* App-specific directories inside; let anyone traverse */ node->perm = PERM_ANDROID_MEDIA; - node->mode = 0771; - } else if (!strcasecmp(node->name, "user")) { - /* User directories must only be accessible to system, protected - * by sdcard_all. Zygote will bind mount the appropriate user- - * specific path. */ - node->perm = PERM_ANDROID_USER; - node->gid = AID_SDCARD_ALL; - node->mode = 0770; } break; case PERM_ANDROID_DATA: case PERM_ANDROID_OBB: case PERM_ANDROID_MEDIA: - appid = (appid_t) (uintptr_t) hashmapGet(fuse->package_to_appid, node->name); + appid = (appid_t) (uintptr_t) hashmapGet(fuse->global->package_to_appid, node->name); if (appid != 0) { node->uid = multiuser_get_uid(parent->userid, appid); } - node->mode = 0770; - break; - case PERM_ANDROID_USER: - /* Root of a secondary user */ - node->perm = PERM_ROOT; - node->userid = strtoul(node->name, NULL, 10); - node->gid = AID_SDCARD_R; - node->mode = 0771; break; } } -/* Return if the calling UID holds sdcard_rw. */ -static bool get_caller_has_rw_locked(struct fuse* fuse, const struct fuse_in_header *hdr) { - /* No additional permissions enforcement */ - if (fuse->derive == DERIVE_NONE) { - return true; - } - - appid_t appid = multiuser_get_app_id(hdr->uid); - return hashmapContainsKey(fuse->appid_with_rw, (void*) (uintptr_t) appid); -} - /* Kernel has already enforced everything we returned through * derive_permissions_locked(), so this is used to lock down access * even further, such as enforcing that apps hold sdcard_rw. */ static bool check_caller_access_to_name(struct fuse* fuse, const struct fuse_in_header *hdr, const struct node* parent_node, - const char* name, int mode, bool has_rw) { + const char* name, int mode) { /* Always block security-sensitive files at root */ if (parent_node && parent_node->perm == PERM_ROOT) { if (!strcasecmp(name, "autorun.inf") @@ -566,34 +522,19 @@ } } - /* No additional permissions enforcement */ - if (fuse->derive == DERIVE_NONE) { - return true; - } - /* Root always has access; access for any other UIDs should always * be controlled through packages.list. */ if (hdr->uid == 0) { return true; } - /* If asking to write, verify that caller either owns the - * parent or holds sdcard_rw. */ - if (mode & W_OK) { - if (parent_node && hdr->uid == parent_node->uid) { - return true; - } - - return has_rw; - } - /* No extra permissions to enforce */ return true; } static bool check_caller_access_to_node(struct fuse* fuse, - const struct fuse_in_header *hdr, const struct node* node, int mode, bool has_rw) { - return check_caller_access_to_name(fuse, hdr, node->parent, node->name, mode, has_rw); + const struct fuse_in_header *hdr, const struct node* node, int mode) { + return check_caller_access_to_name(fuse, hdr, node->parent, node->name, mode); } struct node *create_node_locked(struct fuse* fuse, @@ -604,7 +545,7 @@ // Detect overflows in the inode counter. "4 billion nodes should be enough // for everybody". - if (fuse->inode_ctr == 0) { + if (fuse->global->inode_ctr == 0) { ERROR("No more inode numbers available"); return NULL; } @@ -630,8 +571,8 @@ } node->namelen = namelen; node->nid = ptr_to_id(node); - node->ino = fuse->inode_ctr++; - node->gen = fuse->next_generation++; + node->ino = fuse->global->inode_ctr++; + node->gen = fuse->global->next_generation++; node->deleted = false; @@ -685,7 +626,7 @@ static struct node *lookup_node_by_id_locked(struct fuse *fuse, __u64 nid) { if (nid == FUSE_ROOT_ID) { - return &fuse->root; + return &fuse->global->root; } else { return id_to_ptr(nid); } @@ -728,59 +669,6 @@ return child; } -static void fuse_init(struct fuse *fuse, int fd, const char *source_path, - gid_t write_gid, derive_t derive, bool split_perms) { - pthread_mutex_init(&fuse->lock, NULL); - - fuse->fd = fd; - fuse->next_generation = 0; - fuse->derive = derive; - fuse->split_perms = split_perms; - fuse->write_gid = write_gid; - fuse->inode_ctr = 1; - - memset(&fuse->root, 0, sizeof(fuse->root)); - fuse->root.nid = FUSE_ROOT_ID; /* 1 */ - fuse->root.refcount = 2; - fuse->root.namelen = strlen(source_path); - fuse->root.name = strdup(source_path); - fuse->root.userid = 0; - fuse->root.uid = AID_ROOT; - - /* Set up root node for various modes of operation */ - switch (derive) { - case DERIVE_NONE: - /* Traditional behavior that treats entire device as being accessible - * to sdcard_rw, and no permissions are derived. */ - fuse->root.perm = PERM_ROOT; - fuse->root.mode = 0775; - fuse->root.gid = AID_SDCARD_RW; - break; - case DERIVE_LEGACY: - /* Legacy behavior used to support internal multiuser layout which - * places user_id at the top directory level, with the actual roots - * just below that. Shared OBB path is also at top level. */ - fuse->root.perm = PERM_LEGACY_PRE_ROOT; - fuse->root.mode = 0771; - fuse->root.gid = AID_SDCARD_R; - fuse->package_to_appid = hashmapCreate(256, str_hash, str_icase_equals); - fuse->appid_with_rw = hashmapCreate(128, int_hash, int_equals); - snprintf(fuse->obbpath, sizeof(fuse->obbpath), "%s/obb", source_path); - fs_prepare_dir(fuse->obbpath, 0775, getuid(), getgid()); - break; - case DERIVE_UNIFIED: - /* Unified multiuser layout which places secondary user_id under - * /Android/user and shared OBB path under /Android/obb. */ - fuse->root.perm = PERM_ROOT; - fuse->root.mode = 0771; - fuse->root.gid = AID_SDCARD_R; - fuse->package_to_appid = hashmapCreate(256, str_hash, str_icase_equals); - fuse->appid_with_rw = hashmapCreate(128, int_hash, int_equals); - snprintf(fuse->obbpath, sizeof(fuse->obbpath), "%s/Android/obb", source_path); - break; - } -} - static void fuse_status(struct fuse *fuse, __u64 unique, int err) { struct fuse_out_header hdr; @@ -823,19 +711,19 @@ return -errno; } - pthread_mutex_lock(&fuse->lock); + pthread_mutex_lock(&fuse->global->lock); node = acquire_or_create_child_locked(fuse, parent, name, actual_name); if (!node) { - pthread_mutex_unlock(&fuse->lock); + pthread_mutex_unlock(&fuse->global->lock); return -ENOMEM; } memset(&out, 0, sizeof(out)); - attr_from_stat(&out.attr, &s, node); + attr_from_stat(fuse, &out.attr, &s, node); out.attr_valid = 10; out.entry_valid = 10; out.nodeid = node->nid; out.generation = node->gen; - pthread_mutex_unlock(&fuse->lock); + pthread_mutex_unlock(&fuse->global->lock); fuse_reply(fuse, unique, &out, sizeof(out)); return NO_STATUS; } @@ -850,12 +738,43 @@ return -errno; } memset(&out, 0, sizeof(out)); - attr_from_stat(&out.attr, &s, node); + attr_from_stat(fuse, &out.attr, &s, node); out.attr_valid = 10; fuse_reply(fuse, unique, &out, sizeof(out)); return NO_STATUS; } +static void fuse_notify_delete(struct fuse* fuse, const __u64 parent, + const __u64 child, const char* name) { + struct fuse_out_header hdr; + struct fuse_notify_delete_out data; + struct iovec vec[3]; + size_t namelen = strlen(name); + int res; + + hdr.len = sizeof(hdr) + sizeof(data) + namelen + 1; + hdr.error = FUSE_NOTIFY_DELETE; + hdr.unique = 0; + + data.parent = parent; + data.child = child; + data.namelen = namelen; + data.padding = 0; + + vec[0].iov_base = &hdr; + vec[0].iov_len = sizeof(hdr); + vec[1].iov_base = &data; + vec[1].iov_len = sizeof(data); + vec[2].iov_base = (void*) name; + vec[2].iov_len = namelen + 1; + + res = writev(fuse->fd, vec, 3); + /* Ignore ENOENT, since other views may not have seen the entry */ + if (res < 0 && errno != ENOENT) { + ERROR("*** NOTIFY FAILED *** %d\n", errno); + } +} + static int handle_lookup(struct fuse* fuse, struct fuse_handler* handler, const struct fuse_in_header *hdr, const char* name) { @@ -864,18 +783,18 @@ char child_path[PATH_MAX]; const char* actual_name; - pthread_mutex_lock(&fuse->lock); + pthread_mutex_lock(&fuse->global->lock); parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, parent_path, sizeof(parent_path)); TRACE("[%d] LOOKUP %s @ %"PRIx64" (%s)\n", handler->token, name, hdr->nodeid, parent_node ? parent_node->name : "?"); - pthread_mutex_unlock(&fuse->lock); + pthread_mutex_unlock(&fuse->global->lock); if (!parent_node || !(actual_name = find_file_within(parent_path, name, child_path, sizeof(child_path), 1))) { return -ENOENT; } - if (!check_caller_access_to_name(fuse, hdr, parent_node, name, R_OK, false)) { + if (!check_caller_access_to_name(fuse, hdr, parent_node, name, R_OK)) { return -EACCES; } @@ -887,7 +806,7 @@ { struct node* node; - pthread_mutex_lock(&fuse->lock); + pthread_mutex_lock(&fuse->global->lock); node = lookup_node_by_id_locked(fuse, hdr->nodeid); TRACE("[%d] FORGET #%"PRIu64" @ %"PRIx64" (%s)\n", handler->token, req->nlookup, hdr->nodeid, node ? node->name : "?"); @@ -897,7 +816,7 @@ release_node_locked(node); } } - pthread_mutex_unlock(&fuse->lock); + pthread_mutex_unlock(&fuse->global->lock); return NO_STATUS; /* no reply */ } @@ -907,16 +826,16 @@ struct node* node; char path[PATH_MAX]; - pthread_mutex_lock(&fuse->lock); + pthread_mutex_lock(&fuse->global->lock); node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path)); TRACE("[%d] GETATTR flags=%x fh=%"PRIx64" @ %"PRIx64" (%s)\n", handler->token, req->getattr_flags, req->fh, hdr->nodeid, node ? node->name : "?"); - pthread_mutex_unlock(&fuse->lock); + pthread_mutex_unlock(&fuse->global->lock); if (!node) { return -ENOENT; } - if (!check_caller_access_to_node(fuse, hdr, node, R_OK, false)) { + if (!check_caller_access_to_node(fuse, hdr, node, R_OK)) { return -EACCES; } @@ -926,24 +845,22 @@ static int handle_setattr(struct fuse* fuse, struct fuse_handler* handler, const struct fuse_in_header *hdr, const struct fuse_setattr_in *req) { - bool has_rw; struct node* node; char path[PATH_MAX]; struct timespec times[2]; - pthread_mutex_lock(&fuse->lock); - has_rw = get_caller_has_rw_locked(fuse, hdr); + pthread_mutex_lock(&fuse->global->lock); node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path)); TRACE("[%d] SETATTR fh=%"PRIx64" valid=%x @ %"PRIx64" (%s)\n", handler->token, req->fh, req->valid, hdr->nodeid, node ? node->name : "?"); - pthread_mutex_unlock(&fuse->lock); + pthread_mutex_unlock(&fuse->global->lock); if (!node) { return -ENOENT; } if (!(req->valid & FATTR_FH) && - !check_caller_access_to_node(fuse, hdr, node, W_OK, has_rw)) { + !check_caller_access_to_node(fuse, hdr, node, W_OK)) { return -EACCES; } @@ -991,25 +908,23 @@ static int handle_mknod(struct fuse* fuse, struct fuse_handler* handler, const struct fuse_in_header* hdr, const struct fuse_mknod_in* req, const char* name) { - bool has_rw; struct node* parent_node; char parent_path[PATH_MAX]; char child_path[PATH_MAX]; const char* actual_name; - pthread_mutex_lock(&fuse->lock); - has_rw = get_caller_has_rw_locked(fuse, hdr); + pthread_mutex_lock(&fuse->global->lock); parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, parent_path, sizeof(parent_path)); TRACE("[%d] MKNOD %s 0%o @ %"PRIx64" (%s)\n", handler->token, name, req->mode, hdr->nodeid, parent_node ? parent_node->name : "?"); - pthread_mutex_unlock(&fuse->lock); + pthread_mutex_unlock(&fuse->global->lock); if (!parent_node || !(actual_name = find_file_within(parent_path, name, child_path, sizeof(child_path), 1))) { return -ENOENT; } - if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK, has_rw)) { + if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK)) { return -EACCES; } __u32 mode = (req->mode & (~0777)) | 0664; @@ -1022,25 +937,23 @@ static int handle_mkdir(struct fuse* fuse, struct fuse_handler* handler, const struct fuse_in_header* hdr, const struct fuse_mkdir_in* req, const char* name) { - bool has_rw; struct node* parent_node; char parent_path[PATH_MAX]; char child_path[PATH_MAX]; const char* actual_name; - pthread_mutex_lock(&fuse->lock); - has_rw = get_caller_has_rw_locked(fuse, hdr); + pthread_mutex_lock(&fuse->global->lock); parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, parent_path, sizeof(parent_path)); TRACE("[%d] MKDIR %s 0%o @ %"PRIx64" (%s)\n", handler->token, name, req->mode, hdr->nodeid, parent_node ? parent_node->name : "?"); - pthread_mutex_unlock(&fuse->lock); + pthread_mutex_unlock(&fuse->global->lock); if (!parent_node || !(actual_name = find_file_within(parent_path, name, child_path, sizeof(child_path), 1))) { return -ENOENT; } - if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK, has_rw)) { + if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK)) { return -EACCES; } __u32 mode = (req->mode & (~0777)) | 0775; @@ -1059,7 +972,7 @@ } if (parent_node->perm == PERM_ANDROID && !strcasecmp(name, "obb")) { char nomedia[PATH_MAX]; - snprintf(nomedia, PATH_MAX, "%s/.nomedia", fuse->obbpath); + snprintf(nomedia, PATH_MAX, "%s/.nomedia", fuse->global->obb_path); if (touch(nomedia, 0664) != 0) { ERROR("Failed to touch(%s): %s\n", nomedia, strerror(errno)); return -ENOENT; @@ -1072,72 +985,96 @@ static int handle_unlink(struct fuse* fuse, struct fuse_handler* handler, const struct fuse_in_header* hdr, const char* name) { - bool has_rw; struct node* parent_node; struct node* child_node; char parent_path[PATH_MAX]; char child_path[PATH_MAX]; - pthread_mutex_lock(&fuse->lock); - has_rw = get_caller_has_rw_locked(fuse, hdr); + pthread_mutex_lock(&fuse->global->lock); parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, parent_path, sizeof(parent_path)); TRACE("[%d] UNLINK %s @ %"PRIx64" (%s)\n", handler->token, name, hdr->nodeid, parent_node ? parent_node->name : "?"); - pthread_mutex_unlock(&fuse->lock); + pthread_mutex_unlock(&fuse->global->lock); if (!parent_node || !find_file_within(parent_path, name, child_path, sizeof(child_path), 1)) { return -ENOENT; } - if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK, has_rw)) { + if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK)) { return -EACCES; } if (unlink(child_path) < 0) { return -errno; } - pthread_mutex_lock(&fuse->lock); + pthread_mutex_lock(&fuse->global->lock); child_node = lookup_child_by_name_locked(parent_node, name); if (child_node) { child_node->deleted = true; } - pthread_mutex_unlock(&fuse->lock); + pthread_mutex_unlock(&fuse->global->lock); + if (parent_node && child_node) { + /* Tell all other views that node is gone */ + TRACE("[%d] fuse_notify_delete parent=%"PRIx64", child=%"PRIx64", name=%s\n", + handler->token, (uint64_t) parent_node->nid, (uint64_t) child_node->nid, name); + if (fuse != fuse->global->fuse_default) { + fuse_notify_delete(fuse->global->fuse_default, parent_node->nid, child_node->nid, name); + } + if (fuse != fuse->global->fuse_read) { + fuse_notify_delete(fuse->global->fuse_read, parent_node->nid, child_node->nid, name); + } + if (fuse != fuse->global->fuse_write) { + fuse_notify_delete(fuse->global->fuse_write, parent_node->nid, child_node->nid, name); + } + } return 0; } static int handle_rmdir(struct fuse* fuse, struct fuse_handler* handler, const struct fuse_in_header* hdr, const char* name) { - bool has_rw; struct node* child_node; struct node* parent_node; char parent_path[PATH_MAX]; char child_path[PATH_MAX]; - pthread_mutex_lock(&fuse->lock); - has_rw = get_caller_has_rw_locked(fuse, hdr); + pthread_mutex_lock(&fuse->global->lock); parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, parent_path, sizeof(parent_path)); TRACE("[%d] RMDIR %s @ %"PRIx64" (%s)\n", handler->token, name, hdr->nodeid, parent_node ? parent_node->name : "?"); - pthread_mutex_unlock(&fuse->lock); + pthread_mutex_unlock(&fuse->global->lock); if (!parent_node || !find_file_within(parent_path, name, child_path, sizeof(child_path), 1)) { return -ENOENT; } - if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK, has_rw)) { + if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK)) { return -EACCES; } if (rmdir(child_path) < 0) { return -errno; } - pthread_mutex_lock(&fuse->lock); + pthread_mutex_lock(&fuse->global->lock); child_node = lookup_child_by_name_locked(parent_node, name); if (child_node) { child_node->deleted = true; } - pthread_mutex_unlock(&fuse->lock); + pthread_mutex_unlock(&fuse->global->lock); + if (parent_node && child_node) { + /* Tell all other views that node is gone */ + TRACE("[%d] fuse_notify_delete parent=%"PRIx64", child=%"PRIx64", name=%s\n", + handler->token, (uint64_t) parent_node->nid, (uint64_t) child_node->nid, name); + if (fuse != fuse->global->fuse_default) { + fuse_notify_delete(fuse->global->fuse_default, parent_node->nid, child_node->nid, name); + } + if (fuse != fuse->global->fuse_read) { + fuse_notify_delete(fuse->global->fuse_read, parent_node->nid, child_node->nid, name); + } + if (fuse != fuse->global->fuse_write) { + fuse_notify_delete(fuse->global->fuse_write, parent_node->nid, child_node->nid, name); + } + } return 0; } @@ -1145,7 +1082,6 @@ const struct fuse_in_header* hdr, const struct fuse_rename_in* req, const char* old_name, const char* new_name) { - bool has_rw; struct node* old_parent_node; struct node* new_parent_node; struct node* child_node; @@ -1156,8 +1092,7 @@ const char* new_actual_name; int res; - pthread_mutex_lock(&fuse->lock); - has_rw = get_caller_has_rw_locked(fuse, hdr); + pthread_mutex_lock(&fuse->global->lock); old_parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, old_parent_path, sizeof(old_parent_path)); new_parent_node = lookup_node_and_path_by_id_locked(fuse, req->newdir, @@ -1170,11 +1105,11 @@ res = -ENOENT; goto lookup_error; } - if (!check_caller_access_to_name(fuse, hdr, old_parent_node, old_name, W_OK, has_rw)) { + if (!check_caller_access_to_name(fuse, hdr, old_parent_node, old_name, W_OK)) { res = -EACCES; goto lookup_error; } - if (!check_caller_access_to_name(fuse, hdr, new_parent_node, new_name, W_OK, has_rw)) { + if (!check_caller_access_to_name(fuse, hdr, new_parent_node, new_name, W_OK)) { res = -EACCES; goto lookup_error; } @@ -1185,7 +1120,7 @@ goto lookup_error; } acquire_node_locked(child_node); - pthread_mutex_unlock(&fuse->lock); + pthread_mutex_unlock(&fuse->global->lock); /* Special case for renaming a file where destination is same path * differing only by case. In this case we don't want to look for a case @@ -1206,7 +1141,7 @@ goto io_error; } - pthread_mutex_lock(&fuse->lock); + pthread_mutex_lock(&fuse->global->lock); res = rename_node_locked(child_node, new_name, new_actual_name); if (!res) { remove_node_from_parent_locked(child_node); @@ -1215,11 +1150,11 @@ goto done; io_error: - pthread_mutex_lock(&fuse->lock); + pthread_mutex_lock(&fuse->global->lock); done: release_node_locked(child_node); lookup_error: - pthread_mutex_unlock(&fuse->lock); + pthread_mutex_unlock(&fuse->global->lock); return res; } @@ -1237,24 +1172,22 @@ static int handle_open(struct fuse* fuse, struct fuse_handler* handler, const struct fuse_in_header* hdr, const struct fuse_open_in* req) { - bool has_rw; struct node* node; char path[PATH_MAX]; struct fuse_open_out out; struct handle *h; - pthread_mutex_lock(&fuse->lock); - has_rw = get_caller_has_rw_locked(fuse, hdr); + pthread_mutex_lock(&fuse->global->lock); node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path)); TRACE("[%d] OPEN 0%o @ %"PRIx64" (%s)\n", handler->token, req->flags, hdr->nodeid, node ? node->name : "?"); - pthread_mutex_unlock(&fuse->lock); + pthread_mutex_unlock(&fuse->global->lock); if (!node) { return -ENOENT; } if (!check_caller_access_to_node(fuse, hdr, node, - open_flags_to_access_mode(req->flags), has_rw)) { + open_flags_to_access_mode(req->flags))) { return -EACCES; } h = malloc(sizeof(*h)); @@ -1335,14 +1268,14 @@ struct fuse_statfs_out out; int res; - pthread_mutex_lock(&fuse->lock); + pthread_mutex_lock(&fuse->global->lock); TRACE("[%d] STATFS\n", handler->token); - res = get_node_path_locked(&fuse->root, path, sizeof(path)); - pthread_mutex_unlock(&fuse->lock); + res = get_node_path_locked(&fuse->global->root, path, sizeof(path)); + pthread_mutex_unlock(&fuse->global->lock); if (res < 0) { return -ENOENT; } - if (statfs(fuse->root.name, &stat) < 0) { + if (statfs(fuse->global->root.name, &stat) < 0) { return -errno; } memset(&out, 0, sizeof(out)); @@ -1409,16 +1342,16 @@ struct fuse_open_out out; struct dirhandle *h; - pthread_mutex_lock(&fuse->lock); + pthread_mutex_lock(&fuse->global->lock); node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path)); TRACE("[%d] OPENDIR @ %"PRIx64" (%s)\n", handler->token, hdr->nodeid, node ? node->name : "?"); - pthread_mutex_unlock(&fuse->lock); + pthread_mutex_unlock(&fuse->global->lock); if (!node) { return -ENOENT; } - if (!check_caller_access_to_node(fuse, hdr, node, R_OK, false)) { + if (!check_caller_access_to_node(fuse, hdr, node, R_OK)) { return -EACCES; } h = malloc(sizeof(*h)); @@ -1498,7 +1431,8 @@ return -1; } - out.minor = MIN(req->minor, FUSE_KERNEL_MINOR_VERSION); + /* We limit ourselves to 15 because we don't handle BATCH_FORGET yet */ + out.minor = MIN(req->minor, 15); fuse_struct_size = sizeof(out); #if defined(FUSE_COMPAT_22_INIT_OUT_SIZE) /* FUSE_KERNEL_VERSION >= 23. */ @@ -1648,12 +1582,14 @@ { struct fuse* fuse = handler->fuse; for (;;) { - ssize_t len = read(fuse->fd, - handler->request_buffer, sizeof(handler->request_buffer)); + ssize_t len = TEMP_FAILURE_RETRY(read(fuse->fd, + handler->request_buffer, sizeof(handler->request_buffer))); if (len < 0) { - if (errno != EINTR) { - ERROR("[%d] handle_fuse_requests: errno=%d\n", handler->token, errno); + if (errno == ENODEV) { + ERROR("[%d] someone stole our marbles!\n", handler->token); + exit(2); } + ERROR("[%d] handle_fuse_requests: errno=%d\n", handler->token, errno); continue; } @@ -1700,22 +1636,15 @@ return true; } -static bool remove_int_to_null(void *key, void *value, void *context) { - Hashmap* map = context; - hashmapRemove(map, key); - return true; -} +static int read_package_list(struct fuse_global* global) { + pthread_mutex_lock(&global->lock); -static int read_package_list(struct fuse *fuse) { - pthread_mutex_lock(&fuse->lock); - - hashmapForEach(fuse->package_to_appid, remove_str_to_int, fuse->package_to_appid); - hashmapForEach(fuse->appid_with_rw, remove_int_to_null, fuse->appid_with_rw); + hashmapForEach(global->package_to_appid, remove_str_to_int, global->package_to_appid); FILE* file = fopen(kPackagesListFile, "r"); if (!file) { ERROR("failed to open package list: %s\n", strerror(errno)); - pthread_mutex_unlock(&fuse->lock); + pthread_mutex_unlock(&global->lock); return -1; } @@ -1727,28 +1656,18 @@ if (sscanf(buf, "%s %d %*d %*s %*s %s", package_name, &appid, gids) == 3) { char* package_name_dup = strdup(package_name); - hashmapPut(fuse->package_to_appid, package_name_dup, (void*) (uintptr_t) appid); - - char* token = strtok(gids, ","); - while (token != NULL) { - if (strtoul(token, NULL, 10) == fuse->write_gid) { - hashmapPut(fuse->appid_with_rw, (void*) (uintptr_t) appid, (void*) (uintptr_t) 1); - break; - } - token = strtok(NULL, ","); - } + hashmapPut(global->package_to_appid, package_name_dup, (void*) (uintptr_t) appid); } } - TRACE("read_package_list: found %zu packages, %zu with write_gid\n", - hashmapSize(fuse->package_to_appid), - hashmapSize(fuse->appid_with_rw)); + TRACE("read_package_list: found %zu packages\n", + hashmapSize(global->package_to_appid)); fclose(file); - pthread_mutex_unlock(&fuse->lock); + pthread_mutex_unlock(&global->lock); return 0; } -static void watch_package_list(struct fuse* fuse) { +static void watch_package_list(struct fuse_global* global) { struct inotify_event *event; char event_buf[512]; @@ -1776,7 +1695,7 @@ /* Watch above will tell us about any future changes, so * read the current state. */ - if (read_package_list(fuse) == -1) { + if (read_package_list(global) == -1) { ERROR("read_package_list failed: %s\n", strerror(errno)); return; } @@ -1810,138 +1729,178 @@ } } -static int ignite_fuse(struct fuse* fuse, int num_threads) -{ - struct fuse_handler* handlers; - int i; - - handlers = malloc(num_threads * sizeof(struct fuse_handler)); - if (!handlers) { - ERROR("cannot allocate storage for threads\n"); - return -ENOMEM; - } - - for (i = 0; i < num_threads; i++) { - handlers[i].fuse = fuse; - handlers[i].token = i; - } - - /* When deriving permissions, this thread is used to process inotify events, - * otherwise it becomes one of the FUSE handlers. */ - i = (fuse->derive == DERIVE_NONE) ? 1 : 0; - for (; i < num_threads; i++) { - pthread_t thread; - int res = pthread_create(&thread, NULL, start_handler, &handlers[i]); - if (res) { - ERROR("failed to start thread #%d, error=%d\n", i, res); - goto quit; - } - } - - if (fuse->derive == DERIVE_NONE) { - handle_fuse_requests(&handlers[0]); - } else { - watch_package_list(fuse); - } - - ERROR("terminated prematurely\n"); - - /* don't bother killing all of the other threads or freeing anything, - * should never get here anyhow */ -quit: - exit(1); -} - -static int usage() -{ - ERROR("usage: sdcard [OPTIONS] <source_path> <dest_path>\n" +static int usage() { + ERROR("usage: sdcard [OPTIONS] <source_path> <label>\n" " -u: specify UID to run as\n" " -g: specify GID to run as\n" - " -w: specify GID required to write (default sdcard_rw, requires -d or -l)\n" - " -t: specify number of threads to use (default %d)\n" - " -d: derive file permissions based on path\n" - " -l: derive file permissions based on legacy internal layout\n" - " -s: split derived permissions for pics, av\n" - "\n", DEFAULT_NUM_THREADS); + " -U: specify user ID that owns device\n" + " -m: source_path is multi-user\n" + " -w: runtime write mount has full write access\n" + "\n"); return 1; } -static int run(const char* source_path, const char* dest_path, uid_t uid, - gid_t gid, gid_t write_gid, int num_threads, derive_t derive, - bool split_perms) { - int fd; +static int fuse_setup(struct fuse* fuse, gid_t gid, mode_t mask) { char opts[256]; - int res; - struct fuse fuse; - /* cleanup from previous instance, if necessary */ - umount2(dest_path, MNT_DETACH); - - fd = open("/dev/fuse", O_RDWR); - if (fd < 0){ - ERROR("cannot open fuse device: %s\n", strerror(errno)); + fuse->fd = open("/dev/fuse", O_RDWR); + if (fuse->fd == -1) { + ERROR("failed to open fuse device: %s\n", strerror(errno)); return -1; } + umount2(fuse->dest_path, MNT_DETACH); + snprintf(opts, sizeof(opts), "fd=%i,rootmode=40000,default_permissions,allow_other,user_id=%d,group_id=%d", - fd, uid, gid); - - res = mount("/dev/fuse", dest_path, "fuse", MS_NOSUID | MS_NODEV | MS_NOEXEC | - MS_NOATIME, opts); - if (res < 0) { - ERROR("cannot mount fuse filesystem: %s\n", strerror(errno)); - goto error; + fuse->fd, fuse->global->uid, fuse->global->gid); + if (mount("/dev/fuse", fuse->dest_path, "fuse", MS_NOSUID | MS_NODEV | MS_NOEXEC | + MS_NOATIME, opts) != 0) { + ERROR("failed to mount fuse filesystem: %s\n", strerror(errno)); + return -1; } - res = setgroups(sizeof(kGroups) / sizeof(kGroups[0]), kGroups); - if (res < 0) { - ERROR("cannot setgroups: %s\n", strerror(errno)); - goto error; - } + fuse->gid = gid; + fuse->mask = mask; - res = setgid(gid); - if (res < 0) { - ERROR("cannot setgid: %s\n", strerror(errno)); - goto error; - } - - res = setuid(uid); - if (res < 0) { - ERROR("cannot setuid: %s\n", strerror(errno)); - goto error; - } - - fuse_init(&fuse, fd, source_path, write_gid, derive, split_perms); - - umask(0); - res = ignite_fuse(&fuse, num_threads); - - /* we do not attempt to umount the file system here because we are no longer - * running as the root user */ - -error: - close(fd); - return res; + return 0; } -int main(int argc, char **argv) -{ - int res; +static void run(const char* source_path, const char* label, uid_t uid, + gid_t gid, userid_t userid, bool multi_user, bool full_write) { + struct fuse_global global; + struct fuse fuse_default; + struct fuse fuse_read; + struct fuse fuse_write; + struct fuse_handler handler_default; + struct fuse_handler handler_read; + struct fuse_handler handler_write; + pthread_t thread_default; + pthread_t thread_read; + pthread_t thread_write; + + memset(&global, 0, sizeof(global)); + memset(&fuse_default, 0, sizeof(fuse_default)); + memset(&fuse_read, 0, sizeof(fuse_read)); + memset(&fuse_write, 0, sizeof(fuse_write)); + memset(&handler_default, 0, sizeof(handler_default)); + memset(&handler_read, 0, sizeof(handler_read)); + memset(&handler_write, 0, sizeof(handler_write)); + + pthread_mutex_init(&global.lock, NULL); + global.package_to_appid = hashmapCreate(256, str_hash, str_icase_equals); + global.uid = uid; + global.gid = gid; + global.multi_user = multi_user; + global.next_generation = 0; + global.inode_ctr = 1; + + memset(&global.root, 0, sizeof(global.root)); + global.root.nid = FUSE_ROOT_ID; /* 1 */ + global.root.refcount = 2; + global.root.namelen = strlen(source_path); + global.root.name = strdup(source_path); + global.root.userid = userid; + global.root.uid = AID_ROOT; + global.root.under_android = false; + + strcpy(global.source_path, source_path); + + if (multi_user) { + global.root.perm = PERM_PRE_ROOT; + snprintf(global.obb_path, sizeof(global.obb_path), "%s/obb", source_path); + } else { + global.root.perm = PERM_ROOT; + snprintf(global.obb_path, sizeof(global.obb_path), "%s/Android/obb", source_path); + } + + fuse_default.global = &global; + fuse_read.global = &global; + fuse_write.global = &global; + + global.fuse_default = &fuse_default; + global.fuse_read = &fuse_read; + global.fuse_write = &fuse_write; + + snprintf(fuse_default.dest_path, PATH_MAX, "/mnt/runtime/default/%s", label); + snprintf(fuse_read.dest_path, PATH_MAX, "/mnt/runtime/read/%s", label); + snprintf(fuse_write.dest_path, PATH_MAX, "/mnt/runtime/write/%s", label); + + handler_default.fuse = &fuse_default; + handler_read.fuse = &fuse_read; + handler_write.fuse = &fuse_write; + + handler_default.token = 0; + handler_read.token = 1; + handler_write.token = 2; + + umask(0); + + if (multi_user) { + /* Multi-user storage is fully isolated per user, so "other" + * permissions are completely masked off. */ + if (fuse_setup(&fuse_default, AID_SDCARD_RW, 0006) + || fuse_setup(&fuse_read, AID_EVERYBODY, 0027) + || fuse_setup(&fuse_write, AID_EVERYBODY, full_write ? 0007 : 0027)) { + ERROR("failed to fuse_setup\n"); + exit(1); + } + } else { + /* Physical storage is readable by all users on device, but + * the Android directories are masked off to a single user + * deep inside attr_from_stat(). */ + if (fuse_setup(&fuse_default, AID_SDCARD_RW, 0006) + || fuse_setup(&fuse_read, AID_EVERYBODY, full_write ? 0027 : 0022) + || fuse_setup(&fuse_write, AID_EVERYBODY, full_write ? 0007 : 0022)) { + ERROR("failed to fuse_setup\n"); + exit(1); + } + } + + /* Drop privs */ + if (setgroups(sizeof(kGroups) / sizeof(kGroups[0]), kGroups) < 0) { + ERROR("cannot setgroups: %s\n", strerror(errno)); + exit(1); + } + if (setgid(gid) < 0) { + ERROR("cannot setgid: %s\n", strerror(errno)); + exit(1); + } + if (setuid(uid) < 0) { + ERROR("cannot setuid: %s\n", strerror(errno)); + exit(1); + } + + if (multi_user) { + fs_prepare_dir(global.obb_path, 0775, uid, gid); + } + + if (pthread_create(&thread_default, NULL, start_handler, &handler_default) + || pthread_create(&thread_read, NULL, start_handler, &handler_read) + || pthread_create(&thread_write, NULL, start_handler, &handler_write)) { + ERROR("failed to pthread_create\n"); + exit(1); + } + + watch_package_list(&global); + ERROR("terminated prematurely\n"); + exit(1); +} + +int main(int argc, char **argv) { const char *source_path = NULL; - const char *dest_path = NULL; + const char *label = NULL; uid_t uid = 0; gid_t gid = 0; - gid_t write_gid = AID_SDCARD_RW; - int num_threads = DEFAULT_NUM_THREADS; - derive_t derive = DERIVE_NONE; - bool split_perms = false; + userid_t userid = 0; + bool multi_user = false; + bool full_write = false; int i; struct rlimit rlim; int fs_version; int opt; - while ((opt = getopt(argc, argv, "u:g:w:t:dls")) != -1) { + while ((opt = getopt(argc, argv, "u:g:U:mw")) != -1) { switch (opt) { case 'u': uid = strtoul(optarg, NULL, 10); @@ -1949,20 +1908,14 @@ case 'g': gid = strtoul(optarg, NULL, 10); break; + case 'U': + userid = strtoul(optarg, NULL, 10); + break; + case 'm': + multi_user = true; + break; case 'w': - write_gid = strtoul(optarg, NULL, 10); - break; - case 't': - num_threads = strtoul(optarg, NULL, 10); - break; - case 'd': - derive = DERIVE_UNIFIED; - break; - case 'l': - derive = DERIVE_LEGACY; - break; - case 's': - split_perms = true; + full_write = true; break; case '?': default: @@ -1974,12 +1927,8 @@ char* arg = argv[i]; if (!source_path) { source_path = arg; - } else if (!dest_path) { - dest_path = arg; - } else if (!uid) { - uid = strtoul(arg, NULL, 10); - } else if (!gid) { - gid = strtoul(arg, NULL, 10); + } else if (!label) { + label = arg; } else { ERROR("too many arguments\n"); return usage(); @@ -1990,22 +1939,14 @@ ERROR("no source path specified\n"); return usage(); } - if (!dest_path) { - ERROR("no dest path specified\n"); + if (!label) { + ERROR("no label specified\n"); return usage(); } if (!uid || !gid) { ERROR("uid and gid must be nonzero\n"); return usage(); } - if (num_threads < 1) { - ERROR("number of threads must be at least 1\n"); - return usage(); - } - if (split_perms && derive == DERIVE_NONE) { - ERROR("cannot split permissions without deriving\n"); - return usage(); - } rlim.rlim_cur = 8192; rlim.rlim_max = 8192; @@ -2018,6 +1959,6 @@ sleep(1); } - res = run(source_path, dest_path, uid, gid, write_gid, num_threads, derive, split_perms); - return res < 0 ? 1 : 0; + run(source_path, label, uid, gid, userid, multi_user, full_write); + return 1; }