DO NOT MERGE libutils/Unicode.cpp: Correct length computation and add checks for utf16->utf8 am: 2806397d1e am: 8c9d02551c
am: 28f28ce635 -s ours
Change-Id: I2ef3ab0e4e3c64c9b5c17454ab738f3d5bdb5dd3
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/elf_utils.cpp b/debuggerd/elf_utils.cpp
index 5ea03e7..65c1904 100644
--- a/debuggerd/elf_utils.cpp
+++ b/debuggerd/elf_utils.cpp
@@ -63,10 +63,10 @@
if (nhdr.n_type == NT_GNU_BUILD_ID) {
// Skip the name (which is the owner and should be "GNU").
addr += NOTE_ALIGN(nhdr.n_namesz);
- uint8_t build_id_data[128];
- if (nhdr.n_namesz > sizeof(build_id_data)) {
- ALOGE("Possible corrupted note, name size value is too large: %u",
- nhdr.n_namesz);
+ uint8_t build_id_data[160];
+ if (nhdr.n_descsz > sizeof(build_id_data)) {
+ ALOGE("Possible corrupted note, desc size value is too large: %u",
+ nhdr.n_descsz);
return false;
}
if (backtrace->Read(addr, build_id_data, nhdr.n_descsz) != nhdr.n_descsz) {
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 a5d2ed5..aeffc66 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);
@@ -779,7 +805,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 e10e8f7..1cdf7bc 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);
@@ -555,6 +563,12 @@
#define android_btWriteLog(tag, type, payload, len) \
__android_log_btwrite(tag, type, payload, len)
+#define android_errorWriteLog(tag, subTag) \
+ __android_log_error_write(tag, subTag, -1, NULL, 0)
+
+#define android_errorWriteWithInfoLog(tag, subTag, uid, data, dataLen) \
+ __android_log_error_write(tag, subTag, uid, data, dataLen)
+
/*
* IF_ALOG uses android_testLog, but IF_ALOG can be overridden.
* android_testLog will remain constant in its purpose as a wrapper
@@ -571,12 +585,6 @@
(__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE) != 0)
#endif
-#define android_errorWriteLog(tag, subTag) \
- __android_log_error_write(tag, subTag, -1, NULL, 0)
-
-#define android_errorWriteWithInfoLog(tag, subTag, uid, data, dataLen) \
- __android_log_error_write(tag, subTag, uid, data, dataLen)
-
// TODO: remove these prototypes and their users
#define android_writevLog(vec,num) do{}while(0)
#define android_write1Log(str,len) do{}while (0)
@@ -596,6 +604,7 @@
LOG_ID_EVENTS = 2,
LOG_ID_SYSTEM = 3,
LOG_ID_CRASH = 4,
+ LOG_ID_KERNEL = 5,
#endif
LOG_ID_MAX
@@ -603,15 +612,15 @@
#define sizeof_log_id_t sizeof(typeof_log_id_t)
#define typeof_log_id_t unsigned char
-int __android_log_error_write(int tag, const char *subTag, int32_t uid, const char *data,
- uint32_t dataLen);
-
/*
* Use the per-tag properties "log.tag.<tagname>" to generate a runtime
* result of non-zero to expose a log.
*/
int __android_log_is_loggable(int prio, const char *tag, int def);
+int __android_log_error_write(int tag, const char *subTag, int32_t uid, const char *data,
+ uint32_t dataLen);
+
/*
* Send a simple string to the log.
*/
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..42769ed 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"
@@ -52,24 +54,8 @@
}
}
-extern "C" char* __cxa_demangle(const char* mangled, char* buf, size_t* len,
- int* status);
-
std::string Backtrace::GetFunctionName(uintptr_t pc, uintptr_t* offset) {
std::string func_name = GetFunctionNameRaw(pc, offset);
- if (!func_name.empty()) {
-#if defined(__APPLE__)
- // Mac OS' __cxa_demangle demangles "f" as "float"; last tested on 10.7.
- if (func_name[0] != '_') {
- return func_name;
- }
-#endif
- char* name = __cxa_demangle(func_name.c_str(), 0, 0, 0);
- if (name) {
- func_name = name;
- free(name);
- }
- }
return func_name;
}
@@ -97,14 +83,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 +103,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 9087f76..115dd79 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 04628b9..c987041 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -17,6 +17,9 @@
#include <fcntl.h>
#include <inttypes.h>
#include <signal.h>
+#include <string.h>
+
+#include <cutils/properties.h>
#include <gtest/gtest.h>
#include <log/log.h>
#include <log/logger.h>
@@ -439,6 +442,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 +607,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));
}
@@ -683,6 +691,193 @@
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]);
+}
+
static inline int32_t get4LE(const char* src)
{
return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
@@ -698,7 +893,7 @@
pid_t pid = getpid();
ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
- LOG_ID_EVENTS, O_RDONLY | O_NDELAY, 1000, pid)));
+ LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
ASSERT_LT(0, android_errorWriteWithInfoLog(
TAG, SUBTAG, UID, max_payload_buf, DATA_LEN));
@@ -779,7 +974,7 @@
pid_t pid = getpid();
ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
- LOG_ID_EVENTS, O_RDONLY | O_NDELAY, 1000, pid)));
+ LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
ASSERT_LT(0, android_errorWriteWithInfoLog(
TAG, SUBTAG, UID, max_payload_buf, DATA_LEN));
@@ -866,7 +1061,7 @@
pid_t pid = getpid();
ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
- LOG_ID_EVENTS, O_RDONLY | O_NDELAY, 1000, pid)));
+ LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
ASSERT_GT(0, android_errorWriteWithInfoLog(
TAG, SUBTAG, UID, NULL, DATA_LEN));
@@ -909,7 +1104,7 @@
pid_t pid = getpid();
ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
- LOG_ID_EVENTS, O_RDONLY | O_NDELAY, 1000, pid)));
+ LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
ASSERT_LT(0, android_errorWriteWithInfoLog(
TAG, SUBTAG, UID, max_payload_buf, DATA_LEN));
@@ -989,7 +1184,7 @@
pid_t pid = getpid();
ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
- LOG_ID_EVENTS, O_RDONLY | O_NDELAY, 1000, pid)));
+ LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
ASSERT_LT(0, android_errorWriteLog(TAG, SUBTAG));
@@ -1046,7 +1241,7 @@
pid_t pid = getpid();
ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
- LOG_ID_EVENTS, O_RDONLY | O_NDELAY, 1000, pid)));
+ LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
ASSERT_GT(0, android_errorWriteLog(TAG, NULL));
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 63a8d70..23a5c59 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 \
@@ -76,6 +74,7 @@
# we have the common sources, plus some device-specific stuff
LOCAL_SRC_FILES:= \
$(commonSources) \
+ BlobCache.cpp \
Looper.cpp \
Trace.cpp
@@ -85,7 +84,8 @@
LOCAL_CFLAGS += -Werror
LOCAL_STATIC_LIBRARIES := \
- libcutils
+ libcutils \
+ libc
LOCAL_SHARED_LIBRARIES := \
libbacktrace \
@@ -113,6 +113,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/libutils/tests/Android.mk b/libutils/tests/Android.mk
index 7cfad89..d4a45fd 100644
--- a/libutils/tests/Android.mk
+++ b/libutils/tests/Android.mk
@@ -38,3 +38,11 @@
libutils \
include $(BUILD_NATIVE_TEST)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libutils_tests_host
+LOCAL_SRC_FILES := Vector_test.cpp
+LOCAL_STATIC_LIBRARIES := libutils liblog
+
+include $(BUILD_HOST_NATIVE_TEST)
diff --git a/libutils/tests/String8_test.cpp b/libutils/tests/String8_test.cpp
index 7394163..7cd67d3 100644
--- a/libutils/tests/String8_test.cpp
+++ b/libutils/tests/String8_test.cpp
@@ -77,8 +77,7 @@
TEST_F(String8Test, CorrectInvalidSurrogate) {
// d841d8 is an invalid start for a surrogate pair. Make sure this is handled by ignoring the
// first character in the pair and handling the rest correctly.
- char16_t char16_arr[] = { 0xd841, 0xd841, 0xdc41, 0x0000 };
- String16 string16(char16_arr);
+ String16 string16(u"\xd841\xd841\xdc41\x0000");
String8 string8(string16);
EXPECT_EQ(4U, string8.length());
@@ -87,7 +86,7 @@
TEST_F(String8Test, CheckUtf32Conversion) {
// Since bound checks were added, check the conversion can be done without fatal errors.
// The utf8 lengths of these are chars are 1 + 2 + 3 + 4 = 10.
- const char32_t string32[] = { 0x0000007f, 0x000007ff, 0x0000911, 0x0010fffe, 0 };
+ const char32_t string32[] = U"\x0000007f\x000007ff\x0000911\x0010fffe";
String8 string8(string32);
EXPECT_EQ(10U, string8.length());
}
diff --git a/libutils/tests/Vector_test.cpp b/libutils/tests/Vector_test.cpp
index 8013187..96d3168 100644
--- a/libutils/tests/Vector_test.cpp
+++ b/libutils/tests/Vector_test.cpp
@@ -89,9 +89,9 @@
vector.add(4);
vector.setCapacity(8);
- ASSERT_EQ(8U, vector.capacity());
+ ASSERT_EQ(8, vector.capacity());
vector.setCapacity(2);
- ASSERT_EQ(8U, vector.capacity());
+ ASSERT_EQ(8, vector.capacity());
}
// NOTE: All of the tests below are useless because of the "TODO" above.
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..c75138b 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
@@ -470,6 +511,11 @@
on property:sys.sysctl.tcp_def_init_rwnd=*
write /proc/sys/net/ipv4/tcp_default_init_rwnd ${sys.sysctl.tcp_def_init_rwnd}
+on property:security.perf_harden=0
+ write /proc/sys/kernel/perf_event_paranoid 1
+
+on property:security.perf_harden=1
+ write /proc/sys/kernel/perf_event_paranoid 3
## Daemon processes to be run by init.
##
@@ -483,6 +529,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 +539,7 @@
class core
critical
seclabel u:r:healthd:s0
+ group root system
service console /system/bin/sh
class core
@@ -531,9 +579,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 +603,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 +646,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 +693,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 dd84584..f6ae8c2 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;
}