Merge changes from topic 'userspace-audit'
* changes:
debuggerd: audit pid, uid and gid on SE Linux denial
property_service: log pid,uid and gid of setprop client
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 1c1683e..1eb3a3c 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -245,7 +245,7 @@
if (pieces.size() > 2) {
const std::string& props = pieces[2];
- for (auto& prop : android::base::Split(props, ";")) {
+ for (const auto& prop : android::base::Split(props, ";")) {
// The list of properties was traditionally ;-terminated rather than ;-separated.
if (prop.empty()) continue;
@@ -904,7 +904,7 @@
}
std::string error_msg;
- atransport* transport = acquire_one_transport(kCsAny, type, serial, &error_msg);
+ atransport* transport = acquire_one_transport(type, serial, nullptr, &error_msg);
if (!transport) {
SendFail(reply_fd, error_msg);
return 1;
@@ -990,13 +990,13 @@
serial = service;
}
- std::string error_msg;
- atransport* t = acquire_one_transport(kCsAny, type, serial, &error_msg);
+ std::string error;
+ atransport* t = acquire_one_transport(type, serial, nullptr, &error);
if (t != nullptr) {
s->transport = t;
SendOkay(reply_fd);
} else {
- SendFail(reply_fd, error_msg);
+ SendFail(reply_fd, error);
}
return 1;
}
@@ -1014,12 +1014,12 @@
}
if (!strcmp(service, "features")) {
- std::string error_msg;
- atransport* t = acquire_one_transport(kCsAny, type, serial, &error_msg);
+ std::string error;
+ atransport* t = acquire_one_transport(type, serial, nullptr, &error);
if (t != nullptr) {
SendOkay(reply_fd, FeatureSetToString(t->features()));
} else {
- SendFail(reply_fd, error_msg);
+ SendFail(reply_fd, error);
}
return 0;
}
@@ -1049,29 +1049,41 @@
return SendOkay(reply_fd, android::base::StringPrintf("disconnected %s", address.c_str()));
}
- // returns our value for ADB_SERVER_VERSION
+ // Returns our value for ADB_SERVER_VERSION.
if (!strcmp(service, "version")) {
return SendOkay(reply_fd, android::base::StringPrintf("%04x", ADB_SERVER_VERSION));
}
// These always report "unknown" rather than the actual error, for scripts.
if (!strcmp(service, "get-serialno")) {
- std::string ignored;
- atransport* t = acquire_one_transport(kCsAny, type, serial, &ignored);
- return SendOkay(reply_fd, (t && t->serial) ? t->serial : "unknown");
+ std::string error;
+ atransport* t = acquire_one_transport(type, serial, nullptr, &error);
+ if (t) {
+ return SendOkay(reply_fd, t->serial ? t->serial : "unknown");
+ } else {
+ return SendFail(reply_fd, error);
+ }
}
if (!strcmp(service, "get-devpath")) {
- std::string ignored;
- atransport* t = acquire_one_transport(kCsAny, type, serial, &ignored);
- return SendOkay(reply_fd, (t && t->devpath) ? t->devpath : "unknown");
+ std::string error;
+ atransport* t = acquire_one_transport(type, serial, nullptr, &error);
+ if (t) {
+ return SendOkay(reply_fd, t->devpath ? t->devpath : "unknown");
+ } else {
+ return SendFail(reply_fd, error);
+ }
}
if (!strcmp(service, "get-state")) {
- std::string ignored;
- atransport* t = acquire_one_transport(kCsAny, type, serial, &ignored);
- return SendOkay(reply_fd, t ? t->connection_state_name() : "unknown");
+ std::string error;
+ atransport* t = acquire_one_transport(type, serial, nullptr, &error);
+ if (t) {
+ return SendOkay(reply_fd, t->connection_state_name());
+ } else {
+ return SendFail(reply_fd, error);
+ }
}
- // indicates a new emulator instance has started
+ // Indicates a new emulator instance has started.
if (!strncmp(service, "emulator:", 9)) {
int port = atoi(service+9);
local_connect(port);
diff --git a/adb/adb_auth_host.cpp b/adb/adb_auth_host.cpp
index 7b314c3..5309519 100644
--- a/adb/adb_auth_host.cpp
+++ b/adb/adb_auth_host.cpp
@@ -368,7 +368,7 @@
return;
}
- for (auto& path : android::base::Split(adb_keys_path, ENV_PATH_SEPARATOR_STR)) {
+ for (const 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'", path.c_str());
}
diff --git a/adb/adb_client.cpp b/adb/adb_client.cpp
index 984910d..fa8ce9e 100644
--- a/adb/adb_client.cpp
+++ b/adb/adb_client.cpp
@@ -209,9 +209,8 @@
adb_close(fd);
if (sscanf(&version_string[0], "%04x", &version) != 1) {
- *error = android::base::StringPrintf(
- "cannot parse version string: %s",
- version_string.c_str());
+ *error = android::base::StringPrintf("cannot parse version string: %s",
+ version_string.c_str());
return -1;
}
} else {
diff --git a/adb/adb_utils.cpp b/adb/adb_utils.cpp
index 7ff5c3d..f7b2e4e 100644
--- a/adb/adb_utils.cpp
+++ b/adb/adb_utils.cpp
@@ -229,3 +229,19 @@
std::string perror_str(const char* msg) {
return android::base::StringPrintf("%s: %s", msg, strerror(errno));
}
+
+#if !defined(_WIN32)
+bool set_file_block_mode(int fd, bool block) {
+ int flags = fcntl(fd, F_GETFL, 0);
+ if (flags == -1) {
+ PLOG(ERROR) << "failed to fcntl(F_GETFL) for fd " << fd;
+ return false;
+ }
+ flags = block ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK);
+ if (fcntl(fd, F_SETFL, flags) != 0) {
+ PLOG(ERROR) << "failed to fcntl(F_SETFL) for fd " << fd << ", flags " << flags;
+ return false;
+ }
+ return true;
+}
+#endif
diff --git a/adb/adb_utils.h b/adb/adb_utils.h
index b9da980..537d0e4 100644
--- a/adb/adb_utils.h
+++ b/adb/adb_utils.h
@@ -46,4 +46,6 @@
std::string perror_str(const char* msg);
+bool set_file_block_mode(int fd, bool block);
+
#endif
diff --git a/adb/adb_utils_test.cpp b/adb/adb_utils_test.cpp
index 17c8d0a..4a2787a 100644
--- a/adb/adb_utils_test.cpp
+++ b/adb/adb_utils_test.cpp
@@ -202,3 +202,19 @@
ASSERT_EQ(0, chdir(td.path)) << strerror(errno);
test_mkdirs(std::string("relative/subrel/file"));
}
+
+#if !defined(_WIN32)
+TEST(adb_utils, set_file_block_mode) {
+ int fd = adb_open("/dev/null", O_RDWR | O_APPEND);
+ ASSERT_GE(fd, 0);
+ int flags = fcntl(fd, F_GETFL, 0);
+ ASSERT_EQ(O_RDWR | O_APPEND, (flags & (O_RDWR | O_APPEND)));
+ ASSERT_TRUE(set_file_block_mode(fd, false));
+ int new_flags = fcntl(fd, F_GETFL, 0);
+ ASSERT_EQ(flags | O_NONBLOCK, new_flags);
+ ASSERT_TRUE(set_file_block_mode(fd, true));
+ new_flags = fcntl(fd, F_GETFL, 0);
+ ASSERT_EQ(flags, new_flags);
+ ASSERT_EQ(0, adb_close(fd));
+}
+#endif
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index d0ca67a..0531cf9 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -146,8 +146,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"
@@ -155,6 +162,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"
@@ -1085,8 +1093,6 @@
}
// TODO: also try TARGET_PRODUCT/TARGET_DEVICE as a hint
- const char* serial = getenv("ANDROID_SERIAL");
-
/* Validate and assign the server port */
const char* server_port_str = getenv("ANDROID_ADB_SERVER_PORT");
int server_port = DEFAULT_ADB_PORT;
@@ -1100,7 +1106,9 @@
}
}
- /* modifiers and flags */
+ // We need to check for -d and -e before we look at $ANDROID_SERIAL.
+ const char* serial = nullptr;
+
while (argc > 0) {
if (!strcmp(argv[0],"server")) {
is_server = 1;
@@ -1191,6 +1199,11 @@
argv++;
}
+ // If none of -d, -e, or -s were specified, try $ANDROID_SERIAL.
+ if (transport_type == kTransportAny && serial == nullptr) {
+ serial = getenv("ANDROID_SERIAL");
+ }
+
adb_set_transport(transport_type, serial);
adb_set_tcp_specifics(server_port);
diff --git a/adb/fdevent.cpp b/adb/fdevent.cpp
index 1ccee9b..ddd15a2 100644
--- a/adb/fdevent.cpp
+++ b/adb/fdevent.cpp
@@ -36,6 +36,7 @@
#include "adb_io.h"
#include "adb_trace.h"
+#include "adb_utils.h"
#if !ADB_HOST
// This socket is used when a subproc shell service exists.
@@ -124,11 +125,11 @@
fde->fd = fd;
fde->func = func;
fde->arg = arg;
- if (fcntl(fd, F_SETFL, O_NONBLOCK) != 0) {
+ if (!set_file_block_mode(fd, false)) {
// Here is not proper to handle the error. If it fails here, some error is
// likely to be detected by poll(), then we can let the callback function
// to handle it.
- LOG(ERROR) << "failed to fcntl(" << fd << ") to be nonblock";
+ LOG(ERROR) << "failed to set non-blocking mode for fd " << fd;
}
auto pair = g_poll_node_map.emplace(fde->fd, PollNode(fde));
CHECK(pair.second) << "install existing fd " << fd;
@@ -198,7 +199,7 @@
static std::string dump_pollfds(const std::vector<pollfd>& pollfds) {
std::string result;
- for (auto& pollfd : pollfds) {
+ for (const auto& pollfd : pollfds) {
std::string op;
if (pollfd.events & POLLIN) {
op += "R";
@@ -213,8 +214,8 @@
static void fdevent_process() {
std::vector<pollfd> pollfds;
- for (auto it = g_poll_node_map.begin(); it != g_poll_node_map.end(); ++it) {
- pollfds.push_back(it->second.pollfd);
+ for (const auto& pair : g_poll_node_map) {
+ pollfds.push_back(pair.second.pollfd);
}
CHECK_GT(pollfds.size(), 0u);
D("poll(), pollfds = %s", dump_pollfds(pollfds).c_str());
@@ -223,7 +224,7 @@
PLOG(ERROR) << "poll(), ret = " << ret;
return;
}
- for (auto& pollfd : pollfds) {
+ for (const auto& pollfd : pollfds) {
if (pollfd.revents != 0) {
D("for fd %d, revents = %x", pollfd.fd, pollfd.revents);
}
diff --git a/adb/fdevent_test.cpp b/adb/fdevent_test.cpp
index 7457712..7fe3d37 100644
--- a/adb/fdevent_test.cpp
+++ b/adb/fdevent_test.cpp
@@ -18,14 +18,14 @@
#include <gtest/gtest.h>
+#include <pthread.h>
+#include <signal.h>
+
#include <limits>
#include <queue>
#include <string>
#include <vector>
-#include <pthread.h>
-#include <signal.h>
-
#include "adb_io.h"
class FdHandler {
diff --git a/adb/file_sync_client.cpp b/adb/file_sync_client.cpp
index 9bc634c..5513e8f 100644
--- a/adb/file_sync_client.cpp
+++ b/adb/file_sync_client.cpp
@@ -25,6 +25,7 @@
#include <sys/time.h>
#include <sys/types.h>
#include <time.h>
+#include <unistd.h>
#include <utime.h>
#include <memory>
@@ -37,6 +38,7 @@
#include "adb_utils.h"
#include "file_sync_service.h"
+#include <base/file.h>
#include <base/strings.h>
#include <base/stringprintf.h>
@@ -46,8 +48,6 @@
char data[SYNC_DATA_MAX];
};
-static syncsendbuf send_buffer;
-
static long long NOW() {
struct timeval tv;
gettimeofday(&tv, 0);
@@ -69,26 +69,6 @@
fflush(stderr);
}
-static bool SendRequest(int fd, int id, const char* path) {
- size_t path_length = strlen(path);
- if (path_length > 1024) {
- fprintf(stderr, "SendRequest failed: path too long: %zu", path_length);
- errno = ENAMETOOLONG;
- return false;
- }
-
- // Sending header and payload in a single write makes a noticeable
- // difference to "adb sync" performance.
- char buf[sizeof(SyncRequest) + path_length] __attribute__((aligned(8)));
- SyncRequest* req = reinterpret_cast<SyncRequest*>(buf);
- req->id = id;
- req->path_length = path_length;
- char* data = reinterpret_cast<char*>(req + 1);
- memcpy(data, path, path_length);
-
- return WriteFdExactly(fd, buf, sizeof(buf));
-}
-
class SyncConnection {
public:
SyncConnection() : total_bytes(0), start_time_(NOW()) {
@@ -111,6 +91,93 @@
bool IsValid() { return fd >= 0; }
+ bool SendRequest(int id, const char* path_and_mode) {
+ size_t path_length = strlen(path_and_mode);
+ if (path_length > 1024) {
+ fprintf(stderr, "SendRequest failed: path too long: %zu", path_length);
+ errno = ENAMETOOLONG;
+ return false;
+ }
+
+ // Sending header and payload in a single write makes a noticeable
+ // difference to "adb sync" performance.
+ char buf[sizeof(SyncRequest) + path_length];
+ SyncRequest* req = reinterpret_cast<SyncRequest*>(buf);
+ req->id = id;
+ req->path_length = path_length;
+ char* data = reinterpret_cast<char*>(req + 1);
+ memcpy(data, path_and_mode, path_length);
+
+ return WriteFdExactly(fd, buf, sizeof(buf));
+ }
+
+ // Sending header, payload, and footer in a single write makes a huge
+ // difference to "adb sync" performance.
+ bool SendSmallFile(const char* path_and_mode,
+ const char* data, size_t data_length,
+ unsigned mtime) {
+ size_t path_length = strlen(path_and_mode);
+ if (path_length > 1024) {
+ fprintf(stderr, "SendSmallFile failed: path too long: %zu", path_length);
+ errno = ENAMETOOLONG;
+ return false;
+ }
+
+ char buf[sizeof(SyncRequest) + path_length +
+ sizeof(SyncRequest) + data_length +
+ sizeof(SyncRequest)];
+ char* p = buf;
+
+ SyncRequest* req_send = reinterpret_cast<SyncRequest*>(p);
+ req_send->id = ID_SEND;
+ req_send->path_length = path_length;
+ p += sizeof(SyncRequest);
+ memcpy(p, path_and_mode, path_length);
+ p += path_length;
+
+ SyncRequest* req_data = reinterpret_cast<SyncRequest*>(p);
+ req_data->id = ID_DATA;
+ req_data->path_length = data_length;
+ p += sizeof(SyncRequest);
+ memcpy(p, data, data_length);
+ p += data_length;
+
+ SyncRequest* req_done = reinterpret_cast<SyncRequest*>(p);
+ req_done->id = ID_DONE;
+ req_done->path_length = mtime;
+ p += sizeof(SyncRequest);
+
+ if (!WriteFdExactly(fd, buf, (p-buf))) return false;
+
+ total_bytes += data_length;
+ return true;
+ }
+
+ bool CopyDone(const char* from, const char* to) {
+ syncmsg msg;
+ if (!ReadFdExactly(fd, &msg.status, sizeof(msg.status))) {
+ fprintf(stderr, "failed to copy '%s' to '%s': no ID_DONE: %s\n",
+ from, to, strerror(errno));
+ return false;
+ }
+ if (msg.status.id == ID_OKAY) {
+ return true;
+ }
+ if (msg.status.id != ID_FAIL) {
+ fprintf(stderr, "failed to copy '%s' to '%s': unknown reason\n", from, to);
+ return false;
+ }
+ char buffer[msg.status.msglen + 1];
+ if (!ReadFdExactly(fd, buffer, msg.status.msglen)) {
+ fprintf(stderr, "failed to copy '%s' to '%s'; failed to read reason (!): %s\n",
+ from, to, strerror(errno));
+ return false;
+ }
+ buffer[msg.status.msglen] = 0;
+ fprintf(stderr, "failed to copy '%s' to '%s': %s\n", from, to, buffer);
+ return false;
+ }
+
uint64_t total_bytes;
// TODO: add a char[max] buffer here, to replace syncsendbuf...
@@ -121,7 +188,7 @@
uint64_t start_time_;
void SendQuit() {
- SendRequest(fd, ID_QUIT, ""); // TODO: add a SendResponse?
+ SendRequest(ID_QUIT, ""); // TODO: add a SendResponse?
}
void ShowTransferRate() {
@@ -136,12 +203,12 @@
typedef void (*sync_ls_cb)(unsigned mode, unsigned size, unsigned time, const char* name, void* cookie);
-static bool sync_ls(int fd, const char* path, sync_ls_cb func, void* cookie) {
- if (!SendRequest(fd, ID_LIST, path)) return false;
+static bool sync_ls(SyncConnection& sc, const char* path, sync_ls_cb func, void* cookie) {
+ if (!sc.SendRequest(ID_LIST, path)) return false;
while (true) {
syncmsg msg;
- if (!ReadFdExactly(fd, &msg.dent, sizeof(msg.dent))) return false;
+ if (!ReadFdExactly(sc.fd, &msg.dent, sizeof(msg.dent))) return false;
if (msg.dent.id == ID_DONE) return true;
if (msg.dent.id != ID_DENT) return false;
@@ -150,17 +217,13 @@
if (len > 256) return false; // TODO: resize buffer? continue?
char buf[257];
- if (!ReadFdExactly(fd, buf, len)) return false;
+ if (!ReadFdExactly(sc.fd, buf, len)) return false;
buf[len] = 0;
func(msg.dent.mode, msg.dent.size, msg.dent.time, buf, cookie);
}
}
-static bool sync_start_stat(SyncConnection& sc, const char* path) {
- return SendRequest(sc.fd, ID_STAT, path);
-}
-
static bool sync_finish_stat(SyncConnection& sc, unsigned int* timestamp,
unsigned int* mode, unsigned int* size) {
syncmsg msg;
@@ -177,10 +240,17 @@
static bool sync_stat(SyncConnection& sc, const char* path,
unsigned int* timestamp, unsigned int* mode, unsigned int* size) {
- return sync_start_stat(sc, path) && sync_finish_stat(sc, timestamp, mode, size);
+ return sc.SendRequest(ID_STAT, path) && sync_finish_stat(sc, timestamp, mode, size);
}
-static bool write_data_file(SyncConnection& sc, const char* path, syncsendbuf* sbuf, bool show_progress) {
+static bool SendLargeFile(SyncConnection& sc, const char* path_and_mode, const char* path,
+ unsigned mtime, bool show_progress) {
+ if (!sc.SendRequest(ID_SEND, path_and_mode)) {
+ fprintf(stderr, "failed to send ID_SEND message '%s': %s\n",
+ path_and_mode, strerror(errno));
+ return false;
+ }
+
unsigned long long size = 0;
if (show_progress) {
// Determine local file size.
@@ -199,9 +269,10 @@
return false;
}
- sbuf->id = ID_DATA;
+ syncsendbuf sbuf;
+ sbuf.id = ID_DATA;
while (true) {
- int ret = adb_read(lfd, sbuf->data, sc.max);
+ int ret = adb_read(lfd, sbuf.data, sc.max);
if (ret <= 0) {
if (ret < 0) {
fprintf(stderr, "cannot read '%s': %s\n", path, strerror(errno));
@@ -211,8 +282,8 @@
break;
}
- sbuf->size = ret;
- if (!WriteFdExactly(sc.fd, sbuf, sizeof(unsigned) * 2 + ret)) {
+ sbuf.size = ret;
+ if (!WriteFdExactly(sc.fd, &sbuf, sizeof(unsigned) * 2 + ret)) {
adb_close(lfd);
return false;
}
@@ -224,103 +295,77 @@
}
adb_close(lfd);
- return true;
-}
-
-#if defined(_WIN32)
-extern bool write_data_link(SyncConnection& sc, const char* path, syncsendbuf* sbuf) __attribute__((error("no symlinks on Windows")));
-#else
-static bool write_data_link(SyncConnection& sc, const char* path, syncsendbuf* sbuf) {
- ssize_t len = readlink(path, sbuf->data, sc.max - 1);
- if (len < 0) {
- fprintf(stderr, "error reading link '%s': %s\n", path, strerror(errno));
- return false;
- }
- sbuf->data[len] = '\0';
-
- sbuf->size = len + 1;
- sbuf->id = ID_DATA;
-
- if (!WriteFdExactly(sc.fd, sbuf, sizeof(unsigned) * 2 + len + 1)) {
- return false;
- }
-
- sc.total_bytes += len + 1;
-
- return true;
-}
-#endif
-
-static bool sync_send(SyncConnection& sc, const char *lpath, const char *rpath,
- unsigned mtime, mode_t mode, bool show_progress)
-{
- syncsendbuf* sbuf = &send_buffer;
-
- std::string path_and_mode = android::base::StringPrintf("%s,%d", rpath, mode);
- if (!SendRequest(sc.fd, ID_SEND, path_and_mode.c_str())) {
- fprintf(stderr, "failed to send ID_SEND message '%s': %s\n",
- path_and_mode.c_str(), strerror(errno));
- return false;
- }
-
- if (S_ISREG(mode)) {
- if (!write_data_file(sc, lpath, sbuf, show_progress)) return false;
- } else if (S_ISLNK(mode)) {
- if (!write_data_link(sc, lpath, sbuf)) return false;
- } else {
- fprintf(stderr, "local file '%s' has unsupported mode: 0o%o\n", lpath, mode);
- return false;
- }
syncmsg msg;
msg.data.id = ID_DONE;
msg.data.size = mtime;
if (!WriteFdExactly(sc.fd, &msg.data, sizeof(msg.data))) {
- fprintf(stderr, "failed to send ID_DONE message for '%s': %s\n", lpath, strerror(errno));
- return false;
- }
-
- if (!ReadFdExactly(sc.fd, &msg.status, sizeof(msg.status))) {
- fprintf(stderr, "failed to read ID_DONE response for '%s': %s\n", lpath, strerror(errno));
- return false;
- }
- if (msg.status.id != ID_OKAY) {
- if (msg.status.id == ID_FAIL) {
- size_t len = msg.status.msglen;
- if (len > 256) len = 256;
- if (!ReadFdExactly(sc.fd, sbuf->data, len)) {
- fprintf(stderr, "failed to read failure reason (!): %s\n", strerror(errno));
- return false;
- }
- sbuf->data[len] = 0;
- } else {
- strcpy(sbuf->data, "unknown reason");
- }
- fprintf(stderr, "failed to copy '%s' to '%s': %s\n", lpath, rpath, sbuf->data);
+ fprintf(stderr, "failed to send ID_DONE message for '%s': %s\n", path, strerror(errno));
return false;
}
return true;
}
-static int sync_recv(SyncConnection& sc, const char* rpath, const char* lpath, bool show_progress) {
+static bool sync_send(SyncConnection& sc, const char* lpath, const char* rpath,
+ unsigned mtime, mode_t mode, bool show_progress)
+{
+ std::string path_and_mode = android::base::StringPrintf("%s,%d", rpath, mode);
+
+ if (S_ISLNK(mode)) {
+#if !defined(_WIN32)
+ char buf[PATH_MAX];
+ ssize_t data_length = readlink(lpath, buf, PATH_MAX - 1);
+ if (data_length == -1) {
+ fprintf(stderr, "readlink '%s' failed: %s\n", lpath, strerror(errno));
+ return false;
+ }
+ buf[data_length++] = '\0';
+
+ if (!sc.SendSmallFile(path_and_mode.c_str(), buf, data_length, mtime)) return false;
+ return sc.CopyDone(lpath, rpath);
+#endif
+ }
+
+ if (!S_ISREG(mode)) {
+ fprintf(stderr, "local file '%s' has unsupported mode: 0o%o\n", lpath, mode);
+ return false;
+ }
+
+ struct stat st;
+ if (stat(lpath, &st) == -1) {
+ fprintf(stderr, "stat '%s' failed: %s\n", lpath, strerror(errno));
+ return false;
+ }
+ if (st.st_size < SYNC_DATA_MAX) {
+ std::string data;
+ if (!android::base::ReadFileToString(lpath, &data)) {
+ fprintf(stderr, "failed to read all of '%s': %s\n", lpath, strerror(errno));
+ return false;
+ }
+ if (!sc.SendSmallFile(path_and_mode.c_str(), data.data(), data.size(), mtime)) return false;
+ } else {
+ if (!SendLargeFile(sc, path_and_mode.c_str(), lpath, mtime, show_progress)) return false;
+ }
+ return sc.CopyDone(lpath, rpath);
+}
+
+static bool sync_recv(SyncConnection& sc, const char* rpath, const char* lpath, bool show_progress) {
syncmsg msg;
int lfd = -1;
- char *buffer = send_buffer.data;
- unsigned id;
size_t len = strlen(rpath);
- if (len > 1024) return -1;
+ if (len > 1024) return false;
unsigned size = 0;
if (show_progress) {
- if (!sync_stat(sc, rpath, nullptr, nullptr, &size)) return -1;
+ if (!sync_stat(sc, rpath, nullptr, nullptr, &size)) return false;
}
- if (!SendRequest(sc.fd, ID_RECV, rpath)) return -1;
- if (!ReadFdExactly(sc.fd, &msg.data, sizeof(msg.data))) return -1;
+ if (!sc.SendRequest(ID_RECV, rpath)) return false;
+ if (!ReadFdExactly(sc.fd, &msg.data, sizeof(msg.data))) return false;
- id = msg.data.id;
+ unsigned id = msg.data.id;
if (id == ID_DATA || id == ID_DONE) {
adb_unlink(lpath);
@@ -328,7 +373,7 @@
lfd = adb_creat(lpath, 0644);
if(lfd < 0) {
fprintf(stderr, "cannot create '%s': %s\n", lpath, strerror(errno));
- return -1;
+ return false;
}
goto handle_data;
} else {
@@ -336,9 +381,11 @@
}
while (true) {
- if(!ReadFdExactly(sc.fd, &msg.data, sizeof(msg.data))) {
+ char buffer[SYNC_DATA_MAX];
+
+ if (!ReadFdExactly(sc.fd, &msg.data, sizeof(msg.data))) {
adb_close(lfd);
- return -1;
+ return false;
}
id = msg.data.id;
@@ -349,18 +396,18 @@
if (len > sc.max) {
fprintf(stderr, "msg.data.size too large: %zu (max %zu)\n", len, sc.max);
adb_close(lfd);
- return -1;
+ return false;
}
- if(!ReadFdExactly(sc.fd, buffer, len)) {
+ if (!ReadFdExactly(sc.fd, buffer, len)) {
adb_close(lfd);
- return -1;
+ return false;
}
- if(!WriteFdExactly(lfd, buffer, len)) {
+ if (!WriteFdExactly(lfd, buffer, len)) {
fprintf(stderr, "cannot write '%s': %s\n", rpath, strerror(errno));
adb_close(lfd);
- return -1;
+ return false;
}
sc.total_bytes += len;
@@ -371,25 +418,13 @@
}
adb_close(lfd);
- return 0;
+ return true;
remote_error:
adb_close(lfd);
adb_unlink(lpath);
-
- if(id == ID_FAIL) {
- len = msg.data.size;
- if(len > 256) len = 256;
- if(!ReadFdExactly(sc.fd, buffer, len)) {
- return -1;
- }
- buffer[len] = 0;
- } else {
- memcpy(buffer, &id, 4);
- buffer[4] = 0;
- }
- fprintf(stderr, "failed to copy '%s' to '%s': %s\n", rpath, lpath, buffer);
- return 0;
+ sc.CopyDone(rpath, lpath);
+ return false;
}
static void do_sync_ls_cb(unsigned mode, unsigned size, unsigned time,
@@ -401,7 +436,7 @@
SyncConnection sc;
if (!sc.IsValid()) return false;
- return sync_ls(sc.fd, path, do_sync_ls_cb, 0);
+ return sync_ls(sc, path, do_sync_ls_cb, 0);
}
struct copyinfo
@@ -411,7 +446,7 @@
const char *dst;
unsigned int time;
unsigned int mode;
- unsigned int size;
+ uint64_t size;
int flag;
};
@@ -531,7 +566,7 @@
if (check_timestamps) {
for (ci = filelist; ci != 0; ci = ci->next) {
- if (!sync_start_stat(sc, ci->dst)) return false;
+ if (!sc.SendRequest(ID_STAT, ci->dst)) return false;
}
for(ci = filelist; ci != 0; ci = ci->next) {
unsigned int timestamp, mode, size;
@@ -629,7 +664,7 @@
}
}
-static bool remote_build_list(int syncfd, copyinfo **filelist,
+static bool remote_build_list(SyncConnection& sc, copyinfo **filelist,
const char *rpath, const char *lpath) {
copyinfo *dirlist = NULL;
sync_ls_build_list_cb_args args;
@@ -640,14 +675,14 @@
args.lpath = lpath;
// Put the files/dirs in rpath on the lists.
- if (!sync_ls(syncfd, rpath, sync_ls_build_list_cb, (void *)&args)) {
+ if (!sync_ls(sc, rpath, sync_ls_build_list_cb, (void *)&args)) {
return false;
}
// Recurse into each directory we found.
while (dirlist != NULL) {
copyinfo *next = dirlist->next;
- if (!remote_build_list(syncfd, filelist, dirlist->src, dirlist->dst)) {
+ if (!remote_build_list(sc, filelist, dirlist->src, dirlist->dst)) {
return false;
}
free(dirlist);
@@ -682,7 +717,7 @@
// Recursively build the list of files to copy.
fprintf(stderr, "pull: building file list...\n");
copyinfo* filelist = nullptr;
- if (!remote_build_list(sc.fd, &filelist, rpath_clean.c_str(), lpath_clean.c_str())) return false;
+ if (!remote_build_list(sc, &filelist, rpath_clean.c_str(), lpath_clean.c_str())) return false;
int pulled = 0;
int skipped = 0;
@@ -691,7 +726,7 @@
copyinfo* next = ci->next;
if (ci->flag == 0) {
fprintf(stderr, "pull: %s -> %s\n", ci->src, ci->dst);
- if (sync_recv(sc, ci->src, ci->dst, false)) {
+ if (!sync_recv(sc, ci->src, ci->dst, false)) {
return false;
}
@@ -734,7 +769,7 @@
lpath = path_holder.c_str();
}
}
- if (sync_recv(sc, rpath, lpath, show_progress)) {
+ if (!sync_recv(sc, rpath, lpath, show_progress)) {
return false;
} else {
if (copy_attrs && set_time_and_mode(lpath, time, mode)) {
diff --git a/adb/file_sync_service.cpp b/adb/file_sync_service.cpp
index f588b40..298ed82 100644
--- a/adb/file_sync_service.cpp
+++ b/adb/file_sync_service.cpp
@@ -56,12 +56,12 @@
path_components.pop_back(); // For "/system/bin/sh", only create "/system/bin".
std::string partial_path;
- for (auto& path_component : path_components) {
+ for (const auto& path_component : path_components) {
if (partial_path.back() != OS_PATH_SEPARATOR) partial_path += OS_PATH_SEPARATOR;
partial_path += path_component;
if (should_use_fs_config(partial_path)) {
- fs_config(partial_path.c_str(), 1, &uid, &gid, &mode, &cap);
+ fs_config(partial_path.c_str(), 1, nullptr, &uid, &gid, &mode, &cap);
}
if (adb_mkdir(partial_path.c_str(), mode) == -1) {
if (errno != EEXIST) {
@@ -308,7 +308,7 @@
uint64_t cap = 0;
if (should_use_fs_config(path)) {
unsigned int broken_api_hack = mode;
- fs_config(path.c_str(), 0, &uid, &gid, &broken_api_hack, &cap);
+ fs_config(path.c_str(), 0, nullptr, &uid, &gid, &broken_api_hack, &cap);
mode = broken_api_hack;
}
return handle_send_file(s, path.c_str(), uid, gid, mode, buffer, do_unlink);
diff --git a/adb/jdwp_service.cpp b/adb/jdwp_service.cpp
index 4f0f740..cc2d44e 100644
--- a/adb/jdwp_service.cpp
+++ b/adb/jdwp_service.cpp
@@ -27,6 +27,7 @@
#include <unistd.h>
#include "adb.h"
+#include "adb_utils.h"
/* here's how these things work.
@@ -343,7 +344,6 @@
struct iovec iov;
char dummy = '!';
char buffer[sizeof(struct cmsghdr) + sizeof(int)];
- int flags;
iov.iov_base = &dummy;
iov.iov_len = 1;
@@ -361,18 +361,8 @@
cmsg->cmsg_type = SCM_RIGHTS;
((int*)CMSG_DATA(cmsg))[0] = fd;
- flags = fcntl(proc->socket,F_GETFL,0);
-
- if (flags == -1) {
- D("failed to get cntl flags for socket %d: %s",
- proc->pid, strerror(errno));
- goto CloseProcess;
-
- }
-
- if (fcntl(proc->socket, F_SETFL, flags & ~O_NONBLOCK) == -1) {
- D("failed to remove O_NONBLOCK flag for socket %d: %s",
- proc->pid, strerror(errno));
+ if (!set_file_block_mode(proc->socket, true)) {
+ VLOG(JDWP) << "failed to set blocking mode for fd " << proc->socket;
goto CloseProcess;
}
@@ -395,9 +385,8 @@
for (n = 1; n < proc->out_count; n++)
proc->out_fds[n-1] = proc->out_fds[n];
- if (fcntl(proc->socket, F_SETFL, flags) == -1) {
- D("failed to set O_NONBLOCK flag for socket %d: %s",
- proc->pid, strerror(errno));
+ if (!set_file_block_mode(proc->socket, false)) {
+ VLOG(JDWP) << "failed to set non-blocking mode for fd " << proc->socket;
goto CloseProcess;
}
diff --git a/adb/services.cpp b/adb/services.cpp
index e832b1e..e24b470 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -363,23 +363,31 @@
ConnectionState state;
};
-static void wait_for_state(int fd, void* cookie)
-{
+static void wait_for_state(int fd, void* cookie) {
state_info* sinfo = reinterpret_cast<state_info*>(cookie);
D("wait_for_state %d", sinfo->state);
- std::string error_msg = "unknown error";
- atransport* t = acquire_one_transport(sinfo->state, sinfo->transport_type, sinfo->serial,
- &error_msg);
- if (t != nullptr) {
- SendOkay(fd);
- } else {
- SendFail(fd, error_msg);
+ while (true) {
+ bool is_ambiguous = false;
+ std::string error = "unknown error";
+ atransport* t = acquire_one_transport(sinfo->transport_type, sinfo->serial,
+ &is_ambiguous, &error);
+ if (t != nullptr && t->connection_state == sinfo->state) {
+ SendOkay(fd);
+ break;
+ } else if (!is_ambiguous) {
+ adb_sleep_ms(1000);
+ // Try again...
+ } else {
+ SendFail(fd, error);
+ break;
+ }
}
- if (sinfo->serial)
+ if (sinfo->serial) {
free(sinfo->serial);
+ }
free(sinfo);
adb_close(fd);
D("wait_for_state is done");
diff --git a/adb/shell_service.cpp b/adb/shell_service.cpp
index 714a2d8..8aeea81 100644
--- a/adb/shell_service.cpp
+++ b/adb/shell_service.cpp
@@ -77,9 +77,9 @@
#define TRACE_TAG SHELL
-#include "shell_service.h"
+#include "sysdeps.h"
-#if !ADB_HOST
+#include "shell_service.h"
#include <errno.h>
#include <pty.h>
@@ -95,7 +95,7 @@
#include "adb.h"
#include "adb_io.h"
#include "adb_trace.h"
-#include "sysdeps.h"
+#include "adb_utils.h"
namespace {
@@ -327,9 +327,8 @@
// the pipe fills up.
for (int fd : {stdinout_sfd_.fd(), stderr_sfd_.fd()}) {
if (fd >= 0) {
- int flags = fcntl(fd, F_GETFL, 0);
- if (flags < 0 || fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) {
- PLOG(ERROR) << "error making FD " << fd << " non-blocking";
+ if (!set_file_block_mode(fd, false)) {
+ LOG(ERROR) << "failed to set non-blocking mode for fd " << fd;
return false;
}
}
@@ -624,5 +623,3 @@
subprocess->local_socket_fd(), subprocess->pid());
return subprocess->local_socket_fd();
}
-
-#endif // !ADB_HOST
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index 8562496..f8c2f64 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -671,8 +671,8 @@
{
unsigned len;
#if ADB_HOST
- char *service = NULL;
- char* serial = NULL;
+ char *service = nullptr;
+ char* serial = nullptr;
TransportType type = kTransportAny;
#endif
@@ -739,7 +739,7 @@
type = kTransportAny;
service += strlen("host:");
} else {
- service = NULL;
+ service = nullptr;
}
if (service) {
@@ -782,7 +782,7 @@
SendOkay(s->peer->fd);
s->peer->ready = local_socket_ready;
- s->peer->shutdown = NULL;
+ s->peer->shutdown = nullptr;
s->peer->close = local_socket_close;
s->peer->peer = s2;
s2->peer = s->peer;
@@ -795,12 +795,10 @@
return 0;
}
#else /* !ADB_HOST */
- if (s->transport == NULL) {
+ if (s->transport == nullptr) {
std::string error_msg = "unknown failure";
- s->transport =
- acquire_one_transport(kCsAny, kTransportAny, NULL, &error_msg);
-
- if (s->transport == NULL) {
+ s->transport = acquire_one_transport(kTransportAny, nullptr, nullptr, &error_msg);
+ if (s->transport == nullptr) {
SendFail(s->peer->fd, error_msg);
goto fail;
}
@@ -822,7 +820,7 @@
** tear down
*/
s->peer->ready = local_socket_ready_notify;
- s->peer->shutdown = NULL;
+ s->peer->shutdown = nullptr;
s->peer->close = local_socket_close_notify;
s->peer->peer = 0;
/* give him our transport and upref it */
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 501a39a..e9e774f 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -653,22 +653,28 @@
return !*to_test;
}
-atransport* acquire_one_transport(ConnectionState state, TransportType type,
- const char* serial, std::string* error_out) {
- atransport *result = NULL;
- int ambiguous = 0;
+atransport* acquire_one_transport(TransportType type, const char* serial,
+ bool* is_ambiguous, std::string* error_out) {
+ atransport* result = nullptr;
-retry:
- *error_out = serial ? android::base::StringPrintf("device '%s' not found", serial) : "no devices found";
+ if (serial) {
+ *error_out = android::base::StringPrintf("device '%s' not found", serial);
+ } else if (type == kTransportLocal) {
+ *error_out = "no emulators found";
+ } else if (type == kTransportAny) {
+ *error_out = "no devices/emulators found";
+ } else {
+ *error_out = "no devices found";
+ }
adb_mutex_lock(&transport_lock);
- for (auto t : transport_list) {
+ for (const auto& t : transport_list) {
if (t->connection_state == kCsNoPerm) {
*error_out = "insufficient permissions for device";
continue;
}
- /* check for matching serial number */
+ // Check for matching serial number.
if (serial) {
if ((t->serial && !strcmp(serial, t->serial)) ||
(t->devpath && !strcmp(serial, t->devpath)) ||
@@ -677,8 +683,8 @@
qual_match(serial, "device:", t->device, false)) {
if (result) {
*error_out = "more than one device";
- ambiguous = 1;
- result = NULL;
+ if (is_ambiguous) *is_ambiguous = true;
+ result = nullptr;
break;
}
result = t;
@@ -687,24 +693,24 @@
if (type == kTransportUsb && t->type == kTransportUsb) {
if (result) {
*error_out = "more than one device";
- ambiguous = 1;
- result = NULL;
+ if (is_ambiguous) *is_ambiguous = true;
+ result = nullptr;
break;
}
result = t;
} else if (type == kTransportLocal && t->type == kTransportLocal) {
if (result) {
*error_out = "more than one emulator";
- ambiguous = 1;
- result = NULL;
+ if (is_ambiguous) *is_ambiguous = true;
+ result = nullptr;
break;
}
result = t;
} else if (type == kTransportAny) {
if (result) {
*error_out = "more than one device/emulator";
- ambiguous = 1;
- result = NULL;
+ if (is_ambiguous) *is_ambiguous = true;
+ result = nullptr;
break;
}
result = t;
@@ -713,37 +719,26 @@
}
adb_mutex_unlock(&transport_lock);
- if (result) {
- if (result->connection_state == kCsUnauthorized) {
- *error_out = "device unauthorized.\n";
- char* ADB_VENDOR_KEYS = getenv("ADB_VENDOR_KEYS");
- *error_out += "This adb server's $ADB_VENDOR_KEYS is ";
- *error_out += ADB_VENDOR_KEYS ? ADB_VENDOR_KEYS : "not set";
- *error_out += "\n";
- *error_out += "Try 'adb kill-server' if that seems wrong.\n";
- *error_out += "Otherwise check for a confirmation dialog on your device.";
- result = NULL;
- }
+ // Don't return unauthorized devices; the caller can't do anything with them.
+ if (result && result->connection_state == kCsUnauthorized) {
+ *error_out = "device unauthorized.\n";
+ char* ADB_VENDOR_KEYS = getenv("ADB_VENDOR_KEYS");
+ *error_out += "This adb server's $ADB_VENDOR_KEYS is ";
+ *error_out += ADB_VENDOR_KEYS ? ADB_VENDOR_KEYS : "not set";
+ *error_out += "\n";
+ *error_out += "Try 'adb kill-server' if that seems wrong.\n";
+ *error_out += "Otherwise check for a confirmation dialog on your device.";
+ result = nullptr;
+ }
- /* offline devices are ignored -- they are either being born or dying */
- if (result && result->connection_state == kCsOffline) {
- *error_out = "device offline";
- result = NULL;
- }
-
- /* check for required connection state */
- if (result && state != kCsAny && result->connection_state != state) {
- *error_out = "invalid device state";
- result = NULL;
- }
+ // Don't return offline devices; the caller can't do anything with them.
+ if (result && result->connection_state == kCsOffline) {
+ *error_out = "device offline";
+ result = nullptr;
}
if (result) {
- /* found one that we can take */
*error_out = "success";
- } else if (state != kCsAny && (serial || !ambiguous)) {
- adb_sleep_ms(1000);
- goto retry;
}
return result;
@@ -830,7 +825,7 @@
}
void atransport::RunDisconnects() {
- for (auto& disconnect : disconnects_) {
+ for (const auto& disconnect : disconnects_) {
disconnect->func(disconnect->opaque, this);
}
disconnects_.clear();
@@ -877,7 +872,7 @@
std::string list_transports(bool long_listing) {
std::string result;
adb_mutex_lock(&transport_lock);
- for (const auto t : transport_list) {
+ for (const auto& t : transport_list) {
append_transport(t, &result, long_listing);
}
adb_mutex_unlock(&transport_lock);
@@ -887,7 +882,7 @@
/* hack for osx */
void close_usb_devices() {
adb_mutex_lock(&transport_lock);
- for (auto t : transport_list) {
+ for (const auto& t : transport_list) {
if (!t->kicked) {
t->kicked = 1;
t->kick(t);
@@ -913,7 +908,7 @@
}
adb_mutex_lock(&transport_lock);
- for (auto transport : pending_list) {
+ for (const auto& transport : pending_list) {
if (transport->serial && strcmp(serial, transport->serial) == 0) {
adb_mutex_unlock(&transport_lock);
delete t;
@@ -921,7 +916,7 @@
}
}
- for (auto transport : transport_list) {
+ for (const auto& transport : transport_list) {
if (transport->serial && strcmp(serial, transport->serial) == 0) {
adb_mutex_unlock(&transport_lock);
delete t;
diff --git a/adb/transport.h b/adb/transport.h
index dfe8875..f41a8d4 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -122,12 +122,13 @@
/*
* Obtain a transport from the available transports.
- * If state is != kCsAny, only transports in that state are considered.
- * If serial is non-NULL then only the device with that serial will be chosen.
- * If no suitable transport is found, error is set.
+ * If serial is non-null then only the device with that serial will be chosen.
+ * If multiple devices/emulators would match, *is_ambiguous (if non-null)
+ * is set to true and nullptr returned.
+ * If no suitable transport is found, error is set and nullptr returned.
*/
-atransport* acquire_one_transport(ConnectionState state, TransportType type,
- const char* serial, std::string* error_out);
+atransport* acquire_one_transport(TransportType type, const char* serial,
+ bool* is_ambiguous, std::string* error_out);
void kick_transport(atransport* t);
void update_transports(void);
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/crash_reporter/crash_reporter.rc b/crash_reporter/crash_reporter.rc
index 30e87f5..7bfe0c2 100644
--- a/crash_reporter/crash_reporter.rc
+++ b/crash_reporter/crash_reporter.rc
@@ -1,6 +1,6 @@
on property:crash_reporter.coredump.enabled=1
write /proc/sys/kernel/core_pattern \
- "|/system/bin/crash_reporter --user=%P:%s:%u:%e"
+ "|/system/bin/crash_reporter --user=%P:%s:%u:%g:%e"
on property:crash_reporter.coredump.enabled=0
write /proc/sys/kernel/core_pattern "core"
@@ -14,11 +14,14 @@
rmdir /data/misc/crash_reporter/lock/crash_sender
# Create crash directories.
- mkdir /data/misc/crash_reporter 0700 root root
+ # These directories are group-writable by root so that crash_reporter can
+ # still access them when it switches users.
+ mkdir /data/misc/crash_reporter 0770 root root
+ mkdir /data/misc/crash_reporter/crash 0770 root root
mkdir /data/misc/crash_reporter/lock 0700 root root
mkdir /data/misc/crash_reporter/log 0700 root root
mkdir /data/misc/crash_reporter/run 0700 root root
- mkdir /data/misc/crash_reporter/tmp 0700 root root
+ mkdir /data/misc/crash_reporter/tmp 0770 root root
service crash_reporter /system/bin/crash_reporter --init
class late_start
diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc
index a9522cc..c4f02af 100644
--- a/crash_reporter/user_collector.cc
+++ b/crash_reporter/user_collector.cc
@@ -23,9 +23,11 @@
#include <pwd.h> // For struct passwd.
#include <stdint.h>
#include <sys/cdefs.h> // For __WORDSIZE
+#include <sys/fsuid.h>
#include <sys/types.h> // For getpwuid_r, getgrnam_r, WEXITSTATUS.
#include <unistd.h> // For setgroups
+#include <iostream> // For std::oct
#include <string>
#include <vector>
@@ -49,8 +51,9 @@
static const char kCoreTempFolder[] = "/data/misc/crash_reporter/tmp";
-// Define an otherwise invalid value that represents an unknown UID.
+// Define an otherwise invalid value that represents an unknown UID and GID.
static const uid_t kUnknownUid = -1;
+static const gid_t kUnknownGid = -1;
const char *UserCollector::kUserId = "Uid:\t";
const char *UserCollector::kGroupId = "Gid:\t";
@@ -87,9 +90,9 @@
directory_failure_ = directory_failure;
filter_in_ = filter_in;
- gid_t groups[] = { AID_SYSTEM, AID_DBUS };
+ gid_t groups[] = { AID_ROOT, AID_SYSTEM, AID_DBUS };
if (setgroups(arraysize(groups), groups) != 0) {
- PLOG(FATAL) << "Unable to set groups to system and dbus";
+ PLOG(FATAL) << "Unable to set groups to root, system, and dbus";
}
}
@@ -115,22 +118,6 @@
}
}
-// Return the string that should be used for the kernel's core_pattern file.
-// Note that if you change the format of the enabled pattern, you'll probably
-// also need to change the ParseCrashAttributes() function below, the
-// user_collector_test.cc unittest, and the logging_UserCrash.py autotest.
-std::string UserCollector::GetPattern(bool enabled) const {
- if (enabled) {
- // Combine the four crash attributes into one parameter to try to reduce
- // the size of the invocation line for crash_reporter, since the kernel
- // has a fixed-sized (128B) buffer for it (before parameter expansion).
- // Note that the kernel does not support quoted arguments in core_pattern.
- return StringPrintf("|%s --user=%%P:%%s:%%u:%%e", our_path_.c_str());
- } else {
- return "core";
- }
-}
-
bool UserCollector::SetUpInternal(bool enabled) {
CHECK(initialized_);
LOG(INFO) << (enabled ? "Enabling" : "Disabling") << " user crash handling";
@@ -227,7 +214,19 @@
bool UserCollector::CopyOffProcFiles(pid_t pid,
const FilePath &container_dir) {
if (!base::CreateDirectory(container_dir)) {
- PLOG(ERROR) << "Could not create " << container_dir.value().c_str();
+ PLOG(ERROR) << "Could not create " << container_dir.value();
+ return false;
+ }
+ int dir_mask = base::FILE_PERMISSION_READ_BY_USER
+ | base::FILE_PERMISSION_WRITE_BY_USER
+ | base::FILE_PERMISSION_EXECUTE_BY_USER
+ | base::FILE_PERMISSION_READ_BY_GROUP
+ | base::FILE_PERMISSION_WRITE_BY_GROUP;
+ if (!base::SetPosixFilePermissions(container_dir,
+ base::FILE_PERMISSION_MASK & dir_mask)) {
+ PLOG(ERROR) << "Could not set permissions for " << container_dir.value()
+ << " to " << std::oct
+ << (base::FILE_PERMISSION_MASK & dir_mask);
return false;
}
FilePath process_path = GetProcessPath(pid);
@@ -410,6 +409,43 @@
bool proc_files_usable =
CopyOffProcFiles(pid, container_dir) && ValidateProcFiles(container_dir);
+ // Switch back to the original UID/GID.
+ gid_t rgid, egid, sgid;
+ if (getresgid(&rgid, &egid, &sgid) != 0) {
+ PLOG(FATAL) << "Unable to read saved gid";
+ }
+ if (setresgid(sgid, sgid, -1) != 0) {
+ PLOG(FATAL) << "Unable to set real group ID back to saved gid";
+ } else {
+ if (getresgid(&rgid, &egid, &sgid) != 0) {
+ // If the groups cannot be read at this point, the rgid variable will
+ // contain the previously read group ID from before changing it. This
+ // will cause the chown call below to set the incorrect group for
+ // non-root crashes. But do not treat this as a fatal error, so that
+ // the rest of the collection will continue for potential manual
+ // collection by a developer.
+ PLOG(ERROR) << "Unable to read real group ID after setting it";
+ }
+ }
+
+ uid_t ruid, euid, suid;
+ if (getresuid(&ruid, &euid, &suid) != 0) {
+ PLOG(FATAL) << "Unable to read saved uid";
+ }
+ if (setresuid(suid, suid, -1) != 0) {
+ PLOG(FATAL) << "Unable to set real user ID back to saved uid";
+ } else {
+ if (getresuid(&ruid, &euid, &suid) != 0) {
+ // If the user ID cannot be read at this point, the ruid variable will
+ // contain the previously read user ID from before changing it. This
+ // will cause the chown call below to set the incorrect user for
+ // non-root crashes. But do not treat this as a fatal error, so that
+ // the rest of the collection will continue for potential manual
+ // collection by a developer.
+ PLOG(ERROR) << "Unable to read real user ID after setting it";
+ }
+ }
+
if (!CopyStdinToCoreFile(core_path)) {
return kErrorReadCoreData;
}
@@ -425,6 +461,13 @@
return error;
}
+ // Chown the temp container directory back to the original user/group that
+ // crash_reporter is run as, so that additional files can be written to
+ // the temp folder.
+ if (chown(container_dir.value().c_str(), ruid, rgid) < 0) {
+ PLOG(ERROR) << "Could not set owner for " << container_dir.value();
+ }
+
if (!RunCoreToMinidump(core_path,
container_dir, // procfs directory
minidump_path,
@@ -496,15 +539,18 @@
bool UserCollector::ParseCrashAttributes(const std::string &crash_attributes,
pid_t *pid, int *signal, uid_t *uid,
+ gid_t *gid,
std::string *kernel_supplied_name) {
- pcrecpp::RE re("(\\d+):(\\d+):(\\d+):(.*)");
- if (re.FullMatch(crash_attributes, pid, signal, uid, kernel_supplied_name))
+ pcrecpp::RE re("(\\d+):(\\d+):(\\d+):(\\d+):(.*)");
+ if (re.FullMatch(crash_attributes, pid, signal, uid, gid,
+ kernel_supplied_name))
return true;
LOG(INFO) << "Falling back to parsing crash attributes '"
- << crash_attributes << "' without UID";
+ << crash_attributes << "' without UID and GID";
pcrecpp::RE re_without_uid("(\\d+):(\\d+):(.*)");
*uid = kUnknownUid;
+ *gid = kUnknownGid;
return re_without_uid.FullMatch(crash_attributes, pid, signal,
kernel_supplied_name);
}
@@ -537,14 +583,25 @@
pid_t pid = 0;
int signal = 0;
uid_t supplied_ruid = kUnknownUid;
+ gid_t supplied_rgid = kUnknownGid;
std::string kernel_supplied_name;
if (!ParseCrashAttributes(crash_attributes, &pid, &signal, &supplied_ruid,
- &kernel_supplied_name)) {
+ &supplied_rgid, &kernel_supplied_name)) {
LOG(ERROR) << "Invalid parameter: --user=" << crash_attributes;
return false;
}
+ // Switch to the group and user that ran the crashing binary in order to
+ // access their /proc files. Do not set suid/sgid, so that we can switch
+ // back after copying the necessary files.
+ if (setresgid(supplied_rgid, supplied_rgid, -1) != 0) {
+ PLOG(FATAL) << "Unable to set real group ID to access process files";
+ }
+ if (setresuid(supplied_ruid, supplied_ruid, -1) != 0) {
+ PLOG(FATAL) << "Unable to set real user ID to access process files";
+ }
+
std::string exec;
if (force_exec) {
exec.assign(force_exec);
diff --git a/crash_reporter/user_collector.h b/crash_reporter/user_collector.h
index 8c38aa2..7261ed4 100644
--- a/crash_reporter/user_collector.h
+++ b/crash_reporter/user_collector.h
@@ -105,7 +105,6 @@
// crash_reporter-user-collection signature.
std::string GetErrorTypeSignature(ErrorType error_type) const;
- std::string GetPattern(bool enabled) const;
bool SetUpInternal(bool enabled);
// Returns, via |line|, the first line in |lines| that starts with |prefix|.
@@ -166,7 +165,7 @@
ErrorType ConvertAndEnqueueCrash(pid_t pid, const std::string &exec_name,
uid_t supplied_ruid, bool *out_of_capacity);
bool ParseCrashAttributes(const std::string &crash_attributes,
- pid_t *pid, int *signal, uid_t *uid,
+ pid_t *pid, int *signal, uid_t *uid, gid_t *gid,
std::string *kernel_supplied_name);
bool ShouldDump(bool has_owner_consent,
diff --git a/crash_reporter/user_collector_test.cc b/crash_reporter/user_collector_test.cc
index 4419e7c..1dfac2a 100644
--- a/crash_reporter/user_collector_test.cc
+++ b/crash_reporter/user_collector_test.cc
@@ -98,35 +98,38 @@
pid_t pid;
int signal;
uid_t uid;
+ gid_t gid;
std::string exec_name;
- EXPECT_TRUE(collector_.ParseCrashAttributes("123456:11:1000:foobar",
- &pid, &signal, &uid, &exec_name));
+ EXPECT_TRUE(collector_.ParseCrashAttributes("123456:11:1000:2000:foobar",
+ &pid, &signal, &uid, &gid, &exec_name));
EXPECT_EQ(123456, pid);
EXPECT_EQ(11, signal);
EXPECT_EQ(1000, uid);
+ EXPECT_EQ(2000, gid);
EXPECT_EQ("foobar", exec_name);
EXPECT_TRUE(collector_.ParseCrashAttributes("4321:6:barfoo",
- &pid, &signal, &uid, &exec_name));
+ &pid, &signal, &uid, &gid, &exec_name));
EXPECT_EQ(4321, pid);
EXPECT_EQ(6, signal);
EXPECT_EQ(-1, uid);
+ EXPECT_EQ(-1, gid);
EXPECT_EQ("barfoo", exec_name);
EXPECT_FALSE(collector_.ParseCrashAttributes("123456:11",
- &pid, &signal, &uid, &exec_name));
+ &pid, &signal, &uid, &gid, &exec_name));
EXPECT_TRUE(collector_.ParseCrashAttributes("123456:11:exec:extra",
- &pid, &signal, &uid, &exec_name));
+ &pid, &signal, &uid, &gid, &exec_name));
EXPECT_EQ("exec:extra", exec_name);
EXPECT_FALSE(collector_.ParseCrashAttributes("12345p:11:foobar",
- &pid, &signal, &uid, &exec_name));
+ &pid, &signal, &uid, &gid, &exec_name));
EXPECT_FALSE(collector_.ParseCrashAttributes("123456:1 :foobar",
- &pid, &signal, &uid, &exec_name));
+ &pid, &signal, &uid, &gid, &exec_name));
EXPECT_FALSE(collector_.ParseCrashAttributes("123456::foobar",
- &pid, &signal, &uid, &exec_name));
+ &pid, &signal, &uid, &gid, &exec_name));
}
TEST_F(UserCollectorTest, ShouldDumpDeveloperImageOverridesConsent) {
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/Android.mk b/fs_mgr/Android.mk
index cf2965e..5e6c6f9 100644
--- a/fs_mgr/Android.mk
+++ b/fs_mgr/Android.mk
@@ -3,9 +3,13 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_SRC_FILES:= fs_mgr.c fs_mgr_verity.c fs_mgr_fstab.c fs_mgr_slotselect.c
+LOCAL_SRC_FILES:= fs_mgr.c fs_mgr_verity.c fs_mgr_fstab.c
+LOCAL_SRC_FILES += fs_mgr_format.c fs_mgr_slotselect.c
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include \
+ system/vold \
+ system/extras/ext4_utils \
+ external/openssl/include
LOCAL_MODULE:= libfs_mgr
LOCAL_STATIC_LIBRARIES := liblogwrap libmincrypt libext4_utils_static libsquashfs_utils
@@ -36,6 +40,7 @@
LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED)
LOCAL_STATIC_LIBRARIES := libfs_mgr liblogwrap libcutils liblog libc libmincrypt libext4_utils_static libsquashfs_utils
+LOCAL_STATIC_LIBRARIES += libsparse_static libz libselinux
LOCAL_CXX_STL := libc++_static
LOCAL_CFLAGS := -Werror
diff --git a/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.c
index 044c895..c47a585 100644
--- a/fs_mgr/fs_mgr.c
+++ b/fs_mgr/fs_mgr.c
@@ -44,6 +44,9 @@
#include "mincrypt/sha.h"
#include "mincrypt/sha256.h"
+#include "ext4_utils.h"
+#include "wipe.h"
+
#include "fs_mgr_priv.h"
#include "fs_mgr_priv_verity.h"
@@ -557,6 +560,8 @@
}
}
int last_idx_inspected;
+ int top_idx = i;
+
mret = mount_with_alternatives(fstab, i, &last_idx_inspected, &attempted_idx);
i = last_idx_inspected;
mount_errno = errno;
@@ -582,10 +587,38 @@
continue;
}
- /* mount(2) returned an error, check if it's encryptable and deal with it */
+ /* mount(2) returned an error, handle the encryptable/formattable case */
+ bool wiped = partition_wiped(fstab->recs[top_idx].blk_device);
+ if (mret && mount_errno != EBUSY && mount_errno != EACCES &&
+ fs_mgr_is_formattable(&fstab->recs[top_idx]) && wiped) {
+ /* top_idx and attempted_idx point at the same partition, but sometimes
+ * at two different lines in the fstab. Use the top one for formatting
+ * as that is the preferred one.
+ */
+ ERROR("%s(): %s is wiped and %s %s is formattable. Format it.\n", __func__,
+ fstab->recs[top_idx].blk_device, fstab->recs[top_idx].mount_point,
+ fstab->recs[top_idx].fs_type);
+ if (fs_mgr_is_encryptable(&fstab->recs[top_idx]) &&
+ strcmp(fstab->recs[top_idx].key_loc, KEY_IN_FOOTER)) {
+ int fd = open(fstab->recs[top_idx].key_loc, O_WRONLY, 0644);
+ if (fd >= 0) {
+ INFO("%s(): also wipe %s\n", __func__, fstab->recs[top_idx].key_loc);
+ wipe_block_device(fd, get_file_size(fd));
+ close(fd);
+ } else {
+ ERROR("%s(): %s wouldn't open (%s)\n", __func__,
+ fstab->recs[top_idx].key_loc, strerror(errno));
+ }
+ }
+ if (fs_mgr_do_format(&fstab->recs[top_idx]) == 0) {
+ /* Let's replay the mount actions. */
+ i = top_idx - 1;
+ continue;
+ }
+ }
if (mret && mount_errno != EBUSY && mount_errno != EACCES &&
fs_mgr_is_encryptable(&fstab->recs[attempted_idx])) {
- if(partition_wiped(fstab->recs[attempted_idx].blk_device)) {
+ if (wiped) {
ERROR("%s(): %s is wiped and %s %s is encryptable. Suggest recovery...\n", __func__,
fstab->recs[attempted_idx].blk_device, fstab->recs[attempted_idx].mount_point,
fstab->recs[attempted_idx].fs_type);
diff --git a/fs_mgr/fs_mgr_format.c b/fs_mgr/fs_mgr_format.c
new file mode 100644
index 0000000..c73045d
--- /dev/null
+++ b/fs_mgr/fs_mgr_format.c
@@ -0,0 +1,119 @@
+/*
+ * 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 <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <cutils/partition_utils.h>
+#include <sys/mount.h>
+#include "ext4_utils.h"
+#include "ext4.h"
+#include "make_ext4fs.h"
+#include "fs_mgr_priv.h"
+
+extern struct fs_info info; /* magic global from ext4_utils */
+extern void reset_ext4fs_info();
+
+static int format_ext4(char *fs_blkdev, char *fs_mnt_point)
+{
+ unsigned int nr_sec;
+ int fd, rc = 0;
+
+ if ((fd = open(fs_blkdev, O_WRONLY, 0644)) < 0) {
+ ERROR("Cannot open block device. %s\n", strerror(errno));
+ return -1;
+ }
+
+ if ((ioctl(fd, BLKGETSIZE, &nr_sec)) == -1) {
+ ERROR("Cannot get block device size. %s\n", strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ /* Format the partition using the calculated length */
+ reset_ext4fs_info();
+ info.len = ((off64_t)nr_sec * 512);
+
+ /* Use make_ext4fs_internal to avoid wiping an already-wiped partition. */
+ 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);
+ }
+ close(fd);
+
+ return rc;
+}
+
+static int format_f2fs(char *fs_blkdev)
+{
+ char * args[3];
+ int pid;
+ int rc = 0;
+
+ args[0] = (char *)"/sbin/mkfs.f2fs";
+ args[1] = fs_blkdev;
+ args[2] = (char *)0;
+
+ pid = fork();
+ if (pid < 0) {
+ return pid;
+ }
+ if (!pid) {
+ /* This doesn't return */
+ execv("/sbin/mkfs.f2fs", args);
+ exit(1);
+ }
+ for(;;) {
+ pid_t p = waitpid(pid, &rc, 0);
+ if (p != pid) {
+ ERROR("Error waiting for child process - %d\n", p);
+ rc = -1;
+ break;
+ }
+ if (WIFEXITED(rc)) {
+ rc = WEXITSTATUS(rc);
+ INFO("%s done, status %d\n", args[0], rc);
+ if (rc) {
+ rc = -1;
+ }
+ break;
+ }
+ ERROR("Still waiting for %s...\n", args[0]);
+ }
+
+ return rc;
+}
+
+int fs_mgr_do_format(struct fstab_rec *fstab)
+{
+ int rc = -EINVAL;
+
+ ERROR("%s: Format %s as '%s'.\n", __func__, fstab->blk_device, fstab->fs_type);
+
+ if (!strncmp(fstab->fs_type, "f2fs", 4)) {
+ rc = format_f2fs(fstab->blk_device);
+ } else if (!strncmp(fstab->fs_type, "ext4", 4)) {
+ rc = format_ext4(fstab->blk_device, fstab->mount_point);
+ } else {
+ ERROR("File system type '%s' is not supported\n", fstab->fs_type);
+ }
+
+ return rc;
+}
diff --git a/fs_mgr/fs_mgr_fstab.c b/fs_mgr/fs_mgr_fstab.c
index 4f18339..cf35b3f 100644
--- a/fs_mgr/fs_mgr_fstab.c
+++ b/fs_mgr/fs_mgr_fstab.c
@@ -15,10 +15,12 @@
*/
#include <ctype.h>
+#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mount.h>
+#include <unistd.h>
#include "fs_mgr_priv.h"
@@ -70,11 +72,26 @@
{ "zramsize=", MF_ZRAMSIZE },
{ "verify", MF_VERIFY },
{ "noemulatedsd", MF_NOEMULATEDSD },
+ { "notrim", MF_NOTRIM },
+ { "formattable", MF_FORMATTABLE },
{ "slotselect", MF_SLOTSELECT },
{ "defaults", 0 },
{ 0, 0 },
};
+static uint64_t calculate_zram_size(unsigned int percentage)
+{
+ uint64_t total;
+
+ total = sysconf(_SC_PHYS_PAGES);
+ total *= percentage;
+ total /= 100;
+
+ total *= sysconf(_SC_PAGESIZE);
+
+ return total;
+}
+
static int parse_flags(char *flags, struct flag_list *fl,
struct fs_mgr_flag_values *flag_vals,
char *fs_options, int fs_options_len)
@@ -156,7 +173,12 @@
} else if ((fl[i].flag == MF_SWAPPRIO) && flag_vals) {
flag_vals->swap_prio = strtoll(strchr(p, '=') + 1, NULL, 0);
} else if ((fl[i].flag == MF_ZRAMSIZE) && flag_vals) {
- flag_vals->zram_size = strtoll(strchr(p, '=') + 1, NULL, 0);
+ int is_percent = !!strrchr(p, '%');
+ unsigned int val = strtoll(strchr(p, '=') + 1, NULL, 0);
+ if (is_percent)
+ flag_vals->zram_size = calculate_zram_size(val);
+ else
+ flag_vals->zram_size = val;
}
break;
}
@@ -455,6 +477,16 @@
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);
+}
+
int fs_mgr_is_slotselect(struct fstab_rec *fstab)
{
return fstab->fs_mgr_flags & MF_SLOTSELECT;
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index 992b544..367ab6e 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -76,7 +76,9 @@
#define MF_FORCECRYPT 0x400
#define MF_NOEMULATEDSD 0x800 /* no emulated sdcard daemon, sd card is the only
external storage */
+#define MF_NOTRIM 0x1000
#define MF_FILEENCRYPTION 0x2000
+#define MF_FORMATTABLE 0x4000
#define MF_SLOTSELECT 0x8000
#define DM_BUF_SIZE 4096
@@ -85,4 +87,3 @@
int fs_mgr_update_for_slotselect(struct fstab *fstab);
#endif /* __CORE_FS_MGR_PRIV_H */
-
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index c58a888..27fccf7 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -103,10 +103,14 @@
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);
+
+int fs_mgr_do_format(struct fstab_rec *fstab);
+
#ifdef __cplusplus
}
#endif
#endif /* __CORE_FS_MGR_H */
-
diff --git a/gatekeeperd/Android.mk b/gatekeeperd/Android.mk
new file mode 100644
index 0000000..3f78955
--- /dev/null
+++ b/gatekeeperd/Android.mk
@@ -0,0 +1,42 @@
+#
+# 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
+LOCAL_INIT_RC := gatekeeperd.rc
+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/gatekeeperd.rc b/gatekeeperd/gatekeeperd.rc
new file mode 100644
index 0000000..3f1b92d
--- /dev/null
+++ b/gatekeeperd/gatekeeperd.rc
@@ -0,0 +1,3 @@
+service gatekeeperd /system/bin/gatekeeperd /data/misc/gatekeeper
+ class late_start
+ user system
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 cc6824a..85888c3 100644
--- a/healthd/healthd.cpp
+++ b/healthd/healthd.cpp
@@ -55,6 +55,7 @@
.batteryFullChargePath = String8(String8::kEmptyString),
.batteryCycleCountPath = String8(String8::kEmptyString),
.energyCounter = NULL,
+ .boot_min_cap = 0,
.screen_on = NULL,
};
diff --git a/healthd/healthd.h b/healthd/healthd.h
index 048fcd5..34ea55f 100644
--- a/healthd/healthd.h
+++ b/healthd/healthd.h
@@ -69,6 +69,7 @@
android::String8 batteryCycleCountPath;
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/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/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 967e667..c7eb34b 100644
--- a/include/private/android_filesystem_config.h
+++ b/include/private/android_filesystem_config.h
@@ -216,13 +216,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 ced2005..0000000
--- a/include/system/audio.h
+++ /dev/null
@@ -1,1375 +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 ((audio_is_output_devices(device) &&
- (device & AUDIO_DEVICE_OUT_REMOTE_SUBMIX) == AUDIO_DEVICE_OUT_REMOTE_SUBMIX)
- || (!audio_is_output_devices(device) &&
- (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 inline 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/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/init/Android.mk b/init/Android.mk
index 7670951..c1cdc1a 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -95,7 +95,9 @@
libselinux \
libmincrypt \
libc++_static \
- libdl
+ libdl \
+ libsparse_static \
+ libz
# Create symlinks
LOCAL_POST_INSTALL_CMD := $(hide) mkdir -p $(TARGET_ROOT_OUT)/sbin; \
diff --git a/init/devices.cpp b/init/devices.cpp
index 3652c57..d556e30 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -528,7 +528,7 @@
make_device(devpath, path, block, major, minor, (const char **)links);
if (links) {
for (i = 0; links[i]; i++)
- make_link(devpath, links[i]);
+ make_link_init(devpath, links[i]);
}
}
diff --git a/init/init_parser.cpp b/init/init_parser.cpp
index 02b3985..5b8e27b 100644
--- a/init/init_parser.cpp
+++ b/init/init_parser.cpp
@@ -105,7 +105,10 @@
for (const auto& sp : section_parsers_) {
sp.second->EndFile(path);
}
- DumpState();
+
+ // Turning this on and letting the INFO logging be discarded adds 0.2s to
+ // Nexus 9 boot time, so it's disabled by default.
+ if (false) DumpState();
NOTICE("(Parsing %s took %.2fs.)\n", path.c_str(), t.duration());
return true;
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 579b92d..ebf768e 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -207,6 +207,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) {
@@ -238,9 +248,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;
diff --git a/init/util.cpp b/init/util.cpp
index 1eb90e0..b316d6e 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -337,7 +337,7 @@
}
}
-void make_link(const char *oldpath, const char *newpath)
+void make_link_init(const char *oldpath, const char *newpath)
{
int ret;
char buf[256];
@@ -376,10 +376,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;
diff --git a/init/util.h b/init/util.h
index a33209e..c2efb01 100644
--- a/init/util.h
+++ b/init/util.h
@@ -54,7 +54,7 @@
int mkdir_recursive(const char *pathname, mode_t mode);
void sanitize(char *p);
-void make_link(const char *oldpath, const char *newpath);
+void make_link_init(const char *oldpath, const char *newpath);
void remove_link(const char *oldpath, const char *newpath);
int wait_for_file(const char *filename, int timeout);
void open_devnull_stdio(void);
diff --git a/libcutils/Android.mk b/libcutils/Android.mk
index 015bd3f..3ca544f 100644
--- a/libcutils/Android.mk
+++ b/libcutils/Android.mk
@@ -112,6 +112,9 @@
LOCAL_C_INCLUDES := $(libcutils_c_includes)
LOCAL_STATIC_LIBRARIES := liblog
+ifneq ($(ENABLE_CPUSETS),)
+LOCAL_CFLAGS += -DUSE_CPUSETS
+endif
LOCAL_CFLAGS += -Werror -Wall -Wextra -std=gnu90
LOCAL_CLANG := true
LOCAL_SANITIZE := integer
@@ -123,6 +126,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 -Wall -Wextra
LOCAL_C_INCLUDES := $(libcutils_c_includes)
LOCAL_CLANG := true
diff --git a/libcutils/fs_config.c b/libcutils/fs_config.c
index 27981ff..970b386 100644
--- a/libcutils/fs_config.c
+++ b/libcutils/fs_config.c
@@ -126,11 +126,10 @@
{ 00644, AID_SYSTEM, AID_SYSTEM, 0, "data/app-private/*" },
{ 00644, AID_APP, AID_APP, 0, "data/data/*" },
- /* the following five files are INTENTIONALLY set-uid, but they
+ /* the following four files are INTENTIONALLY set-uid, but they
* are NOT included on user builds. */
{ 04750, AID_ROOT, AID_SHELL, 0, "system/xbin/su" },
{ 06755, AID_ROOT, AID_ROOT, 0, "system/xbin/librank" },
- { 06755, AID_ROOT, AID_ROOT, 0, "system/xbin/procrank" },
{ 06755, AID_ROOT, AID_ROOT, 0, "system/xbin/procmem" },
{ 04770, AID_ROOT, AID_RADIO, 0, "system/bin/pppd-ril" },
@@ -154,14 +153,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;
- if (asprintf(&name, "%s%s", out, dir ? conf_dir : conf_file) != -1) {
+ int target_out_path_len = strlen(target_out_path);
+ int skip_len = strlen("/system");
+
+ if (target_out_path[target_out_path_len] == '/') {
+ skip_len++;
+ }
+ if (asprintf(&name, "%s%s", target_out_path, (dir ? conf_dir : conf_file) + skip_len) != -1) {
fd = TEMP_FAILURE_RETRY(open(name, O_RDONLY | O_BINARY));
free(name);
}
@@ -191,7 +197,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;
@@ -203,7 +209,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/native_handle.c b/libcutils/native_handle.c
index 9a4a5bb..61fa38e 100644
--- a/libcutils/native_handle.c
+++ b/libcutils/native_handle.c
@@ -25,11 +25,17 @@
#include <cutils/log.h>
#include <cutils/native_handle.h>
+static const int kMaxNativeFds = 1024;
+static const int kMaxNativeInts = 1024;
+
native_handle_t* native_handle_create(int numFds, int numInts)
{
- native_handle_t* h = malloc(
- sizeof(native_handle_t) + sizeof(int)*(numFds+numInts));
+ if (numFds < 0 || numInts < 0 || numFds > kMaxNativeFds || numInts > kMaxNativeInts) {
+ return NULL;
+ }
+ size_t mallocSize = sizeof(native_handle_t) + (sizeof(int) * (numFds + numInts));
+ native_handle_t* h = malloc(mallocSize);
if (h) {
h->version = sizeof(native_handle_t);
h->numFds = numFds;
diff --git a/libcutils/sched_policy.c b/libcutils/sched_policy.c
index 103ff66..298e3da 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,18 @@
static int bg_cgroup_fd = -1;
static int fg_cgroup_fd = -1;
+#ifdef USE_CPUSETS
+// 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;
+#endif
+
/* 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 +92,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 +120,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 +240,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 +326,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 +352,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 +394,3 @@
else
return "error";
}
-
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/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 ae12cb5..bae16bb 100644
--- a/libutils/Android.mk
+++ b/libutils/Android.mk
@@ -15,11 +15,9 @@
LOCAL_PATH:= $(call my-dir)
commonSources:= \
- BlobCache.cpp \
CallStack.cpp \
FileMap.cpp \
JenkinsHash.cpp \
- LinearAllocator.cpp \
LinearTransform.cpp \
Log.cpp \
NativeHandle.cpp \
@@ -66,6 +64,7 @@
# we have the common sources, plus some device-specific stuff
LOCAL_SRC_FILES:= \
$(commonSources) \
+ BlobCache.cpp \
Looper.cpp \
Trace.cpp
@@ -75,7 +74,8 @@
LOCAL_CFLAGS += -Werror
LOCAL_STATIC_LIBRARIES := \
- libcutils
+ libcutils \
+ libc
LOCAL_SHARED_LIBRARIES := \
libbacktrace \
@@ -103,6 +103,22 @@
LOCAL_SANITIZE := integer
include $(BUILD_SHARED_LIBRARY)
+# Include subdirectory makefiles
+# ============================================================
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := SharedBufferTest
+LOCAL_STATIC_LIBRARIES := libutils libcutils
+LOCAL_SHARED_LIBRARIES := liblog
+LOCAL_SRC_FILES := SharedBufferTest.cpp
+include $(BUILD_NATIVE_TEST)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := SharedBufferTest
+LOCAL_STATIC_LIBRARIES := libutils libcutils
+LOCAL_SHARED_LIBRARIES := liblog
+LOCAL_SRC_FILES := SharedBufferTest.cpp
+include $(BUILD_HOST_NATIVE_TEST)
# Build the tests in the tests/ subdirectory.
include $(call first-makefiles-under,$(LOCAL_PATH))
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 20c1e92..b14884b 100644
--- a/libutils/Looper.cpp
+++ b/libutils/Looper.cpp
@@ -20,7 +20,9 @@
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
+#include <inttypes.h>
#include <string.h>
+#include <sys/eventfd.h>
#include <unistd.h>
@@ -70,41 +72,21 @@
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: %s", strerror(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: %s",
+ strerror(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: %s",
- strerror(errno));
-
- result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);
- LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking: %s",
- strerror(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: %s", strerror(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: %s",
- strerror(errno));
+ AutoMutex _l(mLock);
+ rebuildEpollLocked();
}
Looper::~Looper() {
- close(mWakeReadPipeFd);
- close(mWakeWritePipeFd);
- close(mEpollFd);
+ close(mWakeEventFd);
+ if (mEpollFd >= 0) {
+ close(mEpollFd);
+ }
}
void Looper::initTLSKey() {
@@ -158,6 +140,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: %s", strerror(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: %s",
+ strerror(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: %s",
+ request.fd, strerror(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 (;;) {
@@ -208,7 +234,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
}
@@ -219,17 +245,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) {
@@ -257,11 +290,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);
@@ -328,10 +361,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();
@@ -372,12 +409,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: %s", strerror(errno));
}
@@ -389,11 +423,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) {
@@ -427,23 +458,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) {
@@ -456,8 +484,36 @@
} else {
int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_MOD, fd, & eventItem);
if (epollResult < 0) {
- ALOGE("Error modifying epoll events for fd %d: %s", fd, strerror(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: %s",
+ this, strerror(errno));
+#endif
+ epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);
+ if (epollResult < 0) {
+ ALOGE("Error modifying or adding epoll events for fd %d: %s",
+ fd, strerror(errno));
+ return -1;
+ }
+ scheduleEpollRebuildLocked();
+ } else {
+ ALOGE("Error modifying epoll events for fd %d: %s", fd, strerror(errno));
+ return -1;
+ }
}
mRequests.replaceValueAt(requestIndex, request);
}
@@ -466,8 +522,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
@@ -477,13 +537,48 @@
return 0;
}
- int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_DEL, fd, NULL);
- if (epollResult < 0) {
- ALOGE("Error removing epoll events for fd %d: %s", fd, strerror(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: %s", this, strerror(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: %s", fd, strerror(errno));
+ scheduleEpollRebuildLocked();
+ return -1;
+ }
+ }
} // release lock
return 1;
}
@@ -502,7 +597,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
@@ -568,8 +663,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/SharedBuffer.cpp b/libutils/SharedBuffer.cpp
index 003e386..c7dd1ab 100644
--- a/libutils/SharedBuffer.cpp
+++ b/libutils/SharedBuffer.cpp
@@ -14,9 +14,12 @@
* limitations under the License.
*/
+#define __STDC_LIMIT_MACROS
+#include <stdint.h>
#include <stdlib.h>
#include <string.h>
+#include <log/log.h>
#include <utils/Atomic.h>
#include "SharedBuffer.h"
@@ -27,6 +30,11 @@
SharedBuffer* SharedBuffer::alloc(size_t size)
{
+ // Don't overflow if the combined size of the buffer / header is larger than
+ // size_max.
+ LOG_ALWAYS_FATAL_IF((size >= (SIZE_MAX - sizeof(SharedBuffer))),
+ "Invalid buffer size %zu", size);
+
SharedBuffer* sb = static_cast<SharedBuffer *>(malloc(sizeof(SharedBuffer) + size));
if (sb) {
sb->mRefs = 1;
@@ -53,7 +61,7 @@
memcpy(sb->data(), data(), size());
release();
}
- return sb;
+ return sb;
}
SharedBuffer* SharedBuffer::editResize(size_t newSize) const
@@ -61,6 +69,11 @@
if (onlyOwner()) {
SharedBuffer* buf = const_cast<SharedBuffer*>(this);
if (buf->mSize == newSize) return buf;
+ // Don't overflow if the combined size of the new buffer / header is larger than
+ // size_max.
+ LOG_ALWAYS_FATAL_IF((newSize >= (SIZE_MAX - sizeof(SharedBuffer))),
+ "Invalid buffer size %zu", newSize);
+
buf = (SharedBuffer*)realloc(buf, sizeof(SharedBuffer) + newSize);
if (buf != NULL) {
buf->mSize = newSize;
diff --git a/libutils/SharedBufferTest.cpp b/libutils/SharedBufferTest.cpp
new file mode 100644
index 0000000..a0484ff
--- /dev/null
+++ b/libutils/SharedBufferTest.cpp
@@ -0,0 +1,58 @@
+/*
+ * 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 __STDC_LIMIT_MACROS
+
+#include <gtest/gtest.h>
+
+#include <memory>
+#include <stdint.h>
+
+#include "SharedBuffer.h"
+
+TEST(SharedBufferTest, TestAlloc) {
+ EXPECT_DEATH(android::SharedBuffer::alloc(SIZE_MAX), "");
+ EXPECT_DEATH(android::SharedBuffer::alloc(SIZE_MAX - sizeof(android::SharedBuffer)), "");
+
+ // Make sure we don't die here.
+ // Check that null is returned, as we are asking for the whole address space.
+ android::SharedBuffer* buf =
+ android::SharedBuffer::alloc(SIZE_MAX - sizeof(android::SharedBuffer) - 1);
+ ASSERT_TRUE(NULL == buf);
+
+ buf = android::SharedBuffer::alloc(0);
+ ASSERT_FALSE(NULL == buf);
+ ASSERT_EQ(0U, buf->size());
+ buf->release();
+}
+
+TEST(SharedBufferTest, TestEditResize) {
+ android::SharedBuffer* buf = android::SharedBuffer::alloc(10);
+ EXPECT_DEATH(buf->editResize(SIZE_MAX - sizeof(android::SharedBuffer)), "");
+ buf = android::SharedBuffer::alloc(10);
+ EXPECT_DEATH(buf->editResize(SIZE_MAX), "");
+
+ buf = android::SharedBuffer::alloc(10);
+ // Make sure we don't die here.
+ // Check that null is returned, as we are asking for the whole address space.
+ buf = buf->editResize(SIZE_MAX - sizeof(android::SharedBuffer) - 1);
+ ASSERT_TRUE(NULL == buf);
+
+ buf = android::SharedBuffer::alloc(10);
+ buf = buf->editResize(0);
+ ASSERT_EQ(0U, buf->size());
+ buf->release();
+}
diff --git a/libutils/String8.cpp b/libutils/String8.cpp
index 81bfccf..771d312 100644
--- a/libutils/String8.cpp
+++ b/libutils/String8.cpp
@@ -14,6 +14,9 @@
* limitations under the License.
*/
+#define __STDC_LIMIT_MACROS
+#include <stdint.h>
+
#include <utils/String8.h>
#include <utils/Compat.h>
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 9440b68..ddbfb3e 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -656,7 +656,7 @@
break;
case 'f':
- if ((tail_time == log_time::EPOCH) && (tail_lines != 0)) {
+ if ((tail_time == log_time::EPOCH) && (tail_lines == 0)) {
tail_time = lastLogTime(optarg);
}
// redirect output to a file
@@ -832,49 +832,66 @@
} else {
logger_list = android_logger_list_alloc(mode, tail_lines, 0);
}
+ const char *openDeviceFail = NULL;
+ const char *clearFail = NULL;
+ const char *setSizeFail = NULL;
+ const char *getSizeFail = NULL;
+ // We have three orthogonal actions below to clear, set log size and
+ // get log size. All sharing the same iteration loop.
while (dev) {
dev->logger_list = logger_list;
dev->logger = android_logger_open(logger_list,
android_name_to_log_id(dev->device));
if (!dev->logger) {
- logcat_panic(false, "Unable to open log device '%s'\n",
- dev->device);
+ openDeviceFail = openDeviceFail ?: dev->device;
+ dev = dev->next;
+ continue;
}
if (clearLog) {
- int ret;
- ret = android_logger_clear(dev->logger);
- if (ret) {
- logcat_panic(false, "failed to clear the log");
+ if (android_logger_clear(dev->logger)) {
+ clearFail = clearFail ?: dev->device;
}
}
- if (setLogSize && android_logger_set_log_size(dev->logger, setLogSize)) {
- logcat_panic(false, "failed to set the log size");
+ if (setLogSize) {
+ if (android_logger_set_log_size(dev->logger, setLogSize)) {
+ setSizeFail = setSizeFail ?: dev->device;
+ }
}
if (getLogSize) {
- long size, readable;
+ long size = android_logger_get_log_size(dev->logger);
+ long readable = android_logger_get_log_readable_size(dev->logger);
- size = android_logger_get_log_size(dev->logger);
- if (size < 0) {
- logcat_panic(false, "failed to get the log size");
+ if ((size < 0) || (readable < 0)) {
+ getSizeFail = getSizeFail ?: dev->device;
+ } else {
+ printf("%s: ring buffer is %ld%sb (%ld%sb consumed), "
+ "max entry is %db, max payload is %db\n", dev->device,
+ value_of_size(size), multiplier_of_size(size),
+ value_of_size(readable), multiplier_of_size(readable),
+ (int) LOGGER_ENTRY_MAX_LEN,
+ (int) LOGGER_ENTRY_MAX_PAYLOAD);
}
-
- readable = android_logger_get_log_readable_size(dev->logger);
- if (readable < 0) {
- logcat_panic(false, "failed to get the readable log size");
- }
-
- printf("%s: ring buffer is %ld%sb (%ld%sb consumed), "
- "max entry is %db, max payload is %db\n", dev->device,
- value_of_size(size), multiplier_of_size(size),
- value_of_size(readable), multiplier_of_size(readable),
- (int) LOGGER_ENTRY_MAX_LEN, (int) LOGGER_ENTRY_MAX_PAYLOAD);
}
dev = dev->next;
}
+ // report any errors in the above loop and exit
+ if (openDeviceFail) {
+ logcat_panic(false, "Unable to open log device '%s'\n", openDeviceFail);
+ }
+ if (clearFail) {
+ logcat_panic(false, "failed to clear the '%s' log\n", clearFail);
+ }
+ if (setSizeFail) {
+ logcat_panic(false, "failed to set the '%s' log size\n", setSizeFail);
+ }
+ if (getSizeFail) {
+ logcat_panic(false, "failed to get the readable '%s' log size",
+ getSizeFail);
+ }
if (setPruneList) {
size_t len = strlen(setPruneList);
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index bcf8d82..610a6ec 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -15,10 +15,12 @@
*/
#include <ctype.h>
+#include <dirent.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/types.h>
#include <gtest/gtest.h>
#include <log/log.h>
@@ -368,7 +370,7 @@
while (fgets(buffer, sizeof(buffer), fp)) {
int size, consumed, max, payload;
- char size_mult[2], consumed_mult[2];
+ char size_mult[3], consumed_mult[3];
long full_size, full_consumed;
size = consumed = max = payload = 0;
@@ -573,12 +575,12 @@
static const char comm[] = "logcat -b radio -b events -b system -b main"
" -d -f %s/log.txt -n 7 -r 1";
char command[sizeof(buf) + sizeof(comm)];
- sprintf(command, comm, buf);
+ snprintf(command, sizeof(command), comm, buf);
int ret;
EXPECT_FALSE((ret = system(command)));
if (!ret) {
- sprintf(command, "ls -s %s 2>/dev/null", buf);
+ snprintf(command, sizeof(command), "ls -s %s 2>/dev/null", buf);
FILE *fp;
EXPECT_TRUE(NULL != (fp = popen(command, "r")));
@@ -587,16 +589,12 @@
int count = 0;
while (fgets(buffer, sizeof(buffer), fp)) {
- static const char match_1[] = "4 log.txt";
- static const char match_2[] = "8 log.txt";
- static const char match_3[] = "12 log.txt";
- static const char match_4[] = "16 log.txt";
static const char total[] = "total ";
+ int num;
+ char c;
- if (!strncmp(buffer, match_1, sizeof(match_1) - 1)
- || !strncmp(buffer, match_2, sizeof(match_2) - 1)
- || !strncmp(buffer, match_3, sizeof(match_3) - 1)
- || !strncmp(buffer, match_4, sizeof(match_4) - 1)) {
+ if ((2 == sscanf(buffer, "%d log.tx%c", &num, &c)) &&
+ (num <= 24)) {
++count;
} else if (strncmp(buffer, total, sizeof(total) - 1)) {
fprintf(stderr, "WARNING: Parse error: %s", buffer);
@@ -606,7 +604,7 @@
EXPECT_TRUE(count == 7 || count == 8);
}
}
- sprintf(command, "rm -rf %s", buf);
+ snprintf(command, sizeof(command), "rm -rf %s", buf);
EXPECT_FALSE(system(command));
}
@@ -618,12 +616,12 @@
static const char logcat_cmd[] = "logcat -b radio -b events -b system -b main"
" -d -f %s/log.txt -n 10 -r 1";
char command[sizeof(tmp_out_dir) + sizeof(logcat_cmd)];
- sprintf(command, logcat_cmd, tmp_out_dir);
+ snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir);
int ret;
EXPECT_FALSE((ret = system(command)));
if (!ret) {
- sprintf(command, "ls %s 2>/dev/null", tmp_out_dir);
+ snprintf(command, sizeof(command), "ls %s 2>/dev/null", tmp_out_dir);
FILE *fp;
EXPECT_TRUE(NULL != (fp = popen(command, "r")));
@@ -659,7 +657,113 @@
pclose(fp);
EXPECT_EQ(11, log_file_count);
}
- sprintf(command, "rm -rf %s", tmp_out_dir);
+ snprintf(command, sizeof(command), "rm -rf %s", tmp_out_dir);
+ EXPECT_FALSE(system(command));
+}
+
+TEST(logcat, logrotate_continue) {
+ static const char tmp_out_dir_form[] = "/data/local/tmp/logcat.logrotate.XXXXXX";
+ char tmp_out_dir[sizeof(tmp_out_dir_form)];
+ ASSERT_TRUE(NULL != mkdtemp(strcpy(tmp_out_dir, tmp_out_dir_form)));
+
+ static const char log_filename[] = "log.txt";
+ static const char logcat_cmd[] = "logcat -b all -d -f %s/%s -n 256 -r 1024";
+ static const char cleanup_cmd[] = "rm -rf %s";
+ char command[sizeof(tmp_out_dir) + sizeof(logcat_cmd) + sizeof(log_filename)];
+ snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir, log_filename);
+
+ int ret;
+ EXPECT_FALSE((ret = system(command)));
+ if (ret) {
+ snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
+ EXPECT_FALSE(system(command));
+ return;
+ }
+ FILE *fp;
+ snprintf(command, sizeof(command), "%s/%s", tmp_out_dir, log_filename);
+ EXPECT_TRUE(NULL != ((fp = fopen(command, "r"))));
+ if (!fp) {
+ snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
+ EXPECT_FALSE(system(command));
+ return;
+ }
+ char *line = NULL;
+ char *last_line = NULL; // this line is allowed to stutter, one-line overlap
+ char *second_last_line = NULL;
+ size_t len = 0;
+ while (getline(&line, &len, fp) != -1) {
+ free(second_last_line);
+ second_last_line = last_line;
+ last_line = line;
+ line = NULL;
+ }
+ fclose(fp);
+ free(line);
+ if (second_last_line == NULL) {
+ fprintf(stderr, "No second to last line, using last, test may fail\n");
+ second_last_line = last_line;
+ last_line = NULL;
+ }
+ free(last_line);
+ EXPECT_TRUE(NULL != second_last_line);
+ if (!second_last_line) {
+ snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
+ EXPECT_FALSE(system(command));
+ return;
+ }
+ // re-run the command, it should only add a few lines more content if it
+ // continues where it left off.
+ snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir, log_filename);
+ EXPECT_FALSE((ret = system(command)));
+ if (ret) {
+ snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
+ EXPECT_FALSE(system(command));
+ return;
+ }
+ DIR *dir;
+ EXPECT_TRUE(NULL != (dir = opendir(tmp_out_dir)));
+ if (!dir) {
+ snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
+ EXPECT_FALSE(system(command));
+ return;
+ }
+ struct dirent *entry;
+ unsigned count = 0;
+ while ((entry = readdir(dir))) {
+ if (strncmp(entry->d_name, log_filename, sizeof(log_filename) - 1)) {
+ continue;
+ }
+ snprintf(command, sizeof(command), "%s/%s", tmp_out_dir, entry->d_name);
+ EXPECT_TRUE(NULL != ((fp = fopen(command, "r"))));
+ if (!fp) {
+ fprintf(stderr, "%s ?\n", command);
+ continue;
+ }
+ line = NULL;
+ size_t number = 0;
+ while (getline(&line, &len, fp) != -1) {
+ ++number;
+ if (!strcmp(line, second_last_line)) {
+ EXPECT_TRUE(++count <= 1);
+ fprintf(stderr, "%s(%zu):\n", entry->d_name, number);
+ }
+ }
+ fclose(fp);
+ free(line);
+ unlink(command);
+ }
+ closedir(dir);
+ if (count > 1) {
+ char *brk = strpbrk(second_last_line, "\r\n");
+ if (!brk) {
+ brk = second_last_line + strlen(second_last_line);
+ }
+ fprintf(stderr, "\"%.*s\" occured %u times\n",
+ (int)(brk - second_last_line), second_last_line, count);
+ }
+ free(second_last_line);
+
+ snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
EXPECT_FALSE(system(command));
}
diff --git a/logd/CommandListener.cpp b/logd/CommandListener.cpp
index 031c740..eafa28f 100644
--- a/logd/CommandListener.cpp
+++ b/logd/CommandListener.cpp
@@ -96,8 +96,7 @@
return 0;
}
- mBuf.clear((log_id_t) id, uid);
- cli->sendMsg("success");
+ cli->sendMsg(mBuf.clear((log_id_t) id, uid) ? "busy" : "success");
return 0;
}
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index c2f846e..5cfc02f 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -374,8 +374,10 @@
//
// mLogElementsLock must be held when this function is called.
//
-void LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
+bool LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
LogTimeEntry *oldest = NULL;
+ bool busy = false;
+ bool clearAll = pruneRows == ULONG_MAX;
LogTimeEntry::lock();
@@ -393,35 +395,31 @@
LogBufferElementCollection::iterator it;
if (caller_uid != AID_ROOT) {
+ // Only here if clearAll condition (pruneRows == ULONG_MAX)
for(it = mLogElements.begin(); it != mLogElements.end();) {
LogBufferElement *e = *it;
- if (oldest && (oldest->mStart <= e->getSequence())) {
- break;
- }
-
- if (e->getLogId() != id) {
+ if ((e->getLogId() != id) || (e->getUid() != caller_uid)) {
++it;
continue;
}
- if (e->getUid() == caller_uid) {
- it = erase(it);
- pruneRows--;
- if (pruneRows == 0) {
- break;
- }
- } else {
- ++it;
+ if (oldest && (oldest->mStart <= e->getSequence())) {
+ oldest->triggerSkip_Locked(id, pruneRows);
+ busy = true;
+ break;
}
+
+ it = erase(it);
+ pruneRows--;
}
LogTimeEntry::unlock();
- return;
+ return busy;
}
// prune by worst offender by uid
bool hasBlacklist = mPrune.naughty();
- while (pruneRows > 0) {
+ while (!clearAll && (pruneRows > 0)) {
// recalculate the worst offender on every batched pass
uid_t worst = (uid_t) -1;
size_t worst_sizes = 0;
@@ -478,6 +476,7 @@
LogBufferElement *e = *it;
if (oldest && (oldest->mStart <= e->getSequence())) {
+ busy = true;
break;
}
@@ -585,7 +584,7 @@
}
bool whitelist = false;
- bool hasWhitelist = mPrune.nice();
+ bool hasWhitelist = mPrune.nice() && !clearAll;
it = mLogElements.begin();
while((pruneRows > 0) && (it != mLogElements.end())) {
LogBufferElement *e = *it;
@@ -596,6 +595,8 @@
}
if (oldest && (oldest->mStart <= e->getSequence())) {
+ busy = true;
+
if (whitelist) {
break;
}
@@ -631,6 +632,7 @@
}
if (oldest && (oldest->mStart <= e->getSequence())) {
+ busy = true;
if (stats.sizes(id) > (2 * log_buffer_size(id))) {
// kick a misbehaving log reader client off the island
oldest->release_Locked();
@@ -646,13 +648,50 @@
}
LogTimeEntry::unlock();
+
+ return (pruneRows > 0) && busy;
}
// clear all rows of type "id" from the buffer.
-void LogBuffer::clear(log_id_t id, uid_t uid) {
- pthread_mutex_lock(&mLogElementsLock);
- prune(id, ULONG_MAX, uid);
- pthread_mutex_unlock(&mLogElementsLock);
+bool LogBuffer::clear(log_id_t id, uid_t uid) {
+ bool busy = true;
+ // If it takes more than 4 tries (seconds) to clear, then kill reader(s)
+ for (int retry = 4;;) {
+ if (retry == 1) { // last pass
+ // Check if it is still busy after the sleep, we say prune
+ // one entry, not another clear run, so we are looking for
+ // the quick side effect of the return value to tell us if
+ // we have a _blocked_ reader.
+ pthread_mutex_lock(&mLogElementsLock);
+ busy = prune(id, 1, uid);
+ pthread_mutex_unlock(&mLogElementsLock);
+ // It is still busy, blocked reader(s), lets kill them all!
+ // otherwise, lets be a good citizen and preserve the slow
+ // readers and let the clear run (below) deal with determining
+ // if we are still blocked and return an error code to caller.
+ if (busy) {
+ LogTimeEntry::lock();
+ LastLogTimes::iterator times = mTimes.begin();
+ while (times != mTimes.end()) {
+ LogTimeEntry *entry = (*times);
+ // Killer punch
+ if (entry->owned_Locked() && entry->isWatching(id)) {
+ entry->release_Locked();
+ }
+ times++;
+ }
+ LogTimeEntry::unlock();
+ }
+ }
+ pthread_mutex_lock(&mLogElementsLock);
+ busy = prune(id, ULONG_MAX, uid);
+ pthread_mutex_unlock(&mLogElementsLock);
+ if (!busy || !--retry) {
+ break;
+ }
+ sleep (1); // Let reader(s) catch up after notification
+ }
+ return busy;
}
// get the used space associated with "id".
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index de76693..d8e0b90 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -63,7 +63,7 @@
int (*filter)(const LogBufferElement *element, void *arg) = NULL,
void *arg = NULL);
- void clear(log_id_t id, uid_t uid = AID_ROOT);
+ bool clear(log_id_t id, uid_t uid = AID_ROOT);
unsigned long getSize(log_id_t id);
int setSize(log_id_t id, unsigned long size);
unsigned long getSizeUsed(log_id_t id);
@@ -86,7 +86,7 @@
private:
void maybePrune(log_id_t id);
- void prune(log_id_t id, unsigned long pruneRows, uid_t uid = AID_ROOT);
+ bool prune(log_id_t id, unsigned long pruneRows, uid_t uid = AID_ROOT);
LogBufferElementCollection::iterator erase(
LogBufferElementCollection::iterator it, bool engageStats = true);
};
diff --git a/logd/LogCommand.cpp b/logd/LogCommand.cpp
index 06d865c..6d0e92e 100644
--- a/logd/LogCommand.cpp
+++ b/logd/LogCommand.cpp
@@ -42,7 +42,6 @@
static bool groupIsLog(char *buf) {
char *ptr;
static const char ws[] = " \n";
- bool ret = false;
for (buf = strtok_r(buf, ws, &ptr); buf; buf = strtok_r(NULL, ws, &ptr)) {
errno = 0;
@@ -51,10 +50,10 @@
return false;
}
if (Gid == AID_LOG) {
- ret = true;
+ return true;
}
}
- return ret;
+ return false;
}
bool clientHasLogCredentials(SocketClient * cli) {
@@ -69,61 +68,79 @@
}
// FYI We will typically be here for 'adb logcat'
- bool ret = false;
+ char filename[256];
+ snprintf(filename, sizeof(filename), "/proc/%u/status", cli->getPid());
- char filename[1024];
- snprintf(filename, sizeof(filename), "/proc/%d/status", cli->getPid());
-
- FILE *file = fopen(filename, "r");
- if (!file) {
- return ret;
- }
-
+ bool ret;
+ bool foundLog = false;
bool foundGid = false;
bool foundUid = false;
- char line[1024];
- while (fgets(line, sizeof(line), file)) {
- static const char groups_string[] = "Groups:\t";
- static const char uid_string[] = "Uid:\t";
- static const char gid_string[] = "Gid:\t";
-
- if (strncmp(groups_string, line, strlen(groups_string)) == 0) {
- ret = groupIsLog(line + strlen(groups_string));
- if (!ret) {
- break;
- }
- } else if (strncmp(uid_string, line, strlen(uid_string)) == 0) {
- uid_t u[4] = { (uid_t) -1, (uid_t) -1, (uid_t) -1, (uid_t) -1};
-
- sscanf(line + strlen(uid_string), "%u\t%u\t%u\t%u",
- &u[0], &u[1], &u[2], &u[3]);
-
- // Protect against PID reuse by checking that the UID is the same
- if ((uid != u[0]) || (uid != u[1]) || (uid != u[2]) || (uid != u[3])) {
- ret = false;
- break;
- }
- foundUid = true;
- } else if (strncmp(gid_string, line, strlen(gid_string)) == 0) {
- gid_t g[4] = { (gid_t) -1, (gid_t) -1, (gid_t) -1, (gid_t) -1};
-
- sscanf(line + strlen(gid_string), "%u\t%u\t%u\t%u",
- &g[0], &g[1], &g[2], &g[3]);
-
- // Protect against PID reuse by checking that the GID is the same
- if ((gid != g[0]) || (gid != g[1]) || (gid != g[2]) || (gid != g[3])) {
- ret = false;
- break;
- }
- foundGid = true;
+ //
+ // Reading /proc/<pid>/status is rife with race conditions. All of /proc
+ // suffers from this and its use should be minimized. However, we have no
+ // choice.
+ //
+ // Notably the content from one 4KB page to the next 4KB page can be from a
+ // change in shape even if we are gracious enough to attempt to read
+ // atomically. getline can not even guarantee a page read is not split up
+ // and in effect can read from different vintages of the content.
+ //
+ // We are finding out in the field that a 'logcat -c' via adb occasionally
+ // is returned with permission denied when we did only one pass and thus
+ // breaking scripts. For security we still err on denying access if in
+ // doubt, but we expect the falses should be reduced significantly as
+ // three times is a charm.
+ //
+ for (int retry = 3;
+ !(ret = foundGid && foundUid && foundLog) && retry;
+ --retry) {
+ FILE *file = fopen(filename, "r");
+ if (!file) {
+ continue;
}
- }
- fclose(file);
+ char *line = NULL;
+ size_t len = 0;
+ while (getline(&line, &len, file) > 0) {
+ static const char groups_string[] = "Groups:\t";
+ static const char uid_string[] = "Uid:\t";
+ static const char gid_string[] = "Gid:\t";
- if (!foundGid || !foundUid) {
- ret = false;
+ if (strncmp(groups_string, line, sizeof(groups_string) - 1) == 0) {
+ if (groupIsLog(line + sizeof(groups_string) - 1)) {
+ foundLog = true;
+ }
+ } else if (strncmp(uid_string, line, sizeof(uid_string) - 1) == 0) {
+ uid_t u[4] = { (uid_t) -1, (uid_t) -1, (uid_t) -1, (uid_t) -1};
+
+ sscanf(line + sizeof(uid_string) - 1, "%u\t%u\t%u\t%u",
+ &u[0], &u[1], &u[2], &u[3]);
+
+ // Protect against PID reuse by checking that UID is the same
+ if ((uid == u[0])
+ && (uid == u[1])
+ && (uid == u[2])
+ && (uid == u[3])) {
+ foundUid = true;
+ }
+ } else if (strncmp(gid_string, line, sizeof(gid_string) - 1) == 0) {
+ gid_t g[4] = { (gid_t) -1, (gid_t) -1, (gid_t) -1, (gid_t) -1};
+
+ sscanf(line + sizeof(gid_string) - 1, "%u\t%u\t%u\t%u",
+ &g[0], &g[1], &g[2], &g[3]);
+
+ // Protect against PID reuse by checking that GID is the same
+ if ((gid == g[0])
+ && (gid == g[1])
+ && (gid == g[2])
+ && (gid == g[3])) {
+ foundGid = true;
+ }
+ }
+ }
+ free(line);
+ fclose(file);
}
return ret;
diff --git a/logd/LogTimes.cpp b/logd/LogTimes.cpp
index 68a0680..229be3c 100644
--- a/logd/LogTimes.cpp
+++ b/logd/LogTimes.cpp
@@ -128,9 +128,9 @@
lock();
- while (me->threadRunning && !me->isError_Locked()) {
- uint64_t start = me->mStart;
+ uint64_t start = me->mStart;
+ while (me->threadRunning && !me->isError_Locked()) {
unlock();
if (me->mTail) {
@@ -143,8 +143,11 @@
if (start == LogBufferElement::FLUSH_ERROR) {
me->error_Locked();
+ break;
}
+ me->mStart = start + 1;
+
if (me->mNonBlock || !me->threadRunning || me->isError_Locked()) {
break;
}
diff --git a/logd/logd.rc b/logd/logd.rc
index 498baec..da6a0bc 100644
--- a/logd/logd.rc
+++ b/logd/logd.rc
@@ -3,6 +3,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
diff --git a/logd/main.cpp b/logd/main.cpp
index f0a39b4..60262e9 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -104,6 +104,10 @@
return -1;
}
+ if (setgroups(0, NULL) == -1) {
+ return -1;
+ }
+
if (setgid(AID_LOGD) != 0) {
return -1;
}
diff --git a/logwrapper/logwrap.c b/logwrapper/logwrap.c
index 39bc8fd..28d6de7 100644
--- a/logwrapper/logwrap.c
+++ b/logwrapper/logwrap.c
@@ -326,7 +326,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/metricsd/Android.mk b/metricsd/Android.mk
index 9fd27f5..afb45da 100644
--- a/metricsd/Android.mk
+++ b/metricsd/Android.mk
@@ -25,44 +25,30 @@
metrics_client_sources := \
metrics_client.cc
-metrics_daemon_sources := \
+metrics_daemon_common := \
collectors/averaged_statistics_collector.cc \
collectors/disk_usage_collector.cc \
metrics_daemon.cc \
- metrics_daemon_main.cc \
persistent_integer.cc \
- uploader/metrics_hashes.cc \
- uploader/metrics_log_base.cc \
- uploader/metrics_log.cc \
- uploader/sender_http.cc \
- uploader/system_profile_cache.cc \
- uploader/upload_service.cc \
- serialization/metric_sample.cc \
- serialization/serialization_utils.cc
-
-metrics_tests_sources := \
- collectors/averaged_statistics_collector.cc \
- collectors/averaged_statistics_collector_test.cc \
- collectors/disk_usage_collector.cc \
- metrics_daemon.cc \
- metrics_daemon_test.cc \
- metrics_library_test.cc \
- persistent_integer.cc \
- persistent_integer_test.cc \
serialization/metric_sample.cc \
serialization/serialization_utils.cc \
- serialization/serialization_utils_unittest.cc \
- timer.cc \
- timer_test.cc \
uploader/metrics_hashes.cc \
- uploader/metrics_hashes_unittest.cc \
uploader/metrics_log_base.cc \
- uploader/metrics_log_base_unittest.cc \
uploader/metrics_log.cc \
- uploader/mock/sender_mock.cc \
uploader/sender_http.cc \
uploader/system_profile_cache.cc \
uploader/upload_service.cc \
+
+metrics_tests_sources := \
+ collectors/averaged_statistics_collector_test.cc \
+ metrics_daemon_test.cc \
+ metrics_library_test.cc \
+ persistent_integer_test.cc \
+ serialization/serialization_utils_unittest.cc \
+ timer_test.cc \
+ uploader/metrics_hashes_unittest.cc \
+ uploader/metrics_log_base_unittest.cc \
+ uploader/mock/sender_mock.cc \
uploader/upload_service_test.cc \
metrics_CFLAGS := -Wall \
@@ -78,7 +64,17 @@
-fvisibility=default
metrics_includes := external/gtest/include \
$(LOCAL_PATH)/include
-metrics_shared_libraries := libchrome libchromeos
+libmetrics_shared_libraries := libchrome libchromeos
+metrics_daemon_shared_libraries := $(libmetrics_shared_libraries) \
+ libchrome-dbus \
+ libchromeos-http \
+ libchromeos-dbus \
+ libcutils \
+ libdbus \
+ libmetrics \
+ libprotobuf-cpp-lite \
+ librootdev \
+ libweaved-client \
# Shared library for metrics.
# ========================================================
@@ -91,7 +87,7 @@
LOCAL_CPPFLAGS := $(metrics_CPPFLAGS)
LOCAL_RTTI_FLAG := -frtti
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_SHARED_LIBRARIES := $(metrics_shared_libraries)
+LOCAL_SHARED_LIBRARIES := $(libmetrics_shared_libraries)
LOCAL_SRC_FILES := $(libmetrics_sources)
include $(BUILD_SHARED_LIBRARY)
@@ -104,7 +100,7 @@
LOCAL_CLANG := true
LOCAL_CPP_EXTENSION := $(metrics_cpp_extension)
LOCAL_CPPFLAGS := $(metrics_CPPFLAGS)
-LOCAL_SHARED_LIBRARIES := $(metrics_shared_libraries) \
+LOCAL_SHARED_LIBRARIES := $(libmetrics_shared_libraries) \
libmetrics
LOCAL_SRC_FILES := $(metrics_client_sources)
include $(BUILD_EXECUTABLE)
@@ -130,18 +126,15 @@
LOCAL_CPP_EXTENSION := $(metrics_cpp_extension)
LOCAL_CPPFLAGS := $(metrics_CPPFLAGS)
LOCAL_INIT_RC := metrics_daemon.rc
-LOCAL_RTTI_FLAG := -frtti
-LOCAL_SHARED_LIBRARIES := $(metrics_shared_libraries) \
- libmetrics \
- libprotobuf-cpp-lite \
- libchromeos-http \
- libchromeos-dbus \
- libcutils \
- libdbus \
- librootdev
+LOCAL_REQUIRED_MODULES := \
+ metrics.json \
+ metrics.schema.json \
+LOCAL_RTTI_FLAG := -frtti
+LOCAL_SHARED_LIBRARIES := $(metrics_daemon_shared_libraries)
LOCAL_CLANG := true
-LOCAL_SRC_FILES := $(metrics_daemon_sources)
+LOCAL_SRC_FILES := $(metrics_daemon_common) \
+ metrics_daemon_main.cc
LOCAL_STATIC_LIBRARIES := metrics_daemon_protos
include $(BUILD_EXECUTABLE)
@@ -154,15 +147,24 @@
LOCAL_CPP_EXTENSION := $(metrics_cpp_extension)
LOCAL_CPPFLAGS := $(metrics_CPPFLAGS) -Wno-sign-compare
LOCAL_RTTI_FLAG := -frtti
-LOCAL_SHARED_LIBRARIES := $(metrics_shared_libraries) \
- libmetrics \
- libprotobuf-cpp-lite \
- libchromeos-http \
- libchromeos-dbus \
- libcutils \
- libdbus \
-
-LOCAL_SRC_FILES := $(metrics_tests_sources)
+LOCAL_SHARED_LIBRARIES := $(metrics_daemon_shared_libraries)
+LOCAL_SRC_FILES := $(metrics_tests_sources) $(metrics_daemon_common)
LOCAL_STATIC_LIBRARIES := libBionicGtestMain libgmock metrics_daemon_protos
include $(BUILD_NATIVE_TEST)
+
+# Weave schema files
+# ========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := metrics.json
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/weaved/commands
+LOCAL_SRC_FILES := etc/weaved/commands/$(LOCAL_MODULE)
+include $(BUILD_PREBUILT)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := metrics.schema.json
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/weaved/states
+LOCAL_SRC_FILES := etc/weaved/states/$(LOCAL_MODULE)
+include $(BUILD_PREBUILT)
diff --git a/metricsd/etc/weaved/commands/metrics.json b/metricsd/etc/weaved/commands/metrics.json
new file mode 100644
index 0000000..b7f32d5
--- /dev/null
+++ b/metricsd/etc/weaved/commands/metrics.json
@@ -0,0 +1,10 @@
+{
+ "_metrics": {
+ "_enableAnalyticsReporting": {
+ "minimalRole": "manager"
+ },
+ "_disableAnalyticsReporting": {
+ "minimalRole": "manager"
+ }
+ }
+}
diff --git a/metricsd/etc/weaved/states/metrics.schema.json b/metricsd/etc/weaved/states/metrics.schema.json
new file mode 100644
index 0000000..130ac46
--- /dev/null
+++ b/metricsd/etc/weaved/states/metrics.schema.json
@@ -0,0 +1,8 @@
+{
+ "_metrics": {
+ "_AnalyticsReportingState": {
+ "enum": ["enabled", "disabled"],
+ "default": "disabled"
+ }
+ }
+}
diff --git a/metricsd/include/metrics/metrics_library.h b/metricsd/include/metrics/metrics_library.h
index 5556e57..b766194 100644
--- a/metricsd/include/metrics/metrics_library.h
+++ b/metricsd/include/metrics/metrics_library.h
@@ -48,6 +48,12 @@
// Initializes the library.
void Init() override;
+ // Initializes the library and disables the cache of whether or not the
+ // metrics collection is enabled.
+ // By disabling this, we may have to check for the metrics status more often
+ // but the result will never be stale.
+ void InitWithNoCaching();
+
// Returns whether or not the machine is running in guest mode.
bool IsGuestMode();
@@ -125,6 +131,7 @@
friend class MetricsLibraryTest;
friend class UploadServiceTest;
FRIEND_TEST(MetricsLibraryTest, AreMetricsEnabled);
+ FRIEND_TEST(MetricsLibraryTest, AreMetricsEnabledNoCaching);
FRIEND_TEST(MetricsLibraryTest, FormatChromeMessage);
FRIEND_TEST(MetricsLibraryTest, FormatChromeMessageTooLong);
FRIEND_TEST(MetricsLibraryTest, IsDeviceMounted);
@@ -147,6 +154,9 @@
// Cached state of whether or not metrics were enabled.
bool cached_enabled_;
+ // True iff we should cache the enabled/disabled status.
+ bool use_caching_;
+
base::FilePath uma_events_file_;
base::FilePath consent_file_;
diff --git a/metricsd/metrics_daemon.cc b/metricsd/metrics_daemon.cc
index 9eb6802..f83c5d4 100644
--- a/metricsd/metrics_daemon.cc
+++ b/metricsd/metrics_daemon.cc
@@ -41,6 +41,8 @@
using base::TimeDelta;
using base::TimeTicks;
using chromeos_metrics::PersistentInteger;
+using com::android::Weave::CommandProxy;
+using com::android::Weave::ManagerProxy;
using std::map;
using std::string;
using std::vector;
@@ -280,6 +282,12 @@
LOG(ERROR) << "DBus isn't connected.";
return EX_UNAVAILABLE;
}
+
+ weaved_object_mgr_.reset(new com::android::Weave::ObjectManagerProxy{bus_});
+ weaved_object_mgr_->SetCommandAddedCallback(
+ base::Bind(&MetricsDaemon::OnWeaveCommand, base::Unretained(this)));
+ weaved_object_mgr_->SetManagerAddedCallback(
+ base::Bind(&MetricsDaemon::UpdateWeaveState, base::Unretained(this)));
}
base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
@@ -317,6 +325,62 @@
chromeos::DBusDaemon::OnShutdown(return_code);
}
+void MetricsDaemon::OnWeaveCommand(CommandProxy* command) {
+ if (command->state() != "queued") {
+ return;
+ }
+
+ VLOG(1) << "received weave command: " << command->name();
+ if (command->name() == "_metrics._enableAnalyticsReporting") {
+ OnEnableMetrics(command);
+ } else if (command->name() == "_metrics._disableAnalyticsReporting") {
+ OnDisableMetrics(command);
+ }
+}
+
+void MetricsDaemon::OnEnableMetrics(CommandProxy* command) {
+ if (base::WriteFile(metrics_directory_.Append(metrics::kConsentFileName),
+ "", 0) != 0) {
+ PLOG(ERROR) << "Could not create the consent file.";
+ command->Abort("metrics_error", "Could not create the consent file",
+ nullptr);
+ return;
+ }
+
+ NotifyStateChanged();
+ command->Complete({}, nullptr);
+}
+
+void MetricsDaemon::OnDisableMetrics(CommandProxy* command) {
+ if (!base::DeleteFile(metrics_directory_.Append(metrics::kConsentFileName),
+ false)) {
+ PLOG(ERROR) << "Could not delete the consent file.";
+ command->Abort("metrics_error", "Could not delete the consent file",
+ nullptr);
+ return;
+ }
+
+ NotifyStateChanged();
+ command->Complete({}, nullptr);
+}
+
+void MetricsDaemon::NotifyStateChanged() {
+ ManagerProxy* manager = weaved_object_mgr_->GetManagerProxy();
+ if (manager)
+ UpdateWeaveState(manager);
+}
+
+void MetricsDaemon::UpdateWeaveState(ManagerProxy* manager) {
+ chromeos::VariantDictionary state_change{
+ { "_metrics._AnalyticsReportingState",
+ metrics_lib_->AreMetricsEnabled() ? "enabled" : "disabled" }
+ };
+
+ if (!manager->UpdateState(state_change, nullptr)) {
+ LOG(ERROR) << "failed to update weave's state";
+ }
+}
+
// static
DBusHandlerResult MetricsDaemon::MessageFilter(DBusConnection* connection,
DBusMessage* message,
diff --git a/metricsd/metrics_daemon.h b/metricsd/metrics_daemon.h
index 612dfe2..0a8f6a5 100644
--- a/metricsd/metrics_daemon.h
+++ b/metricsd/metrics_daemon.h
@@ -26,6 +26,7 @@
#include <base/files/file_path.h>
#include <base/memory/scoped_ptr.h>
#include <base/time/time.h>
+#include <buffet/dbus-proxies.h>
#include <chromeos/daemons/dbus_daemon.h>
#include <gtest/gtest_prod.h> // for FRIEND_TEST
@@ -121,6 +122,21 @@
DBusMessage* message,
void* user_data);
+ // Callback for Weave commands.
+ void OnWeaveCommand(com::android::Weave::CommandProxy* command);
+
+ // Enables metrics reporting.
+ void OnEnableMetrics(com::android::Weave::CommandProxy* command);
+
+ // Disables metrics reporting.
+ void OnDisableMetrics(com::android::Weave::CommandProxy* command);
+
+ // Updates the weave device state.
+ void UpdateWeaveState(com::android::Weave::ManagerProxy* manager);
+
+ // Tells Weave that the state has changed.
+ void NotifyStateChanged();
+
// Updates the active use time and logs time between user-space
// process crashes.
void ProcessUserCrash();
@@ -301,6 +317,7 @@
std::string server_;
scoped_ptr<UploadService> upload_service_;
+ scoped_ptr<com::android::Weave::ObjectManagerProxy> weaved_object_mgr_;
};
#endif // METRICS_METRICS_DAEMON_H_
diff --git a/metricsd/metrics_daemon_main.cc b/metricsd/metrics_daemon_main.cc
index c2e794e..df13944 100644
--- a/metricsd/metrics_daemon_main.cc
+++ b/metricsd/metrics_daemon_main.cc
@@ -90,7 +90,7 @@
}
MetricsLibrary metrics_lib;
- metrics_lib.Init();
+ metrics_lib.InitWithNoCaching();
MetricsDaemon daemon;
daemon.Init(FLAGS_uploader_test,
FLAGS_uploader | FLAGS_uploader_test,
diff --git a/metricsd/metrics_library.cc b/metricsd/metrics_library.cc
index 3109704..a651b76 100644
--- a/metricsd/metrics_library.cc
+++ b/metricsd/metrics_library.cc
@@ -126,7 +126,7 @@
bool MetricsLibrary::AreMetricsEnabled() {
static struct stat stat_buffer;
time_t this_check_time = time(nullptr);
- if (this_check_time != cached_enabled_time_) {
+ if (!use_caching_ || this_check_time != cached_enabled_time_) {
cached_enabled_time_ = this_check_time;
cached_enabled_ = stat(consent_file_.value().data(), &stat_buffer) >= 0;
}
@@ -139,6 +139,12 @@
consent_file_ = dir.Append(metrics::kConsentFileName);
cached_enabled_ = false;
cached_enabled_time_ = 0;
+ use_caching_ = true;
+}
+
+void MetricsLibrary::InitWithNoCaching() {
+ Init();
+ use_caching_ = false;
}
void MetricsLibrary::InitForTest(const base::FilePath& metrics_directory) {
@@ -146,6 +152,7 @@
consent_file_ = metrics_directory.Append(metrics::kConsentFileName);
cached_enabled_ = false;
cached_enabled_time_ = 0;
+ use_caching_ = true;
}
bool MetricsLibrary::SendToUMA(const std::string& name,
diff --git a/metricsd/metrics_library_test.cc b/metricsd/metrics_library_test.cc
index f300d17..be8a4bb 100644
--- a/metricsd/metrics_library_test.cc
+++ b/metricsd/metrics_library_test.cc
@@ -93,3 +93,16 @@
VerifyEnabledCacheEviction(false);
VerifyEnabledCacheEviction(true);
}
+
+TEST_F(MetricsLibraryTest, AreMetricsEnabledNoCaching) {
+ // disable caching.
+ lib_.use_caching_ = false;
+
+ // Checking the consent repeatedly should return the right result.
+ for (int i=0; i<100; ++i) {
+ SetMetricsConsent(true);
+ ASSERT_EQ(true, lib_.AreMetricsEnabled());
+ SetMetricsConsent(false);
+ ASSERT_EQ(false, lib_.AreMetricsEnabled());
+ }
+}
diff --git a/metricsd/uploader/proto/system_profile.proto b/metricsd/uploader/proto/system_profile.proto
index 4cab0d9..bac828b 100644
--- a/metricsd/uploader/proto/system_profile.proto
+++ b/metricsd/uploader/proto/system_profile.proto
@@ -88,7 +88,7 @@
optional string application_locale = 4;
message BrilloDeviceData {
- optional string build_target_id = 1;
+ optional string product_id = 1;
}
optional BrilloDeviceData brillo = 21;
diff --git a/metricsd/uploader/system_profile_cache.cc b/metricsd/uploader/system_profile_cache.cc
index 2437b56..1d87be5 100644
--- a/metricsd/uploader/system_profile_cache.cc
+++ b/metricsd/uploader/system_profile_cache.cc
@@ -136,7 +136,7 @@
profile_proto->set_channel(profile_.channel);
metrics::SystemProfileProto_BrilloDeviceData* device_data =
profile_proto->mutable_brillo();
- device_data->set_build_target_id(profile_.product_id);
+ device_data->set_product_id(profile_.product_id);
return true;
}
diff --git a/rootdir/init.environ.rc.in b/rootdir/init.environ.rc.in
index e8b46eb..32817fa 100644
--- a/rootdir/init.environ.rc.in
+++ b/rootdir/init.environ.rc.in
@@ -5,8 +5,8 @@
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%
%EXPORT_GLOBAL_ASAN_OPTIONS%
diff --git a/rootdir/init.rc b/rootdir/init.rc
index dc85d98..38c686d 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -13,6 +13,9 @@
# Set init and its forked children's oom_adj.
write /proc/1/oom_score_adj -1000
+ # Disable sysrq from keyboard
+ write /proc/sys/kernel/sysrq 0
+
# Set the security context of /adb_keys if present.
restorecon /adb_keys
@@ -48,28 +51,33 @@
chown root system /sys/fs/cgroup/memory/sw/tasks
chmod 0660 /sys/fs/cgroup/memory/sw/tasks
+ # 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
+ 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
@@ -97,6 +105,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
@@ -115,6 +127,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.
@@ -178,8 +212,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
@@ -224,6 +260,8 @@
start vold
installkey /data
+ # Emulated internal storage area
+ mkdir /data/media 0770 media_rw media_rw
# Start bootcharting as soon as possible after the data partition is
# mounted to collect more data.
mkdir /data/bootchart 0755 shell shell
@@ -234,7 +272,10 @@
# create basic filesystem structure
mkdir /data/misc 01771 system misc
- 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
@@ -258,6 +299,7 @@
mkdir /data/local 0751 root root
mkdir /data/misc/media 0700 media media
mkdir /data/misc/boottrace 0771 system shell
+ mkdir /data/misc/update_engine 0700 root root
# For security reasons, /data/local/tmp should always be empty.
# Do not place files or directories in /data/local/tmp
@@ -337,9 +379,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
@@ -478,6 +520,7 @@
class core
critical
seclabel u:r:healthd:s0
+ group root system
service console /system/bin/sh
class core
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 5b657bb..d2d2315 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;
}