Merge "Add a feature to show which bugs are tracking which se denials"
diff --git a/adb/Android.mk b/adb/Android.mk
index ae03593..e195bec 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -108,7 +108,6 @@
sysdeps_win32_test.cpp \
include $(CLEAR_VARS)
-LOCAL_CLANG := true
LOCAL_MODULE := libadbd_usb
LOCAL_CFLAGS := $(LIBADB_CFLAGS) -DADB_HOST=0
LOCAL_SRC_FILES := daemon/usb.cpp
@@ -122,7 +121,6 @@
include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
-LOCAL_CLANG := true
LOCAL_MODULE := libadbd
LOCAL_CFLAGS := $(LIBADB_CFLAGS) -DADB_HOST=0
LOCAL_SRC_FILES := \
@@ -171,7 +169,6 @@
include $(BUILD_HOST_STATIC_LIBRARY)
include $(CLEAR_VARS)
-LOCAL_CLANG := true
LOCAL_MODULE := adbd_test
LOCAL_CFLAGS := -DADB_HOST=0 $(LIBADB_CFLAGS)
LOCAL_SRC_FILES := \
@@ -330,8 +327,6 @@
include $(CLEAR_VARS)
-LOCAL_CLANG := true
-
LOCAL_SRC_FILES := \
daemon/main.cpp \
daemon/mdns.cpp \
diff --git a/adb/adb.cpp b/adb/adb.cpp
index ff7b71f..8c24bbb 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -956,8 +956,8 @@
// Try to handle a network forwarding request.
// This returns 1 on success, 0 on failure, and -1 to indicate this is not
// a forwarding-related request.
-int handle_forward_request(const char* service, TransportType type, const char* serial, int reply_fd)
-{
+int handle_forward_request(const char* service, TransportType type, const char* serial,
+ TransportId transport_id, int reply_fd) {
if (!strcmp(service, "list-forward")) {
// Create the list of forward redirections.
std::string listeners = format_listeners();
@@ -1010,7 +1010,8 @@
}
std::string error_msg;
- atransport* transport = acquire_one_transport(type, serial, nullptr, &error_msg);
+ atransport* transport =
+ acquire_one_transport(type, serial, transport_id, nullptr, &error_msg);
if (!transport) {
SendFail(reply_fd, error_msg);
return 1;
@@ -1068,8 +1069,8 @@
return 0;
}
-int handle_host_request(const char* service, TransportType type,
- const char* serial, int reply_fd, asocket* s) {
+int handle_host_request(const char* service, TransportType type, const char* serial,
+ TransportId transport_id, int reply_fd, asocket* s) {
if (strcmp(service, "kill") == 0) {
fprintf(stderr, "adb server killed by remote request\n");
fflush(stdout);
@@ -1089,7 +1090,14 @@
if (!strncmp(service, "transport", strlen("transport"))) {
TransportType type = kTransportAny;
- if (!strncmp(service, "transport-usb", strlen("transport-usb"))) {
+ if (!strncmp(service, "transport-id:", strlen("transport-id:"))) {
+ service += strlen("transport-id:");
+ transport_id = strtoll(service, const_cast<char**>(&service), 10);
+ if (*service != '\0') {
+ SendFail(reply_fd, "invalid transport id");
+ return 1;
+ }
+ } else if (!strncmp(service, "transport-usb", strlen("transport-usb"))) {
type = kTransportUsb;
} else if (!strncmp(service, "transport-local", strlen("transport-local"))) {
type = kTransportLocal;
@@ -1101,7 +1109,7 @@
}
std::string error;
- atransport* t = acquire_one_transport(type, serial, nullptr, &error);
+ atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
if (t != nullptr) {
s->transport = t;
SendOkay(reply_fd);
@@ -1144,7 +1152,7 @@
if (!strcmp(service, "features")) {
std::string error;
- atransport* t = acquire_one_transport(type, serial, nullptr, &error);
+ atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
if (t != nullptr) {
SendOkay(reply_fd, FeatureSetToString(t->features()));
} else {
@@ -1197,7 +1205,7 @@
// These always report "unknown" rather than the actual error, for scripts.
if (!strcmp(service, "get-serialno")) {
std::string error;
- atransport* t = acquire_one_transport(type, serial, nullptr, &error);
+ atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
if (t) {
return SendOkay(reply_fd, t->serial ? t->serial : "unknown");
} else {
@@ -1206,7 +1214,7 @@
}
if (!strcmp(service, "get-devpath")) {
std::string error;
- atransport* t = acquire_one_transport(type, serial, nullptr, &error);
+ atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
if (t) {
return SendOkay(reply_fd, t->devpath ? t->devpath : "unknown");
} else {
@@ -1215,7 +1223,7 @@
}
if (!strcmp(service, "get-state")) {
std::string error;
- atransport* t = acquire_one_transport(type, serial, nullptr, &error);
+ atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
if (t) {
return SendOkay(reply_fd, t->connection_state_name());
} else {
@@ -1233,7 +1241,7 @@
if (!strcmp(service, "reconnect")) {
std::string response;
- atransport* t = acquire_one_transport(type, serial, nullptr, &response, true);
+ atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &response, true);
if (t != nullptr) {
kick_transport(t);
response =
@@ -1242,7 +1250,7 @@
return SendOkay(reply_fd, response);
}
- int ret = handle_forward_request(service, type, serial, reply_fd);
+ int ret = handle_forward_request(service, type, serial, transport_id, reply_fd);
if (ret >= 0)
return ret - 1;
return -1;
diff --git a/adb/adb.h b/adb/adb.h
index d6b2b81..88e13b6 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -56,6 +56,7 @@
// Increment this when we want to force users to start a new adb server.
#define ADB_SERVER_VERSION 39
+using TransportId = uint64_t;
class atransport;
struct amessage {
@@ -149,7 +150,7 @@
int service_to_fd(const char* name, const atransport* transport);
#if ADB_HOST
-asocket *host_service_to_socket(const char* name, const char *serial);
+asocket* host_service_to_socket(const char* name, const char* serial, TransportId transport_id);
#endif
#if !ADB_HOST
@@ -159,7 +160,8 @@
int create_jdwp_connection_fd(int jdwp_pid);
#endif
-int handle_forward_request(const char* service, TransportType type, const char* serial, int reply_fd);
+int handle_forward_request(const char* service, TransportType type, const char* serial,
+ TransportId transport_id, int reply_fd);
#if !ADB_HOST
void framebuffer_service(int fd, void *cookie);
@@ -216,7 +218,8 @@
#define USB_FFS_ADB_IN USB_FFS_ADB_EP(ep2)
#endif
-int handle_host_request(const char* service, TransportType type, const char* serial, int reply_fd, asocket *s);
+int handle_host_request(const char* service, TransportType type, const char* serial,
+ TransportId transport_id, int reply_fd, asocket* s);
void handle_online(atransport *t);
void handle_offline(atransport *t);
diff --git a/adb/adb_client.cpp b/adb/adb_client.cpp
index e533a00..849a6e7 100644
--- a/adb/adb_client.cpp
+++ b/adb/adb_client.cpp
@@ -20,6 +20,7 @@
#include "adb_client.h"
#include <errno.h>
+#include <inttypes.h>
#include <limits.h>
#include <stdarg.h>
#include <stdio.h>
@@ -46,12 +47,20 @@
static TransportType __adb_transport = kTransportAny;
static const char* __adb_serial = NULL;
+static TransportId __adb_transport_id = 0;
static const char* __adb_server_socket_spec;
-void adb_set_transport(TransportType type, const char* serial) {
+void adb_set_transport(TransportType type, const char* serial, TransportId transport_id) {
__adb_transport = type;
__adb_serial = serial;
+ __adb_transport_id = transport_id;
+}
+
+void adb_get_transport(TransportType* type, const char** serial, TransportId* transport_id) {
+ if (type) *type = __adb_transport;
+ if (serial) *serial = __adb_serial;
+ if (transport_id) *transport_id = __adb_transport_id;
}
void adb_set_socket_spec(const char* socket_spec) {
@@ -63,7 +72,10 @@
static int switch_socket_transport(int fd, std::string* error) {
std::string service;
- if (__adb_serial) {
+ if (__adb_transport_id) {
+ service += "host:transport-id:";
+ service += std::to_string(__adb_transport_id);
+ } else if (__adb_serial) {
service += "host:transport:";
service += __adb_serial;
} else {
@@ -292,15 +304,18 @@
return true;
}
-std::string format_host_command(const char* command, TransportType type, const char* serial) {
- if (serial) {
- return android::base::StringPrintf("host-serial:%s:%s", serial, command);
+std::string format_host_command(const char* command) {
+ if (__adb_transport_id) {
+ return android::base::StringPrintf("host-transport-id:%" PRIu64 ":%s", __adb_transport_id,
+ command);
+ } else if (__adb_serial) {
+ return android::base::StringPrintf("host-serial:%s:%s", __adb_serial, command);
}
const char* prefix = "host";
- if (type == kTransportUsb) {
+ if (__adb_transport == kTransportUsb) {
prefix = "host-usb";
- } else if (type == kTransportLocal) {
+ } else if (__adb_transport == kTransportLocal) {
prefix = "host-local";
}
return android::base::StringPrintf("%s:%s", prefix, command);
@@ -308,7 +323,7 @@
bool adb_get_feature_set(FeatureSet* feature_set, std::string* error) {
std::string result;
- if (adb_query(format_host_command("features", __adb_transport, __adb_serial), &result, error)) {
+ if (adb_query(format_host_command("features"), &result, error)) {
*feature_set = StringToFeatureSet(result);
return true;
}
diff --git a/adb/adb_client.h b/adb/adb_client.h
index fabec00..fca435e 100644
--- a/adb/adb_client.h
+++ b/adb/adb_client.h
@@ -40,7 +40,9 @@
std::string* _Nonnull error);
// Set the preferred transport to connect to.
-void adb_set_transport(TransportType type, const char* _Nullable serial);
+void adb_set_transport(TransportType type, const char* _Nullable serial, TransportId transport_id);
+void adb_get_transport(TransportType* _Nullable type, const char* _Nullable* _Nullable serial,
+ TransportId* _Nullable transport_id);
// Set the socket specification for the adb server.
// This function can only be called once, and the argument must live to the end of the process.
@@ -57,8 +59,7 @@
bool adb_status(int fd, std::string* _Nonnull error);
// Create a host command corresponding to selected transport type/serial.
-std::string format_host_command(const char* _Nonnull command, TransportType type,
- const char* _Nullable serial);
+std::string format_host_command(const char* _Nonnull command);
// Get the feature set of the current preferred transport.
bool adb_get_feature_set(FeatureSet* _Nonnull feature_set, std::string* _Nonnull error);
diff --git a/adb/adbd_auth.cpp b/adb/adbd_auth.cpp
index b5f87be..3488ad1 100644
--- a/adb/adbd_auth.cpp
+++ b/adb/adbd_auth.cpp
@@ -217,8 +217,8 @@
send_packet(p, t);
}
-void adbd_auth_verified(atransport *t)
-{
+void adbd_auth_verified(atransport* t) {
+ LOG(INFO) << "adb client authorized";
handle_online(t);
send_connect(t);
}
diff --git a/adb/bugreport.cpp b/adb/bugreport.cpp
index 372a3b4..abef86a 100644
--- a/adb/bugreport.cpp
+++ b/adb/bugreport.cpp
@@ -102,7 +102,7 @@
std::vector<const char*> srcs{src_file_.c_str()};
SetLineMessage("pulling");
status_ =
- br_->DoSyncPull(srcs, destination.c_str(), true, line_message_.c_str()) ? 0 : 1;
+ br_->DoSyncPull(srcs, destination.c_str(), false, line_message_.c_str()) ? 0 : 1;
if (status_ != 0) {
fprintf(stderr,
"Bug report finished but could not be copied to '%s'.\n"
@@ -195,13 +195,13 @@
DISALLOW_COPY_AND_ASSIGN(BugreportStandardStreamsCallback);
};
-int Bugreport::DoIt(TransportType transport_type, const char* serial, int argc, const char** argv) {
+int Bugreport::DoIt(int argc, const char** argv) {
if (argc > 2) return syntax_error("adb bugreport [PATH]");
// Gets bugreportz version.
std::string bugz_stdout, bugz_stderr;
DefaultStandardStreamsCallback version_callback(&bugz_stdout, &bugz_stderr);
- int status = SendShellCommand(transport_type, serial, "bugreportz -v", false, &version_callback);
+ int status = SendShellCommand("bugreportz -v", false, &version_callback);
std::string bugz_version = android::base::Trim(bugz_stderr);
std::string bugz_output = android::base::Trim(bugz_stdout);
@@ -214,7 +214,7 @@
fprintf(stderr,
"Failed to get bugreportz version, which is only available on devices "
"running Android 7.0 or later.\nTrying a plain-text bug report instead.\n");
- return SendShellCommand(transport_type, serial, "bugreport", false);
+ return SendShellCommand("bugreport", false);
}
// But if user explicitly asked for a zipped bug report, fails instead (otherwise calling
@@ -265,7 +265,7 @@
bugz_command = "bugreportz";
}
BugreportStandardStreamsCallback bugz_callback(dest_dir, dest_file, show_progress, this);
- return SendShellCommand(transport_type, serial, bugz_command, false, &bugz_callback);
+ return SendShellCommand(bugz_command, false, &bugz_callback);
}
void Bugreport::UpdateProgress(const std::string& message, int progress_percentage) {
@@ -274,10 +274,9 @@
LinePrinter::INFO);
}
-int Bugreport::SendShellCommand(TransportType transport_type, const char* serial,
- const std::string& command, bool disable_shell_protocol,
+int Bugreport::SendShellCommand(const std::string& command, bool disable_shell_protocol,
StandardStreamsCallbackInterface* callback) {
- return send_shell_command(transport_type, serial, command, disable_shell_protocol, callback);
+ return send_shell_command(command, disable_shell_protocol, callback);
}
bool Bugreport::DoSyncPull(const std::vector<const char*>& srcs, const char* dst, bool copy_attrs,
diff --git a/adb/bugreport.h b/adb/bugreport.h
index d9a4468..413439b 100644
--- a/adb/bugreport.h
+++ b/adb/bugreport.h
@@ -29,14 +29,13 @@
public:
Bugreport() : line_printer_() {
}
- int DoIt(TransportType transport_type, const char* serial, int argc, const char** argv);
+ int DoIt(int argc, const char** argv);
protected:
// Functions below are abstractions of external functions so they can be
// mocked on tests.
virtual int SendShellCommand(
- TransportType transport_type, const char* serial, const std::string& command,
- bool disable_shell_protocol,
+ const std::string& command, bool disable_shell_protocol,
StandardStreamsCallbackInterface* callback = &DEFAULT_STANDARD_STREAMS_CALLBACK);
virtual bool DoSyncPull(const std::vector<const char*>& srcs, const char* dst, bool copy_attrs,
diff --git a/adb/bugreport_test.cpp b/adb/bugreport_test.cpp
index d3787b4..72ca59a 100644
--- a/adb/bugreport_test.cpp
+++ b/adb/bugreport_test.cpp
@@ -51,8 +51,8 @@
// Empty functions so tests don't need to be linked against commandline.cpp
DefaultStandardStreamsCallback DEFAULT_STANDARD_STREAMS_CALLBACK(nullptr, nullptr);
-int send_shell_command(TransportType transport_type, const char* serial, const std::string& command,
- bool disable_shell_protocol, StandardStreamsCallbackInterface* callback) {
+int send_shell_command(const std::string& command, bool disable_shell_protocol,
+ StandardStreamsCallbackInterface* callback) {
ADD_FAILURE() << "send_shell_command() should have been mocked";
return -42;
}
@@ -62,7 +62,7 @@
kStreamStderr,
};
-// gmock black magic to provide a WithArg<4>(WriteOnStdout(output)) matcher
+// gmock black magic to provide a WithArg<2>(WriteOnStdout(output)) matcher
typedef void OnStandardStreamsCallbackFunction(StandardStreamsCallbackInterface*);
class OnStandardStreamsCallbackAction : public ActionInterface<OnStandardStreamsCallbackFunction> {
@@ -118,9 +118,8 @@
class BugreportMock : public Bugreport {
public:
- MOCK_METHOD5(SendShellCommand,
- int(TransportType transport_type, const char* serial, const std::string& command,
- bool disable_shell_protocol, StandardStreamsCallbackInterface* callback));
+ MOCK_METHOD3(SendShellCommand, int(const std::string& command, bool disable_shell_protocol,
+ StandardStreamsCallbackInterface* callback));
MOCK_METHOD4(DoSyncPull, bool(const std::vector<const char*>& srcs, const char* dst,
bool copy_attrs, const char* name));
MOCK_METHOD2(UpdateProgress, void(const std::string&, int));
@@ -136,10 +135,9 @@
}
void ExpectBugreportzVersion(const std::string& version) {
- EXPECT_CALL(br_,
- SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -v", false, _))
- .WillOnce(DoAll(WithArg<4>(WriteOnStderr(version.c_str())),
- WithArg<4>(ReturnCallbackDone(0))));
+ EXPECT_CALL(br_, SendShellCommand("bugreportz -v", false, _))
+ .WillOnce(DoAll(WithArg<2>(WriteOnStderr(version.c_str())),
+ WithArg<2>(ReturnCallbackDone(0))));
}
void ExpectProgress(int progress_percentage, const std::string& file = "file.zip") {
@@ -153,26 +151,26 @@
// Tests when called with invalid number of arguments
TEST_F(BugreportTest, InvalidNumberArgs) {
const char* args[] = {"bugreport", "to", "principal"};
- ASSERT_EQ(1, br_.DoIt(kTransportLocal, "HannibalLecter", 3, args));
+ ASSERT_EQ(1, br_.DoIt(3, args));
}
// Tests the 'adb bugreport' option when the device does not support 'bugreportz' - it falls back
// to the flat-file format ('bugreport' binary on device)
TEST_F(BugreportTest, NoArgumentsPreNDevice) {
// clang-format off
- EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -v", false, _))
- .WillOnce(DoAll(WithArg<4>(WriteOnStderr("")),
+ EXPECT_CALL(br_, SendShellCommand("bugreportz -v", false, _))
+ .WillOnce(DoAll(WithArg<2>(WriteOnStderr("")),
// Write some bogus output on stdout to make sure it's ignored
- WithArg<4>(WriteOnStdout("Dude, where is my bugreportz?")),
- WithArg<4>(ReturnCallbackDone(0))));
+ WithArg<2>(WriteOnStdout("Dude, where is my bugreportz?")),
+ WithArg<2>(ReturnCallbackDone(0))));
// clang-format on
std::string bugreport = "Reported the bug was.";
CaptureStdout();
- EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreport", false, _))
- .WillOnce(DoAll(WithArg<4>(WriteOnStdout(bugreport)), Return(0)));
+ EXPECT_CALL(br_, SendShellCommand("bugreport", false, _))
+ .WillOnce(DoAll(WithArg<2>(WriteOnStdout(bugreport)), Return(0)));
const char* args[] = {"bugreport"};
- ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 1, args));
+ ASSERT_EQ(0, br_.DoIt(1, args));
ASSERT_THAT(GetCapturedStdout(), StrEq(bugreport));
}
@@ -183,15 +181,15 @@
std::string dest_file =
android::base::StringPrintf("%s%cda_bugreport.zip", cwd_.c_str(), OS_PATH_SEPARATOR);
- EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz", false, _))
- .WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device/da_bugreport.zip")),
- WithArg<4>(ReturnCallbackDone())));
+ EXPECT_CALL(br_, SendShellCommand("bugreportz", false, _))
+ .WillOnce(DoAll(WithArg<2>(WriteOnStdout("OK:/device/da_bugreport.zip")),
+ WithArg<2>(ReturnCallbackDone())));
EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/da_bugreport.zip")), StrEq(dest_file),
- true, StrEq("pulling da_bugreport.zip")))
+ false, StrEq("pulling da_bugreport.zip")))
.WillOnce(Return(true));
const char* args[] = {"bugreport"};
- ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 1, args));
+ ASSERT_EQ(0, br_.DoIt(1, args));
}
// Tests the 'adb bugreport' option when the device supports 'bugreportz' version 1.1 - it will
@@ -201,47 +199,47 @@
std::string dest_file =
android::base::StringPrintf("%s%cda_bugreport.zip", cwd_.c_str(), OS_PATH_SEPARATOR);
ExpectProgress(50, "da_bugreport.zip");
- EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
- .WillOnce(DoAll(WithArg<4>(WriteOnStdout("BEGIN:/device/da_bugreport.zip\n")),
- WithArg<4>(WriteOnStdout("PROGRESS:50/100\n")),
- WithArg<4>(WriteOnStdout("OK:/device/da_bugreport.zip\n")),
- WithArg<4>(ReturnCallbackDone())));
+ EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _))
+ .WillOnce(DoAll(WithArg<2>(WriteOnStdout("BEGIN:/device/da_bugreport.zip\n")),
+ WithArg<2>(WriteOnStdout("PROGRESS:50/100\n")),
+ WithArg<2>(WriteOnStdout("OK:/device/da_bugreport.zip\n")),
+ WithArg<2>(ReturnCallbackDone())));
EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/da_bugreport.zip")), StrEq(dest_file),
- true, StrEq("pulling da_bugreport.zip")))
+ false, StrEq("pulling da_bugreport.zip")))
.WillOnce(Return(true));
const char* args[] = {"bugreport"};
- ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 1, args));
+ ASSERT_EQ(0, br_.DoIt(1, args));
}
// Tests 'adb bugreport file.zip' when it succeeds and device does not support progress.
TEST_F(BugreportTest, OkNDevice) {
ExpectBugreportzVersion("1.0");
- EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz", false, _))
- .WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device/bugreport.zip")),
- WithArg<4>(ReturnCallbackDone())));
+ EXPECT_CALL(br_, SendShellCommand("bugreportz", false, _))
+ .WillOnce(DoAll(WithArg<2>(WriteOnStdout("OK:/device/bugreport.zip")),
+ WithArg<2>(ReturnCallbackDone())));
EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
- true, StrEq("pulling file.zip")))
+ false, StrEq("pulling file.zip")))
.WillOnce(Return(true));
const char* args[] = {"bugreport", "file.zip"};
- ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+ ASSERT_EQ(0, br_.DoIt(2, args));
}
// Tests 'adb bugreport file.zip' when it succeeds but response was sent in
// multiple buffer writers and without progress updates.
TEST_F(BugreportTest, OkNDeviceSplitBuffer) {
ExpectBugreportzVersion("1.0");
- EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz", false, _))
- .WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device")),
- WithArg<4>(WriteOnStdout("/bugreport.zip")),
- WithArg<4>(ReturnCallbackDone())));
+ EXPECT_CALL(br_, SendShellCommand("bugreportz", false, _))
+ .WillOnce(DoAll(WithArg<2>(WriteOnStdout("OK:/device")),
+ WithArg<2>(WriteOnStdout("/bugreport.zip")),
+ WithArg<2>(ReturnCallbackDone())));
EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
- true, StrEq("pulling file.zip")))
+ false, StrEq("pulling file.zip")))
.WillOnce(Return(true));
const char* args[] = {"bugreport", "file.zip"};
- ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+ ASSERT_EQ(0, br_.DoIt(2, args));
}
// Tests 'adb bugreport file.zip' when it succeeds and displays progress.
@@ -252,32 +250,32 @@
ExpectProgress(50);
ExpectProgress(99);
// clang-format off
- EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
+ EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _))
// NOTE: DoAll accepts at most 10 arguments, and we're almost reached that limit...
.WillOnce(DoAll(
// Name might change on OK, so make sure the right one is picked.
- WithArg<4>(WriteOnStdout("BEGIN:/device/bugreport___NOT.zip\n")),
+ WithArg<2>(WriteOnStdout("BEGIN:/device/bugreport___NOT.zip\n")),
// Progress line in one write
- WithArg<4>(WriteOnStdout("PROGRESS:1/100\n")),
+ WithArg<2>(WriteOnStdout("PROGRESS:1/100\n")),
// Add some bogus lines
- WithArg<4>(WriteOnStdout("\nDUDE:SWEET\n\nBLA\n\nBLA\nBLA\n\n")),
+ WithArg<2>(WriteOnStdout("\nDUDE:SWEET\n\nBLA\n\nBLA\nBLA\n\n")),
// Multiple progress lines in one write
- WithArg<4>(WriteOnStdout("PROGRESS:10/100\nPROGRESS:50/100\n")),
+ WithArg<2>(WriteOnStdout("PROGRESS:10/100\nPROGRESS:50/100\n")),
// Progress line in multiple writes
- WithArg<4>(WriteOnStdout("PROG")),
- WithArg<4>(WriteOnStdout("RESS:99")),
- WithArg<4>(WriteOnStdout("/100\n")),
+ WithArg<2>(WriteOnStdout("PROG")),
+ WithArg<2>(WriteOnStdout("RESS:99")),
+ WithArg<2>(WriteOnStdout("/100\n")),
// Split last message as well, just in case
- WithArg<4>(WriteOnStdout("OK:/device/bugreport")),
- WithArg<4>(WriteOnStdout(".zip")),
- WithArg<4>(ReturnCallbackDone())));
+ WithArg<2>(WriteOnStdout("OK:/device/bugreport")),
+ WithArg<2>(WriteOnStdout(".zip")),
+ WithArg<2>(ReturnCallbackDone())));
// clang-format on
EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
- true, StrEq("pulling file.zip")))
+ false, StrEq("pulling file.zip")))
.WillOnce(Return(true));
const char* args[] = {"bugreport", "file.zip"};
- ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+ ASSERT_EQ(0, br_.DoIt(2, args));
}
// Tests 'adb bugreport file.zip' when it succeeds and displays progress, even if progress recedes.
@@ -287,28 +285,28 @@
ExpectProgress(50);
ExpectProgress(75);
// clang-format off
- EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
+ EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _))
// NOTE: DoAll accepts at most 10 arguments, and we're almost reached that limit...
.WillOnce(DoAll(
- WithArg<4>(WriteOnStdout("BEGIN:/device/bugreport.zip\n")),
- WithArg<4>(WriteOnStdout("PROGRESS:1/100\n")), // 1%
- WithArg<4>(WriteOnStdout("PROGRESS:50/100\n")), // 50%
+ WithArg<2>(WriteOnStdout("BEGIN:/device/bugreport.zip\n")),
+ WithArg<2>(WriteOnStdout("PROGRESS:1/100\n")), // 1%
+ WithArg<2>(WriteOnStdout("PROGRESS:50/100\n")), // 50%
// 25% should be ignored becaused it receded.
- WithArg<4>(WriteOnStdout("PROGRESS:25/100\n")), // 25%
- WithArg<4>(WriteOnStdout("PROGRESS:75/100\n")), // 75%
+ WithArg<2>(WriteOnStdout("PROGRESS:25/100\n")), // 25%
+ WithArg<2>(WriteOnStdout("PROGRESS:75/100\n")), // 75%
// 75% should be ignored becaused it didn't change.
- WithArg<4>(WriteOnStdout("PROGRESS:75/100\n")), // 75%
+ WithArg<2>(WriteOnStdout("PROGRESS:75/100\n")), // 75%
// Try a receeding percentage with a different max progress
- WithArg<4>(WriteOnStdout("PROGRESS:700/1000\n")), // 70%
- WithArg<4>(WriteOnStdout("OK:/device/bugreport.zip")),
- WithArg<4>(ReturnCallbackDone())));
+ WithArg<2>(WriteOnStdout("PROGRESS:700/1000\n")), // 70%
+ WithArg<2>(WriteOnStdout("OK:/device/bugreport.zip")),
+ WithArg<2>(ReturnCallbackDone())));
// clang-format on
EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
- true, StrEq("pulling file.zip")))
+ false, StrEq("pulling file.zip")))
.WillOnce(Return(true));
const char* args[] = {"bugreport", "file.zip"};
- ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+ ASSERT_EQ(0, br_.DoIt(2, args));
}
// Tests 'adb bugreport file.zip' when it succeeds and displays the initial progress of 0%
@@ -317,21 +315,21 @@
ExpectProgress(0);
ExpectProgress(1);
// clang-format off
- EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
+ EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _))
// NOTE: DoAll accepts at most 10 arguments, and we're almost reached that limit...
.WillOnce(DoAll(
- WithArg<4>(WriteOnStdout("BEGIN:/device/bugreport.zip\n")),
- WithArg<4>(WriteOnStdout("PROGRESS:1/100000\n")),
- WithArg<4>(WriteOnStdout("PROGRESS:1/100\n")), // 1%
- WithArg<4>(WriteOnStdout("OK:/device/bugreport.zip")),
- WithArg<4>(ReturnCallbackDone())));
+ WithArg<2>(WriteOnStdout("BEGIN:/device/bugreport.zip\n")),
+ WithArg<2>(WriteOnStdout("PROGRESS:1/100000\n")),
+ WithArg<2>(WriteOnStdout("PROGRESS:1/100\n")), // 1%
+ WithArg<2>(WriteOnStdout("OK:/device/bugreport.zip")),
+ WithArg<2>(ReturnCallbackDone())));
// clang-format on
EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
- true, StrEq("pulling file.zip")))
+ false, StrEq("pulling file.zip")))
.WillOnce(Return(true));
const char* args[] = {"bugreport", "file.zip"};
- ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+ ASSERT_EQ(0, br_.DoIt(2, args));
}
// Tests 'adb bugreport dir' when it succeeds and destination is a directory.
@@ -341,30 +339,30 @@
std::string dest_file =
android::base::StringPrintf("%s%cda_bugreport.zip", td.path, OS_PATH_SEPARATOR);
- EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
- .WillOnce(DoAll(WithArg<4>(WriteOnStdout("BEGIN:/device/da_bugreport.zip\n")),
- WithArg<4>(WriteOnStdout("OK:/device/da_bugreport.zip")),
- WithArg<4>(ReturnCallbackDone())));
+ EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _))
+ .WillOnce(DoAll(WithArg<2>(WriteOnStdout("BEGIN:/device/da_bugreport.zip\n")),
+ WithArg<2>(WriteOnStdout("OK:/device/da_bugreport.zip")),
+ WithArg<2>(ReturnCallbackDone())));
EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/da_bugreport.zip")), StrEq(dest_file),
- true, StrEq("pulling da_bugreport.zip")))
+ false, StrEq("pulling da_bugreport.zip")))
.WillOnce(Return(true));
const char* args[] = {"bugreport", td.path};
- ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+ ASSERT_EQ(0, br_.DoIt(2, args));
}
// Tests 'adb bugreport file' when it succeeds
TEST_F(BugreportTest, OkNoExtension) {
ExpectBugreportzVersion("1.1");
- EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
- .WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device/bugreport.zip\n")),
- WithArg<4>(ReturnCallbackDone())));
+ EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _))
+ .WillOnce(DoAll(WithArg<2>(WriteOnStdout("OK:/device/bugreport.zip\n")),
+ WithArg<2>(ReturnCallbackDone())));
EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
- true, StrEq("pulling file.zip")))
+ false, StrEq("pulling file.zip")))
.WillOnce(Return(true));
const char* args[] = {"bugreport", "file"};
- ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+ ASSERT_EQ(0, br_.DoIt(2, args));
}
// Tests 'adb bugreport dir' when it succeeds and destination is a directory and device runs N.
@@ -374,28 +372,28 @@
std::string dest_file =
android::base::StringPrintf("%s%cda_bugreport.zip", td.path, OS_PATH_SEPARATOR);
- EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz", false, _))
- .WillOnce(DoAll(WithArg<4>(WriteOnStdout("BEGIN:/device/da_bugreport.zip\n")),
- WithArg<4>(WriteOnStdout("OK:/device/da_bugreport.zip")),
- WithArg<4>(ReturnCallbackDone())));
+ EXPECT_CALL(br_, SendShellCommand("bugreportz", false, _))
+ .WillOnce(DoAll(WithArg<2>(WriteOnStdout("BEGIN:/device/da_bugreport.zip\n")),
+ WithArg<2>(WriteOnStdout("OK:/device/da_bugreport.zip")),
+ WithArg<2>(ReturnCallbackDone())));
EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/da_bugreport.zip")), StrEq(dest_file),
- true, StrEq("pulling da_bugreport.zip")))
+ false, StrEq("pulling da_bugreport.zip")))
.WillOnce(Return(true));
const char* args[] = {"bugreport", td.path};
- ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+ ASSERT_EQ(0, br_.DoIt(2, args));
}
// Tests 'adb bugreport file.zip' when the bugreport itself failed
TEST_F(BugreportTest, BugreportzReturnedFail) {
ExpectBugreportzVersion("1.1");
- EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
+ EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _))
.WillOnce(
- DoAll(WithArg<4>(WriteOnStdout("FAIL:D'OH!\n")), WithArg<4>(ReturnCallbackDone())));
+ DoAll(WithArg<2>(WriteOnStdout("FAIL:D'OH!\n")), WithArg<2>(ReturnCallbackDone())));
CaptureStderr();
const char* args[] = {"bugreport", "file.zip"};
- ASSERT_EQ(-1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+ ASSERT_EQ(-1, br_.DoIt(2, args));
ASSERT_THAT(GetCapturedStderr(), HasSubstr("D'OH!"));
}
@@ -404,13 +402,13 @@
// multiple buffer writes
TEST_F(BugreportTest, BugreportzReturnedFailSplitBuffer) {
ExpectBugreportzVersion("1.1");
- EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
- .WillOnce(DoAll(WithArg<4>(WriteOnStdout("FAIL")), WithArg<4>(WriteOnStdout(":D'OH!\n")),
- WithArg<4>(ReturnCallbackDone())));
+ EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _))
+ .WillOnce(DoAll(WithArg<2>(WriteOnStdout("FAIL")), WithArg<2>(WriteOnStdout(":D'OH!\n")),
+ WithArg<2>(ReturnCallbackDone())));
CaptureStderr();
const char* args[] = {"bugreport", "file.zip"};
- ASSERT_EQ(-1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+ ASSERT_EQ(-1, br_.DoIt(2, args));
ASSERT_THAT(GetCapturedStderr(), HasSubstr("D'OH!"));
}
@@ -418,23 +416,22 @@
// response.
TEST_F(BugreportTest, BugreportzReturnedUnsupported) {
ExpectBugreportzVersion("1.1");
- EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
- .WillOnce(DoAll(WithArg<4>(WriteOnStdout("bugreportz? What am I, a zombie?")),
- WithArg<4>(ReturnCallbackDone())));
+ EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _))
+ .WillOnce(DoAll(WithArg<2>(WriteOnStdout("bugreportz? What am I, a zombie?")),
+ WithArg<2>(ReturnCallbackDone())));
CaptureStderr();
const char* args[] = {"bugreport", "file.zip"};
- ASSERT_EQ(-1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+ ASSERT_EQ(-1, br_.DoIt(2, args));
ASSERT_THAT(GetCapturedStderr(), HasSubstr("bugreportz? What am I, a zombie?"));
}
// Tests 'adb bugreport file.zip' when the bugreportz -v command failed
TEST_F(BugreportTest, BugreportzVersionFailed) {
- EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -v", false, _))
- .WillOnce(Return(666));
+ EXPECT_CALL(br_, SendShellCommand("bugreportz -v", false, _)).WillOnce(Return(666));
const char* args[] = {"bugreport", "file.zip"};
- ASSERT_EQ(666, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+ ASSERT_EQ(666, br_.DoIt(2, args));
}
// Tests 'adb bugreport file.zip' when the bugreportz -v returns status 0 but with no output.
@@ -442,29 +439,28 @@
ExpectBugreportzVersion("");
const char* args[] = {"bugreport", "file.zip"};
- ASSERT_EQ(-1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+ ASSERT_EQ(-1, br_.DoIt(2, args));
}
// Tests 'adb bugreport file.zip' when the main bugreportz command failed
TEST_F(BugreportTest, BugreportzFailed) {
ExpectBugreportzVersion("1.1");
- EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
- .WillOnce(Return(666));
+ EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _)).WillOnce(Return(666));
const char* args[] = {"bugreport", "file.zip"};
- ASSERT_EQ(666, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+ ASSERT_EQ(666, br_.DoIt(2, args));
}
// Tests 'adb bugreport file.zip' when the bugreport could not be pulled
TEST_F(BugreportTest, PullFails) {
ExpectBugreportzVersion("1.1");
- EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
- .WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device/bugreport.zip")),
- WithArg<4>(ReturnCallbackDone())));
+ EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _))
+ .WillOnce(DoAll(WithArg<2>(WriteOnStdout("OK:/device/bugreport.zip")),
+ WithArg<2>(ReturnCallbackDone())));
EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
- true, HasSubstr("file.zip")))
+ false, HasSubstr("file.zip")))
.WillOnce(Return(false));
const char* args[] = {"bugreport", "file.zip"};
- ASSERT_EQ(1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+ ASSERT_EQ(1, br_.DoIt(2, args));
}
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index c9f1ee9..d126f52 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -62,11 +62,11 @@
#include "shell_service.h"
#include "sysdeps/chrono.h"
-static int install_app(TransportType t, const char* serial, int argc, const char** argv);
-static int install_multiple_app(TransportType t, const char* serial, int argc, const char** argv);
-static int uninstall_app(TransportType t, const char* serial, int argc, const char** argv);
-static int install_app_legacy(TransportType t, const char* serial, int argc, const char** argv);
-static int uninstall_app_legacy(TransportType t, const char* serial, int argc, const char** argv);
+static int install_app(int argc, const char** argv);
+static int install_multiple_app(int argc, const char** argv);
+static int uninstall_app(int argc, const char** argv);
+static int install_app_legacy(int argc, const char** argv);
+static int uninstall_app_legacy(int argc, const char** argv);
extern int gListenAll;
@@ -90,6 +90,7 @@
" -d use USB device (error if multiple devices connected)\n"
" -e use TCP/IP device (error if multiple TCP/IP devices available)\n"
" -s SERIAL use device with given serial (overrides $ANDROID_SERIAL)\n"
+ " -t ID use device with given transport id\n"
" -H name of adb server host [default=localhost]\n"
" -P port of adb server [default=5037]\n"
" -L SOCKET listen on given socket for adb server [default=tcp:localhost:5037]\n"
@@ -685,6 +686,10 @@
// Parse shell-specific command-line options.
argv[0] = "adb shell"; // So getopt(3) error messages start "adb shell".
+#ifdef _WIN32
+ // fixes "adb shell -l" crash on Windows, b/37284906
+ __argv = const_cast<char**>(argv);
+#endif
optind = 1; // argv[0] is always "shell", so set `optind` appropriately.
int opt;
while ((opt = getopt(argc, const_cast<char**>(argv), "+e:ntTx")) != -1) {
@@ -986,13 +991,16 @@
#endif /* !defined(_WIN32) */
}
-static bool wait_for_device(const char* service, TransportType t, const char* serial) {
+static bool wait_for_device(const char* service) {
std::vector<std::string> components = android::base::Split(service, "-");
if (components.size() < 3 || components.size() > 4) {
fprintf(stderr, "adb: couldn't parse 'wait-for' command: %s\n", service);
return false;
}
+ TransportType t;
+ adb_get_transport(&t, nullptr, nullptr);
+
// Was the caller vague about what they'd like us to wait for?
// If so, check they weren't more specific in their choice of transport type.
if (components.size() == 3) {
@@ -1019,7 +1027,7 @@
return false;
}
- std::string cmd = format_host_command(android::base::Join(components, "-").c_str(), t, serial);
+ std::string cmd = format_host_command(android::base::Join(components, "-").c_str());
return adb_command(cmd);
}
@@ -1065,8 +1073,8 @@
return true;
}
-int send_shell_command(TransportType transport_type, const char* serial, const std::string& command,
- bool disable_shell_protocol, StandardStreamsCallbackInterface* callback) {
+int send_shell_command(const std::string& command, bool disable_shell_protocol,
+ StandardStreamsCallbackInterface* callback) {
int fd;
bool use_shell_protocol = false;
@@ -1097,7 +1105,7 @@
}
fprintf(stderr, "- waiting for device -\n");
- if (!wait_for_device("wait-for-device", transport_type, serial)) {
+ if (!wait_for_device("wait-for-device")) {
return 1;
}
}
@@ -1111,7 +1119,7 @@
return exit_code;
}
-static int logcat(TransportType transport, const char* serial, int argc, const char** argv) {
+static int logcat(int argc, const char** argv) {
char* log_tags = getenv("ANDROID_LOG_TAGS");
std::string quoted = escape_arg(log_tags == nullptr ? "" : log_tags);
@@ -1128,7 +1136,7 @@
}
// No need for shell protocol with logcat, always disable for simplicity.
- return send_shell_command(transport, serial, cmd, true);
+ return send_shell_command(cmd, true);
}
static void write_zeros(int bytes, int fd) {
@@ -1340,6 +1348,7 @@
// We need to check for -d and -e before we look at $ANDROID_SERIAL.
const char* serial = nullptr;
+ TransportId transport_id = 0;
while (argc > 0) {
if (!strcmp(argv[0],"server")) {
@@ -1359,7 +1368,7 @@
fprintf(stderr, "adb: invalid reply fd \"%s\"\n", reply_fd_str);
return 1;
}
- } else if (argv[0][0]=='-' && argv[0][1]=='s') {
+ } else if (!strncmp(argv[0], "-s", 2)) {
if (isdigit(argv[0][2])) {
serial = argv[0] + 2;
} else {
@@ -1368,6 +1377,19 @@
argc--;
argv++;
}
+ } else if (!strncmp(argv[0], "-t", 2)) {
+ const char* id;
+ if (isdigit(argv[0][2])) {
+ id = argv[0] + 2;
+ } else {
+ id = argv[1];
+ argc--;
+ argv++;
+ }
+ transport_id = strtoll(id, const_cast<char**>(&id), 10);
+ if (*id != '\0') {
+ return syntax_error("invalid transport id");
+ }
} else if (!strcmp(argv[0],"-d")) {
transport_type = kTransportUsb;
} else if (!strcmp(argv[0],"-e")) {
@@ -1451,7 +1473,7 @@
serial = getenv("ANDROID_SERIAL");
}
- adb_set_transport(transport_type, serial);
+ adb_set_transport(transport_type, serial, transport_id);
if (is_server) {
if (no_daemon || is_daemon) {
@@ -1478,7 +1500,7 @@
if (!strncmp(argv[0], "wait-for-", strlen("wait-for-"))) {
const char* service = argv[0];
- if (!wait_for_device(service, transport_type, serial)) {
+ if (!wait_for_device(service)) {
return 1;
}
@@ -1566,9 +1588,13 @@
} else {
return 0;
}
- }
- else if (!strcmp(argv[0], "tcpip") && argc > 1) {
- return adb_connect_command(android::base::StringPrintf("tcpip:%s", argv[1]));
+ } else if (!strcmp(argv[0], "tcpip")) {
+ if (argc != 2) return syntax_error("tcpip requires an argument");
+ int port;
+ if (!android::base::ParseInt(argv[1], &port, 1, 65535)) {
+ return syntax_error("tcpip: invalid port: %s", argv[1]);
+ }
+ return adb_connect_command(android::base::StringPrintf("tcpip:%d", port));
}
else if (!strcmp(argv[0], "remount") ||
!strcmp(argv[0], "reboot") ||
@@ -1589,7 +1615,7 @@
return adb_root(argv[0]) ? 0 : 1;
} else if (!strcmp(argv[0], "bugreport")) {
Bugreport bugreport;
- return bugreport.DoIt(transport_type, serial, argc, argv);
+ return bugreport.DoIt(argc, argv);
} else if (!strcmp(argv[0], "forward") || !strcmp(argv[0], "reverse")) {
bool reverse = !strcmp(argv[0], "reverse");
++argv;
@@ -1685,20 +1711,20 @@
else if (!strcmp(argv[0], "install")) {
if (argc < 2) return syntax_error("install requires an argument");
if (_use_legacy_install()) {
- return install_app_legacy(transport_type, serial, argc, argv);
+ return install_app_legacy(argc, argv);
}
- return install_app(transport_type, serial, argc, argv);
+ return install_app(argc, argv);
}
else if (!strcmp(argv[0], "install-multiple")) {
if (argc < 2) return syntax_error("install-multiple requires an argument");
- return install_multiple_app(transport_type, serial, argc, argv);
+ return install_multiple_app(argc, argv);
}
else if (!strcmp(argv[0], "uninstall")) {
if (argc < 2) return syntax_error("uninstall requires an argument");
if (_use_legacy_install()) {
- return uninstall_app_legacy(transport_type, serial, argc, argv);
+ return uninstall_app_legacy(argc, argv);
}
- return uninstall_app(transport_type, serial, argc, argv);
+ return uninstall_app(argc, argv);
}
else if (!strcmp(argv[0], "sync")) {
std::string src;
@@ -1752,11 +1778,11 @@
!strcmp(argv[0],"get-serialno") ||
!strcmp(argv[0],"get-devpath"))
{
- return adb_query_command(format_host_command(argv[0], transport_type, serial));
+ return adb_query_command(format_host_command(argv[0]));
}
/* other commands */
else if (!strcmp(argv[0],"logcat") || !strcmp(argv[0],"lolcat") || !strcmp(argv[0],"longcat")) {
- return logcat(transport_type, serial, argc, argv);
+ return logcat(argc, argv);
}
else if (!strcmp(argv[0],"ppp")) {
return ppp(argc, argv);
@@ -1819,7 +1845,7 @@
return adb_query_command("host:host-features");
} else if (!strcmp(argv[0], "reconnect")) {
if (argc == 1) {
- return adb_query_command(format_host_command(argv[0], transport_type, serial));
+ return adb_query_command(format_host_command(argv[0]));
} else if (argc == 2) {
if (!strcmp(argv[1], "device")) {
std::string err;
@@ -1838,7 +1864,7 @@
return 1;
}
-static int uninstall_app(TransportType transport, const char* serial, int argc, const char** argv) {
+static int uninstall_app(int argc, const char** argv) {
// 'adb uninstall' takes the same arguments as 'cmd package uninstall' on device
std::string cmd = "cmd package";
while (argc-- > 0) {
@@ -1854,10 +1880,10 @@
cmd += " " + escape_arg(*argv++);
}
- return send_shell_command(transport, serial, cmd, false);
+ return send_shell_command(cmd, false);
}
-static int install_app(TransportType transport, const char* serial, int argc, const char** argv) {
+static int install_app(int argc, const char** argv) {
// The last argument must be the APK file
const char* file = argv[argc - 1];
if (!android::base::EndsWithIgnoreCase(file, ".apk")) {
@@ -1910,9 +1936,7 @@
return 1;
}
-static int install_multiple_app(TransportType transport, const char* serial, int argc,
- const char** argv)
-{
+static int install_multiple_app(int argc, const char** argv) {
// Find all APK arguments starting at end.
// All other arguments passed through verbatim.
int first_apk = -1;
@@ -2037,17 +2061,17 @@
return EXIT_FAILURE;
}
-static int pm_command(TransportType transport, const char* serial, int argc, const char** argv) {
+static int pm_command(int argc, const char** argv) {
std::string cmd = "pm";
while (argc-- > 0) {
cmd += " " + escape_arg(*argv++);
}
- return send_shell_command(transport, serial, cmd, false);
+ return send_shell_command(cmd, false);
}
-static int uninstall_app_legacy(TransportType transport, const char* serial, int argc, const char** argv) {
+static int uninstall_app_legacy(int argc, const char** argv) {
/* if the user choose the -k option, we refuse to do it until devices are
out with the option to uninstall the remaining data somehow (adb/ui) */
int i;
@@ -2063,15 +2087,15 @@
}
/* 'adb uninstall' takes the same arguments as 'pm uninstall' on device */
- return pm_command(transport, serial, argc, argv);
+ return pm_command(argc, argv);
}
-static int delete_file(TransportType transport, const char* serial, const std::string& filename) {
+static int delete_file(const std::string& filename) {
std::string cmd = "rm -f " + escape_arg(filename);
- return send_shell_command(transport, serial, cmd, false);
+ return send_shell_command(cmd, false);
}
-static int install_app_legacy(TransportType transport, const char* serial, int argc, const char** argv) {
+static int install_app_legacy(int argc, const char** argv) {
static const char *const DATA_DEST = "/data/local/tmp/%s";
static const char *const SD_DEST = "/sdcard/tmp/%s";
const char* where = DATA_DEST;
@@ -2100,9 +2124,9 @@
where, android::base::Basename(argv[last_apk]).c_str());
if (!do_sync_push(apk_file, apk_dest.c_str(), false)) goto cleanup_apk;
argv[last_apk] = apk_dest.c_str(); /* destination name, not source location */
- result = pm_command(transport, serial, argc, argv);
+ result = pm_command(argc, argv);
cleanup_apk:
- delete_file(transport, serial, apk_dest);
+ delete_file(apk_dest);
return result;
}
diff --git a/adb/commandline.h b/adb/commandline.h
index 9ba69a3..36cd798 100644
--- a/adb/commandline.h
+++ b/adb/commandline.h
@@ -91,8 +91,8 @@
// Connects to the device "shell" service with |command| and prints the
// resulting output.
// if |callback| is non-null, stdout/stderr output will be handled by it.
-int send_shell_command(TransportType transport_type, const char* serial, const std::string& command,
- bool disable_shell_protocol, StandardStreamsCallbackInterface* callback =
- &DEFAULT_STANDARD_STREAMS_CALLBACK);
+int send_shell_command(
+ const std::string& command, bool disable_shell_protocol,
+ StandardStreamsCallbackInterface* callback = &DEFAULT_STANDARD_STREAMS_CALLBACK);
#endif // COMMANDLINE_H
diff --git a/adb/daemon/usb.cpp b/adb/daemon/usb.cpp
index d3b2f3d..0f92282 100644
--- a/adb/daemon/usb.cpp
+++ b/adb/daemon/usb.cpp
@@ -235,6 +235,8 @@
};
bool init_functionfs(struct usb_handle* h) {
+ LOG(INFO) << "initializing functionfs";
+
ssize_t ret;
struct desc_v1 v1_descriptor;
struct desc_v2 v2_descriptor;
@@ -255,10 +257,10 @@
v2_descriptor.os_desc = os_desc_compat;
if (h->control < 0) { // might have already done this before
- D("OPENING %s", USB_FFS_ADB_EP0);
+ LOG(INFO) << "opening control endpoint " << USB_FFS_ADB_EP0;
h->control = adb_open(USB_FFS_ADB_EP0, O_RDWR);
if (h->control < 0) {
- D("[ %s: cannot open control endpoint: errno=%d]", USB_FFS_ADB_EP0, errno);
+ PLOG(ERROR) << "cannot open control endpoint " << USB_FFS_ADB_EP0;
goto err;
}
@@ -289,13 +291,13 @@
h->bulk_out = adb_open(USB_FFS_ADB_OUT, O_RDWR);
if (h->bulk_out < 0) {
- D("[ %s: cannot open bulk-out ep: errno=%d ]", USB_FFS_ADB_OUT, errno);
+ PLOG(ERROR) << "cannot open bulk-out endpoint " << USB_FFS_ADB_OUT;
goto err;
}
h->bulk_in = adb_open(USB_FFS_ADB_IN, O_RDWR);
if (h->bulk_in < 0) {
- D("[ %s: cannot open bulk-in ep: errno=%d ]", USB_FFS_ADB_IN, errno);
+ PLOG(ERROR) << "cannot open bulk-in endpoint " << USB_FFS_ADB_IN;
goto err;
}
@@ -356,12 +358,13 @@
while (true) {
if (init_functionfs(usb)) {
+ LOG(INFO) << "functionfs successfully initialized";
break;
}
std::this_thread::sleep_for(1s);
}
- D("[ usb_thread - registering device ]");
+ LOG(INFO) << "registering usb transport";
register_usb_transport(usb, 0, 0, 1);
}
@@ -430,6 +433,8 @@
}
static void usb_ffs_close(usb_handle* h) {
+ LOG(INFO) << "closing functionfs transport";
+
h->kicked = false;
adb_close(h->bulk_out);
adb_close(h->bulk_in);
diff --git a/adb/services.cpp b/adb/services.cpp
index 9605e6e..dbf71d3 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -187,7 +187,7 @@
return -1;
}
VLOG(SERVICES) << "service socketpair: " << s[0] << ", " << s[1];
- if (handle_forward_request(command, kTransportAny, nullptr, s[1]) < 0) {
+ if (handle_forward_request(command, kTransportAny, nullptr, 0, s[1]) < 0) {
SendFail(s[1], "not a reverse forwarding command");
}
adb_close(s[1]);
@@ -334,6 +334,7 @@
struct state_info {
TransportType transport_type;
std::string serial;
+ TransportId transport_id;
ConnectionState state;
};
@@ -346,7 +347,8 @@
bool is_ambiguous = false;
std::string error = "unknown error";
const char* serial = sinfo->serial.length() ? sinfo->serial.c_str() : NULL;
- atransport* t = acquire_one_transport(sinfo->transport_type, serial, &is_ambiguous, &error);
+ atransport* t = acquire_one_transport(sinfo->transport_type, serial, sinfo->transport_id,
+ &is_ambiguous, &error);
if (t != nullptr && (sinfo->state == kCsAny || sinfo->state == t->GetConnectionState())) {
SendOkay(fd);
break;
@@ -437,9 +439,11 @@
#endif
#if ADB_HOST
-asocket* host_service_to_socket(const char* name, const char* serial) {
+asocket* host_service_to_socket(const char* name, const char* serial, TransportId transport_id) {
if (!strcmp(name,"track-devices")) {
- return create_device_tracker();
+ return create_device_tracker(false);
+ } else if (!strcmp(name, "track-devices-l")) {
+ return create_device_tracker(true);
} else if (android::base::StartsWith(name, "wait-for-")) {
name += strlen("wait-for-");
@@ -450,6 +454,7 @@
}
if (serial) sinfo->serial = serial;
+ sinfo->transport_id = transport_id;
if (android::base::StartsWith(name, "local")) {
name += strlen("local");
@@ -478,11 +483,17 @@
return nullptr;
}
- int fd = create_service_thread(wait_for_state, sinfo.release());
+ int fd = create_service_thread(wait_for_state, sinfo.get());
+ if (fd != -1) {
+ sinfo.release();
+ }
return create_local_socket(fd);
} else if (!strncmp(name, "connect:", 8)) {
char* host = strdup(name + 8);
int fd = create_service_thread(connect_service, host);
+ if (fd == -1) {
+ free(host);
+ }
return create_local_socket(fd);
}
return NULL;
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index e0143c6..f28a3df 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -430,10 +430,11 @@
}
#if ADB_HOST
-static asocket* create_host_service_socket(const char* name, const char* serial) {
+static asocket* create_host_service_socket(const char* name, const char* serial,
+ TransportId transport_id) {
asocket* s;
- s = host_service_to_socket(name, serial);
+ s = host_service_to_socket(name, serial, transport_id);
if (s != NULL) {
D("LS(%d) bound to '%s'", s->id, name);
@@ -658,6 +659,7 @@
#if ADB_HOST
char* service = nullptr;
char* serial = nullptr;
+ TransportId transport_id = 0;
TransportType type = kTransportAny;
#endif
@@ -715,6 +717,14 @@
serial = service;
service = serial_end + 1;
}
+ } else if (!strncmp(service, "host-transport-id:", strlen("host-transport-id:"))) {
+ service += strlen("host-transport-id:");
+ transport_id = strtoll(service, &service, 10);
+
+ if (*service != ':') {
+ return -1;
+ }
+ service++;
} else if (!strncmp(service, "host-usb:", strlen("host-usb:"))) {
type = kTransportUsb;
service += strlen("host-usb:");
@@ -736,7 +746,7 @@
** the OKAY or FAIL message and all we have to do
** is clean up.
*/
- if (handle_host_request(service, type, serial, s->peer->fd, s) == 0) {
+ if (handle_host_request(service, type, serial, transport_id, s->peer->fd, s) == 0) {
/* XXX fail message? */
D("SS(%d): handled host service '%s'", s->id, service);
goto fail;
@@ -751,7 +761,7 @@
** if no such service exists, we'll fail out
** and tear down here.
*/
- s2 = create_host_service_socket(service, serial);
+ s2 = create_host_service_socket(service, serial, transport_id);
if (s2 == 0) {
D("SS(%d): couldn't create host service '%s'", s->id, service);
SendFail(s->peer->fd, "unknown host service");
@@ -783,7 +793,7 @@
#else /* !ADB_HOST */
if (s->transport == nullptr) {
std::string error_msg = "unknown failure";
- s->transport = acquire_one_transport(kTransportAny, nullptr, nullptr, &error_msg);
+ s->transport = acquire_one_transport(kTransportAny, nullptr, 0, nullptr, &error_msg);
if (s->transport == nullptr) {
SendFail(s->peer->fd, error_msg);
goto fail;
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index 49c7847..0abb680 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -582,18 +582,12 @@
#ifdef __APPLE__
return pthread_setname_np(name.c_str());
#else
- const char *s = name.c_str();
-
- // pthread_setname_np fails rather than truncating long strings.
- const int max_task_comm_len = 16; // including the null terminator
- if (name.length() > (max_task_comm_len - 1)) {
- char buf[max_task_comm_len];
- strncpy(buf, name.c_str(), sizeof(buf) - 1);
- buf[sizeof(buf) - 1] = '\0';
- s = buf;
- }
-
- return pthread_setname_np(pthread_self(), s) ;
+ // Both bionic and glibc's pthread_setname_np fails rather than truncating long strings.
+ // glibc doesn't have strlcpy, so we have to fake it.
+ char buf[16]; // MAX_TASK_COMM_LEN, but that's not exported by the kernel headers.
+ strncpy(buf, name.c_str(), sizeof(buf) - 1);
+ buf[sizeof(buf) - 1] = '\0';
+ return pthread_setname_np(pthread_self(), buf);
#endif
}
diff --git a/adb/test_adb.py b/adb/test_adb.py
index cb3e0d8..98c8a59 100644
--- a/adb/test_adb.py
+++ b/adb/test_adb.py
@@ -60,13 +60,13 @@
stderr=subprocess.STDOUT)
out, _ = p.communicate()
self.assertEqual(1, p.returncode)
- self.assertIn('help message', out)
+ self.assertIn('requires an argument', out)
p = subprocess.Popen(['adb', 'tcpip', 'foo'], stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
out, _ = p.communicate()
self.assertEqual(1, p.returncode)
- self.assertIn('error', out)
+ self.assertIn('invalid port', out)
# Helper method that reads a pipe until it is closed, then sets the event.
def _read_pipe_and_set_event(self, pipe, event):
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 2bbbefd..b2e03a0 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -21,6 +21,7 @@
#include <ctype.h>
#include <errno.h>
+#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -36,6 +37,7 @@
#include <android-base/quick_exit.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
+#include <android-base/thread_annotations.h>
#include "adb.h"
#include "adb_auth.h"
@@ -46,10 +48,11 @@
static void transport_unref(atransport *t);
+// TODO: unordered_map<TransportId, atransport*>
static auto& transport_list = *new std::list<atransport*>();
static auto& pending_list = *new std::list<atransport*>();
-static std::mutex& transport_lock = *new std::mutex();
+static auto& transport_lock = *new std::recursive_mutex();
const char* const kFeatureShell2 = "shell_v2";
const char* const kFeatureCmd = "cmd";
@@ -57,6 +60,11 @@
const char* const kFeatureLibusb = "libusb";
const char* const kFeaturePushSync = "push_sync";
+TransportId NextTransportId() {
+ static std::atomic<TransportId> next(1);
+ return next++;
+}
+
static std::string dump_packet(const char* name, const char* func, apacket* p) {
unsigned command = p->msg.command;
int len = p->msg.data_length;
@@ -298,9 +306,11 @@
}
void kick_transport(atransport* t) {
- std::lock_guard<std::mutex> lock(transport_lock);
+ std::lock_guard<std::recursive_mutex> lock(transport_lock);
// As kick_transport() can be called from threads without guarantee that t is valid,
// check if the transport is in transport_list first.
+ //
+ // TODO(jmgao): WTF? Is this actually true?
if (std::find(transport_list.begin(), transport_list.end(), t) != transport_list.end()) {
t->Kick();
}
@@ -319,7 +329,8 @@
*/
struct device_tracker {
asocket socket;
- int update_needed;
+ bool update_needed;
+ bool long_output;
device_tracker* next;
};
@@ -330,7 +341,7 @@
device_tracker** pnode = &device_tracker_list;
device_tracker* node = *pnode;
- std::lock_guard<std::mutex> lock(transport_lock);
+ std::lock_guard<std::recursive_mutex> lock(transport_lock);
while (node) {
if (node == tracker) {
*pnode = node->next;
@@ -376,15 +387,15 @@
// We want to send the device list when the tracker connects
// for the first time, even if no update occurred.
- if (tracker->update_needed > 0) {
- tracker->update_needed = 0;
+ if (tracker->update_needed) {
+ tracker->update_needed = false;
- std::string transports = list_transports(false);
+ std::string transports = list_transports(tracker->long_output);
device_tracker_send(tracker, transports);
}
}
-asocket* create_device_tracker(void) {
+asocket* create_device_tracker(bool long_output) {
device_tracker* tracker = reinterpret_cast<device_tracker*>(calloc(1, sizeof(*tracker)));
if (tracker == nullptr) fatal("cannot allocate device tracker");
@@ -393,7 +404,8 @@
tracker->socket.enqueue = device_tracker_enqueue;
tracker->socket.ready = device_tracker_ready;
tracker->socket.close = device_tracker_close;
- tracker->update_needed = 1;
+ tracker->update_needed = true;
+ tracker->long_output = long_output;
tracker->next = device_tracker_list;
device_tracker_list = tracker;
@@ -403,7 +415,7 @@
// Check if all of the USB transports are connected.
bool iterate_transports(std::function<bool(const atransport*)> fn) {
- std::lock_guard<std::mutex> lock(transport_lock);
+ std::lock_guard<std::recursive_mutex> lock(transport_lock);
for (const auto& t : transport_list) {
if (!fn(t)) {
return false;
@@ -507,7 +519,7 @@
adb_close(t->fd);
{
- std::lock_guard<std::mutex> lock(transport_lock);
+ std::lock_guard<std::recursive_mutex> lock(transport_lock);
transport_list.remove(t);
}
@@ -546,7 +558,7 @@
}
{
- std::lock_guard<std::mutex> lock(transport_lock);
+ std::lock_guard<std::recursive_mutex> lock(transport_lock);
pending_list.remove(t);
transport_list.push_front(t);
}
@@ -573,7 +585,7 @@
void kick_all_transports() {
// To avoid only writing part of a packet to a transport after exit, kick all transports.
- std::lock_guard<std::mutex> lock(transport_lock);
+ std::lock_guard<std::recursive_mutex> lock(transport_lock);
for (auto t : transport_list) {
t->Kick();
}
@@ -638,11 +650,15 @@
return !*to_test;
}
-atransport* acquire_one_transport(TransportType type, const char* serial, bool* is_ambiguous,
- std::string* error_out, bool accept_any_state) {
+atransport* acquire_one_transport(TransportType type, const char* serial, TransportId transport_id,
+ bool* is_ambiguous, std::string* error_out,
+ bool accept_any_state) {
atransport* result = nullptr;
- if (serial) {
+ if (transport_id != 0) {
+ *error_out =
+ android::base::StringPrintf("no device with transport id '%" PRIu64 "'", transport_id);
+ } else if (serial) {
*error_out = android::base::StringPrintf("device '%s' not found", serial);
} else if (type == kTransportLocal) {
*error_out = "no emulators found";
@@ -652,7 +668,7 @@
*error_out = "no devices found";
}
- std::unique_lock<std::mutex> lock(transport_lock);
+ std::unique_lock<std::recursive_mutex> lock(transport_lock);
for (const auto& t : transport_list) {
if (t->GetConnectionState() == kCsNoPerm) {
#if ADB_HOST
@@ -661,8 +677,12 @@
continue;
}
- // Check for matching serial number.
- if (serial) {
+ if (transport_id) {
+ if (t->id == transport_id) {
+ result = t;
+ break;
+ }
+ } else if (serial) {
if (t->MatchesTarget(serial)) {
if (result) {
*error_out = "more than one device";
@@ -889,18 +909,23 @@
#if ADB_HOST
+// We use newline as our delimiter, make sure to never output it.
+static std::string sanitize(std::string str, bool alphanumeric) {
+ auto pred = alphanumeric ? [](const char c) { return !isalnum(c); }
+ : [](const char c) { return c == '\n'; };
+ std::replace_if(str.begin(), str.end(), pred, '_');
+ return str;
+}
+
static void append_transport_info(std::string* result, const char* key, const char* value,
- bool sanitize) {
+ bool alphanumeric) {
if (value == nullptr || *value == '\0') {
return;
}
*result += ' ';
*result += key;
-
- for (const char* p = value; *p; ++p) {
- result->push_back((!sanitize || isalnum(*p)) ? *p : '_');
- }
+ *result += sanitize(value, alphanumeric);
}
static void append_transport(const atransport* t, std::string* result, bool long_listing) {
@@ -920,6 +945,11 @@
append_transport_info(result, "product:", t->product, false);
append_transport_info(result, "model:", t->model, true);
append_transport_info(result, "device:", t->device, false);
+
+ // Put id at the end, so that anyone parsing the output here can always find it by scanning
+ // backwards from newlines, even with hypothetical devices named 'transport_id:1'.
+ *result += " transport_id:";
+ *result += std::to_string(t->id);
}
*result += '\n';
}
@@ -927,7 +957,7 @@
std::string list_transports(bool long_listing) {
std::string result;
- std::lock_guard<std::mutex> lock(transport_lock);
+ std::lock_guard<std::recursive_mutex> lock(transport_lock);
for (const auto& t : transport_list) {
append_transport(t, &result, long_listing);
}
@@ -935,7 +965,7 @@
}
void close_usb_devices(std::function<bool(const atransport*)> predicate) {
- std::lock_guard<std::mutex> lock(transport_lock);
+ std::lock_guard<std::recursive_mutex> lock(transport_lock);
for (auto& t : transport_list) {
if (predicate(t)) {
t->Kick();
@@ -964,7 +994,7 @@
return -1;
}
- std::unique_lock<std::mutex> lock(transport_lock);
+ std::unique_lock<std::recursive_mutex> lock(transport_lock);
for (const auto& transport : pending_list) {
if (transport->serial && strcmp(serial, transport->serial) == 0) {
VLOG(TRANSPORT) << "socket transport " << transport->serial
@@ -996,7 +1026,7 @@
atransport* find_transport(const char* serial) {
atransport* result = nullptr;
- std::lock_guard<std::mutex> lock(transport_lock);
+ std::lock_guard<std::recursive_mutex> lock(transport_lock);
for (auto& t : transport_list) {
if (t->serial && strcmp(serial, t->serial) == 0) {
result = t;
@@ -1008,7 +1038,7 @@
}
void kick_all_tcp_devices() {
- std::lock_guard<std::mutex> lock(transport_lock);
+ std::lock_guard<std::recursive_mutex> lock(transport_lock);
for (auto& t : transport_list) {
if (t->IsTcpDevice()) {
// Kicking breaks the read_transport thread of this transport out of any read, then
@@ -1037,7 +1067,7 @@
}
{
- std::lock_guard<std::mutex> lock(transport_lock);
+ std::lock_guard<std::recursive_mutex> lock(transport_lock);
pending_list.push_front(t);
}
@@ -1046,7 +1076,7 @@
// This should only be used for transports with connection_state == kCsNoPerm.
void unregister_usb_transport(usb_handle* usb) {
- std::lock_guard<std::mutex> lock(transport_lock);
+ std::lock_guard<std::recursive_mutex> lock(transport_lock);
transport_list.remove_if(
[usb](atransport* t) { return t->usb == usb && t->GetConnectionState() == kCsNoPerm; });
}
diff --git a/adb/transport.h b/adb/transport.h
index 4a89ed9..00fad56 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -54,14 +54,17 @@
// The server supports `push --sync`.
extern const char* const kFeaturePushSync;
+TransportId NextTransportId();
+
class atransport {
-public:
+ public:
// TODO(danalbert): We expose waaaaaaay too much stuff because this was
// historically just a struct, but making the whole thing a more idiomatic
// class in one go is a very large change. Given how bad our testing is,
// it's better to do this piece by piece.
- atransport(ConnectionState state = kCsOffline) : ref_count(0), connection_state_(state) {
+ atransport(ConnectionState state = kCsOffline)
+ : id(NextTransportId()), ref_count(0), connection_state_(state) {
transport_fde = {};
protocol_version = A_VERSION;
max_payload = MAX_PAYLOAD;
@@ -72,12 +75,8 @@
void (*close)(atransport* t) = nullptr;
void SetWriteFunction(int (*write_func)(apacket*, atransport*)) { write_func_ = write_func; }
- void SetKickFunction(void (*kick_func)(atransport*)) {
- kick_func_ = kick_func;
- }
- bool IsKicked() {
- return kicked_;
- }
+ void SetKickFunction(void (*kick_func)(atransport*)) { kick_func_ = kick_func; }
+ bool IsKicked() { return kicked_; }
int Write(apacket* p);
void Kick();
@@ -85,6 +84,7 @@
ConnectionState GetConnectionState() const;
void SetConnectionState(ConnectionState state);
+ const TransportId id;
int fd = -1;
int transport_socket = -1;
fdevent transport_fde;
@@ -191,12 +191,14 @@
/*
* Obtain a transport from the available transports.
* If serial is non-null then only the device with that serial will be chosen.
+ * If transport_id is non-zero then only the device with that transport ID 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(TransportType type, const char* serial, bool* is_ambiguous,
- std::string* error_out, bool accept_any_state = false);
+atransport* acquire_one_transport(TransportType type, const char* serial, TransportId transport_id,
+ bool* is_ambiguous, std::string* error_out,
+ bool accept_any_state = false);
void kick_transport(atransport* t);
void update_transports(void);
@@ -231,6 +233,6 @@
void send_packet(apacket* p, atransport* t);
-asocket* create_device_tracker(void);
+asocket* create_device_tracker(bool long_output);
#endif /* __TRANSPORT_H */
diff --git a/adb/transport_usb.cpp b/adb/transport_usb.cpp
index 2f46920..7e8ae67 100644
--- a/adb/transport_usb.cpp
+++ b/adb/transport_usb.cpp
@@ -120,24 +120,24 @@
static int remote_read(apacket *p, atransport *t)
{
if (usb_read(t->usb, &p->msg, sizeof(amessage))) {
- D("remote usb: read terminated (message)");
+ PLOG(ERROR) << "remote usb: read terminated (message)";
return -1;
}
if (!check_header(p, t)) {
- D("remote usb: check_header failed");
+ LOG(ERROR) << "remote usb: check_header failed";
return -1;
}
if (p->msg.data_length) {
if (usb_read(t->usb, p->data, p->msg.data_length)) {
- D("remote usb: terminated (data)");
+ PLOG(ERROR) << "remote usb: terminated (data)";
return -1;
}
}
if (!check_data(p)) {
- D("remote usb: check_data failed");
+ LOG(ERROR) << "remote usb: check_data failed";
return -1;
}
@@ -150,12 +150,12 @@
unsigned size = p->msg.data_length;
if (usb_write(t->usb, &p->msg, sizeof(amessage))) {
- D("remote usb: 1 - write terminated");
+ PLOG(ERROR) << "remote usb: 1 - write terminated";
return -1;
}
- if(p->msg.data_length == 0) return 0;
+ if (p->msg.data_length == 0) return 0;
if (usb_write(t->usb, &p->data, size)) {
- D("remote usb: 2 - write terminated");
+ PLOG(ERROR) << "remote usb: 2 - write terminated";
return -1;
}
diff --git a/base/Android.bp b/base/Android.bp
index b636dc3..82aee2a 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -39,8 +39,11 @@
cc_library {
name: "libbase",
vendor_available: true,
- clang: true,
host_supported: true,
+ vndk: {
+ enabled: true,
+ support_system_process: true,
+ },
srcs: [
"file.cpp",
"logging.cpp",
@@ -109,7 +112,6 @@
cc_test {
name: "libbase_test",
host_supported: true,
- clang: true,
srcs: [
"endian_test.cpp",
"errors_test.cpp",
diff --git a/base/file.cpp b/base/file.cpp
index a2f2887..2f697a1 100644
--- a/base/file.cpp
+++ b/base/file.cpp
@@ -153,6 +153,37 @@
return true;
}
+#if defined(_WIN32)
+// Windows implementation of pread. Note that this DOES move the file descriptors read position,
+// but it does so atomically.
+static ssize_t pread(int fd, void* data, size_t byte_count, off64_t offset) {
+ DWORD bytes_read;
+ OVERLAPPED overlapped;
+ memset(&overlapped, 0, sizeof(OVERLAPPED));
+ overlapped.Offset = static_cast<DWORD>(offset);
+ overlapped.OffsetHigh = static_cast<DWORD>(offset >> 32);
+ if (!ReadFile(reinterpret_cast<HANDLE>(_get_osfhandle(fd)), data, static_cast<DWORD>(byte_count),
+ &bytes_read, &overlapped)) {
+ // In case someone tries to read errno (since this is masquerading as a POSIX call)
+ errno = EIO;
+ return -1;
+ }
+ return static_cast<ssize_t>(bytes_read);
+}
+#endif
+
+bool ReadFullyAtOffset(int fd, void* data, size_t byte_count, off64_t offset) {
+ uint8_t* p = reinterpret_cast<uint8_t*>(data);
+ while (byte_count > 0) {
+ ssize_t n = TEMP_FAILURE_RETRY(pread(fd, p, byte_count, offset));
+ if (n <= 0) return false;
+ p += n;
+ byte_count -= n;
+ offset += n;
+ }
+ return true;
+}
+
bool WriteFully(int fd, const void* data, size_t byte_count) {
const uint8_t* p = reinterpret_cast<const uint8_t*>(data);
size_t remaining = byte_count;
diff --git a/base/include/android-base/file.h b/base/include/android-base/file.h
index 651f529..667d6fb 100644
--- a/base/include/android-base/file.h
+++ b/base/include/android-base/file.h
@@ -18,12 +18,18 @@
#define ANDROID_BASE_FILE_H
#include <sys/stat.h>
+#include <sys/types.h>
#include <string>
#if !defined(_WIN32) && !defined(O_BINARY)
#define O_BINARY 0
#endif
+#if defined(__APPLE__)
+/* Mac OS has always had a 64-bit off_t, so it doesn't have off64_t. */
+typedef off_t off64_t;
+#endif
+
namespace android {
namespace base {
@@ -42,6 +48,17 @@
#endif
bool ReadFully(int fd, void* data, size_t byte_count);
+
+// Reads `byte_count` bytes from the file descriptor at the specified offset.
+// Returns false if there was an IO error or EOF was reached before reading `byte_count` bytes.
+//
+// NOTE: On Linux/Mac, this function wraps pread, which provides atomic read support without
+// modifying the read pointer of the file descriptor. On Windows, however, the read pointer does
+// get modified. This means that ReadFullyAtOffset can be used concurrently with other calls to the
+// same function, but concurrently seeking or reading incrementally can lead to unexpected
+// behavior.
+bool ReadFullyAtOffset(int fd, void* data, size_t byte_count, off64_t offset);
+
bool WriteFully(int fd, const void* data, size_t byte_count);
bool RemoveFileIfExists(const std::string& path, std::string* err = nullptr);
diff --git a/base/include/android-base/macros.h b/base/include/android-base/macros.h
index 88bbe8a..25f2ff4 100644
--- a/base/include/android-base/macros.h
+++ b/base/include/android-base/macros.h
@@ -179,4 +179,19 @@
} while (0)
#endif
+// Current ABI string
+#if defined(__arm__)
+#define ABI_STRING "arm"
+#elif defined(__aarch64__)
+#define ABI_STRING "arm64"
+#elif defined(__i386__)
+#define ABI_STRING "x86"
+#elif defined(__x86_64__)
+#define ABI_STRING "x86_64"
+#elif defined(__mips__) && !defined(__LP64__)
+#define ABI_STRING "mips"
+#elif defined(__mips__) && defined(__LP64__)
+#define ABI_STRING "mips64"
+#endif
+
#endif // ANDROID_BASE_MACROS_H
diff --git a/bootstat/Android.bp b/bootstat/Android.bp
index bc90a6e..dd357ed 100644
--- a/bootstat/Android.bp
+++ b/bootstat/Android.bp
@@ -32,9 +32,6 @@
"liblog",
"libmetricslogger",
],
- whole_static_libs: ["libgtest_prod"],
- // Clang is required because of C++14
- clang: true,
}
// bootstat static library
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 344fa9a..bd611f0 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -169,6 +169,13 @@
{"wdog_bark", 42},
{"wdog_bite", 43},
{"wdog_reset", 44},
+ {"shutdown,", 45}, // Trailing comma is intentional.
+ {"shutdown,userrequested", 46},
+ {"reboot,bootloader", 47},
+ {"reboot,cold", 48},
+ {"reboot,recovery", 49},
+ {"thermal_shutdown", 50},
+ {"s3_wakeup", 51}
};
// Converts a string value representing the reason the system booted to an
diff --git a/bootstat/bootstat.rc b/bootstat/bootstat.rc
index f4756d5..d697efb 100644
--- a/bootstat/bootstat.rc
+++ b/bootstat/bootstat.rc
@@ -1,7 +1,39 @@
# This file is the LOCAL_INIT_RC file for the bootstat command.
on post-fs-data
- mkdir /data/misc/bootstat 0700 root root
+ mkdir /data/misc/bootstat 0700 system log
+ # To deal with ota transition resulting from a change in DAC from
+ # root.root to system.log, may be deleted after ota has settled.
+ chown system log /data/misc/bootstat/absolute_boot_time
+ chown system log /data/misc/bootstat/boot_complete
+ chown system log /data/misc/bootstat/boot_complete_no_encryption
+ chown system log /data/misc/bootstat/boot_reason
+ chown system log /data/misc/bootstat/bootime.bootloader.1BLE
+ chown system log /data/misc/bootstat/bootime.bootloader.1BLL
+ chown system log /data/misc/bootstat/bootime.bootloader.2BLE
+ chown system log /data/misc/bootstat/bootime.bootloader.2BLL
+ chown system log /data/misc/bootstat/bootime.bootloader.AVB
+ chown system log /data/misc/bootstat/bootime.bootloader.KD
+ chown system log /data/misc/bootstat/bootime.bootloader.KL
+ chown system log /data/misc/bootstat/bootime.bootloader.ODT
+ chown system log /data/misc/bootstat/bootime.bootloader.SW
+ chown system log /data/misc/bootstat/bootime.bootloader.total
+ chown system log /data/misc/bootstat/build_date
+ chown system log /data/misc/bootstat/factory_reset
+ chown system log /data/misc/bootstat/factory_reset_boot_complete
+ chown system log /data/misc/bootstat/factory_reset_boot_complete_no_encryption
+ chown system log /data/misc/bootstat/factory_reset_current_time
+ chown system log /data/misc/bootstat/factory_reset_record_value
+ chown system log /data/misc/bootstat/last_boot_time_utc
+ chown system log /data/misc/bootstat/ota_boot_complete
+ chown system log /data/misc/bootstat/ota_boot_complete_no_encryption
+ chown system log /data/misc/bootstat/post_decrypt_time_elapsed
+ chown system log /data/misc/bootstat/ro.boottime.init
+ chown system log /data/misc/bootstat/ro.boottime.init.cold_boot_wait
+ chown system log /data/misc/bootstat/ro.boottime.init.selinux
+ chown system log /data/misc/bootstat/time_since_factory_reset
+ chown system log /data/misc/bootstat/time_since_last_boot
+ # end ota transitional support
# Record the time at which the user has successfully entered the pin to decrypt
# the device, /data is decrypted, and the system is entering the main boot phase.
@@ -10,7 +42,7 @@
# property:init.svc.bootanim=running: The boot animation is running
# property:ro.crypto.type=block: FDE device
on post-fs-data && property:init.svc.bootanim=running && property:ro.crypto.type=block
- exec - root root -- /system/bin/bootstat -r post_decrypt_time_elapsed
+ exec - system log -- /system/bin/bootstat -r post_decrypt_time_elapsed
# sys.logbootcomplete is a signal to enable the bootstat logging mechanism.
# This signaling is necessary to prevent logging boot metrics after a runtime
@@ -33,13 +65,13 @@
# Record boot complete metrics.
on property:sys.boot_completed=1 && property:sys.logbootcomplete=1
# Record boot_complete and related stats (decryption, etc).
- exec - root root -- /system/bin/bootstat --record_boot_complete
+ exec - system log -- /system/bin/bootstat --record_boot_complete
# Record the boot reason.
- exec - root root -- /system/bin/bootstat --record_boot_reason
+ exec - system log -- /system/bin/bootstat --record_boot_reason
# Record time since factory reset.
- exec - root root -- /system/bin/bootstat --record_time_since_factory_reset
+ exec - system log -- /system/bin/bootstat --record_time_since_factory_reset
# Log all boot events.
- exec - root root -- /system/bin/bootstat -l
+ exec - system log -- /system/bin/bootstat -l
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index f86aaa0..7d17cd9 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -106,6 +106,7 @@
"libdebuggerd",
"libbacktrace",
"libunwind",
+ "libunwindstack",
"liblzma",
"libcutils",
],
@@ -171,6 +172,7 @@
static_libs: [
"libbacktrace",
"libunwind",
+ "libunwindstack",
"liblzma",
"libbase",
"libcutils",
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index 4b1e51d..3513980 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -79,9 +79,27 @@
return fstatat(pid_proc_fd, task_path.c_str(), &st, 0) == 0;
}
+static pid_t get_tracer(pid_t tracee) {
+ // Check to see if the thread is being ptraced by another process.
+ android::procinfo::ProcessInfo process_info;
+ if (android::procinfo::GetProcessInfo(tracee, &process_info)) {
+ return process_info.tracer;
+ }
+ return -1;
+}
+
// Attach to a thread, and verify that it's still a member of the given process
static bool ptrace_seize_thread(int pid_proc_fd, pid_t tid, std::string* error) {
if (ptrace(PTRACE_SEIZE, tid, 0, 0) != 0) {
+ if (errno == EPERM) {
+ pid_t tracer = get_tracer(tid);
+ if (tracer != -1) {
+ *error = StringPrintf("failed to attach to thread %d, already traced by %d (%s)", tid,
+ tracer, get_process_name(tracer).c_str());
+ return false;
+ }
+ }
+
*error = StringPrintf("failed to attach to thread %d: %s", tid, strerror(errno));
return false;
}
@@ -329,6 +347,11 @@
LOG(FATAL) << "failed to create backtrace map";
}
}
+ std::unique_ptr<BacktraceMap> backtrace_map_new;
+ backtrace_map_new.reset(BacktraceMap::CreateNew(main_tid));
+ if (!backtrace_map_new) {
+ LOG(FATAL) << "failed to create backtrace map new";
+ }
// Collect the list of open files.
OpenFilesList open_files;
@@ -408,8 +431,9 @@
dump_backtrace(output_fd.get(), backtrace_map.get(), target, main_tid, process_name, threads, 0);
} else {
ATRACE_NAME("engrave_tombstone");
- engrave_tombstone(output_fd.get(), backtrace_map.get(), &open_files, target, main_tid,
- process_name, threads, abort_address, fatal_signal ? &amfd_data : nullptr);
+ engrave_tombstone(output_fd.get(), backtrace_map.get(), backtrace_map_new.get(), &open_files,
+ target, main_tid, process_name, threads, abort_address,
+ fatal_signal ? &amfd_data : nullptr);
}
// We don't actually need to PTRACE_DETACH, as long as our tracees aren't in
diff --git a/debuggerd/crasher/Android.bp b/debuggerd/crasher/Android.bp
index f73f672..b7b1938 100644
--- a/debuggerd/crasher/Android.bp
+++ b/debuggerd/crasher/Android.bp
@@ -2,7 +2,6 @@
name: "crasher-defaults",
cppflags: [
- "-std=gnu++14",
"-W",
"-Wall",
"-Wextra",
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index b51fc66..dbf81a4 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -16,10 +16,11 @@
#include <err.h>
#include <fcntl.h>
-#include <unistd.h>
#include <sys/capability.h>
#include <sys/prctl.h>
+#include <sys/ptrace.h>
#include <sys/types.h>
+#include <unistd.h>
#include <chrono>
#include <regex>
@@ -569,6 +570,40 @@
ASSERT_BACKTRACE_FRAME(result, "tgkill");
}
+TEST_F(CrasherTest, competing_tracer) {
+ int intercept_result;
+ unique_fd output_fd;
+ StartProcess([]() {
+ while (true) {
+ }
+ });
+
+ StartIntercept(&output_fd);
+ FinishCrasher();
+
+ ASSERT_EQ(0, ptrace(PTRACE_SEIZE, crasher_pid, 0, 0));
+ ASSERT_EQ(0, kill(crasher_pid, SIGABRT));
+
+ int status;
+ ASSERT_EQ(crasher_pid, waitpid(crasher_pid, &status, 0));
+ ASSERT_TRUE(WIFSTOPPED(status));
+ ASSERT_EQ(SIGABRT, WSTOPSIG(status));
+
+ ASSERT_EQ(0, ptrace(PTRACE_CONT, crasher_pid, 0, SIGABRT));
+ FinishIntercept(&intercept_result);
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+ std::string result;
+ ConsumeFd(std::move(output_fd), &result);
+ std::string regex = R"(failed to attach to thread \d+, already traced by )";
+ regex += std::to_string(gettid());
+ regex += R"( \(.+debuggerd_test)";
+ ASSERT_MATCH(result, regex.c_str());
+
+ ASSERT_EQ(0, ptrace(PTRACE_DETACH, crasher_pid, 0, SIGABRT));
+ AssertDeath(SIGABRT);
+}
+
TEST(crash_dump, zombie) {
pid_t forkpid = fork();
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index 55cd03e..1275229 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -116,6 +116,26 @@
fatal("%s: %s", buf, strerror(err));
}
+static bool get_main_thread_name(char* buf, size_t len) {
+ int fd = open("/proc/self/comm", O_RDONLY | O_CLOEXEC);
+ if (fd == -1) {
+ return false;
+ }
+
+ ssize_t rc = read(fd, buf, len);
+ close(fd);
+ if (rc == -1) {
+ return false;
+ } else if (rc == 0) {
+ // Should never happen?
+ return false;
+ }
+
+ // There's a trailing newline, replace it with a NUL.
+ buf[rc - 1] = '\0';
+ return true;
+}
+
/*
* Writes a summary of the signal to the log file. We do this so that, if
* for some reason we're not able to contact debuggerd, there is still some
@@ -188,8 +208,14 @@
}
}
- async_safe_format_log(ANDROID_LOG_FATAL, "libc", "Fatal signal %d (%s)%s%s in tid %d (%s)",
- signum, signal_name, code_desc, addr_desc, __gettid(), thread_name);
+ char main_thread_name[MAX_TASK_NAME_LEN + 1];
+ if (!get_main_thread_name(main_thread_name, sizeof(main_thread_name))) {
+ strncpy(main_thread_name, "<unknown>", sizeof(main_thread_name));
+ }
+
+ async_safe_format_log(
+ ANDROID_LOG_FATAL, "libc", "Fatal signal %d (%s)%s%s in tid %d (%s), pid %d (%s)", signum,
+ signal_name, code_desc, addr_desc, __gettid(), thread_name, __getpid(), main_thread_name);
}
/*
diff --git a/debuggerd/libdebuggerd/include/tombstone.h b/debuggerd/libdebuggerd/include/tombstone.h
index 79743b6..45740df 100644
--- a/debuggerd/libdebuggerd/include/tombstone.h
+++ b/debuggerd/libdebuggerd/include/tombstone.h
@@ -35,10 +35,10 @@
int open_tombstone(std::string* path);
/* Creates a tombstone file and writes the crash dump to it. */
-void engrave_tombstone(int tombstone_fd, BacktraceMap* map, const OpenFilesList* open_files,
- pid_t pid, pid_t tid, const std::string& process_name,
- const std::map<pid_t, std::string>& threads, uintptr_t abort_msg_address,
- std::string* amfd_data);
+void engrave_tombstone(int tombstone_fd, BacktraceMap* map, BacktraceMap* map_new,
+ const OpenFilesList* open_files, pid_t pid, pid_t tid,
+ const std::string& process_name, const std::map<pid_t, std::string>& threads,
+ uintptr_t abort_msg_address, std::string* amfd_data);
void engrave_tombstone_ucontext(int tombstone_fd, uintptr_t abort_msg_address, siginfo_t* siginfo,
ucontext_t* ucontext);
diff --git a/debuggerd/libdebuggerd/include/utility.h b/debuggerd/libdebuggerd/include/utility.h
index e5e5106..f481b78 100644
--- a/debuggerd/libdebuggerd/include/utility.h
+++ b/debuggerd/libdebuggerd/include/utility.h
@@ -24,26 +24,9 @@
#include <string>
+#include <android-base/macros.h>
#include <backtrace/Backtrace.h>
-// Figure out the abi based on defined macros.
-#if defined(__arm__)
-#define ABI_STRING "arm"
-#elif defined(__aarch64__)
-#define ABI_STRING "arm64"
-#elif defined(__mips__) && !defined(__LP64__)
-#define ABI_STRING "mips"
-#elif defined(__mips__) && defined(__LP64__)
-#define ABI_STRING "mips64"
-#elif defined(__i386__)
-#define ABI_STRING "x86"
-#elif defined(__x86_64__)
-#define ABI_STRING "x86_64"
-#else
-#error "Unsupported ABI"
-#endif
-
-
struct log_t{
// Tombstone file descriptor.
int tfd;
diff --git a/debuggerd/libdebuggerd/test/tombstone_test.cpp b/debuggerd/libdebuggerd/test/tombstone_test.cpp
index 325210d..6be59e7 100644
--- a/debuggerd/libdebuggerd/test/tombstone_test.cpp
+++ b/debuggerd/libdebuggerd/test/tombstone_test.cpp
@@ -220,21 +220,21 @@
map.start = 0xa434000;
map.end = 0xa435000;
map.offset = 0x1000;
- map.load_base = 0xd000;
+ map.load_bias = 0xd000;
map.flags = PROT_WRITE;
map_mock_->AddMap(map);
map.start = 0xa534000;
map.end = 0xa535000;
map.offset = 0x3000;
- map.load_base = 0x2000;
+ map.load_bias = 0x2000;
map.flags = PROT_EXEC;
map_mock_->AddMap(map);
map.start = 0xa634000;
map.end = 0xa635000;
map.offset = 0;
- map.load_base = 0;
+ map.load_bias = 0;
map.flags = PROT_READ | PROT_WRITE | PROT_EXEC;
map.name = "/system/lib/fake.so";
map_mock_->AddMap(map);
@@ -244,20 +244,20 @@
std::string tombstone_contents;
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
- const char* expected_dump = \
-"\nmemory map:\n"
+ const char* expected_dump =
+ "\nmemory map:\n"
#if defined(__LP64__)
-" 00000000'0a234000-00000000'0a234fff --- 0 1000\n"
-" 00000000'0a334000-00000000'0a334fff r-- f000 1000\n"
-" 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load base 0xd000)\n"
-" 00000000'0a534000-00000000'0a534fff --x 3000 1000 (load base 0x2000)\n"
-" 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n";
+ " 00000000'0a234000-00000000'0a234fff --- 0 1000\n"
+ " 00000000'0a334000-00000000'0a334fff r-- f000 1000\n"
+ " 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load bias 0xd000)\n"
+ " 00000000'0a534000-00000000'0a534fff --x 3000 1000 (load bias 0x2000)\n"
+ " 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n";
#else
-" 0a234000-0a234fff --- 0 1000\n"
-" 0a334000-0a334fff r-- f000 1000\n"
-" 0a434000-0a434fff -w- 1000 1000 (load base 0xd000)\n"
-" 0a534000-0a534fff --x 3000 1000 (load base 0x2000)\n"
-" 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n";
+ " 0a234000-0a234fff --- 0 1000\n"
+ " 0a334000-0a334fff r-- f000 1000\n"
+ " 0a434000-0a434fff -w- 1000 1000 (load bias 0xd000)\n"
+ " 0a534000-0a534fff --x 3000 1000 (load bias 0x2000)\n"
+ " 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n";
#endif
ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
@@ -274,21 +274,21 @@
map.start = 0xa434000;
map.end = 0xa435000;
map.offset = 0x1000;
- map.load_base = 0xd000;
+ map.load_bias = 0xd000;
map.flags = PROT_WRITE;
map_mock_->AddMap(map);
map.start = 0xa534000;
map.end = 0xa535000;
map.offset = 0x3000;
- map.load_base = 0x2000;
+ map.load_bias = 0x2000;
map.flags = PROT_EXEC;
map_mock_->AddMap(map);
map.start = 0xa634000;
map.end = 0xa635000;
map.offset = 0;
- map.load_base = 0;
+ map.load_bias = 0;
map.flags = PROT_READ | PROT_WRITE | PROT_EXEC;
map.name = "/system/lib/fake.so";
map_mock_->AddMap(map);
@@ -304,18 +304,18 @@
std::string tombstone_contents;
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
- const char* expected_dump = \
-"\nmemory map: (fault address prefixed with --->)\n"
+ const char* expected_dump =
+ "\nmemory map: (fault address prefixed with --->)\n"
#if defined(__LP64__)
-"--->Fault address falls at 00000000'00001000 before any mapped regions\n"
-" 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load base 0xd000)\n"
-" 00000000'0a534000-00000000'0a534fff --x 3000 1000 (load base 0x2000)\n"
-" 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n";
+ "--->Fault address falls at 00000000'00001000 before any mapped regions\n"
+ " 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load bias 0xd000)\n"
+ " 00000000'0a534000-00000000'0a534fff --x 3000 1000 (load bias 0x2000)\n"
+ " 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n";
#else
-"--->Fault address falls at 00001000 before any mapped regions\n"
-" 0a434000-0a434fff -w- 1000 1000 (load base 0xd000)\n"
-" 0a534000-0a534fff --x 3000 1000 (load base 0x2000)\n"
-" 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n";
+ "--->Fault address falls at 00001000 before any mapped regions\n"
+ " 0a434000-0a434fff -w- 1000 1000 (load bias 0xd000)\n"
+ " 0a534000-0a534fff --x 3000 1000 (load bias 0x2000)\n"
+ " 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n";
#endif
ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
@@ -332,21 +332,21 @@
map.start = 0xa434000;
map.end = 0xa435000;
map.offset = 0x1000;
- map.load_base = 0xd000;
+ map.load_bias = 0xd000;
map.flags = PROT_WRITE;
map_mock_->AddMap(map);
map.start = 0xa534000;
map.end = 0xa535000;
map.offset = 0x3000;
- map.load_base = 0x2000;
+ map.load_bias = 0x2000;
map.flags = PROT_EXEC;
map_mock_->AddMap(map);
map.start = 0xa634000;
map.end = 0xa635000;
map.offset = 0;
- map.load_base = 0;
+ map.load_bias = 0;
map.flags = PROT_READ | PROT_WRITE | PROT_EXEC;
map.name = "/system/lib/fake.so";
map_mock_->AddMap(map);
@@ -362,18 +362,18 @@
std::string tombstone_contents;
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
- const char* expected_dump = \
-"\nmemory map: (fault address prefixed with --->)\n"
+ const char* expected_dump =
+ "\nmemory map: (fault address prefixed with --->)\n"
#if defined(__LP64__)
-" 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load base 0xd000)\n"
-"--->Fault address falls at 00000000'0a533000 between mapped regions\n"
-" 00000000'0a534000-00000000'0a534fff --x 3000 1000 (load base 0x2000)\n"
-" 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n";
+ " 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load bias 0xd000)\n"
+ "--->Fault address falls at 00000000'0a533000 between mapped regions\n"
+ " 00000000'0a534000-00000000'0a534fff --x 3000 1000 (load bias 0x2000)\n"
+ " 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n";
#else
-" 0a434000-0a434fff -w- 1000 1000 (load base 0xd000)\n"
-"--->Fault address falls at 0a533000 between mapped regions\n"
-" 0a534000-0a534fff --x 3000 1000 (load base 0x2000)\n"
-" 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n";
+ " 0a434000-0a434fff -w- 1000 1000 (load bias 0xd000)\n"
+ "--->Fault address falls at 0a533000 between mapped regions\n"
+ " 0a534000-0a534fff --x 3000 1000 (load bias 0x2000)\n"
+ " 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n";
#endif
ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
@@ -390,21 +390,21 @@
map.start = 0xa434000;
map.end = 0xa435000;
map.offset = 0x1000;
- map.load_base = 0xd000;
+ map.load_bias = 0xd000;
map.flags = PROT_WRITE;
map_mock_->AddMap(map);
map.start = 0xa534000;
map.end = 0xa535000;
map.offset = 0x3000;
- map.load_base = 0x2000;
+ map.load_bias = 0x2000;
map.flags = PROT_EXEC;
map_mock_->AddMap(map);
map.start = 0xa634000;
map.end = 0xa635000;
map.offset = 0;
- map.load_base = 0;
+ map.load_bias = 0;
map.flags = PROT_READ | PROT_WRITE | PROT_EXEC;
map.name = "/system/lib/fake.so";
map_mock_->AddMap(map);
@@ -420,16 +420,16 @@
std::string tombstone_contents;
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
- const char* expected_dump = \
-"\nmemory map: (fault address prefixed with --->)\n"
+ const char* expected_dump =
+ "\nmemory map: (fault address prefixed with --->)\n"
#if defined(__LP64__)
-" 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load base 0xd000)\n"
-"--->00000000'0a534000-00000000'0a534fff --x 3000 1000 (load base 0x2000)\n"
-" 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n";
+ " 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load bias 0xd000)\n"
+ "--->00000000'0a534000-00000000'0a534fff --x 3000 1000 (load bias 0x2000)\n"
+ " 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n";
#else
-" 0a434000-0a434fff -w- 1000 1000 (load base 0xd000)\n"
-"--->0a534000-0a534fff --x 3000 1000 (load base 0x2000)\n"
-" 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n";
+ " 0a434000-0a434fff -w- 1000 1000 (load bias 0xd000)\n"
+ "--->0a534000-0a534fff --x 3000 1000 (load bias 0x2000)\n"
+ " 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n";
#endif
ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
@@ -446,21 +446,21 @@
map.start = 0xa434000;
map.end = 0xa435000;
map.offset = 0x1000;
- map.load_base = 0xd000;
+ map.load_bias = 0xd000;
map.flags = PROT_WRITE;
map_mock_->AddMap(map);
map.start = 0xa534000;
map.end = 0xa535000;
map.offset = 0x3000;
- map.load_base = 0x2000;
+ map.load_bias = 0x2000;
map.flags = PROT_EXEC;
map_mock_->AddMap(map);
map.start = 0xa634000;
map.end = 0xa635000;
map.offset = 0;
- map.load_base = 0;
+ map.load_bias = 0;
map.flags = PROT_READ | PROT_WRITE | PROT_EXEC;
map.name = "/system/lib/fake.so";
map_mock_->AddMap(map);
@@ -480,18 +480,18 @@
std::string tombstone_contents;
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
- const char* expected_dump = \
-"\nmemory map: (fault address prefixed with --->)\n"
+ const char* expected_dump =
+ "\nmemory map: (fault address prefixed with --->)\n"
#if defined(__LP64__)
-" 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load base 0xd000)\n"
-" 00000000'0a534000-00000000'0a534fff --x 3000 1000 (load base 0x2000)\n"
-" 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n"
-"--->Fault address falls at 00001234'5a534040 after any mapped regions\n";
+ " 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load bias 0xd000)\n"
+ " 00000000'0a534000-00000000'0a534fff --x 3000 1000 (load bias 0x2000)\n"
+ " 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n"
+ "--->Fault address falls at 00001234'5a534040 after any mapped regions\n";
#else
-" 0a434000-0a434fff -w- 1000 1000 (load base 0xd000)\n"
-" 0a534000-0a534fff --x 3000 1000 (load base 0x2000)\n"
-" 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n"
-"--->Fault address falls at 0f534040 after any mapped regions\n";
+ " 0a434000-0a434fff -w- 1000 1000 (load bias 0xd000)\n"
+ " 0a534000-0a534fff --x 3000 1000 (load bias 0x2000)\n"
+ " 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n"
+ "--->Fault address falls at 0f534040 after any mapped regions\n";
#endif
ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
@@ -508,7 +508,7 @@
map.start = 0xa434000;
map.end = 0xa435000;
map.offset = 0x1000;
- map.load_base = 0xd000;
+ map.load_bias = 0xd000;
map.flags = PROT_WRITE;
map_mock_->AddMap(map);
@@ -520,12 +520,12 @@
std::string tombstone_contents;
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
- const char* expected_dump = \
-"\nmemory map:\n"
+ const char* expected_dump =
+ "\nmemory map:\n"
#if defined(__LP64__)
-" 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load base 0xd000)\n";
+ " 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load bias 0xd000)\n";
#else
-" 0a434000-0a434fff -w- 1000 1000 (load base 0xd000)\n";
+ " 0a434000-0a434fff -w- 1000 1000 (load bias 0xd000)\n";
#endif
ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index 996d714..b809ed4 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -466,11 +466,11 @@
line += " (BuildId: " + build_id + ")";
}
}
- if (it->load_base != 0) {
+ if (it->load_bias != 0) {
if (space_needed) {
line += ' ';
}
- line += StringPrintf(" (load base 0x%" PRIxPTR ")", it->load_base);
+ line += StringPrintf(" (load bias 0x%" PRIxPTR ")", it->load_bias);
}
_LOG(log, logtype::MAPS, "%s\n", line.c_str());
}
@@ -495,8 +495,55 @@
_LOG(log, logtype::REGISTERS, " register dumping unimplemented on this architecture");
}
-static void dump_thread(log_t* log, pid_t pid, pid_t tid, const std::string& process_name,
- const std::string& thread_name, BacktraceMap* map,
+static bool verify_backtraces_equal(Backtrace* back1, Backtrace* back2) {
+ if (back1->NumFrames() != back2->NumFrames()) {
+ return false;
+ }
+ std::string back1_str;
+ std::string back2_str;
+ for (size_t i = 0; i < back1->NumFrames(); i++) {
+ back1_str += back1->FormatFrameData(i);
+ back2_str += back2->FormatFrameData(i);
+ }
+ return back1_str == back2_str;
+}
+
+static void log_mismatch_data(log_t* log, Backtrace* backtrace) {
+ _LOG(log, logtype::THREAD, "MISMATCH: This unwind is different.\n");
+ if (backtrace->NumFrames() == 0) {
+ _LOG(log, logtype::THREAD, "MISMATCH: No frames in new backtrace.\n");
+ return;
+ }
+ _LOG(log, logtype::THREAD, "MISMATCH: Backtrace from new unwinder.\n");
+ for (size_t i = 0; i < backtrace->NumFrames(); i++) {
+ _LOG(log, logtype::THREAD, "MISMATCH: %s\n", backtrace->FormatFrameData(i).c_str());
+ }
+
+ // Get the stack trace up to 8192 bytes.
+ std::vector<uint64_t> buffer(8192 / sizeof(uint64_t));
+ size_t bytes =
+ backtrace->Read(backtrace->GetFrame(0)->sp, reinterpret_cast<uint8_t*>(buffer.data()),
+ buffer.size() * sizeof(uint64_t));
+ std::string log_data;
+ for (size_t i = 0; i < bytes / sizeof(uint64_t); i++) {
+ if ((i % 4) == 0) {
+ if (!log_data.empty()) {
+ _LOG(log, logtype::THREAD, "MISMATCH: stack_data%s\n", log_data.c_str());
+ log_data = "";
+ }
+ }
+ log_data += android::base::StringPrintf(" 0x%016" PRIx64, buffer[i]);
+ }
+
+ if (!log_data.empty()) {
+ _LOG(log, logtype::THREAD, "MISMATCH: data%s\n", log_data.c_str());
+ }
+
+ // If there is any leftover (bytes % sizeof(uint64_t) != 0, ignore it for now.
+}
+
+static bool dump_thread(log_t* log, pid_t pid, pid_t tid, const std::string& process_name,
+ const std::string& thread_name, BacktraceMap* map, BacktraceMap* map_new,
uintptr_t abort_msg_address, bool primary_thread) {
log->current_tid = tid;
if (!primary_thread) {
@@ -510,7 +557,18 @@
dump_abort_message(backtrace.get(), log, abort_msg_address);
}
dump_registers(log, tid);
+ bool matches = true;
if (backtrace->Unwind(0)) {
+ // Use the new method and verify it is the same as old.
+ std::unique_ptr<Backtrace> backtrace_new(Backtrace::CreateNew(pid, tid, map_new));
+ if (!backtrace_new->Unwind(0)) {
+ _LOG(log, logtype::THREAD, "Failed to unwind with new unwinder: %s\n",
+ backtrace_new->GetErrorString(backtrace_new->GetError()).c_str());
+ matches = false;
+ } else if (!verify_backtraces_equal(backtrace.get(), backtrace_new.get())) {
+ log_mismatch_data(log, backtrace_new.get());
+ matches = false;
+ }
dump_backtrace_and_stack(backtrace.get(), log);
} else {
ALOGE("Unwind failed: pid = %d, tid = %d", pid, tid);
@@ -524,6 +582,8 @@
}
log->current_tid = log->crashed_tid;
+
+ return matches;
}
// Reads the contents of the specified log device, filters out the entries
@@ -657,9 +717,10 @@
}
// Dumps all information about the specified pid to the tombstone.
-static void dump_crash(log_t* log, BacktraceMap* map, const OpenFilesList* open_files, pid_t pid,
- pid_t tid, const std::string& process_name,
- const std::map<pid_t, std::string>& threads, uintptr_t abort_msg_address) {
+static void dump_crash(log_t* log, BacktraceMap* map, BacktraceMap* map_new,
+ const OpenFilesList* open_files, pid_t pid, pid_t tid,
+ const std::string& process_name, const std::map<pid_t, std::string>& threads,
+ uintptr_t abort_msg_address) {
// don't copy log messages to tombstone unless this is a dev device
char value[PROPERTY_VALUE_MAX];
property_get("ro.debuggable", value, "0");
@@ -668,7 +729,8 @@
_LOG(log, logtype::HEADER,
"*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n");
dump_header_info(log);
- dump_thread(log, pid, tid, process_name, threads.find(tid)->second, map, abort_msg_address, true);
+ bool new_unwind_matches = dump_thread(log, pid, tid, process_name, threads.find(tid)->second, map,
+ map_new, abort_msg_address, true);
if (want_logs) {
dump_logs(log, pid, 5);
}
@@ -678,7 +740,9 @@
const std::string& thread_name = it.second;
if (thread_tid != tid) {
- dump_thread(log, pid, thread_tid, process_name, thread_name, map, 0, false);
+ bool match =
+ dump_thread(log, pid, thread_tid, process_name, thread_name, map, map_new, 0, false);
+ new_unwind_matches = new_unwind_matches && match;
}
}
@@ -690,6 +754,14 @@
if (want_logs) {
dump_logs(log, pid, 0);
}
+ if (!new_unwind_matches) {
+ _LOG(log, logtype::THREAD, "MISMATCH: New and old unwinder do not agree.\n");
+ _LOG(log, logtype::THREAD, "MISMATCH: If you see this please file a bug in:\n");
+ _LOG(log, logtype::THREAD,
+ "MISMATCH: Android > Android OS & Apps > Runtime > native > tools "
+ "(debuggerd/gdb/init/simpleperf/strace/valgrind)\n");
+ _LOG(log, logtype::THREAD, "MISMATCH: and attach this tombstone.\n");
+ }
}
// open_tombstone - find an available tombstone slot, if any, of the
@@ -745,16 +817,16 @@
return fd;
}
-void engrave_tombstone(int tombstone_fd, BacktraceMap* map, const OpenFilesList* open_files,
- pid_t pid, pid_t tid, const std::string& process_name,
- const std::map<pid_t, std::string>& threads, uintptr_t abort_msg_address,
- std::string* amfd_data) {
+void engrave_tombstone(int tombstone_fd, BacktraceMap* map, BacktraceMap* map_new,
+ const OpenFilesList* open_files, pid_t pid, pid_t tid,
+ const std::string& process_name, const std::map<pid_t, std::string>& threads,
+ uintptr_t abort_msg_address, std::string* amfd_data) {
log_t log;
log.current_tid = tid;
log.crashed_tid = tid;
log.tfd = tombstone_fd;
log.amfd_data = amfd_data;
- dump_crash(&log, map, open_files, pid, tid, process_name, threads, abort_msg_address);
+ dump_crash(&log, map, map_new, open_files, pid, tid, process_name, threads, abort_msg_address);
}
void engrave_tombstone_ucontext(int tombstone_fd, uintptr_t abort_msg_address, siginfo_t* siginfo,
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index 80def73..dd8bad9 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -40,6 +40,7 @@
LOCAL_MODULE_TAGS := debug
LOCAL_MODULE_HOST_OS := darwin linux windows
LOCAL_CFLAGS += -Wall -Wextra -Werror -Wunreachable-code
+LOCAL_REQUIRED_MODULES := mke2fs e2fsdroid
LOCAL_SRC_FILES_linux := usb_linux.cpp
LOCAL_STATIC_LIBRARIES_linux := libselinux
@@ -85,6 +86,8 @@
include $(BUILD_HOST_EXECUTABLE)
my_dist_files := $(LOCAL_BUILT_MODULE)
+my_dist_files += $(HOST_OUT_EXECUTABLES)/mke2fs$(HOST_EXECUTABLE_SUFFIX)
+my_dist_files += $(HOST_OUT_EXECUTABLES)/e2fsdroid$(HOST_EXECUTABLE_SUFFIX)
ifeq ($(HOST_OS),linux)
my_dist_files += $(HOST_LIBRARY_PATH)/libf2fs_fmt_host_dyn$(HOST_SHLIB_SUFFIX)
endif
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 271ca95..5f2267c 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -93,6 +93,9 @@
static unsigned second_offset = 0x00f00000;
static unsigned tags_offset = 0x00000100;
+static bool g_disable_verity = false;
+static bool g_disable_verification = false;
+
static const std::string convert_fbe_marker_filename("convert_fbe");
enum fb_buffer_type {
@@ -419,6 +422,10 @@
" --skip-reboot Will not reboot the device when\n"
" performing commands that normally\n"
" trigger a reboot.\n"
+ " --disable-verity Set the disable-verity flag in the\n"
+ " the vbmeta image being flashed.\n"
+ " --disable-verification Set the disable-verification flag in"
+ " the vbmeta image being flashed.\n"
#if !defined(_WIN32)
" --wipe-and-use-fbe On devices which support it,\n"
" erase userdata and cache, and\n"
@@ -858,10 +865,55 @@
return load_buf_fd(transport, fd.release(), buf);
}
+static void rewrite_vbmeta_buffer(struct fastboot_buffer* buf) {
+ // Buffer needs to be at least the size of the VBMeta struct which
+ // is 256 bytes.
+ if (buf->sz < 256) {
+ return;
+ }
+
+ int fd = make_temporary_fd();
+ if (fd == -1) {
+ die("Failed to create temporary file for vbmeta rewriting");
+ }
+
+ std::string data;
+ if (!android::base::ReadFdToString(buf->fd, &data)) {
+ die("Failed reading from vbmeta");
+ }
+
+ // There's a 32-bit big endian |flags| field at offset 120 where
+ // bit 0 corresponds to disable-verity and bit 1 corresponds to
+ // disable-verification.
+ //
+ // See external/avb/libavb/avb_vbmeta_image.h for the layout of
+ // the VBMeta struct.
+ if (g_disable_verity) {
+ data[123] |= 0x01;
+ }
+ if (g_disable_verification) {
+ data[123] |= 0x02;
+ }
+
+ if (!android::base::WriteStringToFd(data, fd)) {
+ die("Failed writing to modified vbmeta");
+ }
+ close(buf->fd);
+ buf->fd = fd;
+ lseek(fd, 0, SEEK_SET);
+}
+
static void flash_buf(const char *pname, struct fastboot_buffer *buf)
{
sparse_file** s;
+ // Rewrite vbmeta if that's what we're flashing and modification has been requested.
+ if ((g_disable_verity || g_disable_verification) &&
+ (strcmp(pname, "vbmeta") == 0 || strcmp(pname, "vbmeta_a") == 0 ||
+ strcmp(pname, "vbmeta_b") == 0)) {
+ rewrite_vbmeta_buffer(buf);
+ }
+
switch (buf->type) {
case FB_BUFFER_SPARSE: {
std::vector<std::pair<sparse_file*, int64_t>> sparse_files;
@@ -1411,7 +1463,7 @@
if (fs_generator_generate(gen, output.path, size, initial_dir,
eraseBlkSize, logicalBlkSize)) {
- fprintf(stderr, "Cannot generate image: %s\n", strerror(errno));
+ die("Cannot generate image for %s\n", partition);
return;
}
@@ -1470,6 +1522,8 @@
{"set-active", optional_argument, 0, 'a'},
{"skip-secondary", no_argument, 0, 0},
{"skip-reboot", no_argument, 0, 0},
+ {"disable-verity", no_argument, 0, 0},
+ {"disable-verification", no_argument, 0, 0},
#if !defined(_WIN32)
{"wipe-and-use-fbe", no_argument, 0, 0},
#endif
@@ -1555,6 +1609,10 @@
skip_secondary = true;
} else if (strcmp("skip-reboot", longopts[longindex].name) == 0 ) {
skip_reboot = true;
+ } else if (strcmp("disable-verity", longopts[longindex].name) == 0 ) {
+ g_disable_verity = true;
+ } else if (strcmp("disable-verification", longopts[longindex].name) == 0 ) {
+ g_disable_verification = true;
#if !defined(_WIN32)
} else if (strcmp("wipe-and-use-fbe", longopts[longindex].name) == 0) {
wants_wipe = true;
diff --git a/fastboot/fs.cpp b/fastboot/fs.cpp
index 99ca7dd..709f061 100644
--- a/fastboot/fs.cpp
+++ b/fastboot/fs.cpp
@@ -10,28 +10,148 @@
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
+#ifndef WIN32
+#include <sys/wait.h>
+#else
+#include <tchar.h>
+#include <windows.h>
+#endif
#include <unistd.h>
+#include <vector>
+#include <android-base/errors.h>
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
#include <android-base/unique_fd.h>
#include <ext4_utils/make_ext4fs.h>
#include <sparse/sparse.h>
+using android::base::StringPrintf;
using android::base::unique_fd;
-static int generate_ext4_image(const char* fileName, long long partSize, const std::string& initial_dir,
- unsigned eraseBlkSize, unsigned logicalBlkSize)
-{
- unique_fd fd(open(fileName, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR));
- if (fd == -1) {
- fprintf(stderr, "Unable to open output file for EXT4 filesystem: %s\n", strerror(errno));
+#ifdef WIN32
+static int exec_e2fs_cmd(const char* path, char* const argv[]) {
+ std::string cmd;
+ int i = 0;
+ while (argv[i] != nullptr) {
+ cmd += argv[i++];
+ cmd += " ";
+ }
+ cmd = cmd.substr(0, cmd.size() - 1);
+
+ STARTUPINFO si;
+ PROCESS_INFORMATION pi;
+ DWORD exit_code = 0;
+
+ ZeroMemory(&si, sizeof(si));
+ si.cb = sizeof(si);
+ ZeroMemory(&pi, sizeof(pi));
+
+ SetEnvironmentVariableA("MKE2FS_CONFIG", "");
+
+ if (!CreateProcessA(nullptr, // No module name (use command line)
+ const_cast<char*>(cmd.c_str()), // Command line
+ nullptr, // Process handle not inheritable
+ nullptr, // Thread handle not inheritable
+ FALSE, // Set handle inheritance to FALSE
+ 0, // No creation flags
+ nullptr, // Use parent's environment block
+ nullptr, // Use parent's starting directory
+ &si, // Pointer to STARTUPINFO structure
+ &pi) // Pointer to PROCESS_INFORMATION structure
+ ) {
+ fprintf(stderr, "CreateProcess failed: %s\n",
+ android::base::SystemErrorCodeToString(GetLastError()).c_str());
return -1;
}
- if (initial_dir.empty()) {
- make_ext4fs_sparse_fd_align(fd, partSize, NULL, NULL, eraseBlkSize, logicalBlkSize);
- } else {
- make_ext4fs_sparse_fd_directory_align(fd, partSize, NULL, NULL, initial_dir.c_str(),
- eraseBlkSize, logicalBlkSize);
+
+ WaitForSingleObject(pi.hProcess, INFINITE);
+
+ GetExitCodeProcess(pi.hProcess, &exit_code);
+
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+
+ return exit_code != 0;
+}
+#else
+static int exec_e2fs_cmd(const char* path, char* const argv[]) {
+ int status;
+ pid_t child;
+ if ((child = fork()) == 0) {
+ setenv("MKE2FS_CONFIG", "", 1);
+ execvp(path, argv);
+ _exit(EXIT_FAILURE);
}
+ if (child < 0) {
+ fprintf(stderr, "%s failed with fork %s\n", path, strerror(errno));
+ return -1;
+ }
+ if (TEMP_FAILURE_RETRY(waitpid(child, &status, 0)) == -1) {
+ fprintf(stderr, "%s failed with waitpid %s\n", path, strerror(errno));
+ return -1;
+ }
+ int ret = -1;
+ if (WIFEXITED(status)) {
+ ret = WEXITSTATUS(status);
+ if (ret != 0) {
+ fprintf(stderr, "%s failed with status %d\n", path, ret);
+ }
+ }
+ return ret;
+}
+#endif
+
+static int generate_ext4_image(const char* fileName, long long partSize,
+ const std::string& initial_dir, unsigned eraseBlkSize,
+ unsigned logicalBlkSize) {
+ static constexpr int block_size = 4096;
+ const std::string exec_dir = android::base::GetExecutableDirectory();
+
+ const std::string mke2fs_path = exec_dir + "/mke2fs";
+ std::vector<const char*> mke2fs_args = {mke2fs_path.c_str(), "-t", "ext4", "-b"};
+
+ std::string block_size_str = std::to_string(block_size);
+ mke2fs_args.push_back(block_size_str.c_str());
+
+ std::string ext_attr = "android_sparse";
+ if (eraseBlkSize != 0 && logicalBlkSize != 0) {
+ int raid_stride = logicalBlkSize / block_size;
+ int raid_stripe_width = eraseBlkSize / block_size;
+ // stride should be the max of 8kb and logical block size
+ if (logicalBlkSize != 0 && logicalBlkSize < 8192) raid_stride = 8192 / block_size;
+ ext_attr += StringPrintf(",stride=%d,stripe-width=%d", raid_stride, raid_stripe_width);
+ }
+ mke2fs_args.push_back("-E");
+ mke2fs_args.push_back(ext_attr.c_str());
+ mke2fs_args.push_back("-O");
+ mke2fs_args.push_back("uninit_bg");
+ mke2fs_args.push_back(fileName);
+
+ std::string size_str = std::to_string(partSize / block_size);
+ mke2fs_args.push_back(size_str.c_str());
+ mke2fs_args.push_back(nullptr);
+
+ int ret = exec_e2fs_cmd(mke2fs_args[0], const_cast<char**>(mke2fs_args.data()));
+ if (ret != 0) {
+ fprintf(stderr, "mke2fs failed: %d\n", ret);
+ return -1;
+ }
+
+ if (initial_dir.empty()) {
+ return 0;
+ }
+
+ const std::string e2fsdroid_path = exec_dir + "/e2fsdroid";
+ std::vector<const char*> e2fsdroid_args = {e2fsdroid_path.c_str(), "-f", initial_dir.c_str(),
+ fileName, nullptr};
+
+ ret = exec_e2fs_cmd(e2fsdroid_args[0], const_cast<char**>(e2fsdroid_args.data()));
+ if (ret != 0) {
+ fprintf(stderr, "e2fsdroid failed: %d\n", ret);
+ return -1;
+ }
+
return 0;
}
diff --git a/fastboot/usb_osx.cpp b/fastboot/usb_osx.cpp
index 9069baa..e95b049 100644
--- a/fastboot/usb_osx.cpp
+++ b/fastboot/usb_osx.cpp
@@ -93,12 +93,9 @@
SInt32 score;
UInt8 interfaceNumEndpoints;
- // Placing the constant KIOUSBFindInterfaceDontCare into the following
- // fields of the IOUSBFindInterfaceRequest structure will allow us to
- // find all of the interfaces
- request.bInterfaceClass = kIOUSBFindInterfaceDontCare;
- request.bInterfaceSubClass = kIOUSBFindInterfaceDontCare;
- request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare;
+ request.bInterfaceClass = 0xff;
+ request.bInterfaceSubClass = 0x42;
+ request.bInterfaceProtocol = 0x03;
request.bAlternateSetting = kIOUSBFindInterfaceDontCare;
// Get an iterator for the interfaces on the device
@@ -282,7 +279,6 @@
&plugin, &score);
if ((kr != 0) || (plugin == NULL)) {
- ERR("Unable to create a plug-in (%08x)\n", kr);
goto error;
}
@@ -436,8 +432,7 @@
if (try_device(device, &h) != 0) {
IOObjectRelease(device);
- ret = -1;
- break;
+ continue;
}
if (h.success) {
diff --git a/fingerprintd/Android.mk b/fingerprintd/Android.mk
deleted file mode 100644
index 48b9525..0000000
--- a/fingerprintd/Android.mk
+++ /dev/null
@@ -1,33 +0,0 @@
-#
-# 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
deleted file mode 100644
index b3c0cd7..0000000
--- a/fingerprintd/FingerprintDaemonProxy.cpp
+++ /dev/null
@@ -1,268 +0,0 @@
-/*
- * 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, 1);
-
-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;
- case FINGERPRINT_TEMPLATE_ENUMERATING:
- ALOGD("onEnumerate(fid=%d, gid=%d, rem=%d)",
- msg->data.enumerated.finger.fid,
- msg->data.enumerated.finger.gid,
- msg->data.enumerated.remaining_templates);
- callback->onEnumerate(device,
- msg->data.enumerated.finger.fid,
- msg->data.enumerated.finger.gid,
- msg->data.enumerated.remaining_templates);
- 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);
-}
-
-int32_t FingerprintDaemonProxy::enumerate() {
- ALOG(LOG_VERBOSE, LOG_TAG, "enumerate()\n");
- return mDevice->enumerate(mDevice);
-}
-
-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
deleted file mode 100644
index 145b4c9..0000000
--- a/fingerprintd/FingerprintDaemonProxy.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * 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 int32_t enumerate();
- 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
deleted file mode 100644
index bc4af56..0000000
--- a/fingerprintd/IFingerprintDaemon.cpp
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * 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 ENUMERATE: {
- CHECK_INTERFACE(IFingerprintDaemon, data, reply);
- if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) {
- return PERMISSION_DENIED;
- }
- const int32_t ret = enumerate();
- 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
deleted file mode 100644
index 23c36ff..0000000
--- a/fingerprintd/IFingerprintDaemon.h
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * 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,
- ENUMERATE = IBinder::FIRST_CALL_TRANSACTION + 12,
- };
-
- 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 int32_t enumerate() = 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
deleted file mode 100644
index 1d75aa7..0000000
--- a/fingerprintd/IFingerprintDaemonCallback.cpp
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * 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:
- explicit 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, 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_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
deleted file mode 100644
index e343cb4..0000000
--- a/fingerprintd/IFingerprintDaemonCallback.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * 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, int32_t fingerId, int32_t groupId, int32_t rem) = 0;
-
- DECLARE_META_INTERFACE(FingerprintDaemonCallback);
-};
-
-}; // namespace android
-
-#endif // IFINGERPRINT_DAEMON_CALLBACK_H_
diff --git a/fingerprintd/fingerprintd.cpp b/fingerprintd/fingerprintd.cpp
deleted file mode 100644
index 2fc2d0a..0000000
--- a/fingerprintd/fingerprintd.cpp
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * 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/IPCThreadState.h>
-#include <binder/IServiceManager.h>
-#include <binder/PermissionCache.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 <log/log.h>
-#include <utils/Log.h>
-#include <utils/String16.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 f3ca724..18ccc43 100644
--- a/fs_mgr/Android.mk
+++ b/fs_mgr/Android.mk
@@ -15,7 +15,6 @@
libavb
include $(CLEAR_VARS)
-LOCAL_CLANG := true
LOCAL_SANITIZE := integer
LOCAL_SRC_FILES:= fs_mgr_main.cpp
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index ff9b84f..874189a 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -249,6 +249,13 @@
le32_to_cpu(es->s_r_blocks_count_lo);
}
+static bool is_ext4_superblock_valid(const struct ext4_super_block* es) {
+ if (es->s_magic != EXT4_SUPER_MAGIC) return false;
+ if (es->s_rev_level != EXT4_DYNAMIC_REV && es->s_rev_level != EXT4_GOOD_OLD_REV) return false;
+ if (EXT4_INODES_PER_GROUP(es) == 0) return false;
+ return true;
+}
+
// Read the primary superblock from an ext4 filesystem. On failure return
// false. If it's not an ext4 filesystem, also set FS_STAT_EXT4_INVALID_MAGIC.
static bool read_ext4_superblock(const char* blk_device, struct ext4_super_block* sb, int* fs_stat) {
@@ -264,9 +271,8 @@
return false;
}
- if (sb->s_magic != EXT4_SUPER_MAGIC) {
- LINFO << "Invalid ext4 magic:0x" << std::hex << sb->s_magic << " "
- << "on '" << blk_device << "'";
+ if (!is_ext4_superblock_valid(sb)) {
+ LINFO << "Invalid ext4 superblock on '" << blk_device << "'";
// not a valid fs, tune2fs, fsck, and mount will all fail.
*fs_stat |= FS_STAT_EXT4_INVALID_MAGIC;
return false;
diff --git a/fs_mgr/fs_mgr_boot_config.cpp b/fs_mgr/fs_mgr_boot_config.cpp
index 9117667..9c5d3f3 100644
--- a/fs_mgr/fs_mgr_boot_config.cpp
+++ b/fs_mgr/fs_mgr_boot_config.cpp
@@ -23,19 +23,11 @@
#include "fs_mgr_priv.h"
-// Tries to get the boot config value in properties, kernel cmdline and
-// device tree (in that order). returns 'true' if successfully found, 'false'
-// otherwise
-bool fs_mgr_get_boot_config(const std::string& key, std::string* out_val) {
+// Tries to get the given boot config value from kernel cmdline.
+// Returns true if successfully found, false otherwise.
+bool fs_mgr_get_boot_config_from_kernel_cmdline(const std::string& key, std::string* out_val) {
FS_MGR_CHECK(out_val != nullptr);
- // first check if we have "ro.boot" property already
- *out_val = android::base::GetProperty("ro.boot." + key, "");
- if (!out_val->empty()) {
- return true;
- }
-
- // fallback to kernel cmdline, properties may not be ready yet
std::string cmdline;
std::string cmdline_key("androidboot." + key);
if (android::base::ReadFileToString("/proc/cmdline", &cmdline)) {
@@ -50,9 +42,29 @@
}
}
+ return false;
+}
+
+// Tries to get the boot config value in properties, kernel cmdline and
+// device tree (in that order). returns 'true' if successfully found, 'false'
+// otherwise
+bool fs_mgr_get_boot_config(const std::string& key, std::string* out_val) {
+ FS_MGR_CHECK(out_val != nullptr);
+
+ // first check if we have "ro.boot" property already
+ *out_val = android::base::GetProperty("ro.boot." + key, "");
+ if (!out_val->empty()) {
+ return true;
+ }
+
+ // fallback to kernel cmdline, properties may not be ready yet
+ if (fs_mgr_get_boot_config_from_kernel_cmdline(key, out_val)) {
+ return true;
+ }
+
// lastly, check the device tree
if (is_dt_compatible()) {
- std::string file_name = kAndroidDtDir + "/" + key;
+ std::string file_name = get_android_dt_dir() + "/" + key;
if (android::base::ReadFileToString(file_name, out_val)) {
if (!out_val->empty()) {
out_val->pop_back(); // Trims the trailing '\0' out.
diff --git a/fs_mgr/fs_mgr_format.cpp b/fs_mgr/fs_mgr_format.cpp
index 5705f93..a03d92c 100644
--- a/fs_mgr/fs_mgr_format.cpp
+++ b/fs_mgr/fs_mgr_format.cpp
@@ -86,13 +86,15 @@
static int format_f2fs(char *fs_blkdev)
{
- char * args[3];
+ char * args[5];
int pid;
int rc = 0;
- args[0] = (char *)"/sbin/mkfs.f2fs";
- args[1] = fs_blkdev;
- args[2] = (char *)0;
+ args[0] = (char *)"/system/bin/make_f2fs";
+ args[1] = (char *)"-f";
+ args[2] = (char *)"-O encrypt";
+ args[3] = fs_blkdev;
+ args[4] = (char *)0;
pid = fork();
if (pid < 0) {
@@ -100,7 +102,7 @@
}
if (!pid) {
/* This doesn't return */
- execv("/sbin/mkfs.f2fs", args);
+ execv(args[0], args);
exit(1);
}
for(;;) {
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index b68875b..41a5868 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -29,6 +29,8 @@
#include "fs_mgr_priv.h"
+const std::string kDefaultAndroidDtDir("/proc/device-tree/firmware/android");
+
struct fs_mgr_flag_values {
char *key_loc;
char *verity_loc;
@@ -39,7 +41,8 @@
int max_comp_streams;
unsigned int zram_size;
uint64_t reserved_size;
- unsigned int file_encryption_mode;
+ unsigned int file_contents_mode;
+ unsigned int file_names_mode;
unsigned int erase_blk_size;
unsigned int logical_blk_size;
};
@@ -99,15 +102,51 @@
{ 0, 0 },
};
-#define EM_SOFTWARE 1
-#define EM_ICE 2
+#define EM_AES_256_XTS 1
+#define EM_ICE 2
+#define EM_AES_256_CTS 3
+#define EM_AES_256_HEH 4
-static struct flag_list encryption_modes[] = {
- {"software", EM_SOFTWARE},
- {"ice", EM_ICE},
- {0, 0}
+static const struct flag_list file_contents_encryption_modes[] = {
+ {"aes-256-xts", EM_AES_256_XTS},
+ {"software", EM_AES_256_XTS}, /* alias for backwards compatibility */
+ {"ice", EM_ICE}, /* hardware-specific inline cryptographic engine */
+ {0, 0},
};
+static const struct flag_list file_names_encryption_modes[] = {
+ {"aes-256-cts", EM_AES_256_CTS},
+ {"aes-256-heh", EM_AES_256_HEH},
+ {0, 0},
+};
+
+static unsigned int encryption_mode_to_flag(const struct flag_list *list,
+ const char *mode, const char *type)
+{
+ const struct flag_list *j;
+
+ for (j = list; j->name; ++j) {
+ if (!strcmp(mode, j->name)) {
+ return j->flag;
+ }
+ }
+ LERROR << "Unknown " << type << " encryption mode: " << mode;
+ return 0;
+}
+
+static const char *flag_to_encryption_mode(const struct flag_list *list,
+ unsigned int flag)
+{
+ const struct flag_list *j;
+
+ for (j = list; j->name; ++j) {
+ if (flag == j->flag) {
+ return j->name;
+ }
+ }
+ return nullptr;
+}
+
static uint64_t calculate_zram_size(unsigned int percentage)
{
uint64_t total;
@@ -206,20 +245,28 @@
* location of the keys. Get it and return it.
*/
flag_vals->key_loc = strdup(strchr(p, '=') + 1);
- flag_vals->file_encryption_mode = EM_SOFTWARE;
+ flag_vals->file_contents_mode = EM_AES_256_XTS;
+ flag_vals->file_names_mode = EM_AES_256_CTS;
} else if ((fl[i].flag == MF_FILEENCRYPTION) && flag_vals) {
- /* The fileencryption flag is followed by an = and the
- * type of the encryption. Get it and return it.
+ /* The fileencryption flag is followed by an = and
+ * the mode of contents encryption, then optionally a
+ * : and the mode of filenames encryption (defaults
+ * to aes-256-cts). Get it and return it.
*/
- const struct flag_list *j;
- const char *mode = strchr(p, '=') + 1;
- for (j = encryption_modes; j->name; ++j) {
- if (!strcmp(mode, j->name)) {
- flag_vals->file_encryption_mode = j->flag;
- }
+ char *mode = strchr(p, '=') + 1;
+ char *colon = strchr(mode, ':');
+ if (colon) {
+ *colon = '\0';
}
- if (flag_vals->file_encryption_mode == 0) {
- LERROR << "Unknown file encryption mode: " << mode;
+ flag_vals->file_contents_mode =
+ encryption_mode_to_flag(file_contents_encryption_modes,
+ mode, "file contents");
+ if (colon) {
+ flag_vals->file_names_mode =
+ encryption_mode_to_flag(file_names_encryption_modes,
+ colon + 1, "file names");
+ } else {
+ flag_vals->file_names_mode = EM_AES_256_CTS;
}
} else if ((fl[i].flag == MF_LENGTH) && flag_vals) {
/* The length flag is followed by an = and the
@@ -313,9 +360,26 @@
return f;
}
+static std::string init_android_dt_dir() {
+ std::string android_dt_dir;
+ // The platform may specify a custom Android DT path in kernel cmdline
+ if (!fs_mgr_get_boot_config_from_kernel_cmdline("android_dt_dir", &android_dt_dir)) {
+ // Fall back to the standard procfs-based path
+ android_dt_dir = kDefaultAndroidDtDir;
+ }
+ return android_dt_dir;
+}
+
+// FIXME: The same logic is duplicated in system/core/init/
+const std::string& get_android_dt_dir() {
+ // Set once and saves time for subsequent calls to this function
+ static const std::string kAndroidDtDir = init_android_dt_dir();
+ return kAndroidDtDir;
+}
+
static bool is_dt_fstab_compatible() {
std::string dt_value;
- std::string file_name = kAndroidDtDir + "/fstab/compatible";
+ std::string file_name = get_android_dt_dir() + "/fstab/compatible";
if (read_dt_file(file_name, &dt_value)) {
if (dt_value == "android,fstab") {
return true;
@@ -331,7 +395,7 @@
return fstab;
}
- std::string fstabdir_name = kAndroidDtDir + "/fstab";
+ std::string fstabdir_name = get_android_dt_dir() + "/fstab";
std::unique_ptr<DIR, int (*)(DIR*)> fstabdir(opendir(fstabdir_name.c_str()), closedir);
if (!fstabdir) return fstab;
@@ -394,7 +458,7 @@
}
bool is_dt_compatible() {
- std::string file_name = kAndroidDtDir + "/compatible";
+ std::string file_name = get_android_dt_dir() + "/compatible";
std::string dt_value;
if (read_dt_file(file_name, &dt_value)) {
if (dt_value == "android,firmware") {
@@ -521,7 +585,8 @@
fstab->recs[cnt].max_comp_streams = flag_vals.max_comp_streams;
fstab->recs[cnt].zram_size = flag_vals.zram_size;
fstab->recs[cnt].reserved_size = flag_vals.reserved_size;
- fstab->recs[cnt].file_encryption_mode = flag_vals.file_encryption_mode;
+ fstab->recs[cnt].file_contents_mode = flag_vals.file_contents_mode;
+ fstab->recs[cnt].file_names_mode = flag_vals.file_names_mode;
fstab->recs[cnt].erase_blk_size = flag_vals.erase_blk_size;
fstab->recs[cnt].logical_blk_size = flag_vals.logical_blk_size;
cnt++;
@@ -789,15 +854,14 @@
return fstab->fs_mgr_flags & MF_FILEENCRYPTION;
}
-const char* fs_mgr_get_file_encryption_mode(const struct fstab_rec *fstab)
+void fs_mgr_get_file_encryption_modes(const struct fstab_rec *fstab,
+ const char **contents_mode_ret,
+ const char **filenames_mode_ret)
{
- const struct flag_list *j;
- for (j = encryption_modes; j->name; ++j) {
- if (fstab->file_encryption_mode == j->flag) {
- return j->name;
- }
- }
- return NULL;
+ *contents_mode_ret = flag_to_encryption_mode(file_contents_encryption_modes,
+ fstab->file_contents_mode);
+ *filenames_mode_ret = flag_to_encryption_mode(file_names_encryption_modes,
+ fstab->file_names_mode);
}
int fs_mgr_is_convertible_to_fbe(const struct fstab_rec *fstab)
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index 5ea6e98..7423c1f 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -119,6 +119,7 @@
const std::chrono::milliseconds relative_timeout);
bool fs_mgr_update_for_slotselect(struct fstab *fstab);
bool fs_mgr_is_device_unlocked();
+const std::string& get_android_dt_dir();
bool is_dt_compatible();
bool is_device_secure();
int load_verity_state(struct fstab_rec* fstab, int* mode);
diff --git a/fs_mgr/fs_mgr_priv_boot_config.h b/fs_mgr/fs_mgr_priv_boot_config.h
index 8773d33..d98dc02 100644
--- a/fs_mgr/fs_mgr_priv_boot_config.h
+++ b/fs_mgr/fs_mgr_priv_boot_config.h
@@ -20,8 +20,7 @@
#include <sys/cdefs.h>
#include <string>
-const std::string kAndroidDtDir("/proc/device-tree/firmware/android");
-
+bool fs_mgr_get_boot_config_from_kernel_cmdline(const std::string& key, std::string* out_val);
bool fs_mgr_get_boot_config(const std::string& key, std::string* out_val);
#endif /* __CORE_FS_MGR_PRIV_BOOTCONFIG_H */
diff --git a/fs_mgr/fs_mgr_verity.cpp b/fs_mgr/fs_mgr_verity.cpp
index 5de0903..7f8e1e2 100644
--- a/fs_mgr/fs_mgr_verity.cpp
+++ b/fs_mgr/fs_mgr_verity.cpp
@@ -841,9 +841,15 @@
// verify the signature on the table
if (verify_verity_signature(verity) < 0) {
+ // Allow signature verification error when the device is unlocked
+ if (fs_mgr_is_device_unlocked()) {
+ retval = FS_MGR_SETUP_VERITY_SKIPPED;
+ LWARNING << "Allow signature verification error when the device is unlocked";
+ goto out;
+ }
if (params.mode == VERITY_MODE_LOGGING) {
// the user has been warned, allow mounting without dm-verity
- retval = FS_MGR_SETUP_VERITY_SUCCESS;
+ retval = FS_MGR_SETUP_VERITY_SKIPPED;
goto out;
}
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index 17e1fb1..8a18ec0 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -57,7 +57,8 @@
int max_comp_streams;
unsigned int zram_size;
uint64_t reserved_size;
- unsigned int file_encryption_mode;
+ unsigned int file_contents_mode;
+ unsigned int file_names_mode;
unsigned int erase_blk_size;
unsigned int logical_blk_size;
};
@@ -77,7 +78,8 @@
int fs_mgr_is_avb(const struct fstab_rec* fstab);
int fs_mgr_is_encryptable(const struct fstab_rec* fstab);
int fs_mgr_is_file_encrypted(const struct fstab_rec* fstab);
-const char* fs_mgr_get_file_encryption_mode(const struct fstab_rec* fstab);
+void fs_mgr_get_file_encryption_modes(const struct fstab_rec* fstab, const char** contents_mode_ret,
+ const char** filenames_mode_ret);
int fs_mgr_is_convertible_to_fbe(const struct fstab_rec* fstab);
int fs_mgr_is_noemulatedsd(const struct fstab_rec* fstab);
int fs_mgr_is_notrim(const struct fstab_rec* fstab);
diff --git a/gatekeeperd/Android.mk b/gatekeeperd/Android.mk
index 3f78955..0dfd9d8 100644
--- a/gatekeeperd/Android.mk
+++ b/gatekeeperd/Android.mk
@@ -33,7 +33,12 @@
libbase \
libutils \
libcrypto \
- libkeystore_binder
+ libkeystore_binder \
+ libhidlbase \
+ libhidltransport \
+ libhwbinder \
+ android.hardware.gatekeeper@1.0 \
+
LOCAL_STATIC_LIBRARIES := libscrypt_static
LOCAL_C_INCLUDES := external/scrypt/lib/crypto
LOCAL_INIT_RC := gatekeeperd.rc
diff --git a/gatekeeperd/SoftGateKeeper.h b/gatekeeperd/SoftGateKeeper.h
index cb02a6f..92d1752 100644
--- a/gatekeeperd/SoftGateKeeper.h
+++ b/gatekeeperd/SoftGateKeeper.h
@@ -26,8 +26,8 @@
}
#include <android-base/memory.h>
-#include <UniquePtr.h>
#include <gatekeeper/gatekeeper.h>
+#include <nativehelper/UniquePtr.h>
#include <iostream>
#include <unordered_map>
diff --git a/gatekeeperd/SoftGateKeeperDevice.h b/gatekeeperd/SoftGateKeeperDevice.h
index 3463c29..229f9a9 100644
--- a/gatekeeperd/SoftGateKeeperDevice.h
+++ b/gatekeeperd/SoftGateKeeperDevice.h
@@ -19,7 +19,7 @@
#include "SoftGateKeeper.h"
-#include <UniquePtr.h>
+#include <nativehelper/UniquePtr.h>
using namespace gatekeeper;
diff --git a/gatekeeperd/gatekeeperd.cpp b/gatekeeperd/gatekeeperd.cpp
index 96bda07..e6eb3bc 100644
--- a/gatekeeperd/gatekeeperd.cpp
+++ b/gatekeeperd/gatekeeperd.cpp
@@ -39,6 +39,15 @@
#include "SoftGateKeeperDevice.h"
#include "IUserManager.h"
+#include <hidl/HidlSupport.h>
+#include <android/hardware/gatekeeper/1.0/IGatekeeper.h>
+
+using android::sp;
+using android::hardware::gatekeeper::V1_0::IGatekeeper;
+using android::hardware::gatekeeper::V1_0::GatekeeperStatusCode;
+using android::hardware::gatekeeper::V1_0::GatekeeperResponse;
+using android::hardware::Return;
+
namespace android {
static const String16 KEYGUARD_PERMISSION("android.permission.ACCESS_KEYGUARD_SECURE_STORAGE");
@@ -47,28 +56,22 @@
class GateKeeperProxy : public BnGateKeeperService {
public:
GateKeeperProxy() {
- int ret = hw_get_module_by_class(GATEKEEPER_HARDWARE_MODULE_ID, NULL, &module);
- device = NULL;
+ hw_device = IGatekeeper::getService();
- if (ret < 0) {
+ if (hw_device == nullptr) {
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);
+ if (hw_device != nullptr) {
+ hw_device->deleteAllUsers([](const GatekeeperResponse &){});
}
}
}
virtual ~GateKeeperProxy() {
- if (device) gatekeeper_close(device);
}
void store_sid(uint32_t uid, uint64_t sid) {
@@ -141,7 +144,7 @@
if (desired_password_length == 0) return -EINVAL;
int ret;
- if (device) {
+ if (hw_device != nullptr) {
const gatekeeper::password_handle_t *handle =
reinterpret_cast<const gatekeeper::password_handle_t *>(current_password_handle);
@@ -154,10 +157,37 @@
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);
+ android::hardware::hidl_vec<uint8_t> curPwdHandle;
+ curPwdHandle.setToExternal(const_cast<uint8_t*>(current_password_handle),
+ current_password_handle_length);
+ android::hardware::hidl_vec<uint8_t> curPwd;
+ curPwd.setToExternal(const_cast<uint8_t*>(current_password),
+ current_password_length);
+ android::hardware::hidl_vec<uint8_t> newPwd;
+ newPwd.setToExternal(const_cast<uint8_t*>(desired_password),
+ desired_password_length);
+
+ Return<void> hwRes = hw_device->enroll(uid, curPwdHandle, curPwd, newPwd,
+ [&ret, enrolled_password_handle, enrolled_password_handle_length]
+ (const GatekeeperResponse &rsp) {
+ ret = static_cast<int>(rsp.code); // propagate errors
+ if (rsp.code >= GatekeeperStatusCode::STATUS_OK) {
+ if (enrolled_password_handle != nullptr &&
+ enrolled_password_handle_length != nullptr) {
+ *enrolled_password_handle = new uint8_t[rsp.data.size()];
+ *enrolled_password_handle_length = rsp.data.size();
+ memcpy(*enrolled_password_handle, rsp.data.data(),
+ *enrolled_password_handle_length);
+ }
+ ret = 0; // all success states are reported as 0
+ } else if (rsp.code == GatekeeperStatusCode::ERROR_RETRY_TIMEOUT && rsp.timeout > 0) {
+ ret = rsp.timeout;
+ }
+ });
+ if (!hwRes.isOk()) {
+ ALOGE("enroll transaction failed\n");
+ ret = -1;
+ }
} else {
ret = soft_device->enroll(uid,
current_password_handle, current_password_handle_length,
@@ -214,16 +244,40 @@
return -EINVAL;
int ret;
- if (device) {
+ if (hw_device != nullptr) {
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);
+ android::hardware::hidl_vec<uint8_t> curPwdHandle;
+ curPwdHandle.setToExternal(const_cast<uint8_t*>(enrolled_password_handle),
+ enrolled_password_handle_length);
+ android::hardware::hidl_vec<uint8_t> enteredPwd;
+ enteredPwd.setToExternal(const_cast<uint8_t*>(provided_password),
+ provided_password_length);
+ Return<void> hwRes = hw_device->verify(uid, challenge, curPwdHandle, enteredPwd,
+ [&ret, request_reenroll, auth_token, auth_token_length]
+ (const GatekeeperResponse &rsp) {
+ ret = static_cast<int>(rsp.code); // propagate errors
+ if (auth_token != nullptr && auth_token_length != nullptr &&
+ rsp.code >= GatekeeperStatusCode::STATUS_OK) {
+ *auth_token = new uint8_t[rsp.data.size()];
+ *auth_token_length = rsp.data.size();
+ memcpy(*auth_token, rsp.data.data(), *auth_token_length);
+ if (request_reenroll != nullptr) {
+ *request_reenroll = (rsp.code == GatekeeperStatusCode::STATUS_REENROLL);
+ }
+ ret = 0; // all success states are reported as 0
+ } else if (rsp.code == GatekeeperStatusCode::ERROR_RETRY_TIMEOUT &&
+ rsp.timeout > 0) {
+ ret = rsp.timeout;
+ }
+ });
+ if (!hwRes.isOk()) {
+ ALOGE("verify transaction failed\n");
+ ret = -1;
+ }
} else {
// upgrade scenario, a HAL has been added to this device where there was none before
SoftGateKeeperDevice soft_dev;
@@ -250,9 +304,9 @@
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);
+ auto ret = service->addAuthToken(*auth_token, *auth_token_length);
+ if (!ret.isOk()) {
+ ALOGE("Failure sending auth token to KeyStore: %" PRId32, int32_t(ret));
}
} else {
ALOGE("Unable to communicate with KeyStore");
@@ -295,8 +349,8 @@
}
clear_sid(uid);
- if (device != NULL && device->delete_user != NULL) {
- device->delete_user(device, uid);
+ if (hw_device != nullptr) {
+ hw_device->deleteUser(uid, [] (const GatekeeperResponse &){});
}
}
@@ -308,7 +362,7 @@
return PERMISSION_DENIED;
}
- if (device == NULL) {
+ if (hw_device == NULL) {
const char *result = "Device not available";
write(fd, result, strlen(result) + 1);
} else {
@@ -320,9 +374,8 @@
}
private:
- gatekeeper_device_t *device;
+ sp<IGatekeeper> hw_device;
UniquePtr<SoftGateKeeperDevice> soft_device;
- const hw_module_t *module;
};
}// namespace android
diff --git a/gatekeeperd/tests/Android.mk b/gatekeeperd/tests/Android.mk
index a62b1d4..c38c64b 100644
--- a/gatekeeperd/tests/Android.mk
+++ b/gatekeeperd/tests/Android.mk
@@ -19,7 +19,7 @@
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_CFLAGS += -g -Wall -Werror -Wno-missing-field-initializers
LOCAL_SHARED_LIBRARIES := libgatekeeper libcrypto libbase
LOCAL_STATIC_LIBRARIES := libscrypt_static
LOCAL_C_INCLUDES := external/scrypt/lib/crypto
diff --git a/gatekeeperd/tests/gatekeeper_test.cpp b/gatekeeperd/tests/gatekeeper_test.cpp
index 47a8bfa..b3aea7b 100644
--- a/gatekeeperd/tests/gatekeeper_test.cpp
+++ b/gatekeeperd/tests/gatekeeper_test.cpp
@@ -19,7 +19,7 @@
#include <gtest/gtest.h>
#include <hardware/hw_auth_token.h>
-#include <UniquePtr.h>
+#include <nativehelper/UniquePtr.h>
#include "../SoftGateKeeper.h"
diff --git a/healthd/Android.mk b/healthd/Android.mk
index b292725..8b59964 100644
--- a/healthd/Android.mk
+++ b/healthd/Android.mk
@@ -3,16 +3,6 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := healthd_board_default.cpp
-LOCAL_MODULE := libhealthd.default
-LOCAL_CFLAGS := -Werror
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_STATIC_LIBRARIES := libbinder
-LOCAL_EXPORT_STATIC_LIBRARY_HEADERS := libbinder
-include $(BUILD_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
LOCAL_SRC_FILES := BatteryMonitor.cpp
LOCAL_MODULE := libbatterymonitor
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
@@ -21,26 +11,47 @@
include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
-ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true)
-LOCAL_CFLAGS += -DCHARGER_ENABLE_SUSPEND
-LOCAL_SHARED_LIBRARIES += libsuspend
-endif
LOCAL_SRC_FILES := \
healthd_mode_android.cpp \
- healthd_mode_charger.cpp \
- AnimationParser.cpp \
- BatteryPropertiesRegistrar.cpp \
+ BatteryPropertiesRegistrar.cpp
-LOCAL_MODULE := libhealthd_internal
-LOCAL_C_INCLUDES := bootable/recovery
+LOCAL_MODULE := libhealthd_android
LOCAL_EXPORT_C_INCLUDE_DIRS := \
$(LOCAL_PATH) \
- $(LOCAL_PATH)/include \
+ $(LOCAL_PATH)/include
LOCAL_STATIC_LIBRARIES := \
libbatterymonitor \
libbatteryservice \
- libbinder \
+ libutils \
+ libbase \
+ libcutils \
+ liblog \
+ libc \
+
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+
+LOCAL_CFLAGS := -Werror
+ifeq ($(strip $(BOARD_CHARGER_DISABLE_INIT_BLANK)),true)
+LOCAL_CFLAGS += -DCHARGER_DISABLE_INIT_BLANK
+endif
+ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true)
+LOCAL_CFLAGS += -DCHARGER_ENABLE_SUSPEND
+endif
+
+LOCAL_SRC_FILES := \
+ healthd_mode_charger.cpp \
+ AnimationParser.cpp
+
+LOCAL_MODULE := libhealthd_charger
+LOCAL_C_INCLUDES := bootable/recovery $(LOCAL_PATH)/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := \
+ $(LOCAL_PATH) \
+ $(LOCAL_PATH)/include
+
+LOCAL_STATIC_LIBRARIES := \
libminui \
libpng \
libz \
@@ -51,11 +62,14 @@
libm \
libc \
+ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true)
+LOCAL_STATIC_LIBRARIES += libsuspend
+endif
+
include $(BUILD_STATIC_LIBRARY)
-
+### charger ###
include $(CLEAR_VARS)
-
ifeq ($(strip $(BOARD_CHARGER_NO_UI)),true)
LOCAL_CHARGER_NO_UI := true
endif
@@ -64,80 +78,57 @@
endif
LOCAL_SRC_FILES := \
- healthd.cpp \
- healthd_mode_android.cpp \
- BatteryPropertiesRegistrar.cpp \
+ healthd_common.cpp \
+ charger.cpp \
-ifneq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
-LOCAL_SRC_FILES += healthd_mode_charger.cpp
-endif
-
-LOCAL_MODULE := healthd
+LOCAL_MODULE := charger
LOCAL_MODULE_TAGS := optional
LOCAL_FORCE_STATIC_EXECUTABLE := true
LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT_SBIN)
LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_SBIN_UNSTRIPPED)
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_CFLAGS := -D__STDC_LIMIT_MACROS -Werror
-
-ifeq ($(strip $(BOARD_CHARGER_DISABLE_INIT_BLANK)),true)
-LOCAL_CFLAGS += -DCHARGER_DISABLE_INIT_BLANK
-endif
-
-ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true)
-LOCAL_CFLAGS += -DCHARGER_ENABLE_SUSPEND
-endif
-
+LOCAL_CFLAGS := -Werror
ifeq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
LOCAL_CFLAGS += -DCHARGER_NO_UI
endif
-
-LOCAL_C_INCLUDES := bootable/recovery $(LOCAL_PATH)/include
-
ifneq ($(BOARD_PERIODIC_CHORES_INTERVAL_FAST),)
LOCAL_CFLAGS += -DBOARD_PERIODIC_CHORES_INTERVAL_FAST=$(BOARD_PERIODIC_CHORES_INTERVAL_FAST)
endif
-
ifneq ($(BOARD_PERIODIC_CHORES_INTERVAL_SLOW),)
LOCAL_CFLAGS += -DBOARD_PERIODIC_CHORES_INTERVAL_SLOW=$(BOARD_PERIODIC_CHORES_INTERVAL_SLOW)
endif
LOCAL_STATIC_LIBRARIES := \
- libhealthd_internal \
+ libhealthd_charger \
libbatterymonitor \
- libbatteryservice \
- libbinder \
libbase \
-
-ifneq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
-LOCAL_STATIC_LIBRARIES += \
- libminui \
- libpng \
- libz \
-
-endif
-
-
-LOCAL_STATIC_LIBRARIES += \
libutils \
libcutils \
liblog \
libm \
libc \
+ifneq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
+LOCAL_STATIC_LIBRARIES += \
+ libminui \
+ libpng \
+ libz \
+
+endif
+
ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true)
LOCAL_STATIC_LIBRARIES += libsuspend
endif
LOCAL_HAL_STATIC_LIBRARIES := libhealthd
-# Symlink /charger to /sbin/healthd
+# Symlink /charger to /sbin/charger
LOCAL_POST_INSTALL_CMD := $(hide) mkdir -p $(TARGET_ROOT_OUT) \
- && ln -sf /sbin/healthd $(TARGET_ROOT_OUT)/charger
+ && ln -sf /sbin/charger $(TARGET_ROOT_OUT)/charger
include $(BUILD_EXECUTABLE)
-
ifneq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
define _add-charger-image
include $$(CLEAR_VARS)
@@ -165,3 +156,41 @@
_add-charger-image :=
_img_modules :=
endif # LOCAL_CHARGER_NO_UI
+
+### healthd ###
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ healthd_common.cpp \
+ healthd.cpp \
+
+LOCAL_MODULE := healthd
+LOCAL_MODULE_TAGS := optional
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+
+ifneq ($(BOARD_PERIODIC_CHORES_INTERVAL_FAST),)
+LOCAL_CFLAGS += -DBOARD_PERIODIC_CHORES_INTERVAL_FAST=$(BOARD_PERIODIC_CHORES_INTERVAL_FAST)
+endif
+ifneq ($(BOARD_PERIODIC_CHORES_INTERVAL_SLOW),)
+LOCAL_CFLAGS += -DBOARD_PERIODIC_CHORES_INTERVAL_SLOW=$(BOARD_PERIODIC_CHORES_INTERVAL_SLOW)
+endif
+
+LOCAL_STATIC_LIBRARIES := \
+ libhealthd_android \
+ libbatterymonitor \
+ libbatteryservice \
+ android.hardware.health@1.0-convert \
+
+LOCAL_SHARED_LIBRARIES := \
+ libbinder \
+ libbase \
+ libutils \
+ libcutils \
+ liblog \
+ libm \
+ libc \
+ libhidlbase \
+ libhidltransport \
+ android.hardware.health@1.0 \
+
+include $(BUILD_EXECUTABLE)
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index 4e3b885..676ee41 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -351,6 +351,7 @@
status_t BatteryMonitor::getProperty(int id, struct BatteryProperty *val) {
status_t ret = BAD_VALUE;
+ std::string buf;
val->valueInt64 = LONG_MIN;
@@ -403,6 +404,15 @@
}
break;
+ case BATTERY_PROP_BATTERY_STATUS:
+ if (mAlwaysPluggedDevice) {
+ val->valueInt64 = BATTERY_STATUS_CHARGING;
+ } else {
+ val->valueInt64 = getChargeStatus();
+ }
+ ret = NO_ERROR;
+ break;
+
default:
break;
}
diff --git a/healthd/charger.cpp b/healthd/charger.cpp
new file mode 100644
index 0000000..5a8fe1a
--- /dev/null
+++ b/healthd/charger.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2016 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 "charger"
+#define KLOG_LEVEL 6
+
+#include <healthd/healthd.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <cutils/klog.h>
+
+using namespace android;
+
+// main healthd loop
+extern int healthd_main(void);
+
+// Charger mode
+
+extern void healthd_mode_charger_init(struct healthd_config *config);
+extern int healthd_mode_charger_preparetowait(void);
+extern void healthd_mode_charger_heartbeat(void);
+extern void healthd_mode_charger_battery_update(
+ struct android::BatteryProperties *props);
+
+// NOPs for modes that need no special action
+
+static void healthd_mode_nop_init(struct healthd_config *config);
+static int healthd_mode_nop_preparetowait(void);
+static void healthd_mode_nop_heartbeat(void);
+static void healthd_mode_nop_battery_update(
+ struct android::BatteryProperties *props);
+
+static struct healthd_mode_ops healthd_nops = {
+ .init = healthd_mode_nop_init,
+ .preparetowait = healthd_mode_nop_preparetowait,
+ .heartbeat = healthd_mode_nop_heartbeat,
+ .battery_update = healthd_mode_nop_battery_update,
+};
+
+#ifdef CHARGER_NO_UI
+static struct healthd_mode_ops charger_ops = healthd_nops;
+#else
+static struct healthd_mode_ops charger_ops = {
+ .init = healthd_mode_charger_init,
+ .preparetowait = healthd_mode_charger_preparetowait,
+ .heartbeat = healthd_mode_charger_heartbeat,
+ .battery_update = healthd_mode_charger_battery_update,
+};
+#endif
+
+static void healthd_mode_nop_init(struct healthd_config* /*config*/) {
+}
+
+static int healthd_mode_nop_preparetowait(void) {
+ return -1;
+}
+
+static void healthd_mode_nop_heartbeat(void) {
+}
+
+static void healthd_mode_nop_battery_update(
+ struct android::BatteryProperties* /*props*/) {
+}
+
+int main(int argc, char **argv) {
+ int ch;
+
+ healthd_mode_ops = &charger_ops;
+
+ while ((ch = getopt(argc, argv, "cr")) != -1) {
+ switch (ch) {
+ case 'c':
+ // -c is now a noop
+ break;
+ case 'r':
+ // force nops for recovery
+ healthd_mode_ops = &healthd_nops;
+ break;
+ case '?':
+ default:
+ KLOG_ERROR(LOG_TAG, "Unrecognized charger option: %c\n",
+ optopt);
+ exit(1);
+ }
+ }
+
+ return healthd_main();
+}
diff --git a/healthd/healthd.cpp b/healthd/healthd.cpp
index aa6735d..ed1971a 100644
--- a/healthd/healthd.cpp
+++ b/healthd/healthd.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2016 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.
@@ -18,369 +18,106 @@
#define KLOG_LEVEL 6
#include <healthd/healthd.h>
-#include <healthd/BatteryMonitor.h>
-#include <errno.h>
-#include <libgen.h>
#include <stdio.h>
#include <stdlib.h>
-#include <string.h>
#include <unistd.h>
-#include <batteryservice/BatteryService.h>
#include <cutils/klog.h>
-#include <cutils/uevent.h>
-#include <sys/epoll.h>
-#include <sys/timerfd.h>
-#include <utils/Errors.h>
+
+#include <android/hardware/health/1.0/IHealth.h>
+#include <android/hardware/health/1.0/types.h>
+#include <hal_conversion.h>
using namespace android;
-#ifndef BOARD_PERIODIC_CHORES_INTERVAL_FAST
- // Periodic chores fast interval in seconds
- #define DEFAULT_PERIODIC_CHORES_INTERVAL_FAST (60 * 1)
-#else
- #define DEFAULT_PERIODIC_CHORES_INTERVAL_FAST (BOARD_PERIODIC_CHORES_INTERVAL_FAST)
-#endif
+using IHealth = ::android::hardware::health::V1_0::IHealth;
+using Result = ::android::hardware::health::V1_0::Result;
+using HealthConfig = ::android::hardware::health::V1_0::HealthConfig;
+using HealthInfo = ::android::hardware::health::V1_0::HealthInfo;
-#ifndef BOARD_PERIODIC_CHORES_INTERVAL_SLOW
- // Periodic chores fast interval in seconds
- #define DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW (60 * 10)
-#else
- #define DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW (BOARD_PERIODIC_CHORES_INTERVAL_SLOW)
-#endif
+using ::android::hardware::health::V1_0::hal_conversion::convertToHealthConfig;
+using ::android::hardware::health::V1_0::hal_conversion::convertFromHealthConfig;
+using ::android::hardware::health::V1_0::hal_conversion::convertToHealthInfo;
+using ::android::hardware::health::V1_0::hal_conversion::convertFromHealthInfo;
-static struct healthd_config healthd_config = {
- .periodic_chores_interval_fast = DEFAULT_PERIODIC_CHORES_INTERVAL_FAST,
- .periodic_chores_interval_slow = DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW,
- .batteryStatusPath = String8(String8::kEmptyString),
- .batteryHealthPath = String8(String8::kEmptyString),
- .batteryPresentPath = String8(String8::kEmptyString),
- .batteryCapacityPath = String8(String8::kEmptyString),
- .batteryVoltagePath = String8(String8::kEmptyString),
- .batteryTemperaturePath = String8(String8::kEmptyString),
- .batteryTechnologyPath = String8(String8::kEmptyString),
- .batteryCurrentNowPath = String8(String8::kEmptyString),
- .batteryCurrentAvgPath = String8(String8::kEmptyString),
- .batteryChargeCounterPath = String8(String8::kEmptyString),
- .batteryFullChargePath = String8(String8::kEmptyString),
- .batteryCycleCountPath = String8(String8::kEmptyString),
- .energyCounter = NULL,
- .boot_min_cap = 0,
- .screen_on = NULL,
-};
+// device specific hal interface;
+static sp<IHealth> gHealth;
-static int eventct;
-static int epollfd;
-
-#define POWER_SUPPLY_SUBSYSTEM "power_supply"
-
-// epoll_create() parameter is actually unused
-#define MAX_EPOLL_EVENTS 40
-static int uevent_fd;
-static int wakealarm_fd;
-
-// -1 for no epoll timeout
-static int awake_poll_interval = -1;
-
-static int wakealarm_wake_interval = DEFAULT_PERIODIC_CHORES_INTERVAL_FAST;
-
-static BatteryMonitor* gBatteryMonitor;
-
-struct healthd_mode_ops *healthd_mode_ops;
+// main healthd loop
+extern int healthd_main(void);
// Android mode
-
extern void healthd_mode_android_init(struct healthd_config *config);
extern int healthd_mode_android_preparetowait(void);
+extern void healthd_mode_android_heartbeat(void);
extern void healthd_mode_android_battery_update(
struct android::BatteryProperties *props);
-// Charger mode
-
-extern void healthd_mode_charger_init(struct healthd_config *config);
-extern int healthd_mode_charger_preparetowait(void);
-extern void healthd_mode_charger_heartbeat(void);
-extern void healthd_mode_charger_battery_update(
- struct android::BatteryProperties *props);
-
-// NOPs for modes that need no special action
-
-static void healthd_mode_nop_init(struct healthd_config *config);
-static int healthd_mode_nop_preparetowait(void);
-static void healthd_mode_nop_heartbeat(void);
-static void healthd_mode_nop_battery_update(
- struct android::BatteryProperties *props);
-
static struct healthd_mode_ops android_ops = {
.init = healthd_mode_android_init,
.preparetowait = healthd_mode_android_preparetowait,
- .heartbeat = healthd_mode_nop_heartbeat,
+ .heartbeat = healthd_mode_android_heartbeat,
.battery_update = healthd_mode_android_battery_update,
};
-static struct healthd_mode_ops charger_ops = {
-#ifdef CHARGER_NO_UI
- .init = healthd_mode_nop_init,
- .preparetowait = healthd_mode_nop_preparetowait,
- .heartbeat = healthd_mode_nop_heartbeat,
- .battery_update = healthd_mode_nop_battery_update,
-#else
- .init = healthd_mode_charger_init,
- .preparetowait = healthd_mode_charger_preparetowait,
- .heartbeat = healthd_mode_charger_heartbeat,
- .battery_update = healthd_mode_charger_battery_update,
-#endif
-};
+// default energy counter property redirect to talk to device
+// HAL
+static int healthd_board_get_energy_counter(int64_t *energy) {
-static struct healthd_mode_ops recovery_ops = {
- .init = healthd_mode_nop_init,
- .preparetowait = healthd_mode_nop_preparetowait,
- .heartbeat = healthd_mode_nop_heartbeat,
- .battery_update = healthd_mode_nop_battery_update,
-};
-
-static void healthd_mode_nop_init(struct healthd_config* /*config*/) {
-}
-
-static int healthd_mode_nop_preparetowait(void) {
- return -1;
-}
-
-static void healthd_mode_nop_heartbeat(void) {
-}
-
-static void healthd_mode_nop_battery_update(
- struct android::BatteryProperties* /*props*/) {
-}
-
-int healthd_register_event(int fd, void (*handler)(uint32_t), EventWakeup wakeup) {
- struct epoll_event ev;
-
- ev.events = EPOLLIN;
-
- if (wakeup == EVENT_WAKEUP_FD)
- ev.events |= EPOLLWAKEUP;
-
- ev.data.ptr = (void *)handler;
- if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) == -1) {
- KLOG_ERROR(LOG_TAG,
- "epoll_ctl failed; errno=%d\n", errno);
- return -1;
+ if (gHealth == nullptr) {
+ return NAME_NOT_FOUND;
}
- eventct++;
- return 0;
+ Result result = Result::NOT_SUPPORTED;
+ gHealth->energyCounter([=, &result] (Result ret, int64_t energyOut) {
+ result = ret;
+ *energy = energyOut;
+ });
+
+ return result == Result::SUCCESS ? OK : NAME_NOT_FOUND;
}
-static void wakealarm_set_interval(int interval) {
- struct itimerspec itval;
+void healthd_board_init(struct healthd_config *config) {
- if (wakealarm_fd == -1)
- return;
+ // Initialize the board HAL - Equivalent of healthd_board_init(config)
+ // in charger/recovery mode.
- wakealarm_wake_interval = interval;
-
- if (interval == -1)
- interval = 0;
-
- itval.it_interval.tv_sec = interval;
- itval.it_interval.tv_nsec = 0;
- itval.it_value.tv_sec = interval;
- itval.it_value.tv_nsec = 0;
-
- if (timerfd_settime(wakealarm_fd, 0, &itval, NULL) == -1)
- KLOG_ERROR(LOG_TAG, "wakealarm_set_interval: timerfd_settime failed\n");
-}
-
-status_t healthd_get_property(int id, struct BatteryProperty *val) {
- return gBatteryMonitor->getProperty(id, val);
-}
-
-void healthd_battery_update(void) {
- // Fast wake interval when on charger (watch for overheat);
- // slow wake interval when on battery (watch for drained battery).
-
- int new_wake_interval = gBatteryMonitor->update() ?
- healthd_config.periodic_chores_interval_fast :
- healthd_config.periodic_chores_interval_slow;
-
- if (new_wake_interval != wakealarm_wake_interval)
- wakealarm_set_interval(new_wake_interval);
-
- // During awake periods poll at fast rate. If wake alarm is set at fast
- // rate then just use the alarm; if wake alarm is set at slow rate then
- // poll at fast rate while awake and let alarm wake up at slow rate when
- // asleep.
-
- if (healthd_config.periodic_chores_interval_fast == -1)
- awake_poll_interval = -1;
- else
- awake_poll_interval =
- new_wake_interval == healthd_config.periodic_chores_interval_fast ?
- -1 : healthd_config.periodic_chores_interval_fast * 1000;
-}
-
-void healthd_dump_battery_state(int fd) {
- gBatteryMonitor->dumpState(fd);
- fsync(fd);
-}
-
-static void periodic_chores() {
- healthd_battery_update();
-}
-
-#define UEVENT_MSG_LEN 2048
-static void uevent_event(uint32_t /*epevents*/) {
- char msg[UEVENT_MSG_LEN+2];
- char *cp;
- int n;
-
- n = uevent_kernel_multicast_recv(uevent_fd, msg, UEVENT_MSG_LEN);
- if (n <= 0)
- return;
- if (n >= UEVENT_MSG_LEN) /* overflow -- discard */
- return;
-
- msg[n] = '\0';
- msg[n+1] = '\0';
- cp = msg;
-
- while (*cp) {
- if (!strcmp(cp, "SUBSYSTEM=" POWER_SUPPLY_SUBSYSTEM)) {
- healthd_battery_update();
- break;
- }
-
- /* advance to after the next \0 */
- while (*cp++)
- ;
- }
-}
-
-static void uevent_init(void) {
- uevent_fd = uevent_open_socket(64*1024, true);
-
- if (uevent_fd < 0) {
- KLOG_ERROR(LOG_TAG, "uevent_init: uevent_open_socket failed\n");
+ gHealth = IHealth::getService();
+ if (gHealth == nullptr) {
+ KLOG_WARNING(LOG_TAG, "unable to get HAL interface, using defaults\n");
return;
}
- fcntl(uevent_fd, F_SETFL, O_NONBLOCK);
- if (healthd_register_event(uevent_fd, uevent_event, EVENT_WAKEUP_FD))
- KLOG_ERROR(LOG_TAG,
- "register for uevent events failed\n");
+ HealthConfig halConfig;
+ convertToHealthConfig(config, halConfig);
+ gHealth->init(halConfig, [=] (const auto &halConfigOut) {
+ convertFromHealthConfig(halConfigOut, config);
+ // always redirect energy counter queries
+ config->energyCounter = healthd_board_get_energy_counter;
+ });
}
-static void wakealarm_event(uint32_t /*epevents*/) {
- unsigned long long wakeups;
+int healthd_board_battery_update(struct android::BatteryProperties *props) {
+ int logthis = 0;
- if (read(wakealarm_fd, &wakeups, sizeof(wakeups)) == -1) {
- KLOG_ERROR(LOG_TAG, "wakealarm_event: read wakealarm fd failed\n");
- return;
+ if (gHealth == nullptr) {
+ return logthis;
}
- periodic_chores();
+ HealthInfo info;
+ convertToHealthInfo(props, info);
+ gHealth->update(info,
+ [=, &logthis] (int32_t ret, const auto &infoOut) {
+ logthis = ret;
+ convertFromHealthInfo(infoOut, props);
+ });
+
+ return logthis;
}
-static void wakealarm_init(void) {
- wakealarm_fd = timerfd_create(CLOCK_BOOTTIME_ALARM, TFD_NONBLOCK);
- if (wakealarm_fd == -1) {
- KLOG_ERROR(LOG_TAG, "wakealarm_init: timerfd_create failed\n");
- return;
- }
+int main(int /*argc*/, char ** /*argv*/) {
- if (healthd_register_event(wakealarm_fd, wakealarm_event, EVENT_WAKEUP_FD))
- KLOG_ERROR(LOG_TAG,
- "Registration of wakealarm event failed\n");
-
- wakealarm_set_interval(healthd_config.periodic_chores_interval_fast);
-}
-
-static void healthd_mainloop(void) {
- int nevents = 0;
- while (1) {
- struct epoll_event events[eventct];
- int timeout = awake_poll_interval;
- int mode_timeout;
-
- /* Don't wait for first timer timeout to run periodic chores */
- if (!nevents)
- periodic_chores();
-
- healthd_mode_ops->heartbeat();
-
- mode_timeout = healthd_mode_ops->preparetowait();
- if (timeout < 0 || (mode_timeout > 0 && mode_timeout < timeout))
- timeout = mode_timeout;
- nevents = epoll_wait(epollfd, events, eventct, timeout);
- if (nevents == -1) {
- if (errno == EINTR)
- continue;
- KLOG_ERROR(LOG_TAG, "healthd_mainloop: epoll_wait failed\n");
- break;
- }
-
- for (int n = 0; n < nevents; ++n) {
- if (events[n].data.ptr)
- (*(void (*)(int))events[n].data.ptr)(events[n].events);
- }
- }
-
- return;
-}
-
-static int healthd_init() {
- epollfd = epoll_create(MAX_EPOLL_EVENTS);
- if (epollfd == -1) {
- KLOG_ERROR(LOG_TAG,
- "epoll_create failed; errno=%d\n",
- errno);
- return -1;
- }
-
- healthd_board_init(&healthd_config);
- healthd_mode_ops->init(&healthd_config);
- wakealarm_init();
- uevent_init();
- gBatteryMonitor = new BatteryMonitor();
- gBatteryMonitor->init(&healthd_config);
- return 0;
-}
-
-int main(int argc, char **argv) {
- int ch;
- int ret;
-
- klog_set_level(KLOG_LEVEL);
healthd_mode_ops = &android_ops;
- if (!strcmp(basename(argv[0]), "charger")) {
- healthd_mode_ops = &charger_ops;
- } else {
- while ((ch = getopt(argc, argv, "cr")) != -1) {
- switch (ch) {
- case 'c':
- healthd_mode_ops = &charger_ops;
- break;
- case 'r':
- healthd_mode_ops = &recovery_ops;
- break;
- case '?':
- default:
- KLOG_ERROR(LOG_TAG, "Unrecognized healthd option: %c\n",
- optopt);
- exit(1);
- }
- }
- }
-
- ret = healthd_init();
- if (ret) {
- KLOG_ERROR("Initialization failed, exiting\n");
- exit(2);
- }
-
- healthd_mainloop();
- KLOG_ERROR("Main loop terminated, exiting\n");
- return 3;
+ return healthd_main();
}
diff --git a/healthd/healthd_common.cpp b/healthd/healthd_common.cpp
new file mode 100644
index 0000000..6599919
--- /dev/null
+++ b/healthd/healthd_common.cpp
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2013 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 "healthd-common"
+#define KLOG_LEVEL 6
+
+#include <healthd/healthd.h>
+#include <healthd/BatteryMonitor.h>
+
+#include <errno.h>
+#include <libgen.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <batteryservice/BatteryService.h>
+#include <cutils/klog.h>
+#include <cutils/uevent.h>
+#include <sys/epoll.h>
+#include <sys/timerfd.h>
+#include <utils/Errors.h>
+
+using namespace android;
+
+#ifndef BOARD_PERIODIC_CHORES_INTERVAL_FAST
+ // Periodic chores fast interval in seconds
+ #define DEFAULT_PERIODIC_CHORES_INTERVAL_FAST (60 * 1)
+#else
+ #define DEFAULT_PERIODIC_CHORES_INTERVAL_FAST (BOARD_PERIODIC_CHORES_INTERVAL_FAST)
+#endif
+
+#ifndef BOARD_PERIODIC_CHORES_INTERVAL_SLOW
+ // Periodic chores fast interval in seconds
+ #define DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW (60 * 10)
+#else
+ #define DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW (BOARD_PERIODIC_CHORES_INTERVAL_SLOW)
+#endif
+
+static struct healthd_config healthd_config = {
+ .periodic_chores_interval_fast = DEFAULT_PERIODIC_CHORES_INTERVAL_FAST,
+ .periodic_chores_interval_slow = DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW,
+ .batteryStatusPath = String8(String8::kEmptyString),
+ .batteryHealthPath = String8(String8::kEmptyString),
+ .batteryPresentPath = String8(String8::kEmptyString),
+ .batteryCapacityPath = String8(String8::kEmptyString),
+ .batteryVoltagePath = String8(String8::kEmptyString),
+ .batteryTemperaturePath = String8(String8::kEmptyString),
+ .batteryTechnologyPath = String8(String8::kEmptyString),
+ .batteryCurrentNowPath = String8(String8::kEmptyString),
+ .batteryCurrentAvgPath = String8(String8::kEmptyString),
+ .batteryChargeCounterPath = String8(String8::kEmptyString),
+ .batteryFullChargePath = String8(String8::kEmptyString),
+ .batteryCycleCountPath = String8(String8::kEmptyString),
+ .energyCounter = NULL,
+ .boot_min_cap = 0,
+ .screen_on = NULL,
+};
+
+static int eventct;
+static int epollfd;
+
+#define POWER_SUPPLY_SUBSYSTEM "power_supply"
+
+// epoll_create() parameter is actually unused
+#define MAX_EPOLL_EVENTS 40
+static int uevent_fd;
+static int wakealarm_fd;
+
+// -1 for no epoll timeout
+static int awake_poll_interval = -1;
+
+static int wakealarm_wake_interval = DEFAULT_PERIODIC_CHORES_INTERVAL_FAST;
+
+static BatteryMonitor* gBatteryMonitor;
+
+struct healthd_mode_ops *healthd_mode_ops;
+
+int healthd_register_event(int fd, void (*handler)(uint32_t), EventWakeup wakeup) {
+ struct epoll_event ev;
+
+ ev.events = EPOLLIN;
+
+ if (wakeup == EVENT_WAKEUP_FD)
+ ev.events |= EPOLLWAKEUP;
+
+ ev.data.ptr = (void *)handler;
+ if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) == -1) {
+ KLOG_ERROR(LOG_TAG,
+ "epoll_ctl failed; errno=%d\n", errno);
+ return -1;
+ }
+
+ eventct++;
+ return 0;
+}
+
+static void wakealarm_set_interval(int interval) {
+ struct itimerspec itval;
+
+ if (wakealarm_fd == -1)
+ return;
+
+ wakealarm_wake_interval = interval;
+
+ if (interval == -1)
+ interval = 0;
+
+ itval.it_interval.tv_sec = interval;
+ itval.it_interval.tv_nsec = 0;
+ itval.it_value.tv_sec = interval;
+ itval.it_value.tv_nsec = 0;
+
+ if (timerfd_settime(wakealarm_fd, 0, &itval, NULL) == -1)
+ KLOG_ERROR(LOG_TAG, "wakealarm_set_interval: timerfd_settime failed\n");
+}
+
+status_t healthd_get_property(int id, struct BatteryProperty *val) {
+ return gBatteryMonitor->getProperty(id, val);
+}
+
+void healthd_battery_update(void) {
+ // Fast wake interval when on charger (watch for overheat);
+ // slow wake interval when on battery (watch for drained battery).
+
+ int new_wake_interval = gBatteryMonitor->update() ?
+ healthd_config.periodic_chores_interval_fast :
+ healthd_config.periodic_chores_interval_slow;
+
+ if (new_wake_interval != wakealarm_wake_interval)
+ wakealarm_set_interval(new_wake_interval);
+
+ // During awake periods poll at fast rate. If wake alarm is set at fast
+ // rate then just use the alarm; if wake alarm is set at slow rate then
+ // poll at fast rate while awake and let alarm wake up at slow rate when
+ // asleep.
+
+ if (healthd_config.periodic_chores_interval_fast == -1)
+ awake_poll_interval = -1;
+ else
+ awake_poll_interval =
+ new_wake_interval == healthd_config.periodic_chores_interval_fast ?
+ -1 : healthd_config.periodic_chores_interval_fast * 1000;
+}
+
+void healthd_dump_battery_state(int fd) {
+ gBatteryMonitor->dumpState(fd);
+ fsync(fd);
+}
+
+static void periodic_chores() {
+ healthd_battery_update();
+}
+
+#define UEVENT_MSG_LEN 2048
+static void uevent_event(uint32_t /*epevents*/) {
+ char msg[UEVENT_MSG_LEN+2];
+ char *cp;
+ int n;
+
+ n = uevent_kernel_multicast_recv(uevent_fd, msg, UEVENT_MSG_LEN);
+ if (n <= 0)
+ return;
+ if (n >= UEVENT_MSG_LEN) /* overflow -- discard */
+ return;
+
+ msg[n] = '\0';
+ msg[n+1] = '\0';
+ cp = msg;
+
+ while (*cp) {
+ if (!strcmp(cp, "SUBSYSTEM=" POWER_SUPPLY_SUBSYSTEM)) {
+ healthd_battery_update();
+ break;
+ }
+
+ /* advance to after the next \0 */
+ while (*cp++)
+ ;
+ }
+}
+
+static void uevent_init(void) {
+ uevent_fd = uevent_open_socket(64*1024, true);
+
+ if (uevent_fd < 0) {
+ KLOG_ERROR(LOG_TAG, "uevent_init: uevent_open_socket failed\n");
+ return;
+ }
+
+ fcntl(uevent_fd, F_SETFL, O_NONBLOCK);
+ if (healthd_register_event(uevent_fd, uevent_event, EVENT_WAKEUP_FD))
+ KLOG_ERROR(LOG_TAG,
+ "register for uevent events failed\n");
+}
+
+static void wakealarm_event(uint32_t /*epevents*/) {
+ unsigned long long wakeups;
+
+ if (read(wakealarm_fd, &wakeups, sizeof(wakeups)) == -1) {
+ KLOG_ERROR(LOG_TAG, "wakealarm_event: read wakealarm fd failed\n");
+ return;
+ }
+
+ periodic_chores();
+}
+
+static void wakealarm_init(void) {
+ wakealarm_fd = timerfd_create(CLOCK_BOOTTIME_ALARM, TFD_NONBLOCK);
+ if (wakealarm_fd == -1) {
+ KLOG_ERROR(LOG_TAG, "wakealarm_init: timerfd_create failed\n");
+ return;
+ }
+
+ if (healthd_register_event(wakealarm_fd, wakealarm_event, EVENT_WAKEUP_FD))
+ KLOG_ERROR(LOG_TAG,
+ "Registration of wakealarm event failed\n");
+
+ wakealarm_set_interval(healthd_config.periodic_chores_interval_fast);
+}
+
+static void healthd_mainloop(void) {
+ int nevents = 0;
+ while (1) {
+ struct epoll_event events[eventct];
+ int timeout = awake_poll_interval;
+ int mode_timeout;
+
+ /* Don't wait for first timer timeout to run periodic chores */
+ if (!nevents)
+ periodic_chores();
+
+ healthd_mode_ops->heartbeat();
+
+ mode_timeout = healthd_mode_ops->preparetowait();
+ if (timeout < 0 || (mode_timeout > 0 && mode_timeout < timeout))
+ timeout = mode_timeout;
+ nevents = epoll_wait(epollfd, events, eventct, timeout);
+ if (nevents == -1) {
+ if (errno == EINTR)
+ continue;
+ KLOG_ERROR(LOG_TAG, "healthd_mainloop: epoll_wait failed\n");
+ break;
+ }
+
+ for (int n = 0; n < nevents; ++n) {
+ if (events[n].data.ptr)
+ (*(void (*)(int))events[n].data.ptr)(events[n].events);
+ }
+ }
+
+ return;
+}
+
+static int healthd_init() {
+ epollfd = epoll_create(MAX_EPOLL_EVENTS);
+ if (epollfd == -1) {
+ KLOG_ERROR(LOG_TAG,
+ "epoll_create failed; errno=%d\n",
+ errno);
+ return -1;
+ }
+
+ healthd_board_init(&healthd_config);
+ healthd_mode_ops->init(&healthd_config);
+ wakealarm_init();
+ uevent_init();
+ gBatteryMonitor = new BatteryMonitor();
+ gBatteryMonitor->init(&healthd_config);
+ return 0;
+}
+
+int healthd_main() {
+ int ret;
+
+ klog_set_level(KLOG_LEVEL);
+
+ if (!healthd_mode_ops) {
+ KLOG_ERROR("healthd ops not set, exiting\n");
+ exit(1);
+ }
+
+ ret = healthd_init();
+ if (ret) {
+ KLOG_ERROR("Initialization failed, exiting\n");
+ exit(2);
+ }
+
+ healthd_mainloop();
+ KLOG_ERROR("Main loop terminated, exiting\n");
+ return 3;
+}
diff --git a/healthd/healthd_mode_android.cpp b/healthd/healthd_mode_android.cpp
index 323ef52..c612313 100644
--- a/healthd/healthd_mode_android.cpp
+++ b/healthd/healthd_mode_android.cpp
@@ -42,6 +42,9 @@
return -1;
}
+void healthd_mode_android_heartbeat(void) {
+}
+
static void binder_event(uint32_t /*epevents*/) {
IPCThreadState::self()->handlePolledCommands();
}
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index d7d1454..c76762d 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -40,12 +40,12 @@
#include <linux/netlink.h>
#include <batteryservice/BatteryService.h>
-#include <cutils/android_reboot.h>
#include <cutils/klog.h>
#include <cutils/misc.h>
#include <cutils/uevent.h>
#include <cutils/properties.h>
#include <minui/minui.h>
+#include <sys/reboot.h>
#ifdef CHARGER_ENABLE_SUSPEND
#include <suspend/autosuspend.h>
@@ -642,7 +642,7 @@
} else {
if (charger->batt_anim->cur_level >= charger->boot_min_cap) {
LOGW("[%" PRId64 "] rebooting\n", now);
- android_reboot(ANDROID_RB_RESTART, 0, 0);
+ reboot(RB_AUTOBOOT);
} else {
LOGV("[%" PRId64 "] ignore power-button press, battery level "
"less than minimum\n", now);
@@ -697,7 +697,7 @@
now, (int64_t)UNPLUGGED_SHUTDOWN_TIME, charger->next_pwr_check);
} else if (now >= charger->next_pwr_check) {
LOGW("[%" PRId64 "] shutting down\n", now);
- android_reboot(ANDROID_RB_POWEROFF, 0, 0);
+ reboot(RB_POWER_OFF);
} else {
/* otherwise we already have a shutdown timer scheduled */
}
diff --git a/include/private/fs_config.h b/include/private/fs_config.h
new file mode 100644
index 0000000..e9868a4
--- /dev/null
+++ b/include/private/fs_config.h
@@ -0,0 +1,4 @@
+// TODO(b/63135587) remove this file after the transitive dependency
+// from private/android_filesystem_config.h is resolved. All files that use
+// libcutils/include/private/fs_config.h should include the file directly, not
+// indirectly via private/android_filesystem_config.h.
diff --git a/init/Android.bp b/init/Android.bp
index 47578ea..efa5a02 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -18,7 +18,7 @@
name: "init_defaults",
cpp_std: "experimental",
sanitize: {
- misc_undefined: ["integer"],
+ misc_undefined: ["signed-integer-overflow"],
},
cppflags: [
"-DLOG_UEVENTS=0",
@@ -54,6 +54,9 @@
"-DSHUTDOWN_ZERO_TIMEOUT=1",
],
},
+ uml: {
+ cppflags: ["-DUSER_MODE_LINUX"],
+ }
},
}
@@ -67,10 +70,13 @@
"devices.cpp",
"firmware_handler.cpp",
"import_parser.cpp",
- "init_parser.cpp",
"log.cpp",
"parser.cpp",
+ "property_service.cpp",
+ "security.cpp",
+ "selinux.cpp",
"service.cpp",
+ "tokenizer.cpp",
"uevent_listener.cpp",
"ueventd_parser.cpp",
"util.cpp",
@@ -81,7 +87,12 @@
"libselinux",
"liblog",
"libprocessgroup",
+ "libfs_mgr",
],
+ include_dirs: [
+ "system/core/mkbootimg",
+ ],
+
}
/*
@@ -94,6 +105,10 @@
name: "init",
defaults: ["init_defaults"],
+ required: [
+ "e2fsdroid",
+ "mke2fs",
+ ],
static_executable: true,
srcs: [
"bootchart.cpp",
@@ -101,15 +116,11 @@
"init.cpp",
"init_first_stage.cpp",
"keychords.cpp",
- "property_service.cpp",
"reboot.cpp",
"signal_handler.cpp",
"ueventd.cpp",
"watchdogd.cpp",
],
- include_dirs: [
- "system/core/mkbootimg"
- ],
static_libs: [
"libinit",
"libbootloader_message",
@@ -149,9 +160,9 @@
defaults: ["init_defaults"],
srcs: [
"devices_test.cpp",
- "init_parser_test.cpp",
"init_test.cpp",
"property_service_test.cpp",
+ "result_test.cpp",
"service_test.cpp",
"ueventd_test.cpp",
"util_test.cpp",
@@ -159,9 +170,12 @@
shared_libs: [
"libbase",
"libcutils",
- "libselinux",
],
- static_libs: ["libinit"],
+ static_libs: [
+ "libinit",
+ "libselinux",
+ "libcrypto",
+ ],
}
subdirs = ["*"]
diff --git a/init/Android.mk b/init/Android.mk
index 325614e..23ada73 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -30,10 +30,6 @@
init_options += -DLOG_UEVENTS=0
-ifeq ($(TARGET_USER_MODE_LINUX), true)
- init_cflags += -DUSER_MODE_LINUX
-endif
-
init_cflags += \
$(init_options) \
-Wall -Wextra \
@@ -51,15 +47,12 @@
init.cpp \
init_first_stage.cpp \
keychords.cpp \
- property_service.cpp \
reboot.cpp \
signal_handler.cpp \
ueventd.cpp \
watchdogd.cpp \
LOCAL_MODULE:= init
-LOCAL_C_INCLUDES += \
- system/core/mkbootimg
LOCAL_FORCE_STATIC_EXECUTABLE := true
LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
@@ -77,6 +70,7 @@
libcutils \
libbase \
libc \
+ libseccomp_policy \
libselinux \
liblog \
libcrypto_utils \
@@ -89,11 +83,14 @@
libavb \
libkeyutils \
+LOCAL_REQUIRED_MODULES := \
+ e2fsdroid \
+ mke2fs \
+
# Create symlinks.
LOCAL_POST_INSTALL_CMD := $(hide) mkdir -p $(TARGET_ROOT_OUT)/sbin; \
ln -sf ../init $(TARGET_ROOT_OUT)/sbin/ueventd; \
ln -sf ../init $(TARGET_ROOT_OUT)/sbin/watchdogd
-LOCAL_SANITIZE := integer
-LOCAL_CLANG := true
+LOCAL_SANITIZE := signed-integer-overflow
include $(BUILD_EXECUTABLE)
diff --git a/init/README.md b/init/README.md
index 422fdad..0ea00fb 100644
--- a/init/README.md
+++ b/init/README.md
@@ -260,6 +260,18 @@
> Sets the child's /proc/self/oom\_score\_adj to the specified value,
which must range from -1000 to 1000.
+`memcg.swappiness <value>`
+> Sets the child's memory.swappiness to the specified value (only if memcg is mounted),
+ which must be equal or greater than 0.
+
+`memcg.soft_limit_in_bytes <value>`
+> Sets the child's memory.soft_limit_in_bytes to the specified value (only if memcg is mounted),
+ which must be equal or greater than 0.
+
+`memcg.limit_in_bytes <value>`
+> Sets the child's memory.limit_in_bytes to the specified value (only if memcg is mounted),
+ which must be equal or greater than 0.
+
`shutdown <shutdown_behavior>`
> Set shutdown behavior of the service process. When this is not specified,
the service is killed during shutdown process by using SIGTERM and SIGKILL.
@@ -435,6 +447,9 @@
`rmdir <path>`
> Calls rmdir(2) on the given path.
+`readahead <file|dir>`
+> Calls readahead(2) on the file or files within given directory.
+
`setprop <name> <value>`
> Set system property _name_ to _value_. Properties are expanded
within _value_.
diff --git a/init/action.cpp b/init/action.cpp
index 4ec5f17..60204a8 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -31,14 +31,13 @@
Command::Command(BuiltinFunction f, const std::vector<std::string>& args, int line)
: func_(f), args_(args), line_(line) {}
-int Command::InvokeFunc() const {
+Result<Success> Command::InvokeFunc() const {
std::vector<std::string> expanded_args;
expanded_args.resize(args_.size());
expanded_args[0] = args_[0];
for (std::size_t i = 1; i < args_.size(); ++i) {
if (!expand_props(args_[i], &expanded_args[i])) {
- LOG(ERROR) << args_[0] << ": cannot expand '" << args_[i] << "'";
- return -EINVAL;
+ return Error() << "cannot expand '" << args_[i] << "'";
}
}
@@ -54,19 +53,16 @@
const KeywordMap<BuiltinFunction>* Action::function_map_ = nullptr;
-bool Action::AddCommand(const std::vector<std::string>& args, int line, std::string* err) {
+Result<Success> Action::AddCommand(const std::vector<std::string>& args, int line) {
if (!function_map_) {
- *err = "no function map available";
- return false;
+ return Error() << "no function map available";
}
- auto function = function_map_->FindFunction(args, err);
- if (!function) {
- return false;
- }
+ auto function = function_map_->FindFunction(args);
+ if (!function) return Error() << function.error();
- AddCommand(function, args, line);
- return true;
+ AddCommand(*function, args, line);
+ return Success();
}
void Action::AddCommand(BuiltinFunction f, const std::vector<std::string>& args, int line) {
@@ -92,81 +88,85 @@
void Action::ExecuteCommand(const Command& command) const {
android::base::Timer t;
- int result = command.InvokeFunc();
-
+ auto result = command.InvokeFunc();
auto duration = t.duration();
+
+ // There are many legacy paths in rootdir/init.rc that will virtually never exist on a new
+ // device, such as '/sys/class/leds/jogball-backlight/brightness'. As of this writing, there
+ // are 198 such failures on bullhead. Instead of spamming the log reporting them, we do not
+ // report such failures unless we're running at the DEBUG log level.
+ bool report_failure = !result.has_value();
+ if (report_failure && android::base::GetMinimumLogSeverity() > android::base::DEBUG &&
+ result.error_errno() == ENOENT) {
+ report_failure = false;
+ }
+
// Any action longer than 50ms will be warned to user as slow operation
- if (duration > 50ms || android::base::GetMinimumLogSeverity() <= android::base::DEBUG) {
+ if (report_failure || duration > 50ms ||
+ android::base::GetMinimumLogSeverity() <= android::base::DEBUG) {
std::string trigger_name = BuildTriggersString();
std::string cmd_str = command.BuildCommandString();
LOG(INFO) << "Command '" << cmd_str << "' action=" << trigger_name << " (" << filename_
- << ":" << command.line() << ") returned " << result << " took "
- << duration.count() << "ms.";
+ << ":" << command.line() << ") took " << duration.count() << "ms and "
+ << (result ? "succeeded" : "failed: " + result.error_string());
}
}
-bool Action::ParsePropertyTrigger(const std::string& trigger, std::string* err) {
+Result<Success> Action::ParsePropertyTrigger(const std::string& trigger) {
const static std::string prop_str("property:");
std::string prop_name(trigger.substr(prop_str.length()));
size_t equal_pos = prop_name.find('=');
if (equal_pos == std::string::npos) {
- *err = "property trigger found without matching '='";
- return false;
+ return Error() << "property trigger found without matching '='";
}
std::string prop_value(prop_name.substr(equal_pos + 1));
prop_name.erase(equal_pos);
if (auto [it, inserted] = property_triggers_.emplace(prop_name, prop_value); !inserted) {
- *err = "multiple property triggers found for same property";
- return false;
+ return Error() << "multiple property triggers found for same property";
}
- return true;
+ return Success();
}
-bool Action::InitTriggers(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Action::InitTriggers(const std::vector<std::string>& args) {
const static std::string prop_str("property:");
for (std::size_t i = 0; i < args.size(); ++i) {
if (args[i].empty()) {
- *err = "empty trigger is not valid";
- return false;
+ return Error() << "empty trigger is not valid";
}
if (i % 2) {
if (args[i] != "&&") {
- *err = "&& is the only symbol allowed to concatenate actions";
- return false;
+ return Error() << "&& is the only symbol allowed to concatenate actions";
} else {
continue;
}
}
if (!args[i].compare(0, prop_str.length(), prop_str)) {
- if (!ParsePropertyTrigger(args[i], err)) {
- return false;
+ if (auto result = ParsePropertyTrigger(args[i]); !result) {
+ return result;
}
} else {
if (!event_trigger_.empty()) {
- *err = "multiple event triggers are not allowed";
- return false;
+ return Error() << "multiple event triggers are not allowed";
}
event_trigger_ = args[i];
}
}
- return true;
+ return Success();
}
-bool Action::InitSingleTrigger(const std::string& trigger) {
+Result<Success> Action::InitSingleTrigger(const std::string& trigger) {
std::vector<std::string> name_vector{trigger};
- std::string err;
- bool ret = InitTriggers(name_vector, &err);
- if (!ret) {
- LOG(ERROR) << "InitSingleTrigger failed due to: " << err;
+ if (auto result = InitTriggers(name_vector); !result) {
+ return Error() << "InitTriggers() failed: " << result.error();
}
- return ret;
+ return Success();
}
// This function checks that all property triggers are satisfied, that is
@@ -264,7 +264,8 @@
auto action = std::make_unique<Action>(true, "<Builtin Action>", 0);
std::vector<std::string> name_vector{name};
- if (!action->InitSingleTrigger(name)) {
+ if (auto result = action->InitSingleTrigger(name); !result) {
+ LOG(ERROR) << "Cannot queue BuiltinAction for " << name << ": " << result.error();
return;
}
@@ -333,25 +334,25 @@
current_command_ = 0;
}
-bool ActionParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
- int line, std::string* err) {
+Result<Success> ActionParser::ParseSection(std::vector<std::string>&& args,
+ const std::string& filename, int line) {
std::vector<std::string> triggers(args.begin() + 1, args.end());
if (triggers.size() < 1) {
- *err = "actions must have a trigger";
- return false;
+ return Error() << "Actions must have a trigger";
}
auto action = std::make_unique<Action>(false, filename, line);
- if (!action->InitTriggers(triggers, err)) {
- return false;
+
+ if (auto result = action->InitTriggers(triggers); !result) {
+ return Error() << "InitTriggers() failed: " << result.error();
}
action_ = std::move(action);
- return true;
+ return Success();
}
-bool ActionParser::ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) {
- return action_ ? action_->AddCommand(std::move(args), line, err) : false;
+Result<Success> ActionParser::ParseLineSection(std::vector<std::string>&& args, int line) {
+ return action_ ? action_->AddCommand(std::move(args), line) : Success();
}
void ActionParser::EndSection() {
diff --git a/init/action.h b/init/action.h
index ad15f3f..d977f82 100644
--- a/init/action.h
+++ b/init/action.h
@@ -24,8 +24,9 @@
#include <vector>
#include "builtins.h"
-#include "init_parser.h"
#include "keyword_map.h"
+#include "parser.h"
+#include "result.h"
namespace android {
namespace init {
@@ -34,7 +35,7 @@
public:
Command(BuiltinFunction f, const std::vector<std::string>& args, int line);
- int InvokeFunc() const;
+ Result<Success> InvokeFunc() const;
std::string BuildCommandString() const;
int line() const { return line_; }
@@ -53,10 +54,10 @@
public:
explicit Action(bool oneshot, const std::string& filename, int line);
- bool AddCommand(const std::vector<std::string>& args, int line, std::string* err);
+ Result<Success> AddCommand(const std::vector<std::string>& args, int line);
void AddCommand(BuiltinFunction f, const std::vector<std::string>& args, int line);
- bool InitTriggers(const std::vector<std::string>& args, std::string* err);
- bool InitSingleTrigger(const std::string& trigger);
+ Result<Success> InitTriggers(const std::vector<std::string>& args);
+ Result<Success> InitSingleTrigger(const std::string& trigger);
std::size_t NumCommands() const;
void ExecuteOneCommand(std::size_t command) const;
void ExecuteAllCommands() const;
@@ -78,7 +79,7 @@
void ExecuteCommand(const Command& command) const;
bool CheckPropertyTriggers(const std::string& name = "",
const std::string& value = "") const;
- bool ParsePropertyTrigger(const std::string& trigger, std::string* err);
+ Result<Success> ParsePropertyTrigger(const std::string& trigger);
std::map<std::string, std::string> property_triggers_;
std::string event_trigger_;
@@ -120,9 +121,9 @@
public:
ActionParser(ActionManager* action_manager)
: action_manager_(action_manager), action_(nullptr) {}
- bool ParseSection(std::vector<std::string>&& args, const std::string& filename, int line,
- std::string* err) override;
- bool ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) override;
+ Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
+ int line) override;
+ Result<Success> ParseLineSection(std::vector<std::string>&& args, int line) override;
void EndSection() override;
private:
diff --git a/init/bootchart.cpp b/init/bootchart.cpp
index 4727f92..ec84317 100644
--- a/init/bootchart.cpp
+++ b/init/bootchart.cpp
@@ -163,37 +163,37 @@
LOG(INFO) << "Bootcharting finished";
}
-static int do_bootchart_start() {
- // We don't care about the content, but we do care that /data/bootchart/enabled actually exists.
- std::string start;
- if (!android::base::ReadFileToString("/data/bootchart/enabled", &start)) {
- LOG(VERBOSE) << "Not bootcharting";
- return 0;
- }
+static Result<Success> do_bootchart_start() {
+ // We don't care about the content, but we do care that /data/bootchart/enabled actually exists.
+ std::string start;
+ if (!android::base::ReadFileToString("/data/bootchart/enabled", &start)) {
+ LOG(VERBOSE) << "Not bootcharting";
+ return Success();
+ }
- g_bootcharting_thread = new std::thread(bootchart_thread_main);
- return 0;
+ g_bootcharting_thread = new std::thread(bootchart_thread_main);
+ return Success();
}
-static int do_bootchart_stop() {
- if (!g_bootcharting_thread) return 0;
+static Result<Success> do_bootchart_stop() {
+ if (!g_bootcharting_thread) return Success();
- // Tell the worker thread it's time to quit.
- {
- std::lock_guard<std::mutex> lock(g_bootcharting_finished_mutex);
- g_bootcharting_finished = true;
- g_bootcharting_finished_cv.notify_one();
- }
+ // Tell the worker thread it's time to quit.
+ {
+ std::lock_guard<std::mutex> lock(g_bootcharting_finished_mutex);
+ g_bootcharting_finished = true;
+ g_bootcharting_finished_cv.notify_one();
+ }
- g_bootcharting_thread->join();
- delete g_bootcharting_thread;
- g_bootcharting_thread = nullptr;
- return 0;
+ g_bootcharting_thread->join();
+ delete g_bootcharting_thread;
+ g_bootcharting_thread = nullptr;
+ return Success();
}
-int do_bootchart(const std::vector<std::string>& args) {
- if (args[1] == "start") return do_bootchart_start();
- return do_bootchart_stop();
+Result<Success> do_bootchart(const std::vector<std::string>& args) {
+ if (args[1] == "start") return do_bootchart_start();
+ return do_bootchart_stop();
}
} // namespace init
diff --git a/init/bootchart.h b/init/bootchart.h
index e4f7b59..f614f71 100644
--- a/init/bootchart.h
+++ b/init/bootchart.h
@@ -20,10 +20,12 @@
#include <string>
#include <vector>
+#include "result.h"
+
namespace android {
namespace init {
-int do_bootchart(const std::vector<std::string>& args);
+Result<Success> do_bootchart(const std::vector<std::string>& args);
} // namespace init
} // namespace android
diff --git a/init/builtins.cpp b/init/builtins.cpp
index dfd7b73..54ccf09 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -19,6 +19,7 @@
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
+#include <fts.h>
#include <linux/loop.h>
#include <linux/module.h>
#include <mntent.h>
@@ -44,7 +45,9 @@
#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
#include <bootloader_message/bootloader_message.h>
#include <cutils/android_reboot.h>
#include <ext4_utils/ext4_crypt.h>
@@ -57,7 +60,7 @@
#include "action.h"
#include "bootchart.h"
#include "init.h"
-#include "init_parser.h"
+#include "parser.h"
#include "property_service.h"
#include "reboot.h"
#include "service.h"
@@ -66,6 +69,8 @@
using namespace std::literals::string_literals;
+using android::base::unique_fd;
+
#define chmod DO_NOT_USE_CHMOD_USE_FCHMODAT_SYMLINK_NOFOLLOW
namespace android {
@@ -73,128 +78,124 @@
static constexpr std::chrono::nanoseconds kCommandRetryTimeout = 5s;
-static int insmod(const char *filename, const char *options, int flags) {
- int fd = open(filename, O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
- if (fd == -1) {
- PLOG(ERROR) << "insmod: open(\"" << filename << "\") failed";
- return -1;
- }
- int rc = syscall(__NR_finit_module, fd, options, flags);
- if (rc == -1) {
- PLOG(ERROR) << "finit_module for \"" << filename << "\" failed";
- }
- close(fd);
- return rc;
-}
-
-static int __ifupdown(const char *interface, int up) {
- struct ifreq ifr;
- int s, ret;
-
- strlcpy(ifr.ifr_name, interface, IFNAMSIZ);
-
- s = socket(AF_INET, SOCK_DGRAM, 0);
- if (s < 0)
- return -1;
-
- ret = ioctl(s, SIOCGIFFLAGS, &ifr);
- if (ret < 0) {
- goto done;
- }
-
- if (up)
- ifr.ifr_flags |= IFF_UP;
- else
- ifr.ifr_flags &= ~IFF_UP;
-
- ret = ioctl(s, SIOCSIFFLAGS, &ifr);
-
-done:
- close(s);
- return ret;
-}
-
-static int reboot_into_recovery(const std::vector<std::string>& options) {
+static Result<Success> reboot_into_recovery(const std::vector<std::string>& options) {
std::string err;
if (!write_bootloader_message(options, &err)) {
- LOG(ERROR) << "failed to set bootloader message: " << err;
- return -1;
+ return Error() << "Failed to set bootloader message: " << err;
}
property_set("sys.powerctl", "reboot,recovery");
- return 0;
+ return Success();
}
-static int do_class_start(const std::vector<std::string>& args) {
- /* Starting a class does not start services
- * which are explicitly disabled. They must
- * be started individually.
- */
- ServiceManager::GetInstance().
- ForEachServiceInClass(args[1], [] (Service* s) { s->StartIfNotDisabled(); });
- return 0;
-}
-
-static int do_class_stop(const std::vector<std::string>& args) {
- ServiceManager::GetInstance().
- ForEachServiceInClass(args[1], [] (Service* s) { s->Stop(); });
- return 0;
-}
-
-static int do_class_reset(const std::vector<std::string>& args) {
- ServiceManager::GetInstance().
- ForEachServiceInClass(args[1], [] (Service* s) { s->Reset(); });
- return 0;
-}
-
-static int do_class_restart(const std::vector<std::string>& args) {
- ServiceManager::GetInstance().
- ForEachServiceInClass(args[1], [] (Service* s) { s->Restart(); });
- return 0;
-}
-
-static int do_domainname(const std::vector<std::string>& args) {
- std::string err;
- if (!WriteFile("/proc/sys/kernel/domainname", args[1], &err)) {
- LOG(ERROR) << err;
- return -1;
+template <typename F>
+static void ForEachServiceInClass(const std::string& classname, F function) {
+ for (const auto& service : ServiceList::GetInstance()) {
+ if (service->classnames().count(classname)) std::invoke(function, service);
}
- return 0;
}
-static int do_enable(const std::vector<std::string>& args) {
- Service* svc = ServiceManager::GetInstance().FindServiceByName(args[1]);
- if (!svc) {
- return -1;
+static Result<Success> do_class_start(const std::vector<std::string>& args) {
+ // Starting a class does not start services which are explicitly disabled.
+ // They must be started individually.
+ ForEachServiceInClass(args[1], &Service::StartIfNotDisabled);
+ return Success();
+}
+
+static Result<Success> do_class_stop(const std::vector<std::string>& args) {
+ ForEachServiceInClass(args[1], &Service::Stop);
+ return Success();
+}
+
+static Result<Success> do_class_reset(const std::vector<std::string>& args) {
+ ForEachServiceInClass(args[1], &Service::Reset);
+ return Success();
+}
+
+static Result<Success> do_class_restart(const std::vector<std::string>& args) {
+ ForEachServiceInClass(args[1], &Service::Restart);
+ return Success();
+}
+
+static Result<Success> do_domainname(const std::vector<std::string>& args) {
+ if (auto result = WriteFile("/proc/sys/kernel/domainname", args[1]); !result) {
+ return Error() << "Unable to write to /proc/sys/kernel/domainname: " << result.error();
}
- return svc->Enable();
+ return Success();
}
-static int do_exec(const std::vector<std::string>& args) {
- return ServiceManager::GetInstance().Exec(args) ? 0 : -1;
-}
+static Result<Success> do_enable(const std::vector<std::string>& args) {
+ Service* svc = ServiceList::GetInstance().FindService(args[1]);
+ if (!svc) return Error() << "Could not find service";
-static int do_exec_start(const std::vector<std::string>& args) {
- return ServiceManager::GetInstance().ExecStart(args[1]) ? 0 : -1;
-}
-
-static int do_export(const std::vector<std::string>& args) {
- return add_environment(args[1].c_str(), args[2].c_str());
-}
-
-static int do_hostname(const std::vector<std::string>& args) {
- std::string err;
- if (!WriteFile("/proc/sys/kernel/hostname", args[1], &err)) {
- LOG(ERROR) << err;
- return -1;
+ if (auto result = svc->Enable(); !result) {
+ return Error() << "Could not enable service: " << result.error();
}
- return 0;
+
+ return Success();
}
-static int do_ifup(const std::vector<std::string>& args) {
- return __ifupdown(args[1].c_str(), 1);
+static Result<Success> do_exec(const std::vector<std::string>& args) {
+ auto service = Service::MakeTemporaryOneshotService(args);
+ if (!service) {
+ return Error() << "Could not create exec service";
+ }
+ if (auto result = service->ExecStart(); !result) {
+ return Error() << "Could not start exec service: " << result.error();
+ }
+
+ ServiceList::GetInstance().AddService(std::move(service));
+ return Success();
}
-static int do_insmod(const std::vector<std::string>& args) {
+static Result<Success> do_exec_start(const std::vector<std::string>& args) {
+ Service* service = ServiceList::GetInstance().FindService(args[1]);
+ if (!service) {
+ return Error() << "Service not found";
+ }
+
+ if (auto result = service->ExecStart(); !result) {
+ return Error() << "Could not start exec service: " << result.error();
+ }
+
+ return Success();
+}
+
+static Result<Success> do_export(const std::vector<std::string>& args) {
+ if (setenv(args[1].c_str(), args[2].c_str(), 1) == -1) {
+ return ErrnoError() << "setenv() failed";
+ }
+ return Success();
+}
+
+static Result<Success> do_hostname(const std::vector<std::string>& args) {
+ if (auto result = WriteFile("/proc/sys/kernel/hostname", args[1]); !result) {
+ return Error() << "Unable to write to /proc/sys/kernel/hostname: " << result.error();
+ }
+ return Success();
+}
+
+static Result<Success> do_ifup(const std::vector<std::string>& args) {
+ struct ifreq ifr;
+
+ strlcpy(ifr.ifr_name, args[1].c_str(), IFNAMSIZ);
+
+ unique_fd s(TEMP_FAILURE_RETRY(socket(AF_INET, SOCK_DGRAM, 0)));
+ if (s < 0) return ErrnoError() << "opening socket failed";
+
+ if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0) {
+ return ErrnoError() << "ioctl(..., SIOCGIFFLAGS, ...) failed";
+ }
+
+ ifr.ifr_flags |= IFF_UP;
+
+ if (ioctl(s, SIOCSIFFLAGS, &ifr) < 0) {
+ return ErrnoError() << "ioctl(..., SIOCSIFFLAGS, ...) failed";
+ }
+
+ return Success();
+}
+
+static Result<Success> do_insmod(const std::vector<std::string>& args) {
int flags = 0;
auto it = args.begin() + 1;
@@ -205,53 +206,56 @@
std::string filename = *it++;
std::string options = android::base::Join(std::vector<std::string>(it, args.end()), ' ');
- return insmod(filename.c_str(), options.c_str(), flags);
+
+ unique_fd fd(TEMP_FAILURE_RETRY(open(filename.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
+ if (fd == -1) return ErrnoError() << "open(\"" << filename << "\") failed";
+
+ int rc = syscall(__NR_finit_module, fd.get(), options.c_str(), flags);
+ if (rc == -1) return ErrnoError() << "finit_module for \"" << filename << "\" failed";
+
+ return Success();
}
-static int do_mkdir(const std::vector<std::string>& args) {
+// mkdir <path> [mode] [owner] [group]
+static Result<Success> do_mkdir(const std::vector<std::string>& args) {
mode_t mode = 0755;
- int ret;
-
- /* mkdir <path> [mode] [owner] [group] */
-
if (args.size() >= 3) {
mode = std::strtoul(args[2].c_str(), 0, 8);
}
- ret = make_dir(args[1].c_str(), mode, sehandle);
- /* chmod in case the directory already exists */
- if (ret == -1 && errno == EEXIST) {
- ret = fchmodat(AT_FDCWD, args[1].c_str(), mode, AT_SYMLINK_NOFOLLOW);
- }
- if (ret == -1) {
- return -errno;
+ if (!make_dir(args[1], mode)) {
+ /* chmod in case the directory already exists */
+ if (errno == EEXIST) {
+ if (fchmodat(AT_FDCWD, args[1].c_str(), mode, AT_SYMLINK_NOFOLLOW) == -1) {
+ return ErrnoError() << "fchmodat() failed";
+ }
+ } else {
+ return ErrnoError() << "mkdir() failed";
+ }
}
if (args.size() >= 4) {
- uid_t uid;
- std::string decode_uid_err;
- if (!DecodeUid(args[3], &uid, &decode_uid_err)) {
- LOG(ERROR) << "Unable to find UID for '" << args[3] << "': " << decode_uid_err;
- return -1;
+ auto uid = DecodeUid(args[3]);
+ if (!uid) {
+ return Error() << "Unable to decode UID for '" << args[3] << "': " << uid.error();
}
- gid_t gid = -1;
+ Result<gid_t> gid = -1;
if (args.size() == 5) {
- if (!DecodeUid(args[4], &gid, &decode_uid_err)) {
- LOG(ERROR) << "Unable to find GID for '" << args[3] << "': " << decode_uid_err;
- return -1;
+ gid = DecodeUid(args[4]);
+ if (!gid) {
+ return Error() << "Unable to decode GID for '" << args[3] << "': " << gid.error();
}
}
- if (lchown(args[1].c_str(), uid, gid) == -1) {
- return -errno;
+ if (lchown(args[1].c_str(), *uid, *gid) == -1) {
+ return ErrnoError() << "lchown failed";
}
/* chown may have cleared S_ISUID and S_ISGID, chmod again */
if (mode & (S_ISUID | S_ISGID)) {
- ret = fchmodat(AT_FDCWD, args[1].c_str(), mode, AT_SYMLINK_NOFOLLOW);
- if (ret == -1) {
- return -errno;
+ if (fchmodat(AT_FDCWD, args[1].c_str(), mode, AT_SYMLINK_NOFOLLOW) == -1) {
+ return ErrnoError() << "fchmodat failed";
}
}
}
@@ -262,15 +266,18 @@
"--prompt_and_wipe_data",
"--reason=set_policy_failed:"s + args[1]};
reboot_into_recovery(options);
- return -1;
+ return Error() << "reboot into recovery failed";
}
}
- return 0;
+ return Success();
}
/* umount <path> */
-static int do_umount(const std::vector<std::string>& args) {
- return umount(args[1].c_str());
+static Result<Success> do_umount(const std::vector<std::string>& args) {
+ if (umount(args[1].c_str()) < 0) {
+ return ErrnoError() << "umount() failed";
+ }
+ return Success();
}
static struct {
@@ -298,16 +305,13 @@
#define DATA_MNT_POINT "/data"
/* mount <type> <device> <path> <flags ...> <options> */
-static int do_mount(const std::vector<std::string>& args) {
- char tmp[64];
- const char *source, *target, *system;
- const char *options = NULL;
+static Result<Success> do_mount(const std::vector<std::string>& args) {
+ const char* options = nullptr;
unsigned flags = 0;
- std::size_t na = 0;
- int n, i;
- int wait = 0;
+ bool wait = false;
- for (na = 4; na < args.size(); na++) {
+ for (size_t na = 4; na < args.size(); na++) {
+ size_t i;
for (i = 0; mount_flags[i].name; i++) {
if (!args[na].compare(mount_flags[i].name)) {
flags |= mount_flags[i].flag;
@@ -316,71 +320,54 @@
}
if (!mount_flags[i].name) {
- if (!args[na].compare("wait"))
- wait = 1;
- /* if our last argument isn't a flag, wolf it up as an option string */
- else if (na + 1 == args.size())
+ if (!args[na].compare("wait")) {
+ wait = true;
+ // If our last argument isn't a flag, wolf it up as an option string.
+ } else if (na + 1 == args.size()) {
options = args[na].c_str();
+ }
}
}
- system = args[1].c_str();
- source = args[2].c_str();
- target = args[3].c_str();
+ const char* system = args[1].c_str();
+ const char* source = args[2].c_str();
+ const char* target = args[3].c_str();
- if (!strncmp(source, "loop@", 5)) {
- int mode, loop, fd;
- struct loop_info info;
+ if (android::base::StartsWith(source, "loop@")) {
+ int mode = (flags & MS_RDONLY) ? O_RDONLY : O_RDWR;
+ unique_fd fd(TEMP_FAILURE_RETRY(open(source + 5, mode | O_CLOEXEC)));
+ if (fd < 0) return ErrnoError() << "open(" << source + 5 << ", " << mode << ") failed";
- mode = (flags & MS_RDONLY) ? O_RDONLY : O_RDWR;
- fd = open(source + 5, mode | O_CLOEXEC);
- if (fd < 0) {
- return -1;
- }
+ for (size_t n = 0;; n++) {
+ std::string tmp = android::base::StringPrintf("/dev/block/loop%zu", n);
+ unique_fd loop(TEMP_FAILURE_RETRY(open(tmp.c_str(), mode | O_CLOEXEC)));
+ if (loop < 0) return ErrnoError() << "open(" << tmp << ", " << mode << ") failed";
- for (n = 0; ; n++) {
- snprintf(tmp, sizeof(tmp), "/dev/block/loop%d", n);
- loop = open(tmp, mode | O_CLOEXEC);
- if (loop < 0) {
- close(fd);
- return -1;
- }
-
+ loop_info info;
/* if it is a blank loop device */
if (ioctl(loop, LOOP_GET_STATUS, &info) < 0 && errno == ENXIO) {
/* if it becomes our loop device */
- if (ioctl(loop, LOOP_SET_FD, fd) >= 0) {
- close(fd);
-
- if (mount(tmp, target, system, flags, options) < 0) {
+ if (ioctl(loop, LOOP_SET_FD, fd.get()) >= 0) {
+ if (mount(tmp.c_str(), target, system, flags, options) < 0) {
ioctl(loop, LOOP_CLR_FD, 0);
- close(loop);
- return -1;
+ return ErrnoError() << "mount() failed";
}
-
- close(loop);
- goto exit_success;
+ return Success();
}
}
-
- close(loop);
}
- close(fd);
- LOG(ERROR) << "out of loopback devices";
- return -1;
+ return Error() << "out of loopback devices";
} else {
if (wait)
wait_for_file(source, kCommandRetryTimeout);
if (mount(source, target, system, flags, options) < 0) {
- return -1;
+ return ErrnoError() << "mount() failed";
}
}
-exit_success:
- return 0;
-
+ return Success();
}
/* Imports .rc files from the specified paths. Default ones are applied if none is given.
@@ -388,21 +375,15 @@
* start_index: index of the first path in the args list
*/
static void import_late(const std::vector<std::string>& args, size_t start_index, size_t end_index) {
- Parser& parser = Parser::GetInstance();
+ auto& action_manager = ActionManager::GetInstance();
+ auto& service_list = ServiceList::GetInstance();
+ Parser parser = CreateParser(action_manager, service_list);
if (end_index <= start_index) {
// Fallbacks for partitions on which early mount isn't enabled.
- if (!parser.is_system_etc_init_loaded()) {
- parser.ParseConfig("/system/etc/init");
- parser.set_is_system_etc_init_loaded(true);
+ for (const auto& path : late_import_paths) {
+ parser.ParseConfig(path);
}
- if (!parser.is_vendor_etc_init_loaded()) {
- parser.ParseConfig("/vendor/etc/init");
- parser.set_is_vendor_etc_init_loaded(true);
- }
- if (!parser.is_odm_etc_init_loaded()) {
- parser.ParseConfig("/odm/etc/init");
- parser.set_is_odm_etc_init_loaded(true);
- }
+ late_import_paths.clear();
} else {
for (size_t i = start_index; i < end_index; ++i) {
parser.ParseConfig(args[i]);
@@ -418,9 +399,7 @@
*
* Call fs_mgr_mount_all() to mount the given fstab
*/
-static int mount_fstab(const char* fstabfile, int mount_mode) {
- int ret = -1;
-
+static Result<int> mount_fstab(const char* fstabfile, int mount_mode) {
/*
* Call fs_mgr_mount_all() to mount all filesystems. We fork(2) and
* do the call in the child to provide protection to the main init
@@ -438,9 +417,9 @@
}
if (WIFEXITED(status)) {
- ret = WEXITSTATUS(status);
+ return WEXITSTATUS(status);
} else {
- ret = -1;
+ return Error() << "child aborted";
}
} else if (pid == 0) {
/* child, call fs_mgr_mount_all() */
@@ -457,10 +436,8 @@
}
_exit(child_ret);
} else {
- /* fork failed, return an error */
- return -1;
+ return Error() << "fork() failed";
}
- return ret;
}
/* Queue event based on fs_mgr return code.
@@ -472,29 +449,33 @@
*
* return code is processed based on input code
*/
-static int queue_fs_event(int code) {
- int ret = code;
+static Result<Success> queue_fs_event(int code) {
if (code == FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION) {
ActionManager::GetInstance().QueueEventTrigger("encrypt");
+ return Success();
} else if (code == FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED) {
property_set("ro.crypto.state", "encrypted");
property_set("ro.crypto.type", "block");
ActionManager::GetInstance().QueueEventTrigger("defaultcrypto");
+ return Success();
} else if (code == FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) {
property_set("ro.crypto.state", "unencrypted");
ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
+ return Success();
} else if (code == FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE) {
property_set("ro.crypto.state", "unsupported");
ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
+ return Success();
} else if (code == FS_MGR_MNTALL_DEV_NEEDS_RECOVERY) {
/* Setup a wipe via recovery, and reboot into recovery */
PLOG(ERROR) << "fs_mgr_mount_all suggested recovery, so wiping data via recovery.";
const std::vector<std::string> options = {"--wipe_data", "--reason=fs_mgr_mount_all" };
- ret = reboot_into_recovery(options);
+ reboot_into_recovery(options);
+ return Error() << "reboot_into_recovery() failed";
/* If reboot worked, there is no return. */
} else if (code == FS_MGR_MNTALL_DEV_FILE_ENCRYPTED) {
if (e4crypt_install_keyring()) {
- return -1;
+ return Error() << "e4crypt_install_keyring() failed";
}
property_set("ro.crypto.state", "encrypted");
property_set("ro.crypto.type", "file");
@@ -502,12 +483,13 @@
// Although encrypted, we have device key, so we do not need to
// do anything different from the nonencrypted case.
ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
+ return Success();
} else if (code > 0) {
- PLOG(ERROR) << "fs_mgr_mount_all returned unexpected error " << code;
+ Error() << "fs_mgr_mount_all() returned unexpected error " << code;
}
/* else ... < 0: error */
- return ret;
+ return Error() << "Invalid code: " << code;
}
/* mount_all <fstab> [ <path> ]* [--<options>]*
@@ -515,7 +497,7 @@
* This function might request a reboot, in which case it will
* not return.
*/
-static int do_mount_all(const std::vector<std::string>& args) {
+static Result<Success> do_mount_all(const std::vector<std::string>& args) {
std::size_t na = 0;
bool import_rc = true;
bool queue_event = true;
@@ -540,7 +522,10 @@
std::string prop_name = "ro.boottime.init.mount_all."s + prop_post_fix;
android::base::Timer t;
- int ret = mount_fstab(fstabfile, mount_mode);
+ auto mount_fstab_return_code = mount_fstab(fstabfile, mount_mode);
+ if (!mount_fstab_return_code) {
+ return Error() << "mount_fstab() failed " << mount_fstab_return_code.error();
+ }
property_set(prop_name, std::to_string(t.duration().count()));
if (import_rc) {
@@ -551,13 +536,16 @@
if (queue_event) {
/* queue_fs_event will queue event based on mount_fstab return code
* and return processed return code*/
- ret = queue_fs_event(ret);
+ auto queue_fs_result = queue_fs_event(*mount_fstab_return_code);
+ if (!queue_fs_result) {
+ return Error() << "queue_fs_event() failed: " << queue_fs_result.error();
+ }
}
- return ret;
+ return Success();
}
-static int do_swapon_all(const std::vector<std::string>& args) {
+static Result<Success> do_swapon_all(const std::vector<std::string>& args) {
struct fstab *fstab;
int ret;
@@ -565,89 +553,110 @@
ret = fs_mgr_swapon_all(fstab);
fs_mgr_free_fstab(fstab);
- return ret;
+ if (ret != 0) return Error() << "fs_mgr_swapon_all() failed";
+ return Success();
}
-static int do_setprop(const std::vector<std::string>& args) {
+static Result<Success> do_setprop(const std::vector<std::string>& args) {
property_set(args[1], args[2]);
- return 0;
+ return Success();
}
-static int do_setrlimit(const std::vector<std::string>& args) {
- struct rlimit limit;
+static Result<Success> do_setrlimit(const std::vector<std::string>& args) {
int resource;
- if (android::base::ParseInt(args[1], &resource) &&
- android::base::ParseUint(args[2], &limit.rlim_cur) &&
- android::base::ParseUint(args[3], &limit.rlim_max)) {
- return setrlimit(resource, &limit);
+ if (!android::base::ParseInt(args[1], &resource)) {
+ return Error() << "unable to parse resource, " << args[1];
}
- LOG(WARNING) << "ignoring setrlimit " << args[1] << " " << args[2] << " " << args[3];
- return -1;
+
+ struct rlimit limit;
+ if (!android::base::ParseUint(args[2], &limit.rlim_cur)) {
+ return Error() << "unable to parse rlim_cur, " << args[2];
+ }
+ if (!android::base::ParseUint(args[3], &limit.rlim_max)) {
+ return Error() << "unable to parse rlim_max, " << args[3];
+ }
+
+ if (setrlimit(resource, &limit) == -1) {
+ return ErrnoError() << "setrlimit failed";
+ }
+ return Success();
}
-static int do_start(const std::vector<std::string>& args) {
- Service* svc = ServiceManager::GetInstance().FindServiceByName(args[1]);
- if (!svc) {
- LOG(ERROR) << "do_start: Service " << args[1] << " not found";
- return -1;
+static Result<Success> do_start(const std::vector<std::string>& args) {
+ Service* svc = ServiceList::GetInstance().FindService(args[1]);
+ if (!svc) return Error() << "service " << args[1] << " not found";
+ if (auto result = svc->Start(); !result) {
+ return Error() << "Could not start service: " << result.error();
}
- if (!svc->Start())
- return -1;
- return 0;
+ return Success();
}
-static int do_stop(const std::vector<std::string>& args) {
- Service* svc = ServiceManager::GetInstance().FindServiceByName(args[1]);
- if (!svc) {
- LOG(ERROR) << "do_stop: Service " << args[1] << " not found";
- return -1;
- }
+static Result<Success> do_stop(const std::vector<std::string>& args) {
+ Service* svc = ServiceList::GetInstance().FindService(args[1]);
+ if (!svc) return Error() << "service " << args[1] << " not found";
svc->Stop();
- return 0;
+ return Success();
}
-static int do_restart(const std::vector<std::string>& args) {
- Service* svc = ServiceManager::GetInstance().FindServiceByName(args[1]);
- if (!svc) {
- LOG(ERROR) << "do_restart: Service " << args[1] << " not found";
- return -1;
- }
+static Result<Success> do_restart(const std::vector<std::string>& args) {
+ Service* svc = ServiceList::GetInstance().FindService(args[1]);
+ if (!svc) return Error() << "service " << args[1] << " not found";
svc->Restart();
- return 0;
+ return Success();
}
-static int do_trigger(const std::vector<std::string>& args) {
+static Result<Success> do_trigger(const std::vector<std::string>& args) {
ActionManager::GetInstance().QueueEventTrigger(args[1]);
- return 0;
+ return Success();
}
-static int do_symlink(const std::vector<std::string>& args) {
- return symlink(args[1].c_str(), args[2].c_str());
-}
-
-static int do_rm(const std::vector<std::string>& args) {
- return unlink(args[1].c_str());
-}
-
-static int do_rmdir(const std::vector<std::string>& args) {
- return rmdir(args[1].c_str());
-}
-
-static int do_sysclktz(const std::vector<std::string>& args) {
- struct timezone tz = {};
- if (android::base::ParseInt(args[1], &tz.tz_minuteswest) && settimeofday(NULL, &tz) != -1) {
- return 0;
+static Result<Success> do_symlink(const std::vector<std::string>& args) {
+ if (symlink(args[1].c_str(), args[2].c_str()) < 0) {
+ // The symlink builtin is often used to create symlinks for older devices to be backwards
+ // compatible with new paths, therefore we skip reporting this error.
+ if (errno == EEXIST && android::base::GetMinimumLogSeverity() > android::base::DEBUG) {
+ return Success();
+ }
+ return ErrnoError() << "symlink() failed";
}
- return -1;
+ return Success();
}
-static int do_verity_load_state(const std::vector<std::string>& args) {
+static Result<Success> do_rm(const std::vector<std::string>& args) {
+ if (unlink(args[1].c_str()) < 0) {
+ return ErrnoError() << "unlink() failed";
+ }
+ return Success();
+}
+
+static Result<Success> do_rmdir(const std::vector<std::string>& args) {
+ if (rmdir(args[1].c_str()) < 0) {
+ return ErrnoError() << "rmdir() failed";
+ }
+ return Success();
+}
+
+static Result<Success> do_sysclktz(const std::vector<std::string>& args) {
+ struct timezone tz = {};
+ if (!android::base::ParseInt(args[1], &tz.tz_minuteswest)) {
+ return Error() << "Unable to parse mins_west_of_gmt";
+ }
+
+ if (settimeofday(nullptr, &tz) == -1) {
+ return ErrnoError() << "settimeofday() failed";
+ }
+ return Success();
+}
+
+static Result<Success> do_verity_load_state(const std::vector<std::string>& args) {
int mode = -1;
bool loaded = fs_mgr_load_verity_state(&mode);
if (loaded && mode != VERITY_MODE_DEFAULT) {
ActionManager::GetInstance().QueueEventTrigger("verity-logging");
}
- return loaded ? 0 : 1;
+ if (!loaded) return Error() << "Could not load verity state";
+
+ return Success();
}
static void verity_update_property(fstab_rec *fstab, const char *mount_point,
@@ -655,55 +664,113 @@
property_set("partition."s + mount_point + ".verified", std::to_string(mode));
}
-static int do_verity_update_state(const std::vector<std::string>& args) {
- return fs_mgr_update_verity_state(verity_update_property) ? 0 : 1;
+static Result<Success> do_verity_update_state(const std::vector<std::string>& args) {
+ if (!fs_mgr_update_verity_state(verity_update_property)) {
+ return Error() << "fs_mgr_update_verity_state() failed";
+ }
+ return Success();
}
-static int do_write(const std::vector<std::string>& args) {
- std::string err;
- if (!WriteFile(args[1], args[2], &err)) {
- LOG(ERROR) << err;
- return -1;
+static Result<Success> do_write(const std::vector<std::string>& args) {
+ if (auto result = WriteFile(args[1], args[2]); !result) {
+ return Error() << "Unable to write to file '" << args[1] << "': " << result.error();
}
- return 0;
+
+ return Success();
}
-static int do_copy(const std::vector<std::string>& args) {
- std::string data;
- std::string err;
- if (!ReadFile(args[1], &data, &err)) {
- LOG(ERROR) << err;
- return -1;
+static Result<Success> do_readahead(const std::vector<std::string>& args) {
+ struct stat sb;
+
+ if (stat(args[1].c_str(), &sb)) {
+ return ErrnoError() << "Error opening " << args[1];
}
- if (!WriteFile(args[2], data, &err)) {
- LOG(ERROR) << err;
- return -1;
+
+ // We will do readahead in a forked process in order not to block init
+ // since it may block while it reads the
+ // filesystem metadata needed to locate the requested blocks. This
+ // occurs frequently with ext[234] on large files using indirect blocks
+ // instead of extents, giving the appearance that the call blocks until
+ // the requested data has been read.
+ pid_t pid = fork();
+ if (pid == 0) {
+ android::base::Timer t;
+ if (S_ISREG(sb.st_mode)) {
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(args[1].c_str(), O_RDONLY)));
+ if (fd == -1) {
+ PLOG(ERROR) << "Error opening file: " << args[1];
+ _exit(EXIT_FAILURE);
+ }
+ if (readahead(fd, 0, std::numeric_limits<size_t>::max())) {
+ PLOG(ERROR) << "Error readahead file: " << args[1];
+ _exit(EXIT_FAILURE);
+ }
+ } else if (S_ISDIR(sb.st_mode)) {
+ char* paths[] = {const_cast<char*>(args[1].data()), nullptr};
+ std::unique_ptr<FTS, decltype(&fts_close)> fts(
+ fts_open(paths, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr), fts_close);
+ if (!fts) {
+ PLOG(ERROR) << "Error opening directory: " << args[1];
+ _exit(EXIT_FAILURE);
+ }
+ // Traverse the entire hierarchy and do readahead
+ for (FTSENT* ftsent = fts_read(fts.get()); ftsent != nullptr;
+ ftsent = fts_read(fts.get())) {
+ if (ftsent->fts_info & FTS_F) {
+ android::base::unique_fd fd(
+ TEMP_FAILURE_RETRY(open(ftsent->fts_accpath, O_RDONLY)));
+ if (fd == -1) {
+ PLOG(ERROR) << "Error opening file: " << args[1];
+ continue;
+ }
+ if (readahead(fd, 0, std::numeric_limits<size_t>::max())) {
+ PLOG(ERROR) << "Unable to readahead on file: " << ftsent->fts_accpath;
+ }
+ }
+ }
+ }
+ LOG(INFO) << "Readahead " << args[1] << " took " << t;
+ _exit(0);
+ } else if (pid < 0) {
+ return ErrnoError() << "Fork failed";
}
- return 0;
+ return Success();
}
-static int do_chown(const std::vector<std::string>& args) {
- uid_t uid;
- std::string decode_uid_err;
- if (!DecodeUid(args[1], &uid, &decode_uid_err)) {
- LOG(ERROR) << "Unable to find UID for '" << args[1] << "': " << decode_uid_err;
- return -1;
+static Result<Success> do_copy(const std::vector<std::string>& args) {
+ auto file_contents = ReadFile(args[1]);
+ if (!file_contents) {
+ return Error() << "Could not read input file '" << args[1] << "': " << file_contents.error();
+ }
+ if (auto result = WriteFile(args[2], *file_contents); !result) {
+ return Error() << "Could not write to output file '" << args[2] << "': " << result.error();
+ }
+
+ return Success();
+}
+
+static Result<Success> do_chown(const std::vector<std::string>& args) {
+ auto uid = DecodeUid(args[1]);
+ if (!uid) {
+ return Error() << "Unable to decode UID for '" << args[1] << "': " << uid.error();
}
// GID is optional and pushes the index of path out by one if specified.
const std::string& path = (args.size() == 4) ? args[3] : args[2];
- gid_t gid = -1;
+ Result<gid_t> gid = -1;
if (args.size() == 4) {
- if (!DecodeUid(args[2], &gid, &decode_uid_err)) {
- LOG(ERROR) << "Unable to find GID for '" << args[2] << "': " << decode_uid_err;
- return -1;
+ gid = DecodeUid(args[2]);
+ if (!gid) {
+ return Error() << "Unable to decode GID for '" << args[2] << "': " << gid.error();
}
}
- if (lchown(path.c_str(), uid, gid) == -1) return -errno;
+ if (lchown(path.c_str(), *uid, *gid) == -1) {
+ return ErrnoError() << "lchown() failed";
+ }
- return 0;
+ return Success();
}
static mode_t get_mode(const char *s) {
@@ -719,15 +786,15 @@
return mode;
}
-static int do_chmod(const std::vector<std::string>& args) {
+static Result<Success> do_chmod(const std::vector<std::string>& args) {
mode_t mode = get_mode(args[1].c_str());
if (fchmodat(AT_FDCWD, args[2].c_str(), mode, AT_SYMLINK_NOFOLLOW) < 0) {
- return -errno;
+ return ErrnoError() << "fchmodat() failed";
}
- return 0;
+ return Success();
}
-static int do_restorecon(const std::vector<std::string>& args) {
+static Result<Success> do_restorecon(const std::vector<std::string>& args) {
int ret = 0;
struct flag_type {const char* name; int value;};
@@ -744,8 +811,7 @@
for (size_t i = 1; i < args.size(); ++i) {
if (android::base::StartsWith(args[i], "--")) {
if (!in_flags) {
- LOG(ERROR) << "restorecon - flags must precede paths";
- return -1;
+ return Error() << "flags must precede paths";
}
bool found = false;
for (size_t j = 0; flags[j].name; ++j) {
@@ -756,26 +822,27 @@
}
}
if (!found) {
- LOG(ERROR) << "restorecon - bad flag " << args[i];
- return -1;
+ return Error() << "bad flag " << args[i];
}
} else {
in_flags = false;
if (selinux_android_restorecon(args[i].c_str(), flag) < 0) {
- ret = -errno;
+ ret = errno;
}
}
}
- return ret;
+
+ if (ret) return ErrnoError() << "selinux_android_restorecon() failed";
+ return Success();
}
-static int do_restorecon_recursive(const std::vector<std::string>& args) {
+static Result<Success> do_restorecon_recursive(const std::vector<std::string>& args) {
std::vector<std::string> non_const_args(args);
non_const_args.insert(std::next(non_const_args.begin()), "--recursive");
return do_restorecon(non_const_args);
}
-static int do_loglevel(const std::vector<std::string>& args) {
+static Result<Success> do_loglevel(const std::vector<std::string>& args) {
// TODO: support names instead/as well?
int log_level = -1;
android::base::ParseInt(args[1], &log_level);
@@ -790,88 +857,73 @@
case 1:
case 0: severity = android::base::FATAL; break;
default:
- LOG(ERROR) << "loglevel: invalid log level " << log_level;
- return -EINVAL;
+ return Error() << "invalid log level " << log_level;
}
android::base::SetMinimumLogSeverity(severity);
- return 0;
+ return Success();
}
-static int do_load_persist_props(const std::vector<std::string>& args) {
+static Result<Success> do_load_persist_props(const std::vector<std::string>& args) {
load_persist_props();
- return 0;
+ return Success();
}
-static int do_load_system_props(const std::vector<std::string>& args) {
+static Result<Success> do_load_system_props(const std::vector<std::string>& args) {
load_system_props();
- return 0;
+ return Success();
}
-static int do_wait(const std::vector<std::string>& args) {
- if (args.size() == 2) {
- return wait_for_file(args[1].c_str(), kCommandRetryTimeout);
- } else if (args.size() == 3) {
- int timeout;
- if (android::base::ParseInt(args[2], &timeout)) {
- return wait_for_file(args[1].c_str(), std::chrono::seconds(timeout));
+static Result<Success> do_wait(const std::vector<std::string>& args) {
+ auto timeout = kCommandRetryTimeout;
+ if (args.size() == 3) {
+ int timeout_int;
+ if (!android::base::ParseInt(args[2], &timeout_int)) {
+ return Error() << "failed to parse timeout";
}
+ timeout = std::chrono::seconds(timeout_int);
}
- return -1;
+
+ if (wait_for_file(args[1].c_str(), timeout) != 0) {
+ return Error() << "wait_for_file() failed";
+ }
+
+ return Success();
}
-static int do_wait_for_prop(const std::vector<std::string>& args) {
+static Result<Success> do_wait_for_prop(const std::vector<std::string>& args) {
const char* name = args[1].c_str();
const char* value = args[2].c_str();
size_t value_len = strlen(value);
if (!is_legal_property_name(name)) {
- LOG(ERROR) << "do_wait_for_prop(\"" << name << "\", \"" << value
- << "\") failed: bad name";
- return -1;
+ return Error() << "is_legal_property_name(" << name << ") failed";
}
if (value_len >= PROP_VALUE_MAX) {
- LOG(ERROR) << "do_wait_for_prop(\"" << name << "\", \"" << value
- << "\") failed: value too long";
- return -1;
+ return Error() << "value too long";
}
if (!start_waiting_for_property(name, value)) {
- LOG(ERROR) << "do_wait_for_prop(\"" << name << "\", \"" << value
- << "\") failed: init already in waiting";
- return -1;
+ return Error() << "already waiting for a property";
}
- return 0;
-}
-
-/*
- * Callback to make a directory from the ext4 code
- */
-static int do_installkeys_ensure_dir_exists(const char* dir) {
- if (make_dir(dir, 0700, sehandle) && errno != EEXIST) {
- return -1;
- }
-
- return 0;
+ return Success();
}
static bool is_file_crypto() {
return android::base::GetProperty("ro.crypto.type", "") == "file";
}
-static int do_installkey(const std::vector<std::string>& args) {
- if (!is_file_crypto()) {
- return 0;
- }
+static Result<Success> do_installkey(const std::vector<std::string>& args) {
+ if (!is_file_crypto()) return Success();
+
auto unencrypted_dir = args[1] + e4crypt_unencrypted_folder;
- if (do_installkeys_ensure_dir_exists(unencrypted_dir.c_str())) {
- PLOG(ERROR) << "Failed to create " << unencrypted_dir;
- return -1;
+ if (!make_dir(unencrypted_dir, 0700) && errno != EEXIST) {
+ return ErrnoError() << "Failed to create " << unencrypted_dir;
}
std::vector<std::string> exec_args = {"exec", "/system/bin/vdc", "--wait", "cryptfs",
"enablefilecrypto"};
return do_exec(exec_args);
}
-static int do_init_user0(const std::vector<std::string>& args) {
+static Result<Success> do_init_user0(const std::vector<std::string>& args) {
std::vector<std::string> exec_args = {"exec", "/system/bin/vdc", "--wait", "cryptfs",
"init_user0"};
return do_exec(exec_args);
@@ -906,6 +958,7 @@
{"mount_all", {1, kMax, do_mount_all}},
{"mount", {3, kMax, do_mount}},
{"umount", {1, 1, do_umount}},
+ {"readahead", {1, 1, do_readahead}},
{"restart", {1, 1, do_restart}},
{"restorecon", {1, kMax, do_restorecon}},
{"restorecon_recursive", {1, kMax, do_restorecon_recursive}},
diff --git a/init/builtins.h b/init/builtins.h
index b110f61..f66ae19 100644
--- a/init/builtins.h
+++ b/init/builtins.h
@@ -23,11 +23,12 @@
#include <vector>
#include "keyword_map.h"
+#include "result.h"
namespace android {
namespace init {
-using BuiltinFunction = std::function<int(const std::vector<std::string>&)>;
+using BuiltinFunction = std::function<Result<Success>(const std::vector<std::string>&)>;
class BuiltinFunctionMap : public KeywordMap<BuiltinFunction> {
public:
BuiltinFunctionMap() {}
diff --git a/init/descriptors.cpp b/init/descriptors.cpp
index 0cb639a..6265687 100644
--- a/init/descriptors.cpp
+++ b/init/descriptors.cpp
@@ -28,7 +28,6 @@
#include <cutils/android_get_control_file.h>
#include <cutils/sockets.h>
-#include "init.h"
#include "util.h"
namespace android {
@@ -62,7 +61,7 @@
[] (char& c) { c = isalnum(c) ? c : '_'; });
std::string val = std::to_string(fd);
- add_environment(publishedName.c_str(), val.c_str());
+ setenv(publishedName.c_str(), val.c_str(), 1);
// make sure we don't close on exec
fcntl(fd, F_SETFD, 0);
@@ -86,8 +85,7 @@
int flags =
((types[0] == "stream" ? SOCK_STREAM : (types[0] == "dgram" ? SOCK_DGRAM : SOCK_SEQPACKET)));
bool passcred = types.size() > 1 && types[1] == "passcred";
- return CreateSocket(name().c_str(), flags, passcred, perm(), uid(), gid(), context.c_str(),
- sehandle);
+ return CreateSocket(name().c_str(), flags, passcred, perm(), uid(), gid(), context.c_str());
}
const std::string SocketInfo::key() const {
diff --git a/init/devices.cpp b/init/devices.cpp
index 13cf991..af6b50a 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -30,6 +30,7 @@
#include <selinux/android.h>
#include <selinux/selinux.h>
+#include "selinux.h"
#include "ueventd.h"
#include "util.h"
@@ -224,18 +225,13 @@
auto[mode, uid, gid] = GetDevicePermissions(path, links);
mode |= (block ? S_IFBLK : S_IFCHR);
- char* secontext = nullptr;
- if (sehandle_) {
- std::vector<const char*> c_links;
- for (const auto& link : links) {
- c_links.emplace_back(link.c_str());
- }
- c_links.emplace_back(nullptr);
- if (selabel_lookup_best_match(sehandle_, &secontext, path.c_str(), &c_links[0], mode)) {
- PLOG(ERROR) << "Device '" << path << "' not created; cannot find SELinux label";
- return;
- }
- setfscreatecon(secontext);
+ std::string secontext;
+ if (!SelabelLookupFileContextBestMatch(path, links, mode, &secontext)) {
+ PLOG(ERROR) << "Device '" << path << "' not created; cannot find SELinux label";
+ return;
+ }
+ if (!secontext.empty()) {
+ setfscreatecon(secontext.c_str());
}
dev_t dev = makedev(major, minor);
@@ -250,7 +246,7 @@
}
/* If the node already exists update its SELinux label to handle cases when
* it was created with the wrong context during coldboot procedure. */
- if (mknod(path.c_str(), mode, dev) && (errno == EEXIST) && secontext) {
+ if (mknod(path.c_str(), mode, dev) && (errno == EEXIST) && !secontext.empty()) {
char* fcon = nullptr;
int rc = lgetfilecon(path.c_str(), &fcon);
if (rc < 0) {
@@ -258,10 +254,10 @@
goto out;
}
- bool different = strcmp(fcon, secontext) != 0;
+ bool different = fcon != secontext;
freecon(fcon);
- if (different && lsetfilecon(path.c_str(), secontext)) {
+ if (different && lsetfilecon(path.c_str(), secontext.c_str())) {
PLOG(ERROR) << "Cannot set '" << secontext << "' SELinux label on '" << path
<< "' device";
}
@@ -273,8 +269,7 @@
PLOG(FATAL) << "setegid(AID_ROOT) failed";
}
- if (secontext) {
- freecon(secontext);
+ if (!secontext.empty()) {
setfscreatecon(nullptr);
}
}
@@ -351,7 +346,7 @@
if (action == "add") {
MakeDevice(devpath, block, major, minor, links);
for (const auto& link : links) {
- if (mkdir_recursive(Dirname(link), 0755, sehandle_)) {
+ if (!mkdir_recursive(Dirname(link), 0755)) {
PLOG(ERROR) << "Failed to create directory " << Dirname(link);
}
@@ -391,31 +386,29 @@
if (StartsWith(uevent.path, "/devices")) {
links = GetBlockDeviceSymlinks(uevent);
}
- } else if (StartsWith(uevent.subsystem, "usb")) {
- if (uevent.subsystem == "usb") {
- if (!uevent.device_name.empty()) {
- devpath = "/dev/" + uevent.device_name;
- } else {
- // This imitates the file system that would be created
- // if we were using devfs instead.
- // Minors are broken up into groups of 128, starting at "001"
- int bus_id = uevent.minor / 128 + 1;
- int device_id = uevent.minor % 128 + 1;
- devpath = StringPrintf("/dev/bus/usb/%03d/%03d", bus_id, device_id);
- }
- } else {
- // ignore other USB events
- return;
- }
} else if (const auto subsystem =
std::find(subsystems_.cbegin(), subsystems_.cend(), uevent.subsystem);
subsystem != subsystems_.cend()) {
devpath = subsystem->ParseDevPath(uevent);
+ } else if (uevent.subsystem == "usb") {
+ if (!uevent.device_name.empty()) {
+ devpath = "/dev/" + uevent.device_name;
+ } else {
+ // This imitates the file system that would be created
+ // if we were using devfs instead.
+ // Minors are broken up into groups of 128, starting at "001"
+ int bus_id = uevent.minor / 128 + 1;
+ int device_id = uevent.minor % 128 + 1;
+ devpath = StringPrintf("/dev/bus/usb/%03d/%03d", bus_id, device_id);
+ }
+ } else if (StartsWith(uevent.subsystem, "usb")) {
+ // ignore other USB events
+ return;
} else {
devpath = "/dev/" + Basename(uevent.path);
}
- mkdir_recursive(Dirname(devpath), 0755, sehandle_);
+ mkdir_recursive(Dirname(devpath), 0755);
HandleDevice(uevent.action, devpath, block, uevent.major, uevent.minor, links);
}
@@ -426,7 +419,6 @@
: dev_permissions_(std::move(dev_permissions)),
sysfs_permissions_(std::move(sysfs_permissions)),
subsystems_(std::move(subsystems)),
- sehandle_(selinux_android_file_context_handle()),
skip_restorecon_(skip_restorecon),
sysfs_mount_point_("/sys") {}
diff --git a/init/devices.h b/init/devices.h
index c64f5fb..1f8f1e8 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -72,6 +72,7 @@
friend class SubsystemParser;
Subsystem() {}
+ Subsystem(std::string name) : name_(std::move(name)) {}
// Returns the full path for a uevent of a device that is a member of this subsystem,
// according to the rules parsed from ueventd.rc
@@ -124,7 +125,6 @@
std::vector<Permissions> dev_permissions_;
std::vector<SysfsPermissions> sysfs_permissions_;
std::vector<Subsystem> subsystems_;
- selabel_handle* sehandle_;
bool skip_restorecon_;
std::string sysfs_mount_point_;
};
diff --git a/init/devices_test.cpp b/init/devices_test.cpp
index ac4ab9b..eba00cb 100644
--- a/init/devices_test.cpp
+++ b/init/devices_test.cpp
@@ -35,13 +35,13 @@
device_handler_.sysfs_mount_point_ = fake_sys_root.path;
std::string platform_device_dir = fake_sys_root.path + platform_device;
- mkdir_recursive(platform_device_dir, 0777, nullptr);
+ mkdir_recursive(platform_device_dir, 0777);
std::string platform_bus = fake_sys_root.path + "/bus/platform"s;
- mkdir_recursive(platform_bus, 0777, nullptr);
+ mkdir_recursive(platform_bus, 0777);
symlink(platform_bus.c_str(), (platform_device_dir + "/subsystem").c_str());
- mkdir_recursive(android::base::Dirname(fake_sys_root.path + uevent.path), 0777, nullptr);
+ mkdir_recursive(android::base::Dirname(fake_sys_root.path + uevent.path), 0777);
std::vector<std::string> result;
result = device_handler_.GetBlockDeviceSymlinks(uevent);
diff --git a/init/import_parser.cpp b/init/import_parser.cpp
index b9fa2ce..e335fd1 100644
--- a/init/import_parser.cpp
+++ b/init/import_parser.cpp
@@ -23,24 +23,22 @@
namespace android {
namespace init {
-bool ImportParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
- int line, std::string* err) {
+Result<Success> ImportParser::ParseSection(std::vector<std::string>&& args,
+ const std::string& filename, int line) {
if (args.size() != 2) {
- *err = "single argument needed for import\n";
- return false;
+ return Error() << "single argument needed for import\n";
}
std::string conf_file;
bool ret = expand_props(args[1], &conf_file);
if (!ret) {
- *err = "error while expanding import";
- return false;
+ return Error() << "error while expanding import";
}
LOG(INFO) << "Added '" << conf_file << "' to import list";
if (filename_.empty()) filename_ = filename;
imports_.emplace_back(std::move(conf_file), line);
- return true;
+ return Success();
}
void ImportParser::EndFile() {
diff --git a/init/import_parser.h b/init/import_parser.h
index b774c57..5a2f894 100644
--- a/init/import_parser.h
+++ b/init/import_parser.h
@@ -17,19 +17,19 @@
#ifndef _INIT_IMPORT_PARSER_H
#define _INIT_IMPORT_PARSER_H
-#include "init_parser.h"
-
#include <string>
#include <vector>
+#include "parser.h"
+
namespace android {
namespace init {
class ImportParser : public SectionParser {
public:
ImportParser(Parser* parser) : parser_(parser) {}
- bool ParseSection(std::vector<std::string>&& args, const std::string& filename, int line,
- std::string* err) override;
+ Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
+ int line) override;
void EndFile() override;
private:
diff --git a/init/init.cpp b/init/init.cpp
index b0b2e49..e1bd3a2 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -16,26 +16,17 @@
#include "init.h"
-#include <ctype.h>
#include <dirent.h>
-#include <errno.h>
#include <fcntl.h>
-#include <inttypes.h>
-#include <libgen.h>
#include <paths.h>
+#include <seccomp_policy.h>
#include <signal.h>
-#include <stdarg.h>
-#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/mount.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <sys/types.h>
-#include <sys/un.h>
-#include <sys/wait.h>
#include <unistd.h>
#include <android-base/chrono_utils.h>
@@ -43,27 +34,23 @@
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
+#include <cutils/android_reboot.h>
#include <keyutils.h>
#include <libavb/libavb.h>
#include <private/android_filesystem_config.h>
#include <selinux/android.h>
-#include <selinux/selinux.h>
-#include <fstream>
#include <memory>
-#include <vector>
+#include <optional>
-#include "action.h"
-#include "bootchart.h"
#include "import_parser.h"
#include "init_first_stage.h"
-#include "init_parser.h"
#include "keychords.h"
#include "log.h"
#include "property_service.h"
#include "reboot.h"
-#include "service.h"
+#include "security.h"
+#include "selinux.h"
#include "signal_handler.h"
#include "ueventd.h"
#include "util.h"
@@ -78,17 +65,11 @@
namespace android {
namespace init {
-struct selabel_handle *sehandle;
-struct selabel_handle *sehandle_prop;
-
static int property_triggers_enabled = 0;
static char qemu[32];
std::string default_console = "/dev/console";
-static time_t process_needs_restart_at;
-
-const char *ENV[32];
static int epoll_fd = -1;
@@ -97,11 +78,43 @@
static std::string wait_prop_value;
static bool shutting_down;
+std::vector<std::string> late_import_paths;
+
void DumpState() {
- ServiceManager::GetInstance().DumpState();
+ ServiceList::GetInstance().DumpState();
ActionManager::GetInstance().DumpState();
}
+Parser CreateParser(ActionManager& action_manager, ServiceList& service_list) {
+ Parser parser;
+
+ parser.AddSectionParser("service", std::make_unique<ServiceParser>(&service_list));
+ parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager));
+ parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
+
+ return parser;
+}
+
+static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
+ Parser parser = CreateParser(action_manager, service_list);
+
+ std::string bootscript = GetProperty("ro.boot.init_rc", "");
+ if (bootscript.empty()) {
+ parser.ParseConfig("/init.rc");
+ if (!parser.ParseConfig("/system/etc/init")) {
+ late_import_paths.emplace_back("/system/etc/init");
+ }
+ if (!parser.ParseConfig("/vendor/etc/init")) {
+ late_import_paths.emplace_back("/vendor/etc/init");
+ }
+ if (!parser.ParseConfig("/odm/etc/init")) {
+ late_import_paths.emplace_back("/odm/etc/init");
+ }
+ } else {
+ parser.ParseConfig(bootscript);
+ }
+}
+
void register_epoll_handler(int fd, void (*fn)()) {
epoll_event ev;
ev.events = EPOLLIN;
@@ -111,38 +124,6 @@
}
}
-/* add_environment - add "key=value" to the current environment */
-int add_environment(const char *key, const char *val)
-{
- size_t n;
- size_t key_len = strlen(key);
-
- /* The last environment entry is reserved to terminate the list */
- for (n = 0; n < (arraysize(ENV) - 1); n++) {
-
- /* Delete any existing entry for this key */
- if (ENV[n] != NULL) {
- size_t entry_key_len = strcspn(ENV[n], "=");
- if ((entry_key_len == key_len) && (strncmp(ENV[n], key, entry_key_len) == 0)) {
- free((char*)ENV[n]);
- ENV[n] = NULL;
- }
- }
-
- /* Add entry if a free slot is available */
- if (ENV[n] == NULL) {
- char* entry;
- asprintf(&entry, "%s=%s", key, val);
- ENV[n] = entry;
- return 0;
- }
- }
-
- LOG(ERROR) << "No env. room to store: '" << key << "':'" << val << "'";
-
- return -1;
-}
-
bool start_waiting_for_property(const char *name, const char *value)
{
if (waiting_for_prop) {
@@ -189,16 +170,25 @@
}
}
-static void restart_processes()
-{
- process_needs_restart_at = 0;
- ServiceManager::GetInstance().ForEachServiceWithFlags(SVC_RESTARTING, [](Service* s) {
- s->RestartIfNeeded(&process_needs_restart_at);
- });
+static std::optional<boot_clock::time_point> RestartProcesses() {
+ std::optional<boot_clock::time_point> next_process_restart_time;
+ for (const auto& s : ServiceList::GetInstance()) {
+ if (!(s->flags() & SVC_RESTARTING)) continue;
+
+ auto restart_time = s->time_started() + 5s;
+ if (boot_clock::now() > restart_time) {
+ s->Start();
+ } else {
+ if (!next_process_restart_time || restart_time < *next_process_restart_time) {
+ next_process_restart_time = restart_time;
+ }
+ }
+ }
+ return next_process_restart_time;
}
void handle_control_message(const std::string& msg, const std::string& name) {
- Service* svc = ServiceManager::GetInstance().FindServiceByName(name);
+ Service* svc = ServiceList::GetInstance().FindService(name);
if (svc == nullptr) {
LOG(ERROR) << "no such service '" << name << "'";
return;
@@ -215,7 +205,7 @@
}
}
-static int wait_for_coldboot_done_action(const std::vector<std::string>& args) {
+static Result<Success> wait_for_coldboot_done_action(const std::vector<std::string>& args) {
Timer t;
LOG(VERBOSE) << "Waiting for " COLDBOOT_DONE "...";
@@ -229,236 +219,24 @@
// because any build that slow isn't likely to boot at all, and we'd
// rather any test lab devices fail back to the bootloader.
if (wait_for_file(COLDBOOT_DONE, 60s) < 0) {
- LOG(ERROR) << "Timed out waiting for " COLDBOOT_DONE;
- panic();
+ LOG(FATAL) << "Timed out waiting for " COLDBOOT_DONE;
}
property_set("ro.boottime.init.cold_boot_wait", std::to_string(t.duration().count()));
- return 0;
+ return Success();
}
-/*
- * Writes 512 bytes of output from Hardware RNG (/dev/hw_random, backed
- * by Linux kernel's hw_random framework) into Linux RNG's via /dev/urandom.
- * Does nothing if Hardware RNG is not present.
- *
- * Since we don't yet trust the quality of Hardware RNG, these bytes are not
- * mixed into the primary pool of Linux RNG and the entropy estimate is left
- * unmodified.
- *
- * If the HW RNG device /dev/hw_random is present, we require that at least
- * 512 bytes read from it are written into Linux RNG. QA is expected to catch
- * devices/configurations where these I/O operations are blocking for a long
- * time. We do not reboot or halt on failures, as this is a best-effort
- * attempt.
- */
-static int mix_hwrng_into_linux_rng_action(const std::vector<std::string>& args)
-{
- int result = -1;
- int hwrandom_fd = -1;
- int urandom_fd = -1;
- char buf[512];
- ssize_t chunk_size;
- size_t total_bytes_written = 0;
-
- hwrandom_fd = TEMP_FAILURE_RETRY(
- open("/dev/hw_random", O_RDONLY | O_NOFOLLOW | O_CLOEXEC));
- if (hwrandom_fd == -1) {
- if (errno == ENOENT) {
- LOG(ERROR) << "/dev/hw_random not found";
- // It's not an error to not have a Hardware RNG.
- result = 0;
- } else {
- PLOG(ERROR) << "Failed to open /dev/hw_random";
- }
- goto ret;
- }
-
- urandom_fd = TEMP_FAILURE_RETRY(
- open("/dev/urandom", O_WRONLY | O_NOFOLLOW | O_CLOEXEC));
- if (urandom_fd == -1) {
- PLOG(ERROR) << "Failed to open /dev/urandom";
- goto ret;
- }
-
- while (total_bytes_written < sizeof(buf)) {
- chunk_size = TEMP_FAILURE_RETRY(
- read(hwrandom_fd, buf, sizeof(buf) - total_bytes_written));
- if (chunk_size == -1) {
- PLOG(ERROR) << "Failed to read from /dev/hw_random";
- goto ret;
- } else if (chunk_size == 0) {
- LOG(ERROR) << "Failed to read from /dev/hw_random: EOF";
- goto ret;
- }
-
- chunk_size = TEMP_FAILURE_RETRY(write(urandom_fd, buf, chunk_size));
- if (chunk_size == -1) {
- PLOG(ERROR) << "Failed to write to /dev/urandom";
- goto ret;
- }
- total_bytes_written += chunk_size;
- }
-
- LOG(INFO) << "Mixed " << total_bytes_written << " bytes from /dev/hw_random into /dev/urandom";
- result = 0;
-
-ret:
- if (hwrandom_fd != -1) {
- close(hwrandom_fd);
- }
- if (urandom_fd != -1) {
- close(urandom_fd);
- }
- return result;
-}
-
-static void security_failure() {
- LOG(ERROR) << "Security failure...";
- panic();
-}
-
-static bool set_highest_available_option_value(std::string path, int min, int max)
-{
- std::ifstream inf(path, std::fstream::in);
- if (!inf) {
- LOG(ERROR) << "Cannot open for reading: " << path;
- return false;
- }
-
- int current = max;
- while (current >= min) {
- // try to write out new value
- std::string str_val = std::to_string(current);
- std::ofstream of(path, std::fstream::out);
- if (!of) {
- LOG(ERROR) << "Cannot open for writing: " << path;
- return false;
- }
- of << str_val << std::endl;
- of.close();
-
- // check to make sure it was recorded
- inf.seekg(0);
- std::string str_rec;
- inf >> str_rec;
- if (str_val.compare(str_rec) == 0) {
- break;
- }
- current--;
- }
- inf.close();
-
- if (current < min) {
- LOG(ERROR) << "Unable to set minimum option value " << min << " in " << path;
- return false;
- }
- return true;
-}
-
-#define MMAP_RND_PATH "/proc/sys/vm/mmap_rnd_bits"
-#define MMAP_RND_COMPAT_PATH "/proc/sys/vm/mmap_rnd_compat_bits"
-
-/* __attribute__((unused)) due to lack of mips support: see mips block
- * in set_mmap_rnd_bits_action */
-static bool __attribute__((unused)) set_mmap_rnd_bits_min(int start, int min, bool compat) {
- std::string path;
- if (compat) {
- path = MMAP_RND_COMPAT_PATH;
- } else {
- path = MMAP_RND_PATH;
- }
-
- return set_highest_available_option_value(path, min, start);
-}
-
-/*
- * Set /proc/sys/vm/mmap_rnd_bits and potentially
- * /proc/sys/vm/mmap_rnd_compat_bits to the maximum supported values.
- * Returns -1 if unable to set these to an acceptable value.
- *
- * To support this sysctl, the following upstream commits are needed:
- *
- * d07e22597d1d mm: mmap: add new /proc tunable for mmap_base ASLR
- * e0c25d958f78 arm: mm: support ARCH_MMAP_RND_BITS
- * 8f0d3aa9de57 arm64: mm: support ARCH_MMAP_RND_BITS
- * 9e08f57d684a x86: mm: support ARCH_MMAP_RND_BITS
- * ec9ee4acd97c drivers: char: random: add get_random_long()
- * 5ef11c35ce86 mm: ASLR: use get_random_long()
- */
-static int set_mmap_rnd_bits_action(const std::vector<std::string>& args)
-{
- int ret = -1;
-
- /* values are arch-dependent */
-#if defined(USER_MODE_LINUX)
- /* uml does not support mmap_rnd_bits */
- ret = 0;
-#elif defined(__aarch64__)
- /* arm64 supports 18 - 33 bits depending on pagesize and VA_SIZE */
- if (set_mmap_rnd_bits_min(33, 24, false)
- && set_mmap_rnd_bits_min(16, 16, true)) {
- ret = 0;
- }
-#elif defined(__x86_64__)
- /* x86_64 supports 28 - 32 bits */
- if (set_mmap_rnd_bits_min(32, 32, false)
- && set_mmap_rnd_bits_min(16, 16, true)) {
- ret = 0;
- }
-#elif defined(__arm__) || defined(__i386__)
- /* check to see if we're running on 64-bit kernel */
- bool h64 = !access(MMAP_RND_COMPAT_PATH, F_OK);
- /* supported 32-bit architecture must have 16 bits set */
- if (set_mmap_rnd_bits_min(16, 16, h64)) {
- ret = 0;
- }
-#elif defined(__mips__) || defined(__mips64__)
- // TODO: add mips support b/27788820
- ret = 0;
-#else
- LOG(ERROR) << "Unknown architecture";
-#endif
-
- if (ret == -1) {
- LOG(ERROR) << "Unable to set adequate mmap entropy value!";
- security_failure();
- }
- return ret;
-}
-
-#define KPTR_RESTRICT_PATH "/proc/sys/kernel/kptr_restrict"
-#define KPTR_RESTRICT_MINVALUE 2
-#define KPTR_RESTRICT_MAXVALUE 4
-
-/* Set kptr_restrict to the highest available level.
- *
- * Aborts if unable to set this to an acceptable value.
- */
-static int set_kptr_restrict_action(const std::vector<std::string>& args)
-{
- std::string path = KPTR_RESTRICT_PATH;
-
- if (!set_highest_available_option_value(path, KPTR_RESTRICT_MINVALUE, KPTR_RESTRICT_MAXVALUE)) {
- LOG(ERROR) << "Unable to set adequate kptr_restrict value!";
- security_failure();
- }
- return 0;
-}
-
-static int keychord_init_action(const std::vector<std::string>& args)
-{
+static Result<Success> keychord_init_action(const std::vector<std::string>& args) {
keychord_init();
- return 0;
+ return Success();
}
-static int console_init_action(const std::vector<std::string>& args)
-{
+static Result<Success> console_init_action(const std::vector<std::string>& args) {
std::string console = GetProperty("ro.boot.console", "");
if (!console.empty()) {
default_console = "/dev/" + console;
}
- return 0;
+ return Success();
}
static void import_kernel_nv(const std::string& key, const std::string& value, bool for_emulator) {
@@ -513,7 +291,7 @@
return;
}
- std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(kAndroidDtDir.c_str()), closedir);
+ std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(get_android_dt_dir().c_str()), closedir);
if (!dir) return;
std::string dt_file;
@@ -523,7 +301,7 @@
continue;
}
- std::string file_name = kAndroidDtDir + dp->d_name;
+ std::string file_name = get_android_dt_dir() + dp->d_name;
android::base::ReadFileToString(file_name, &dt_file);
std::replace(dt_file.begin(), dt_file.end(), ',', '.');
@@ -540,367 +318,24 @@
if (qemu[0]) import_kernel_cmdline(true, import_kernel_nv);
}
-static int property_enable_triggers_action(const std::vector<std::string>& args)
-{
+static Result<Success> property_enable_triggers_action(const std::vector<std::string>& args) {
/* Enable property triggers. */
property_triggers_enabled = 1;
- return 0;
+ return Success();
}
-static int queue_property_triggers_action(const std::vector<std::string>& args)
-{
+static Result<Success> queue_property_triggers_action(const std::vector<std::string>& args) {
ActionManager::GetInstance().QueueBuiltinAction(property_enable_triggers_action, "enable_property_trigger");
ActionManager::GetInstance().QueueAllPropertyActions();
- return 0;
+ return Success();
}
-static void selinux_init_all_handles(void)
-{
- sehandle = selinux_android_file_context_handle();
- selinux_android_set_sehandle(sehandle);
- sehandle_prop = selinux_android_prop_context_handle();
-}
-
-enum selinux_enforcing_status { SELINUX_PERMISSIVE, SELINUX_ENFORCING };
-
-static selinux_enforcing_status selinux_status_from_cmdline() {
- selinux_enforcing_status status = SELINUX_ENFORCING;
-
- import_kernel_cmdline(false, [&](const std::string& key, const std::string& value, bool in_qemu) {
- if (key == "androidboot.selinux" && value == "permissive") {
- status = SELINUX_PERMISSIVE;
+static void global_seccomp() {
+ import_kernel_cmdline(false, [](const std::string& key, const std::string& value, bool in_qemu) {
+ if (key == "androidboot.seccomp" && value == "global" && !set_global_seccomp_filter()) {
+ LOG(FATAL) << "Failed to globally enable seccomp!";
}
});
-
- return status;
-}
-
-static bool selinux_is_enforcing(void)
-{
- if (ALLOW_PERMISSIVE_SELINUX) {
- return selinux_status_from_cmdline() == SELINUX_ENFORCING;
- }
- return true;
-}
-
-static int audit_callback(void *data, security_class_t /*cls*/, char *buf, size_t len) {
-
- property_audit_data *d = reinterpret_cast<property_audit_data*>(data);
-
- if (!d || !d->name || !d->cr) {
- LOG(ERROR) << "audit_callback invoked with null data arguments!";
- return 0;
- }
-
- snprintf(buf, len, "property=%s pid=%d uid=%d gid=%d", d->name,
- d->cr->pid, d->cr->uid, d->cr->gid);
- return 0;
-}
-
-/*
- * Forks, executes the provided program in the child, and waits for the completion in the parent.
- * Child's stderr is captured and logged using LOG(ERROR).
- *
- * Returns true if the child exited with status code 0, returns false otherwise.
- */
-static bool fork_execve_and_wait_for_completion(const char* filename, char* const argv[],
- char* const envp[]) {
- // Create a pipe used for redirecting child process's output.
- // * pipe_fds[0] is the FD the parent will use for reading.
- // * pipe_fds[1] is the FD the child will use for writing.
- int pipe_fds[2];
- if (pipe(pipe_fds) == -1) {
- PLOG(ERROR) << "Failed to create pipe";
- return false;
- }
-
- pid_t child_pid = fork();
- if (child_pid == -1) {
- PLOG(ERROR) << "Failed to fork for " << filename;
- return false;
- }
-
- if (child_pid == 0) {
- // fork succeeded -- this is executing in the child process
-
- // Close the pipe FD not used by this process
- TEMP_FAILURE_RETRY(close(pipe_fds[0]));
-
- // Redirect stderr to the pipe FD provided by the parent
- if (TEMP_FAILURE_RETRY(dup2(pipe_fds[1], STDERR_FILENO)) == -1) {
- PLOG(ERROR) << "Failed to redirect stderr of " << filename;
- _exit(127);
- return false;
- }
- TEMP_FAILURE_RETRY(close(pipe_fds[1]));
-
- if (execve(filename, argv, envp) == -1) {
- PLOG(ERROR) << "Failed to execve " << filename;
- return false;
- }
- // Unreachable because execve will have succeeded and replaced this code
- // with child process's code.
- _exit(127);
- return false;
- } else {
- // fork succeeded -- this is executing in the original/parent process
-
- // Close the pipe FD not used by this process
- TEMP_FAILURE_RETRY(close(pipe_fds[1]));
-
- // Log the redirected output of the child process.
- // It's unfortunate that there's no standard way to obtain an istream for a file descriptor.
- // As a result, we're buffering all output and logging it in one go at the end of the
- // invocation, instead of logging it as it comes in.
- const int child_out_fd = pipe_fds[0];
- std::string child_output;
- if (!android::base::ReadFdToString(child_out_fd, &child_output)) {
- PLOG(ERROR) << "Failed to capture full output of " << filename;
- }
- TEMP_FAILURE_RETRY(close(child_out_fd));
- if (!child_output.empty()) {
- // Log captured output, line by line, because LOG expects to be invoked for each line
- std::istringstream in(child_output);
- std::string line;
- while (std::getline(in, line)) {
- LOG(ERROR) << filename << ": " << line;
- }
- }
-
- // Wait for child to terminate
- int status;
- if (TEMP_FAILURE_RETRY(waitpid(child_pid, &status, 0)) != child_pid) {
- PLOG(ERROR) << "Failed to wait for " << filename;
- return false;
- }
-
- if (WIFEXITED(status)) {
- int status_code = WEXITSTATUS(status);
- if (status_code == 0) {
- return true;
- } else {
- LOG(ERROR) << filename << " exited with status " << status_code;
- }
- } else if (WIFSIGNALED(status)) {
- LOG(ERROR) << filename << " killed by signal " << WTERMSIG(status);
- } else if (WIFSTOPPED(status)) {
- LOG(ERROR) << filename << " stopped by signal " << WSTOPSIG(status);
- } else {
- LOG(ERROR) << "waitpid for " << filename << " returned unexpected status: " << status;
- }
-
- return false;
- }
-}
-
-static bool read_first_line(const char* file, std::string* line) {
- line->clear();
-
- std::string contents;
- if (!android::base::ReadFileToString(file, &contents, true /* follow symlinks */)) {
- return false;
- }
- std::istringstream in(contents);
- std::getline(in, *line);
- return true;
-}
-
-static bool selinux_find_precompiled_split_policy(std::string* file) {
- file->clear();
-
- static constexpr const char precompiled_sepolicy[] = "/vendor/etc/selinux/precompiled_sepolicy";
- if (access(precompiled_sepolicy, R_OK) == -1) {
- return false;
- }
- std::string actual_plat_id;
- if (!read_first_line("/system/etc/selinux/plat_and_mapping_sepolicy.cil.sha256",
- &actual_plat_id)) {
- PLOG(INFO) << "Failed to read "
- "/system/etc/selinux/plat_and_mapping_sepolicy.cil.sha256";
- return false;
- }
- std::string precompiled_plat_id;
- if (!read_first_line("/vendor/etc/selinux/precompiled_sepolicy.plat_and_mapping.sha256",
- &precompiled_plat_id)) {
- PLOG(INFO) << "Failed to read "
- "/vendor/etc/selinux/"
- "precompiled_sepolicy.plat_and_mapping.sha256";
- return false;
- }
- if ((actual_plat_id.empty()) || (actual_plat_id != precompiled_plat_id)) {
- return false;
- }
-
- *file = precompiled_sepolicy;
- return true;
-}
-
-static constexpr const char plat_policy_cil_file[] = "/system/etc/selinux/plat_sepolicy.cil";
-
-static bool selinux_is_split_policy_device() { return access(plat_policy_cil_file, R_OK) != -1; }
-
-/*
- * Loads SELinux policy split across platform/system and non-platform/vendor files.
- *
- * Returns true upon success, false otherwise (failure cause is logged).
- */
-static bool selinux_load_split_policy() {
- // IMPLEMENTATION NOTE: Split policy consists of three CIL files:
- // * platform -- policy needed due to logic contained in the system image,
- // * non-platform -- policy needed due to logic contained in the vendor image,
- // * mapping -- mapping policy which helps preserve forward-compatibility of non-platform policy
- // with newer versions of platform policy.
- //
- // secilc is invoked to compile the above three policy files into a single monolithic policy
- // file. This file is then loaded into the kernel.
-
- // Load precompiled policy from vendor image, if a matching policy is found there. The policy
- // must match the platform policy on the system image.
- std::string precompiled_sepolicy_file;
- if (selinux_find_precompiled_split_policy(&precompiled_sepolicy_file)) {
- android::base::unique_fd fd(
- open(precompiled_sepolicy_file.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY));
- if (fd != -1) {
- if (selinux_android_load_policy_from_fd(fd, precompiled_sepolicy_file.c_str()) < 0) {
- LOG(ERROR) << "Failed to load SELinux policy from " << precompiled_sepolicy_file;
- return false;
- }
- return true;
- }
- }
- // No suitable precompiled policy could be loaded
-
- LOG(INFO) << "Compiling SELinux policy";
-
- // Determine the highest policy language version supported by the kernel
- set_selinuxmnt("/sys/fs/selinux");
- int max_policy_version = security_policyvers();
- if (max_policy_version == -1) {
- PLOG(ERROR) << "Failed to determine highest policy version supported by kernel";
- return false;
- }
-
- // We store the output of the compilation on /dev because this is the most convenient tmpfs
- // storage mount available this early in the boot sequence.
- char compiled_sepolicy[] = "/dev/sepolicy.XXXXXX";
- android::base::unique_fd compiled_sepolicy_fd(mkostemp(compiled_sepolicy, O_CLOEXEC));
- if (compiled_sepolicy_fd < 0) {
- PLOG(ERROR) << "Failed to create temporary file " << compiled_sepolicy;
- return false;
- }
-
- // clang-format off
- const char* compile_args[] = {
- "/system/bin/secilc",
- plat_policy_cil_file,
- "-M", "true",
- // Target the highest policy language version supported by the kernel
- "-c", std::to_string(max_policy_version).c_str(),
- "/system/etc/selinux/mapping_sepolicy.cil",
- "/vendor/etc/selinux/nonplat_sepolicy.cil",
- "-o", compiled_sepolicy,
- // We don't care about file_contexts output by the compiler
- "-f", "/sys/fs/selinux/null", // /dev/null is not yet available
- nullptr};
- // clang-format on
-
- if (!fork_execve_and_wait_for_completion(compile_args[0], (char**)compile_args, (char**)ENV)) {
- unlink(compiled_sepolicy);
- return false;
- }
- unlink(compiled_sepolicy);
-
- LOG(INFO) << "Loading compiled SELinux policy";
- if (selinux_android_load_policy_from_fd(compiled_sepolicy_fd, compiled_sepolicy) < 0) {
- LOG(ERROR) << "Failed to load SELinux policy from " << compiled_sepolicy;
- return false;
- }
-
- return true;
-}
-
-/*
- * Loads SELinux policy from a monolithic file.
- *
- * Returns true upon success, false otherwise (failure cause is logged).
- */
-static bool selinux_load_monolithic_policy() {
- LOG(VERBOSE) << "Loading SELinux policy from monolithic file";
- if (selinux_android_load_policy() < 0) {
- PLOG(ERROR) << "Failed to load monolithic SELinux policy";
- return false;
- }
- return true;
-}
-
-/*
- * Loads SELinux policy into the kernel.
- *
- * Returns true upon success, false otherwise (failure cause is logged).
- */
-static bool selinux_load_policy() {
- return selinux_is_split_policy_device() ? selinux_load_split_policy()
- : selinux_load_monolithic_policy();
-}
-
-static void selinux_initialize(bool in_kernel_domain) {
- Timer t;
-
- selinux_callback cb;
- cb.func_log = selinux_klog_callback;
- selinux_set_callback(SELINUX_CB_LOG, cb);
- cb.func_audit = audit_callback;
- selinux_set_callback(SELINUX_CB_AUDIT, cb);
-
- if (in_kernel_domain) {
- LOG(INFO) << "Loading SELinux policy";
- if (!selinux_load_policy()) {
- panic();
- }
-
- bool kernel_enforcing = (security_getenforce() == 1);
- bool is_enforcing = selinux_is_enforcing();
- if (kernel_enforcing != is_enforcing) {
- if (security_setenforce(is_enforcing)) {
- PLOG(ERROR) << "security_setenforce(%s) failed" << (is_enforcing ? "true" : "false");
- security_failure();
- }
- }
-
- std::string err;
- if (!WriteFile("/sys/fs/selinux/checkreqprot", "0", &err)) {
- LOG(ERROR) << err;
- security_failure();
- }
-
- // init's first stage can't set properties, so pass the time to the second stage.
- setenv("INIT_SELINUX_TOOK", std::to_string(t.duration().count()).c_str(), 1);
- } else {
- selinux_init_all_handles();
- }
-}
-
-// The files and directories that were created before initial sepolicy load or
-// files on ramdisk need to have their security context restored to the proper
-// value. This must happen before /dev is populated by ueventd.
-static void selinux_restore_context() {
- LOG(INFO) << "Running restorecon...";
- selinux_android_restorecon("/dev", 0);
- selinux_android_restorecon("/dev/kmsg", 0);
- if constexpr (WORLD_WRITABLE_KMSG) {
- selinux_android_restorecon("/dev/kmsg_debug", 0);
- }
- selinux_android_restorecon("/dev/socket", 0);
- selinux_android_restorecon("/dev/random", 0);
- selinux_android_restorecon("/dev/urandom", 0);
- selinux_android_restorecon("/dev/__properties__", 0);
- selinux_android_restorecon("/plat_property_contexts", 0);
- selinux_android_restorecon("/nonplat_property_contexts", 0);
- selinux_android_restorecon("/dev/block", SELINUX_ANDROID_RESTORECON_RECURSE);
- selinux_android_restorecon("/dev/device-mapper", 0);
-
- selinux_android_restorecon("/sbin/mke2fs", 0);
- selinux_android_restorecon("/sbin/e2fsdroid", 0);
}
// Set the UDC controller for the ConfigFS USB Gadgets.
@@ -928,8 +363,11 @@
memset(&action, 0, sizeof(action));
sigfillset(&action.sa_mask);
action.sa_handler = [](int) {
- // panic() reboots to bootloader
- panic();
+ // Calling DoReboot() or LOG(FATAL) is not a good option as this is a signal handler.
+ // RebootSystem uses syscall() which isn't actually async-signal-safe, but our only option
+ // and probably good enough given this is already an error case and only enabled for
+ // development builds.
+ RebootSystem(ANDROID_RB_RESTART2, "bootloader");
};
action.sa_flags = SA_RESTART;
sigaction(SIGABRT, &action, nullptr);
@@ -957,8 +395,6 @@
install_reboot_signal_handlers();
}
- add_environment("PATH", _PATH_DEFPATH);
-
bool is_first_stage = (getenv("INIT_SECOND_STAGE") == nullptr);
if (is_first_stage) {
@@ -967,6 +403,8 @@
// Clear the umask.
umask(0);
+ clearenv();
+ setenv("PATH", _PATH_DEFPATH, 1);
// Get the basic filesystem setup we need put together in the initramdisk
// on / and then we'll let the rc file figure out the rest.
mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
@@ -985,7 +423,7 @@
mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11));
if constexpr (WORLD_WRITABLE_KMSG) {
- mknod("/dev/kmsg_debug", S_IFCHR | 0622, makedev(1, 11));
+ mknod("/dev/kmsg_debug", S_IFCHR | 0622, makedev(1, 11));
}
mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8));
@@ -998,20 +436,22 @@
LOG(INFO) << "init first stage started!";
if (!DoFirstStageMount()) {
- LOG(ERROR) << "Failed to mount required partitions early ...";
- panic();
+ LOG(FATAL) << "Failed to mount required partitions early ...";
}
SetInitAvbVersionInRecovery();
+ // Enable seccomp if global boot option was passed (otherwise it is enabled in zygote).
+ global_seccomp();
+
// Set up SELinux, loading the SELinux policy.
- selinux_initialize(true);
+ SelinuxSetupKernelLogging();
+ SelinuxInitialize();
// We're in the kernel domain, so re-exec init to transition to the init domain now
// that the SELinux policy has been loaded.
if (selinux_android_restorecon("/init", 0) == -1) {
- PLOG(ERROR) << "restorecon failed";
- security_failure();
+ PLOG(FATAL) << "restorecon failed of /init failed";
}
setenv("INIT_SECOND_STAGE", "true", 1);
@@ -1026,8 +466,7 @@
// execv() only returns if an error happened, in which case we
// panic and never fall through this conditional.
- PLOG(ERROR) << "execv(\"" << path << "\") failed";
- security_failure();
+ PLOG(FATAL) << "execv(\"" << path << "\") failed";
}
// At this point we're in the second stage of init.
@@ -1068,8 +507,9 @@
unsetenv("INIT_AVB_VERSION");
// Now set up SELinux for second stage.
- selinux_initialize(false);
- selinux_restore_context();
+ SelinuxSetupKernelLogging();
+ SelabelInitialize();
+ SelinuxRestoreContext();
epoll_fd = epoll_create1(EPOLL_CLOEXEC);
if (epoll_fd == -1) {
@@ -1088,26 +528,9 @@
Action::set_function_map(&function_map);
ActionManager& am = ActionManager::GetInstance();
- ServiceManager& sm = ServiceManager::GetInstance();
- Parser& parser = Parser::GetInstance();
+ ServiceList& sm = ServiceList::GetInstance();
- parser.AddSectionParser("service", std::make_unique<ServiceParser>(&sm));
- parser.AddSectionParser("on", std::make_unique<ActionParser>(&am));
- parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
- std::string bootscript = GetProperty("ro.boot.init_rc", "");
- if (bootscript.empty()) {
- parser.ParseConfig("/init.rc");
- parser.set_is_system_etc_init_loaded(
- parser.ParseConfig("/system/etc/init"));
- parser.set_is_vendor_etc_init_loaded(
- parser.ParseConfig("/vendor/etc/init"));
- parser.set_is_odm_etc_init_loaded(parser.ParseConfig("/odm/etc/init"));
- } else {
- parser.ParseConfig(bootscript);
- parser.set_is_system_etc_init_loaded(true);
- parser.set_is_vendor_etc_init_loaded(true);
- parser.set_is_odm_etc_init_loaded(true);
- }
+ LoadBootScripts(am, sm);
// Turning this on and letting the INFO logging be discarded adds 0.2s to
// Nexus 9 boot time, so it's disabled by default.
@@ -1118,9 +541,9 @@
// Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
// ... so that we can start queuing up actions that require stuff from /dev.
- am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
- am.QueueBuiltinAction(set_mmap_rnd_bits_action, "set_mmap_rnd_bits");
- am.QueueBuiltinAction(set_kptr_restrict_action, "set_kptr_restrict");
+ am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
+ am.QueueBuiltinAction(SetMmapRndBitsAction, "SetMmapRndBits");
+ am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
am.QueueBuiltinAction(keychord_init_action, "keychord_init");
am.QueueBuiltinAction(console_init_action, "console_init");
@@ -1129,7 +552,7 @@
// Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
// wasn't ready immediately after wait_for_coldboot_done
- am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
+ am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
// Don't mount filesystems or start core system services in charger mode.
std::string bootmode = GetProperty("ro.bootmode", "");
@@ -1146,16 +569,20 @@
// By default, sleep until something happens.
int epoll_timeout_ms = -1;
- if (!(waiting_for_prop || sm.IsWaitingForExec())) {
+ if (!(waiting_for_prop || Service::is_exec_service_running())) {
am.ExecuteOneCommand();
}
- if (!(waiting_for_prop || sm.IsWaitingForExec())) {
- if (!shutting_down) restart_processes();
+ if (!(waiting_for_prop || Service::is_exec_service_running())) {
+ if (!shutting_down) {
+ auto next_process_restart_time = RestartProcesses();
- // If there's a process that needs restarting, wake up in time for that.
- if (process_needs_restart_at != 0) {
- epoll_timeout_ms = (process_needs_restart_at - time(nullptr)) * 1000;
- if (epoll_timeout_ms < 0) epoll_timeout_ms = 0;
+ // If there's a process that needs restarting, wake up in time for that.
+ if (next_process_restart_time) {
+ epoll_timeout_ms = std::chrono::ceil<std::chrono::milliseconds>(
+ *next_process_restart_time - boot_clock::now())
+ .count();
+ if (epoll_timeout_ms < 0) epoll_timeout_ms = 0;
+ }
}
// If there's more work to do, wake up again immediately.
diff --git a/init/init.h b/init/init.h
index aaab523..b757c1d 100644
--- a/init/init.h
+++ b/init/init.h
@@ -18,8 +18,11 @@
#define _INIT_INIT_H
#include <string>
+#include <vector>
-#include <selinux/label.h>
+#include "action.h"
+#include "parser.h"
+#include "service.h"
namespace android {
namespace init {
@@ -27,10 +30,10 @@
// Note: These globals are *only* valid in init, so they should not be used in ueventd,
// watchdogd, or any files that may be included in those, such as devices.cpp and util.cpp.
// TODO: Have an Init class and remove all globals.
-extern const char *ENV[32];
extern std::string default_console;
-extern struct selabel_handle *sehandle;
-extern struct selabel_handle *sehandle_prop;
+extern std::vector<std::string> late_import_paths;
+
+Parser CreateParser(ActionManager& action_manager, ServiceList& service_list);
void handle_control_message(const std::string& msg, const std::string& arg);
@@ -38,8 +41,6 @@
void register_epoll_handler(int fd, void (*fn)());
-int add_environment(const char* key, const char* val);
-
bool start_waiting_for_property(const char *name, const char *value);
void DumpState();
diff --git a/init/init_parser.cpp b/init/init_parser.cpp
deleted file mode 100644
index 9f7089b..0000000
--- a/init/init_parser.cpp
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * Copyright (C) 2010 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 "init_parser.h"
-
-#include <dirent.h>
-
-#include <android-base/chrono_utils.h>
-#include <android-base/logging.h>
-#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
-
-#include "parser.h"
-#include "util.h"
-
-namespace android {
-namespace init {
-
-Parser::Parser() {
-}
-
-Parser& Parser::GetInstance() {
- static Parser instance;
- return instance;
-}
-
-void Parser::AddSectionParser(const std::string& name,
- std::unique_ptr<SectionParser> parser) {
- section_parsers_[name] = std::move(parser);
-}
-
-void Parser::AddSingleLineParser(const std::string& prefix, LineCallback callback) {
- line_callbacks_.emplace_back(prefix, callback);
-}
-
-void Parser::ParseData(const std::string& filename, const std::string& data) {
- //TODO: Use a parser with const input and remove this copy
- std::vector<char> data_copy(data.begin(), data.end());
- data_copy.push_back('\0');
-
- parse_state state;
- state.line = 0;
- state.ptr = &data_copy[0];
- state.nexttoken = 0;
-
- SectionParser* section_parser = nullptr;
- std::vector<std::string> args;
-
- for (;;) {
- switch (next_token(&state)) {
- case T_EOF:
- if (section_parser) {
- section_parser->EndSection();
- }
- return;
- case T_NEWLINE:
- state.line++;
- if (args.empty()) {
- break;
- }
- // If we have a line matching a prefix we recognize, call its callback and unset any
- // current section parsers. This is meant for /sys/ and /dev/ line entries for uevent.
- for (const auto& [prefix, callback] : line_callbacks_) {
- if (android::base::StartsWith(args[0], prefix.c_str())) {
- if (section_parser) section_parser->EndSection();
-
- std::string ret_err;
- if (!callback(std::move(args), &ret_err)) {
- LOG(ERROR) << filename << ": " << state.line << ": " << ret_err;
- }
- section_parser = nullptr;
- break;
- }
- }
- if (section_parsers_.count(args[0])) {
- if (section_parser) {
- section_parser->EndSection();
- }
- section_parser = section_parsers_[args[0]].get();
- std::string ret_err;
- if (!section_parser->ParseSection(std::move(args), filename, state.line, &ret_err)) {
- LOG(ERROR) << filename << ": " << state.line << ": " << ret_err;
- section_parser = nullptr;
- }
- } else if (section_parser) {
- std::string ret_err;
- if (!section_parser->ParseLineSection(std::move(args), state.line, &ret_err)) {
- LOG(ERROR) << filename << ": " << state.line << ": " << ret_err;
- }
- }
- args.clear();
- break;
- case T_TEXT:
- args.emplace_back(state.text);
- break;
- }
- }
-}
-
-bool Parser::ParseConfigFile(const std::string& path) {
- LOG(INFO) << "Parsing file " << path << "...";
- android::base::Timer t;
- std::string data;
- std::string err;
- if (!ReadFile(path, &data, &err)) {
- LOG(ERROR) << err;
- return false;
- }
-
- data.push_back('\n'); // TODO: fix parse_config.
- ParseData(path, data);
- for (const auto& [section_name, section_parser] : section_parsers_) {
- section_parser->EndFile();
- }
-
- LOG(VERBOSE) << "(Parsing " << path << " took " << t << ".)";
- return true;
-}
-
-bool Parser::ParseConfigDir(const std::string& path) {
- LOG(INFO) << "Parsing directory " << path << "...";
- std::unique_ptr<DIR, int(*)(DIR*)> config_dir(opendir(path.c_str()), closedir);
- if (!config_dir) {
- PLOG(ERROR) << "Could not import directory '" << path << "'";
- return false;
- }
- dirent* current_file;
- std::vector<std::string> files;
- while ((current_file = readdir(config_dir.get()))) {
- // Ignore directories and only process regular files.
- if (current_file->d_type == DT_REG) {
- std::string current_path =
- android::base::StringPrintf("%s/%s", path.c_str(), current_file->d_name);
- files.emplace_back(current_path);
- }
- }
- // Sort first so we load files in a consistent order (bug 31996208)
- std::sort(files.begin(), files.end());
- for (const auto& file : files) {
- if (!ParseConfigFile(file)) {
- LOG(ERROR) << "could not import file '" << file << "'";
- }
- }
- return true;
-}
-
-bool Parser::ParseConfig(const std::string& path) {
- if (is_dir(path.c_str())) {
- return ParseConfigDir(path);
- }
- return ParseConfigFile(path);
-}
-
-} // namespace init
-} // namespace android
diff --git a/init/init_parser.h b/init/init_parser.h
deleted file mode 100644
index c07a699..0000000
--- a/init/init_parser.h
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2010 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 _INIT_INIT_PARSER_H_
-#define _INIT_INIT_PARSER_H_
-
-#include <map>
-#include <memory>
-#include <string>
-#include <vector>
-
-// SectionParser is an interface that can parse a given 'section' in init.
-//
-// You can implement up to 4 functions below, with ParseSection() being mandatory.
-// The first two function return bool with false indicating a failure and has a std::string* err
-// parameter into which an error string can be written. It will be reported along with the
-// filename and line number of where the error occurred.
-//
-// 1) bool ParseSection(std::vector<std::string>&& args, const std::string& filename,
-// int line, std::string* err)
-// This function is called when a section is first encountered.
-//
-// 2) bool ParseLineSection(std::vector<std::string>&& args, int line, std::string* err)
-// This function is called on each subsequent line until the next section is encountered.
-//
-// 3) bool EndSection()
-// This function is called either when a new section is found or at the end of the file.
-// It indicates that parsing of the current section is complete and any relevant objects should
-// be committed.
-//
-// 4) bool EndFile()
-// This function is called at the end of the file.
-// It indicates that the parsing has completed and any relevant objects should be committed.
-
-namespace android {
-namespace init {
-
-class SectionParser {
- public:
- virtual ~SectionParser() {}
- virtual bool ParseSection(std::vector<std::string>&& args, const std::string& filename,
- int line, std::string* err) = 0;
- virtual bool ParseLineSection(std::vector<std::string>&&, int, std::string*) { return true; };
- virtual void EndSection(){};
- virtual void EndFile(){};
-};
-
-class Parser {
- public:
- // LineCallback is the type for callbacks that can parse a line starting with a given prefix.
- //
- // They take the form of bool Callback(std::vector<std::string>&& args, std::string* err)
- //
- // Similar to ParseSection() and ParseLineSection(), this function returns bool with false
- // indicating a failure and has an std::string* err parameter into which an error string can
- // be written.
- using LineCallback = std::function<bool(std::vector<std::string>&&, std::string*)>;
-
- // TODO: init is the only user of this as a singleton; remove it.
- static Parser& GetInstance();
-
- Parser();
-
- bool ParseConfig(const std::string& path);
- void AddSectionParser(const std::string& name, std::unique_ptr<SectionParser> parser);
- void AddSingleLineParser(const std::string& prefix, LineCallback callback);
- void set_is_system_etc_init_loaded(bool loaded) { is_system_etc_init_loaded_ = loaded; }
- void set_is_vendor_etc_init_loaded(bool loaded) { is_vendor_etc_init_loaded_ = loaded; }
- void set_is_odm_etc_init_loaded(bool loaded) { is_odm_etc_init_loaded_ = loaded; }
- bool is_system_etc_init_loaded() { return is_system_etc_init_loaded_; }
- bool is_vendor_etc_init_loaded() { return is_vendor_etc_init_loaded_; }
- bool is_odm_etc_init_loaded() { return is_odm_etc_init_loaded_; }
-
- private:
- void ParseData(const std::string& filename, const std::string& data);
- bool ParseConfigFile(const std::string& path);
- bool ParseConfigDir(const std::string& path);
-
- std::map<std::string, std::unique_ptr<SectionParser>> section_parsers_;
- std::vector<std::pair<std::string, LineCallback>> line_callbacks_;
- bool is_system_etc_init_loaded_ = false;
- bool is_vendor_etc_init_loaded_ = false;
- bool is_odm_etc_init_loaded_ = false;
-};
-
-} // namespace init
-} // namespace android
-
-#endif
diff --git a/init/init_parser_test.cpp b/init/init_parser_test.cpp
deleted file mode 100644
index 95f269a..0000000
--- a/init/init_parser_test.cpp
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * 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 "init_parser.h"
-
-#include <string>
-#include <vector>
-
-#include <gtest/gtest.h>
-
-#include "init.h"
-#include "service.h"
-#include "util.h"
-
-namespace android {
-namespace init {
-
-TEST(init_parser, make_exec_oneshot_service_invalid_syntax) {
- ServiceManager& sm = ServiceManager::GetInstance();
- std::vector<std::string> args;
- // Nothing.
- ASSERT_EQ(nullptr, sm.MakeExecOneshotService(args));
-
- // No arguments to 'exec'.
- args.push_back("exec");
- ASSERT_EQ(nullptr, sm.MakeExecOneshotService(args));
-
- // No command in "exec --".
- args.push_back("--");
- ASSERT_EQ(nullptr, sm.MakeExecOneshotService(args));
-}
-
-TEST(init_parser, make_exec_oneshot_service_too_many_supplementary_gids) {
- ServiceManager& sm = ServiceManager::GetInstance();
- std::vector<std::string> args;
- args.push_back("exec");
- args.push_back("seclabel");
- args.push_back("root"); // uid.
- args.push_back("root"); // gid.
- for (int i = 0; i < NR_SVC_SUPP_GIDS; ++i) {
- args.push_back("root"); // Supplementary gid.
- }
- args.push_back("--");
- args.push_back("/system/bin/id");
- ASSERT_EQ(nullptr, sm.MakeExecOneshotService(args));
-}
-
-static void Test_make_exec_oneshot_service(bool dash_dash, bool seclabel, bool uid,
- bool gid, bool supplementary_gids) {
- ServiceManager& sm = ServiceManager::GetInstance();
- std::vector<std::string> args;
- args.push_back("exec");
- if (seclabel) {
- args.push_back("u:r:su:s0"); // seclabel
- if (uid) {
- args.push_back("log"); // uid
- if (gid) {
- args.push_back("shell"); // gid
- if (supplementary_gids) {
- args.push_back("system"); // supplementary gid 0
- args.push_back("adb"); // supplementary gid 1
- }
- }
- }
- }
- if (dash_dash) {
- args.push_back("--");
- }
- args.push_back("/system/bin/toybox");
- args.push_back("id");
- Service* svc = sm.MakeExecOneshotService(args);
- ASSERT_NE(nullptr, svc);
-
- if (seclabel) {
- ASSERT_EQ("u:r:su:s0", svc->seclabel());
- } else {
- ASSERT_EQ("", svc->seclabel());
- }
- if (uid) {
- uid_t decoded_uid;
- std::string err;
- ASSERT_TRUE(DecodeUid("log", &decoded_uid, &err));
- ASSERT_EQ(decoded_uid, svc->uid());
- } else {
- ASSERT_EQ(0U, svc->uid());
- }
- if (gid) {
- uid_t decoded_uid;
- std::string err;
- ASSERT_TRUE(DecodeUid("shell", &decoded_uid, &err));
- ASSERT_EQ(decoded_uid, svc->gid());
- } else {
- ASSERT_EQ(0U, svc->gid());
- }
- if (supplementary_gids) {
- ASSERT_EQ(2U, svc->supp_gids().size());
- uid_t decoded_uid;
- std::string err;
- ASSERT_TRUE(DecodeUid("system", &decoded_uid, &err));
- ASSERT_EQ(decoded_uid, svc->supp_gids()[0]);
- ASSERT_TRUE(DecodeUid("adb", &decoded_uid, &err));
- ASSERT_EQ(decoded_uid, svc->supp_gids()[1]);
- } else {
- ASSERT_EQ(0U, svc->supp_gids().size());
- }
-
- ASSERT_EQ(static_cast<std::size_t>(2), svc->args().size());
- ASSERT_EQ("/system/bin/toybox", svc->args()[0]);
- ASSERT_EQ("id", svc->args()[1]);
-}
-
-TEST(init_parser, make_exec_oneshot_service_with_everything) {
- Test_make_exec_oneshot_service(true, true, true, true, true);
-}
-
-TEST(init_parser, make_exec_oneshot_service_with_seclabel_uid_gid) {
- Test_make_exec_oneshot_service(true, true, true, true, false);
-}
-
-TEST(init_parser, make_exec_oneshot_service_with_seclabel_uid) {
- Test_make_exec_oneshot_service(true, true, true, false, false);
-}
-
-TEST(init_parser, make_exec_oneshot_service_with_seclabel) {
- Test_make_exec_oneshot_service(true, true, false, false, false);
-}
-
-TEST(init_parser, make_exec_oneshot_service_with_just_command) {
- Test_make_exec_oneshot_service(true, false, false, false, false);
-}
-
-TEST(init_parser, make_exec_oneshot_service_with_just_command_no_dash) {
- Test_make_exec_oneshot_service(false, false, false, false, false);
-}
-
-} // namespace init
-} // namespace android
diff --git a/init/init_test.cpp b/init/init_test.cpp
index 0a4071b..27659f9 100644
--- a/init/init_test.cpp
+++ b/init/init_test.cpp
@@ -23,8 +23,8 @@
#include "action.h"
#include "builtins.h"
#include "import_parser.h"
-#include "init_parser.h"
#include "keyword_map.h"
+#include "parser.h"
#include "util.h"
namespace android {
@@ -37,7 +37,7 @@
void Add(const std::string& name, const BuiltinFunctionNoArgs function) {
Add(name, 0, 0, [function](const std::vector<std::string>&) {
function();
- return 0;
+ return Success();
});
}
@@ -156,10 +156,9 @@
"execute 3";
// clang-format on
// WriteFile() ensures the right mode is set
- std::string err;
- ASSERT_TRUE(WriteFile(std::string(dir.path) + "/a.rc", dir_a_script, &err));
+ ASSERT_TRUE(WriteFile(std::string(dir.path) + "/a.rc", dir_a_script));
- ASSERT_TRUE(WriteFile(std::string(dir.path) + "/b.rc", "on boot\nexecute 5", &err));
+ ASSERT_TRUE(WriteFile(std::string(dir.path) + "/b.rc", "on boot\nexecute 5"));
// clang-format off
std::string start_script = "import " + std::string(first_import.path) + "\n"
@@ -175,7 +174,7 @@
auto execute_command = [&num_executed](const std::vector<std::string>& args) {
EXPECT_EQ(2U, args.size());
EXPECT_EQ(++num_executed, std::stoi(args[1]));
- return 0;
+ return Success();
};
TestFunctionMap test_function_map;
diff --git a/init/keychords.cpp b/init/keychords.cpp
index a0d7cc5..2ef0ce7 100644
--- a/init/keychords.cpp
+++ b/init/keychords.cpp
@@ -79,7 +79,7 @@
// Only handle keychords if adb is enabled.
std::string adb_enabled = android::base::GetProperty("init.svc.adbd", "");
if (adb_enabled == "running") {
- Service* svc = ServiceManager::GetInstance().FindServiceByKeychord(id);
+ Service* svc = ServiceList::GetInstance().FindService(id, &Service::keychord_id);
if (svc) {
LOG(INFO) << "Starting service " << svc->name() << " from keychord " << id;
svc->Start();
@@ -92,7 +92,9 @@
}
void keychord_init() {
- ServiceManager::GetInstance().ForEachService(add_service_keycodes);
+ for (const auto& service : ServiceList::GetInstance()) {
+ add_service_keycodes(service.get());
+ }
// Nothing to do if no services require keychords.
if (!keychords) {
diff --git a/init/keyword_map.h b/init/keyword_map.h
index 481d637..c95fc73 100644
--- a/init/keyword_map.h
+++ b/init/keyword_map.h
@@ -22,6 +22,8 @@
#include <android-base/stringprintf.h>
+#include "result.h"
+
namespace android {
namespace init {
@@ -34,20 +36,17 @@
virtual ~KeywordMap() {
}
- const Function FindFunction(const std::vector<std::string>& args, std::string* err) const {
+ const Result<Function> FindFunction(const std::vector<std::string>& args) const {
using android::base::StringPrintf;
- if (args.empty()) {
- *err = "keyword needed, but not provided";
- return nullptr;
- }
+ if (args.empty()) return Error() << "Keyword needed, but not provided";
+
auto& keyword = args[0];
auto num_args = args.size() - 1;
auto function_info_it = map().find(keyword);
if (function_info_it == map().end()) {
- *err = StringPrintf("invalid keyword '%s'", keyword.c_str());
- return nullptr;
+ return Error() << StringPrintf("Invalid keyword '%s'", keyword.c_str());
}
auto function_info = function_info_it->second;
@@ -55,22 +54,18 @@
auto min_args = std::get<0>(function_info);
auto max_args = std::get<1>(function_info);
if (min_args == max_args && num_args != min_args) {
- *err = StringPrintf("%s requires %zu argument%s",
- keyword.c_str(), min_args,
- (min_args > 1 || min_args == 0) ? "s" : "");
- return nullptr;
+ return Error() << StringPrintf("%s requires %zu argument%s", keyword.c_str(), min_args,
+ (min_args > 1 || min_args == 0) ? "s" : "");
}
if (num_args < min_args || num_args > max_args) {
if (max_args == std::numeric_limits<decltype(max_args)>::max()) {
- *err = StringPrintf("%s requires at least %zu argument%s",
- keyword.c_str(), min_args,
- min_args > 1 ? "s" : "");
+ return Error() << StringPrintf("%s requires at least %zu argument%s",
+ keyword.c_str(), min_args, min_args > 1 ? "s" : "");
} else {
- *err = StringPrintf("%s requires between %zu and %zu arguments",
- keyword.c_str(), min_args, max_args);
+ return Error() << StringPrintf("%s requires between %zu and %zu arguments",
+ keyword.c_str(), min_args, max_args);
}
- return nullptr;
}
return std::get<Function>(function_info);
diff --git a/init/log.cpp b/init/log.cpp
index 1830077..391bc1f 100644
--- a/init/log.cpp
+++ b/init/log.cpp
@@ -21,17 +21,35 @@
#include <string.h>
#include <android-base/logging.h>
+#include <cutils/android_reboot.h>
#include <selinux/selinux.h>
+#include "reboot.h"
+
namespace android {
namespace init {
+static void RebootAborter(const char* abort_message) {
+ // DoReboot() does a lot to try to shutdown the system cleanly. If something happens to call
+ // LOG(FATAL) in the shutdown path, we want to catch this and immediately use the syscall to
+ // reboot instead of recursing here.
+ static bool has_aborted = false;
+ if (!has_aborted) {
+ has_aborted = true;
+ // Do not queue "shutdown" trigger since we want to shutdown immediately and it's not likely
+ // that we can even run the ActionQueue at this point.
+ DoReboot(ANDROID_RB_RESTART2, "reboot", "bootloader", false);
+ } else {
+ RebootSystem(ANDROID_RB_RESTART2, "bootloader");
+ }
+}
+
void InitKernelLogging(char* argv[]) {
// Make stdin/stdout/stderr all point to /dev/null.
int fd = open("/sys/fs/selinux/null", O_RDWR);
if (fd == -1) {
int saved_errno = errno;
- android::base::InitLogging(argv, &android::base::KernelLogger);
+ android::base::InitLogging(argv, &android::base::KernelLogger, RebootAborter);
errno = saved_errno;
PLOG(FATAL) << "Couldn't open /sys/fs/selinux/null";
}
@@ -40,7 +58,7 @@
dup2(fd, 2);
if (fd > 2) close(fd);
- android::base::InitLogging(argv, &android::base::KernelLogger);
+ android::base::InitLogging(argv, &android::base::KernelLogger, RebootAborter);
}
int selinux_klog_callback(int type, const char *fmt, ...) {
diff --git a/init/parser.cpp b/init/parser.cpp
index c0fa6d9..8a4e798 100644
--- a/init/parser.cpp
+++ b/init/parser.cpp
@@ -1,123 +1,154 @@
+/*
+ * Copyright (C) 2010 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 "parser.h"
+#include <dirent.h>
+
+#include <android-base/chrono_utils.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+#include "tokenizer.h"
+#include "util.h"
+
namespace android {
namespace init {
-int next_token(struct parse_state *state)
-{
- char *x = state->ptr;
- char *s;
+Parser::Parser() {}
- if (state->nexttoken) {
- int t = state->nexttoken;
- state->nexttoken = 0;
- return t;
- }
+void Parser::AddSectionParser(const std::string& name, std::unique_ptr<SectionParser> parser) {
+ section_parsers_[name] = std::move(parser);
+}
+
+void Parser::AddSingleLineParser(const std::string& prefix, LineCallback callback) {
+ line_callbacks_.emplace_back(prefix, callback);
+}
+
+void Parser::ParseData(const std::string& filename, const std::string& data) {
+ // TODO: Use a parser with const input and remove this copy
+ std::vector<char> data_copy(data.begin(), data.end());
+ data_copy.push_back('\0');
+
+ parse_state state;
+ state.line = 0;
+ state.ptr = &data_copy[0];
+ state.nexttoken = 0;
+
+ SectionParser* section_parser = nullptr;
+ std::vector<std::string> args;
for (;;) {
- switch (*x) {
- case 0:
- state->ptr = x;
- return T_EOF;
- case '\n':
- x++;
- state->ptr = x;
- return T_NEWLINE;
- case ' ':
- case '\t':
- case '\r':
- x++;
- continue;
- case '#':
- while (*x && (*x != '\n')) x++;
- if (*x == '\n') {
- state->ptr = x+1;
- return T_NEWLINE;
- } else {
- state->ptr = x;
- return T_EOF;
- }
- default:
- goto text;
+ switch (next_token(&state)) {
+ case T_EOF:
+ if (section_parser) section_parser->EndSection();
+ return;
+ case T_NEWLINE:
+ state.line++;
+ if (args.empty()) break;
+ // If we have a line matching a prefix we recognize, call its callback and unset any
+ // current section parsers. This is meant for /sys/ and /dev/ line entries for
+ // uevent.
+ for (const auto& [prefix, callback] : line_callbacks_) {
+ if (android::base::StartsWith(args[0], prefix.c_str())) {
+ if (section_parser) section_parser->EndSection();
+
+ if (auto result = callback(std::move(args)); !result) {
+ LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
+ }
+ section_parser = nullptr;
+ break;
+ }
+ }
+ if (section_parsers_.count(args[0])) {
+ if (section_parser) section_parser->EndSection();
+ section_parser = section_parsers_[args[0]].get();
+ if (auto result =
+ section_parser->ParseSection(std::move(args), filename, state.line);
+ !result) {
+ LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
+ section_parser = nullptr;
+ }
+ } else if (section_parser) {
+ if (auto result = section_parser->ParseLineSection(std::move(args), state.line);
+ !result) {
+ LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
+ }
+ }
+ args.clear();
+ break;
+ case T_TEXT:
+ args.emplace_back(state.text);
+ break;
}
}
+}
+
+bool Parser::ParseConfigFile(const std::string& path) {
+ LOG(INFO) << "Parsing file " << path << "...";
+ android::base::Timer t;
+ auto config_contents = ReadFile(path);
+ if (!config_contents) {
+ LOG(ERROR) << "Unable to read config file '" << path << "': " << config_contents.error();
+ return false;
+ }
-textdone:
- state->ptr = x;
- *s = 0;
- return T_TEXT;
-text:
- state->text = s = x;
-textresume:
- for (;;) {
- switch (*x) {
- case 0:
- goto textdone;
- case ' ':
- case '\t':
- case '\r':
- x++;
- goto textdone;
- case '\n':
- state->nexttoken = T_NEWLINE;
- x++;
- goto textdone;
- case '"':
- x++;
- for (;;) {
- switch (*x) {
- case 0:
- /* unterminated quoted thing */
- state->ptr = x;
- return T_EOF;
- case '"':
- x++;
- goto textresume;
- default:
- *s++ = *x++;
- }
- }
- break;
- case '\\':
- x++;
- switch (*x) {
- case 0:
- goto textdone;
- case 'n':
- *s++ = '\n';
- break;
- case 'r':
- *s++ = '\r';
- break;
- case 't':
- *s++ = '\t';
- break;
- case '\\':
- *s++ = '\\';
- break;
- case '\r':
- /* \ <cr> <lf> -> line continuation */
- if (x[1] != '\n') {
- x++;
- continue;
- }
- case '\n':
- /* \ <lf> -> line continuation */
- state->line++;
- x++;
- /* eat any extra whitespace */
- while((*x == ' ') || (*x == '\t')) x++;
- continue;
- default:
- /* unknown escape -- just copy */
- *s++ = *x++;
- }
- continue;
- default:
- *s++ = *x++;
+ config_contents->push_back('\n'); // TODO: fix parse_config.
+ ParseData(path, *config_contents);
+ for (const auto& [section_name, section_parser] : section_parsers_) {
+ section_parser->EndFile();
+ }
+
+ LOG(VERBOSE) << "(Parsing " << path << " took " << t << ".)";
+ return true;
+}
+
+bool Parser::ParseConfigDir(const std::string& path) {
+ LOG(INFO) << "Parsing directory " << path << "...";
+ std::unique_ptr<DIR, decltype(&closedir)> config_dir(opendir(path.c_str()), closedir);
+ if (!config_dir) {
+ PLOG(ERROR) << "Could not import directory '" << path << "'";
+ return false;
+ }
+ dirent* current_file;
+ std::vector<std::string> files;
+ while ((current_file = readdir(config_dir.get()))) {
+ // Ignore directories and only process regular files.
+ if (current_file->d_type == DT_REG) {
+ std::string current_path =
+ android::base::StringPrintf("%s/%s", path.c_str(), current_file->d_name);
+ files.emplace_back(current_path);
}
}
- return T_EOF;
+ // Sort first so we load files in a consistent order (bug 31996208)
+ std::sort(files.begin(), files.end());
+ for (const auto& file : files) {
+ if (!ParseConfigFile(file)) {
+ LOG(ERROR) << "could not import file '" << file << "'";
+ }
+ }
+ return true;
+}
+
+bool Parser::ParseConfig(const std::string& path) {
+ if (is_dir(path.c_str())) {
+ return ParseConfigDir(path);
+ }
+ return ParseConfigFile(path);
}
} // namespace init
diff --git a/init/parser.h b/init/parser.h
index 86e4c57..4ab24a4 100644
--- a/init/parser.h
+++ b/init/parser.h
@@ -14,27 +14,79 @@
* limitations under the License.
*/
-#ifndef PARSER_H_
-#define PARSER_H_
+#ifndef _INIT_PARSER_H_
+#define _INIT_PARSER_H_
-#define T_EOF 0
-#define T_TEXT 1
-#define T_NEWLINE 2
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "result.h"
+
+// SectionParser is an interface that can parse a given 'section' in init.
+//
+// You can implement up to 4 functions below, with ParseSection() being mandatory.
+// The first two function return bool with false indicating a failure and has a std::string* err
+// parameter into which an error string can be written. It will be reported along with the
+// filename and line number of where the error occurred.
+//
+// 1) bool ParseSection(std::vector<std::string>&& args, const std::string& filename,
+// int line, std::string* err)
+// This function is called when a section is first encountered.
+//
+// 2) bool ParseLineSection(std::vector<std::string>&& args, int line, std::string* err)
+// This function is called on each subsequent line until the next section is encountered.
+//
+// 3) bool EndSection()
+// This function is called either when a new section is found or at the end of the file.
+// It indicates that parsing of the current section is complete and any relevant objects should
+// be committed.
+//
+// 4) bool EndFile()
+// This function is called at the end of the file.
+// It indicates that the parsing has completed and any relevant objects should be committed.
namespace android {
namespace init {
-struct parse_state
-{
- char *ptr;
- char *text;
- int line;
- int nexttoken;
+class SectionParser {
+ public:
+ virtual ~SectionParser() {}
+ virtual Result<Success> ParseSection(std::vector<std::string>&& args,
+ const std::string& filename, int line) = 0;
+ virtual Result<Success> ParseLineSection(std::vector<std::string>&&, int) { return Success(); };
+ virtual void EndSection(){};
+ virtual void EndFile(){};
};
-int next_token(struct parse_state *state);
+class Parser {
+ public:
+ // LineCallback is the type for callbacks that can parse a line starting with a given prefix.
+ //
+ // They take the form of bool Callback(std::vector<std::string>&& args, std::string* err)
+ //
+ // Similar to ParseSection() and ParseLineSection(), this function returns bool with false
+ // indicating a failure and has an std::string* err parameter into which an error string can
+ // be written.
+ using LineCallback = std::function<Result<Success>(std::vector<std::string>&&)>;
+
+ Parser();
+
+ bool ParseConfig(const std::string& path);
+ void AddSectionParser(const std::string& name, std::unique_ptr<SectionParser> parser);
+ void AddSingleLineParser(const std::string& prefix, LineCallback callback);
+
+ private:
+ void ParseData(const std::string& filename, const std::string& data);
+ bool ParseConfigFile(const std::string& path);
+ bool ParseConfigDir(const std::string& path);
+
+ std::map<std::string, std::unique_ptr<SectionParser>> section_parsers_;
+ std::vector<std::pair<std::string, LineCallback>> line_callbacks_;
+};
} // namespace init
} // namespace android
-#endif /* PARSER_H_ */
+#endif
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 925cc9b..f0e4d2e 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -68,6 +68,8 @@
static int property_set_fd = -1;
+static struct selabel_handle* sehandle_prop;
+
void property_init() {
if (__system_property_area_init()) {
LOG(ERROR) << "Failed to initialize property area";
@@ -586,14 +588,14 @@
// "ro.foo.*" is a prefix match, and "ro.foo.bar" is an exact match.
static void load_properties_from_file(const char* filename, const char* filter) {
Timer t;
- std::string data;
- std::string err;
- if (!ReadFile(filename, &data, &err)) {
- PLOG(WARNING) << "Couldn't load property file: " << err;
+ auto file_contents = ReadFile(filename);
+ if (!file_contents) {
+ PLOG(WARNING) << "Couldn't load property file '" << filename
+ << "': " << file_contents.error();
return;
}
- data.push_back('\n');
- load_properties(&data[0], filter);
+ file_contents->push_back('\n');
+ load_properties(file_contents->data(), filter);
LOG(VERBOSE) << "(Loading properties from " << filename << " took " << t << ".)";
}
@@ -688,6 +690,17 @@
* has mounted /data.
*/
void load_persist_props(void) {
+ // Devices with FDE have load_persist_props called twice; the first time when the temporary
+ // /data partition is mounted and then again once /data is truly mounted. We do not want to
+ // read persistent properties from the temporary /data partition or mark persistent properties
+ // as having been loaded during the first call, so we return in that case.
+ std::string crypto_state = android::base::GetProperty("ro.crypto.state", "");
+ std::string crypto_type = android::base::GetProperty("ro.crypto.type", "");
+ if (crypto_state == "encrypted" && crypto_type == "block") {
+ static size_t num_calls = 0;
+ if (++num_calls == 1) return;
+ }
+
load_override_properties();
/* Read persistent properties after all default values have been loaded. */
load_persistent_properties();
@@ -733,11 +746,30 @@
load_recovery_id_prop();
}
+static int SelinuxAuditCallback(void* data, security_class_t /*cls*/, char* buf, size_t len) {
+ property_audit_data* d = reinterpret_cast<property_audit_data*>(data);
+
+ if (!d || !d->name || !d->cr) {
+ LOG(ERROR) << "AuditCallback invoked with null data arguments!";
+ return 0;
+ }
+
+ snprintf(buf, len, "property=%s pid=%d uid=%d gid=%d", d->name, d->cr->pid, d->cr->uid,
+ d->cr->gid);
+ return 0;
+}
+
void start_property_service() {
+ sehandle_prop = selinux_android_prop_context_handle();
+
+ selinux_callback cb;
+ cb.func_audit = SelinuxAuditCallback;
+ selinux_set_callback(SELINUX_CB_AUDIT, cb);
+
property_set("ro.property_service.version", "2");
property_set_fd = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
- false, 0666, 0, 0, nullptr, sehandle);
+ false, 0666, 0, 0, nullptr);
if (property_set_fd == -1) {
PLOG(ERROR) << "start_property_service socket creation failed";
exit(1);
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 8196d58..97a8ddd 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -48,11 +48,13 @@
#include <fs_mgr.h>
#include <logwrap/logwrap.h>
#include <private/android_filesystem_config.h>
+#include <selinux/selinux.h>
#include "capabilities.h"
#include "init.h"
#include "property_service.h"
#include "service.h"
+#include "signal_handler.h"
using android::base::StringPrintf;
using android::base::Timer;
@@ -189,8 +191,7 @@
return value == CAP_SET;
}
-static void __attribute__((noreturn))
-RebootSystem(unsigned int cmd, const std::string& rebootTarget) {
+void __attribute__((noreturn)) RebootSystem(unsigned int cmd, const std::string& rebootTarget) {
LOG(INFO) << "Reboot ending, jumping to kernel";
if (!IsRebootCapable()) {
@@ -214,7 +215,7 @@
break;
}
// In normal case, reboot should not return.
- PLOG(FATAL) << "reboot call returned";
+ PLOG(ERROR) << "reboot call returned";
abort();
}
@@ -237,7 +238,8 @@
std::string mount_dir(mentry->mnt_dir);
// These are R/O partitions changed to R/W after adb remount.
// Do not umount them as shutdown critical services may rely on them.
- if (mount_dir != "/system" && mount_dir != "/vendor" && mount_dir != "/oem") {
+ if (mount_dir != "/" && mount_dir != "/system" && mount_dir != "/vendor" &&
+ mount_dir != "/oem") {
blockDevPartitions->emplace(blockDevPartitions->begin(), *mentry);
}
} else if (MountEntry::IsEmulatedDevice(*mentry)) {
@@ -264,8 +266,6 @@
static UmountStat UmountPartitions(std::chrono::milliseconds timeout) {
Timer t;
- UmountStat stat = UMOUNT_STAT_TIMEOUT;
- int retry = 0;
/* data partition needs all pending writes to be completed and all emulated partitions
* umounted.If the current waiting is not good enough, give
* up and leave it to e2fsck after reboot to fix it.
@@ -277,25 +277,27 @@
return UMOUNT_STAT_ERROR;
}
if (block_devices.size() == 0) {
- stat = UMOUNT_STAT_SUCCESS;
- break;
+ return UMOUNT_STAT_SUCCESS;
}
- if ((timeout < t.duration()) && retry > 0) { // try umount at least once
- stat = UMOUNT_STAT_TIMEOUT;
- break;
+ bool unmount_done = true;
+ if (emulated_devices.size() > 0) {
+ unmount_done = std::all_of(emulated_devices.begin(), emulated_devices.end(),
+ [](auto& entry) { return entry.Umount(); });
+ if (unmount_done) {
+ sync();
+ }
}
- if (emulated_devices.size() > 0 &&
- std::all_of(emulated_devices.begin(), emulated_devices.end(),
- [](auto& entry) { return entry.Umount(); })) {
- sync();
+ unmount_done = std::all_of(block_devices.begin(), block_devices.end(),
+ [](auto& entry) { return entry.Umount(); }) &&
+ unmount_done;
+ if (unmount_done) {
+ return UMOUNT_STAT_SUCCESS;
}
- for (auto& entry : block_devices) {
- entry.Umount();
+ if ((timeout < t.duration())) { // try umount at least once
+ return UMOUNT_STAT_TIMEOUT;
}
- retry++;
std::this_thread::sleep_for(100ms);
}
- return stat;
}
static void KillAllProcesses() { android::base::WriteStringToFile("i", "/proc/sysrq-trigger"); }
@@ -323,11 +325,11 @@
UmountStat stat = UmountPartitions(timeout - t.duration());
if (stat != UMOUNT_STAT_SUCCESS) {
LOG(INFO) << "umount timeout, last resort, kill all and try";
- if (DUMP_ON_UMOUNT_FAILURE) DumpUmountDebuggingInfo(false);
+ if (DUMP_ON_UMOUNT_FAILURE) DumpUmountDebuggingInfo(true);
KillAllProcesses();
// even if it succeeds, still it is timeout and do not run fsck with all processes killed
- UmountPartitions(0ms);
- if (DUMP_ON_UMOUNT_FAILURE) DumpUmountDebuggingInfo(true);
+ UmountStat st = UmountPartitions(0ms);
+ if ((st != UMOUNT_STAT_SUCCESS) && DUMP_ON_UMOUNT_FAILURE) DumpUmountDebuggingInfo(false);
}
if (stat == UMOUNT_STAT_SUCCESS && runFsck) {
@@ -340,40 +342,35 @@
return stat;
}
-static void __attribute__((noreturn)) DoThermalOff() {
- LOG(WARNING) << "Thermal system shutdown";
- sync();
- RebootSystem(ANDROID_RB_THERMOFF, "");
- abort();
-}
-
void DoReboot(unsigned int cmd, const std::string& reason, const std::string& rebootTarget,
bool runFsck) {
Timer t;
LOG(INFO) << "Reboot start, reason: " << reason << ", rebootTarget: " << rebootTarget;
- android::base::WriteStringToFile(StringPrintf("%s\n", reason.c_str()), LAST_REBOOT_REASON_FILE,
- S_IRUSR | S_IWUSR, AID_SYSTEM, AID_SYSTEM);
+ property_set(LAST_REBOOT_REASON_PROPERTY, reason.c_str());
+ sync();
- if (cmd == ANDROID_RB_THERMOFF) { // do not wait if it is thermal
- DoThermalOff();
- abort();
- }
+ bool is_thermal_shutdown = cmd == ANDROID_RB_THERMOFF;
- auto shutdown_timeout = 0s;
+ auto shutdown_timeout = 0ms;
if (!SHUTDOWN_ZERO_TIMEOUT) {
- constexpr unsigned int shutdown_timeout_default = 6;
- auto shutdown_timeout_property =
- android::base::GetUintProperty("ro.build.shutdown_timeout", shutdown_timeout_default);
- shutdown_timeout = std::chrono::seconds(shutdown_timeout_property);
+ if (is_thermal_shutdown) {
+ constexpr unsigned int thermal_shutdown_timeout = 1;
+ shutdown_timeout = std::chrono::seconds(thermal_shutdown_timeout);
+ } else {
+ constexpr unsigned int shutdown_timeout_default = 6;
+ auto shutdown_timeout_property = android::base::GetUintProperty(
+ "ro.build.shutdown_timeout", shutdown_timeout_default);
+ shutdown_timeout = std::chrono::seconds(shutdown_timeout_property);
+ }
}
- LOG(INFO) << "Shutdown timeout: " << shutdown_timeout.count() << " seconds";
+ LOG(INFO) << "Shutdown timeout: " << shutdown_timeout.count() << " ms";
// keep debugging tools until non critical ones are all gone.
const std::set<std::string> kill_after_apps{"tombstoned", "logd", "adbd"};
// watchdogd is a vendor specific component but should be alive to complete shutdown safely.
const std::set<std::string> to_starts{"watchdogd"};
- ServiceManager::GetInstance().ForEachService([&kill_after_apps, &to_starts](Service* s) {
+ for (const auto& s : ServiceList::GetInstance()) {
if (kill_after_apps.count(s->name())) {
s->SetShutdownCritical();
} else if (to_starts.count(s->name())) {
@@ -382,34 +379,35 @@
} else if (s->IsShutdownCritical()) {
s->Start(); // start shutdown critical service if not started
}
- });
+ }
- Service* bootAnim = ServiceManager::GetInstance().FindServiceByName("bootanim");
- Service* surfaceFlinger = ServiceManager::GetInstance().FindServiceByName("surfaceflinger");
+ Service* bootAnim = ServiceList::GetInstance().FindService("bootanim");
+ Service* surfaceFlinger = ServiceList::GetInstance().FindService("surfaceflinger");
if (bootAnim != nullptr && surfaceFlinger != nullptr && surfaceFlinger->IsRunning()) {
- ServiceManager::GetInstance().ForEachServiceInClass("animation", [](Service* s) {
- s->SetShutdownCritical(); // will not check animation class separately
- });
+ // will not check animation class separately
+ for (const auto& service : ServiceList::GetInstance()) {
+ if (service->classnames().count("animation")) service->SetShutdownCritical();
+ }
}
// optional shutdown step
// 1. terminate all services except shutdown critical ones. wait for delay to finish
- if (shutdown_timeout > 0s) {
+ if (shutdown_timeout > 0ms) {
LOG(INFO) << "terminating init services";
// Ask all services to terminate except shutdown critical ones.
- ServiceManager::GetInstance().ForEachService([](Service* s) {
+ for (const auto& s : ServiceList::GetInstance().services_in_shutdown_order()) {
if (!s->IsShutdownCritical()) s->Terminate();
- });
+ }
int service_count = 0;
- // Up to half as long as shutdown_timeout or 3 seconds, whichever is lower.
- auto termination_wait_timeout = std::min((shutdown_timeout + 1s) / 2, 3s);
+ // Only wait up to half of timeout here
+ auto termination_wait_timeout = shutdown_timeout / 2;
while (t.duration() < termination_wait_timeout) {
- ServiceManager::GetInstance().ReapAnyOutstandingChildren();
+ ReapAnyOutstandingChildren();
service_count = 0;
- ServiceManager::GetInstance().ForEachService([&service_count](Service* s) {
+ for (const auto& s : ServiceList::GetInstance()) {
// Count the number of services running except shutdown critical.
// Exclude the console as it will ignore the SIGTERM signal
// and not exit.
@@ -418,7 +416,7 @@
if (!s->IsShutdownCritical() && s->pid() != 0 && (s->flags() & SVC_CONSOLE) == 0) {
service_count++;
}
- });
+ }
if (service_count == 0) {
// All terminable services terminated. We can exit early.
@@ -434,13 +432,13 @@
// minimum safety steps before restarting
// 2. kill all services except ones that are necessary for the shutdown sequence.
- ServiceManager::GetInstance().ForEachService([](Service* s) {
+ for (const auto& s : ServiceList::GetInstance().services_in_shutdown_order()) {
if (!s->IsShutdownCritical()) s->Stop();
- });
- ServiceManager::GetInstance().ReapAnyOutstandingChildren();
+ }
+ ReapAnyOutstandingChildren();
// 3. send volume shutdown to vold
- Service* voldService = ServiceManager::GetInstance().FindServiceByName("vold");
+ Service* voldService = ServiceList::GetInstance().FindService("vold");
if (voldService != nullptr && voldService->IsRunning()) {
ShutdownVold();
voldService->Stop();
@@ -448,15 +446,15 @@
LOG(INFO) << "vold not running, skipping vold shutdown";
}
// logcat stopped here
- ServiceManager::GetInstance().ForEachService([&kill_after_apps](Service* s) {
+ for (const auto& s : ServiceList::GetInstance().services_in_shutdown_order()) {
if (kill_after_apps.count(s->name())) s->Stop();
- });
+ }
// 4. sync, try umount, and optionally run fsck for user shutdown
sync();
UmountStat stat = TryUmountAndFsck(runFsck, shutdown_timeout - t.duration());
// Follow what linux shutdown is doing: one more sync with little bit delay
sync();
- std::this_thread::sleep_for(100ms);
+ if (!is_thermal_shutdown) std::this_thread::sleep_for(100ms);
LogShutdownTime(stat, &t);
// Reboot regardless of umount status. If umount fails, fsck after reboot will fix it.
RebootSystem(cmd, rebootTarget);
@@ -474,10 +472,15 @@
command_invalid = true;
} else if (cmd_params[0] == "shutdown") {
cmd = ANDROID_RB_POWEROFF;
- if (cmd_params.size() == 2 && cmd_params[1] == "userrequested") {
- // The shutdown reason is PowerManager.SHUTDOWN_USER_REQUESTED.
- // Run fsck once the file system is remounted in read-only mode.
- run_fsck = true;
+ if (cmd_params.size() == 2) {
+ if (cmd_params[1] == "userrequested") {
+ // The shutdown reason is PowerManager.SHUTDOWN_USER_REQUESTED.
+ // Run fsck once the file system is remounted in read-only mode.
+ run_fsck = true;
+ } else if (cmd_params[1] == "thermal") {
+ // run_fsck is false to avoid delay
+ cmd = ANDROID_RB_THERMOFF;
+ }
}
} else if (cmd_params[0] == "reboot") {
cmd = ANDROID_RB_RESTART2;
@@ -493,16 +496,11 @@
<< err;
}
}
- // If there is an additional bootloader parameter, pass it along
- if (cmd_params.size() == 3) {
+ // If there is an additional parameter, pass it along
+ if ((cmd_params.size() == 3) && cmd_params[2].size()) {
reboot_target += "," + cmd_params[2];
}
}
- } else if (command == "thermal-shutdown") { // no additional parameter allowed
- cmd = ANDROID_RB_THERMOFF;
- // Do not queue "shutdown" trigger since we want to shutdown immediately
- DoReboot(cmd, command, reboot_target, run_fsck);
- return true;
} else {
command_invalid = true;
}
@@ -519,16 +517,16 @@
auto shutdown_handler = [cmd, command, reboot_target,
run_fsck](const std::vector<std::string>&) {
DoReboot(cmd, command, reboot_target, run_fsck);
- return 0;
+ return Success();
};
ActionManager::GetInstance().QueueBuiltinAction(shutdown_handler, "shutdown_done");
// Skip wait for prop if it is in progress
ResetWaitForProp();
- // Skip wait for exec if it is in progress
- if (ServiceManager::GetInstance().IsWaitingForExec()) {
- ServiceManager::GetInstance().ClearExecWait();
+ // Clear EXEC flag if there is one pending
+ for (const auto& s : ServiceList::GetInstance()) {
+ s->UnSetExec();
}
return true;
diff --git a/init/reboot.h b/init/reboot.h
index e559540..ece407f 100644
--- a/init/reboot.h
+++ b/init/reboot.h
@@ -22,9 +22,12 @@
namespace android {
namespace init {
+// This is a wrapper around the actual reboot calls. DoReboot() should be preferred in most cases.
+void __attribute__((noreturn)) RebootSystem(unsigned int cmd, const std::string& rebootTarget);
+
/* Reboot / shutdown the system.
* cmd ANDROID_RB_* as defined in android_reboot.h
- * reason Reason string like "reboot", "userrequested"
+ * reason Reason string like "reboot", "shutdown,userrequested"
* rebootTarget Reboot target string like "bootloader". Otherwise, it should be an
* empty string.
* runFsck Whether to run fsck after umount is done.
diff --git a/init/result.h b/init/result.h
new file mode 100644
index 0000000..36c3b83
--- /dev/null
+++ b/init/result.h
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+// This file contains classes for returning a successful result along with an optional
+// arbitrarily typed return value or for returning a failure result along with an optional string
+// indicating why the function failed.
+
+// There are 3 classes that implement this functionality and one additional helper type.
+//
+// Result<T> either contains a member of type T that can be accessed using similar semantics as
+// std::optional<T> or it contains a ResultError describing an error, which can be accessed via
+// Result<T>::error().
+//
+// ResultError is a type that contains both a std::string describing the error and a copy of errno
+// from when the error occurred. ResultError can be used in an ostream directly to print its
+// string value.
+//
+// Success is a typedef that aids in creating Result<T> that do not contain a return value.
+// Result<Success> is the correct return type for a function that either returns successfully or
+// returns an error value. Returning Success() from a function that returns Result<Success> is the
+// correct way to indicate that a function without a return type has completed successfully.
+//
+// A successful Result<T> is constructed implicitly from any type that can be implicitly converted
+// to T or from the constructor arguments for T. This allows you to return a type T directly from
+// a function that returns Result<T>.
+//
+// Error and ErrnoError are used to construct a Result<T> that has failed. The Error class takes
+// an ostream as an input and are implicitly cast to a Result<T> containing that failure.
+// ErrnoError() is a helper function to create an Error class that appends ": " + strerror(errno)
+// to the end of the failure string to aid in interacting with C APIs. Alternatively, an errno
+// value can be directly specified via the Error() constructor.
+//
+// ResultError can be used in the ostream when using Error to construct a Result<T>. In this case,
+// the string that the ResultError takes is passed through the stream normally, but the errno is
+// passed to the Result<T>. This can be used to pass errno from a failing C function up multiple
+// callers.
+//
+// ResultError can also directly construct a Result<T>. This is particularly useful if you have a
+// function that return Result<T> but you have a Result<U> and want to return its error. In this
+// case, you can return the .error() from the Result<U> to construct the Result<T>.
+
+// An example of how to use these is below:
+// Result<U> CalculateResult(const T& input) {
+// U output;
+// if (!SomeOtherCppFunction(input, &output)) {
+// return Error() << "SomeOtherCppFunction(" << input << ") failed";
+// }
+// if (!c_api_function(output)) {
+// return ErrnoError() << "c_api_function(" << output << ") failed";
+// }
+// return output;
+// }
+//
+// auto output = CalculateResult(input);
+// if (!output) return Error() << "CalculateResult failed: " << output.error();
+// UseOutput(*output);
+
+#ifndef _INIT_RESULT_H
+#define _INIT_RESULT_H
+
+#include <errno.h>
+
+#include <sstream>
+#include <string>
+#include <variant>
+
+namespace android {
+namespace init {
+
+struct ResultError {
+ template <typename T>
+ ResultError(T&& error_string, int error_errno)
+ : error_string(std::forward<T>(error_string)), error_errno(error_errno) {}
+
+ std::string error_string;
+ int error_errno;
+};
+
+inline std::ostream& operator<<(std::ostream& os, const ResultError& t) {
+ os << t.error_string;
+ return os;
+}
+
+inline std::ostream& operator<<(std::ostream& os, ResultError&& t) {
+ os << std::move(t.error_string);
+ return os;
+}
+
+class Error {
+ public:
+ Error() : errno_(0), append_errno_(false) {}
+ Error(int errno_to_append) : errno_(errno_to_append), append_errno_(true) {}
+
+ template <typename T>
+ Error&& operator<<(T&& t) {
+ ss_ << std::forward<T>(t);
+ return std::move(*this);
+ }
+
+ Error&& operator<<(const ResultError& result_error) {
+ ss_ << result_error.error_string;
+ errno_ = result_error.error_errno;
+ return std::move(*this);
+ }
+
+ Error&& operator<<(ResultError&& result_error) {
+ ss_ << std::move(result_error.error_string);
+ errno_ = result_error.error_errno;
+ return std::move(*this);
+ }
+
+ const std::string str() const {
+ std::string str = ss_.str();
+ if (append_errno_) {
+ if (str.empty()) {
+ return strerror(errno_);
+ }
+ return str + ": " + strerror(errno_);
+ }
+ return str;
+ }
+
+ int get_errno() const { return errno_; }
+
+ Error(const Error&) = delete;
+ Error(Error&&) = delete;
+ Error& operator=(const Error&) = delete;
+ Error& operator=(Error&&) = delete;
+
+ private:
+ std::stringstream ss_;
+ int errno_;
+ bool append_errno_;
+};
+
+inline Error ErrnoError() {
+ return Error(errno);
+}
+
+template <typename T>
+class Result {
+ public:
+ template <typename... U>
+ Result(U&&... result) : contents_(std::in_place_index_t<0>(), std::forward<U>(result)...) {}
+
+ Result(Error&& error) : contents_(std::in_place_index_t<1>(), error.str(), error.get_errno()) {}
+ Result(const ResultError& result_error)
+ : contents_(std::in_place_index_t<1>(), result_error.error_string,
+ result_error.error_errno) {}
+ Result(ResultError&& result_error)
+ : contents_(std::in_place_index_t<1>(), std::move(result_error.error_string),
+ result_error.error_errno) {}
+
+ bool has_value() const { return contents_.index() == 0; }
+
+ T& value() & { return std::get<0>(contents_); }
+ const T& value() const & { return std::get<0>(contents_); }
+ T&& value() && { return std::get<0>(std::move(contents_)); }
+ const T&& value() const && { return std::get<0>(std::move(contents_)); }
+
+ const ResultError& error() const & { return std::get<1>(contents_); }
+ ResultError&& error() && { return std::get<1>(std::move(contents_)); }
+ const ResultError&& error() const && { return std::get<1>(std::move(contents_)); }
+
+ const std::string& error_string() const & { return std::get<1>(contents_).error_string; }
+ std::string&& error_string() && { return std::get<1>(std::move(contents_)).error_string; }
+ const std::string&& error_string() const && {
+ return std::get<1>(std::move(contents_)).error_string;
+ }
+
+ int error_errno() const { return std::get<1>(contents_).error_errno; }
+
+ explicit operator bool() const { return has_value(); }
+
+ T& operator*() & { return value(); }
+ const T& operator*() const & { return value(); }
+ T&& operator*() && { return std::move(value()); }
+ const T&& operator*() const && { return std::move(value()); }
+
+ T* operator->() { return &value(); }
+ const T* operator->() const { return &value(); }
+
+ private:
+ std::variant<T, ResultError> contents_;
+};
+
+using Success = std::monostate;
+
+} // namespace init
+} // namespace android
+
+#endif
diff --git a/init/result_test.cpp b/init/result_test.cpp
new file mode 100644
index 0000000..19caaf5
--- /dev/null
+++ b/init/result_test.cpp
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2017 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 "result.h"
+
+#include "errno.h"
+
+#include <string>
+
+#include <gtest/gtest.h>
+
+using namespace std::string_literals;
+
+namespace android {
+namespace init {
+
+TEST(result, result_accessors) {
+ Result<std::string> result = "success";
+ ASSERT_TRUE(result);
+ ASSERT_TRUE(result.has_value());
+
+ EXPECT_EQ("success", *result);
+ EXPECT_EQ("success", result.value());
+
+ EXPECT_EQ('s', result->data()[0]);
+}
+
+TEST(result, result_accessors_rvalue) {
+ ASSERT_TRUE(Result<std::string>("success"));
+ ASSERT_TRUE(Result<std::string>("success").has_value());
+
+ EXPECT_EQ("success", *Result<std::string>("success"));
+ EXPECT_EQ("success", Result<std::string>("success").value());
+
+ EXPECT_EQ('s', Result<std::string>("success")->data()[0]);
+}
+
+TEST(result, result_success) {
+ Result<Success> result = Success();
+ ASSERT_TRUE(result);
+ ASSERT_TRUE(result.has_value());
+
+ EXPECT_EQ(Success(), *result);
+ EXPECT_EQ(Success(), result.value());
+}
+
+TEST(result, result_success_rvalue) {
+ // Success() doesn't actually create a Result<Success> object, but rather an object that can be
+ // implicitly constructed into a Result<Success> object.
+
+ auto MakeRvalueSuccessResult = []() -> Result<Success> { return Success(); };
+ ASSERT_TRUE(MakeRvalueSuccessResult());
+ ASSERT_TRUE(MakeRvalueSuccessResult().has_value());
+
+ EXPECT_EQ(Success(), *MakeRvalueSuccessResult());
+ EXPECT_EQ(Success(), MakeRvalueSuccessResult().value());
+}
+
+TEST(result, result_error) {
+ Result<Success> result = Error() << "failure" << 1;
+ ASSERT_FALSE(result);
+ ASSERT_FALSE(result.has_value());
+
+ EXPECT_EQ(0, result.error_errno());
+ EXPECT_EQ("failure1", result.error_string());
+}
+
+TEST(result, result_error_empty) {
+ Result<Success> result = Error();
+ ASSERT_FALSE(result);
+ ASSERT_FALSE(result.has_value());
+
+ EXPECT_EQ(0, result.error_errno());
+ EXPECT_EQ("", result.error_string());
+}
+
+TEST(result, result_error_rvalue) {
+ // Error() and ErrnoError() aren't actually used to create a Result<T> object.
+ // Under the hood, they are an intermediate class that can be implicitly constructed into a
+ // Result<T>. This is needed both to create the ostream and because Error() itself, by
+ // definition will not know what the type, T, of the underlying Result<T> object that it would
+ // create is.
+
+ auto MakeRvalueErrorResult = []() -> Result<Success> { return Error() << "failure" << 1; };
+ ASSERT_FALSE(MakeRvalueErrorResult());
+ ASSERT_FALSE(MakeRvalueErrorResult().has_value());
+
+ EXPECT_EQ(0, MakeRvalueErrorResult().error_errno());
+ EXPECT_EQ("failure1", MakeRvalueErrorResult().error_string());
+}
+
+TEST(result, result_errno_error) {
+ constexpr int test_errno = 6;
+ errno = test_errno;
+ Result<Success> result = ErrnoError() << "failure" << 1;
+
+ ASSERT_FALSE(result);
+ ASSERT_FALSE(result.has_value());
+
+ EXPECT_EQ(test_errno, result.error_errno());
+ EXPECT_EQ("failure1: "s + strerror(test_errno), result.error_string());
+}
+
+TEST(result, result_errno_error_no_text) {
+ constexpr int test_errno = 6;
+ errno = test_errno;
+ Result<Success> result = ErrnoError();
+
+ ASSERT_FALSE(result);
+ ASSERT_FALSE(result.has_value());
+
+ EXPECT_EQ(test_errno, result.error_errno());
+ EXPECT_EQ(strerror(test_errno), result.error_string());
+}
+
+TEST(result, result_error_from_other_result) {
+ auto error_text = "test error"s;
+ Result<Success> result = Error() << error_text;
+
+ ASSERT_FALSE(result);
+ ASSERT_FALSE(result.has_value());
+
+ Result<std::string> result2 = result.error();
+
+ ASSERT_FALSE(result2);
+ ASSERT_FALSE(result2.has_value());
+
+ EXPECT_EQ(0, result.error_errno());
+ EXPECT_EQ(error_text, result.error_string());
+}
+
+TEST(result, result_error_through_ostream) {
+ auto error_text = "test error"s;
+ Result<Success> result = Error() << error_text;
+
+ ASSERT_FALSE(result);
+ ASSERT_FALSE(result.has_value());
+
+ Result<std::string> result2 = Error() << result.error();
+
+ ASSERT_FALSE(result2);
+ ASSERT_FALSE(result2.has_value());
+
+ EXPECT_EQ(0, result.error_errno());
+ EXPECT_EQ(error_text, result.error_string());
+}
+
+TEST(result, result_errno_error_through_ostream) {
+ auto error_text = "test error"s;
+ constexpr int test_errno = 6;
+ errno = 6;
+ Result<Success> result = ErrnoError() << error_text;
+
+ errno = 0;
+
+ ASSERT_FALSE(result);
+ ASSERT_FALSE(result.has_value());
+
+ Result<std::string> result2 = Error() << result.error();
+
+ ASSERT_FALSE(result2);
+ ASSERT_FALSE(result2.has_value());
+
+ EXPECT_EQ(test_errno, result.error_errno());
+ EXPECT_EQ(error_text + ": " + strerror(test_errno), result.error_string());
+}
+
+TEST(result, constructor_forwarding) {
+ auto result = Result<std::string>(5, 'a');
+
+ ASSERT_TRUE(result);
+ ASSERT_TRUE(result.has_value());
+
+ EXPECT_EQ("aaaaa", *result);
+}
+
+struct ConstructorTracker {
+ static size_t constructor_called;
+ static size_t copy_constructor_called;
+ static size_t move_constructor_called;
+ static size_t copy_assignment_called;
+ static size_t move_assignment_called;
+
+ template <typename T>
+ ConstructorTracker(T&& string) : string(string) {
+ ++constructor_called;
+ }
+
+ ConstructorTracker(const ConstructorTracker& ct) {
+ ++copy_constructor_called;
+ string = ct.string;
+ }
+ ConstructorTracker(ConstructorTracker&& ct) noexcept {
+ ++move_constructor_called;
+ string = std::move(ct.string);
+ }
+ ConstructorTracker& operator=(const ConstructorTracker& ct) {
+ ++copy_assignment_called;
+ string = ct.string;
+ return *this;
+ }
+ ConstructorTracker& operator=(ConstructorTracker&& ct) noexcept {
+ ++move_assignment_called;
+ string = std::move(ct.string);
+ return *this;
+ }
+
+ std::string string;
+};
+
+size_t ConstructorTracker::constructor_called = 0;
+size_t ConstructorTracker::copy_constructor_called = 0;
+size_t ConstructorTracker::move_constructor_called = 0;
+size_t ConstructorTracker::copy_assignment_called = 0;
+size_t ConstructorTracker::move_assignment_called = 0;
+
+Result<ConstructorTracker> ReturnConstructorTracker(const std::string& in) {
+ if (in.empty()) {
+ return "literal string";
+ }
+ if (in == "test2") {
+ return ConstructorTracker(in + in + "2");
+ }
+ ConstructorTracker result(in + " " + in);
+ return result;
+};
+
+TEST(result, no_copy_on_return) {
+ // If returning parameters that may be used to implicitly construct the type T of Result<T>,
+ // then those parameters are forwarded to the construction of Result<T>.
+
+ // If returning an prvalue or xvalue, it will be move constructed during the construction of
+ // Result<T>.
+
+ // This check ensures that that is the case, and particularly that no copy constructors
+ // are called.
+
+ auto result1 = ReturnConstructorTracker("");
+ ASSERT_TRUE(result1);
+ EXPECT_EQ("literal string", result1->string);
+ EXPECT_EQ(1U, ConstructorTracker::constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
+
+ auto result2 = ReturnConstructorTracker("test2");
+ ASSERT_TRUE(result2);
+ EXPECT_EQ("test2test22", result2->string);
+ EXPECT_EQ(2U, ConstructorTracker::constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
+ EXPECT_EQ(1U, ConstructorTracker::move_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
+
+ auto result3 = ReturnConstructorTracker("test3");
+ ASSERT_TRUE(result3);
+ EXPECT_EQ("test3 test3", result3->string);
+ EXPECT_EQ(3U, ConstructorTracker::constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
+ EXPECT_EQ(2U, ConstructorTracker::move_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
+}
+
+TEST(result, die_on_access_failed_result) {
+ Result<std::string> result = Error();
+ ASSERT_DEATH(*result, "");
+}
+
+TEST(result, die_on_get_error_succesful_result) {
+ Result<std::string> result = "success";
+ ASSERT_DEATH(result.error_string(), "");
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/security.cpp b/init/security.cpp
new file mode 100644
index 0000000..aac8f2e
--- /dev/null
+++ b/init/security.cpp
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2017 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 "security.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <fstream>
+
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+
+using android::base::unique_fd;
+
+namespace android {
+namespace init {
+
+// Writes 512 bytes of output from Hardware RNG (/dev/hw_random, backed
+// by Linux kernel's hw_random framework) into Linux RNG's via /dev/urandom.
+// Does nothing if Hardware RNG is not present.
+//
+// Since we don't yet trust the quality of Hardware RNG, these bytes are not
+// mixed into the primary pool of Linux RNG and the entropy estimate is left
+// unmodified.
+//
+// If the HW RNG device /dev/hw_random is present, we require that at least
+// 512 bytes read from it are written into Linux RNG. QA is expected to catch
+// devices/configurations where these I/O operations are blocking for a long
+// time. We do not reboot or halt on failures, as this is a best-effort
+// attempt.
+Result<Success> MixHwrngIntoLinuxRngAction(const std::vector<std::string>& args) {
+ unique_fd hwrandom_fd(
+ TEMP_FAILURE_RETRY(open("/dev/hw_random", O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
+ if (hwrandom_fd == -1) {
+ if (errno == ENOENT) {
+ LOG(INFO) << "/dev/hw_random not found";
+ // It's not an error to not have a Hardware RNG.
+ return Success();
+ }
+ return ErrnoError() << "Failed to open /dev/hw_random";
+ }
+
+ unique_fd urandom_fd(
+ TEMP_FAILURE_RETRY(open("/dev/urandom", O_WRONLY | O_NOFOLLOW | O_CLOEXEC)));
+ if (urandom_fd == -1) {
+ return ErrnoError() << "Failed to open /dev/urandom";
+ }
+
+ char buf[512];
+ size_t total_bytes_written = 0;
+ while (total_bytes_written < sizeof(buf)) {
+ ssize_t chunk_size =
+ TEMP_FAILURE_RETRY(read(hwrandom_fd, buf, sizeof(buf) - total_bytes_written));
+ if (chunk_size == -1) {
+ return ErrnoError() << "Failed to read from /dev/hw_random";
+ } else if (chunk_size == 0) {
+ return Error() << "Failed to read from /dev/hw_random: EOF";
+ }
+
+ chunk_size = TEMP_FAILURE_RETRY(write(urandom_fd, buf, chunk_size));
+ if (chunk_size == -1) {
+ return ErrnoError() << "Failed to write to /dev/urandom";
+ }
+ total_bytes_written += chunk_size;
+ }
+
+ LOG(INFO) << "Mixed " << total_bytes_written << " bytes from /dev/hw_random into /dev/urandom";
+ return Success();
+}
+
+static bool SetHighestAvailableOptionValue(std::string path, int min, int max) {
+ std::ifstream inf(path, std::fstream::in);
+ if (!inf) {
+ LOG(ERROR) << "Cannot open for reading: " << path;
+ return false;
+ }
+
+ int current = max;
+ while (current >= min) {
+ // try to write out new value
+ std::string str_val = std::to_string(current);
+ std::ofstream of(path, std::fstream::out);
+ if (!of) {
+ LOG(ERROR) << "Cannot open for writing: " << path;
+ return false;
+ }
+ of << str_val << std::endl;
+ of.close();
+
+ // check to make sure it was recorded
+ inf.seekg(0);
+ std::string str_rec;
+ inf >> str_rec;
+ if (str_val.compare(str_rec) == 0) {
+ break;
+ }
+ current--;
+ }
+ inf.close();
+
+ if (current < min) {
+ LOG(ERROR) << "Unable to set minimum option value " << min << " in " << path;
+ return false;
+ }
+ return true;
+}
+
+#define MMAP_RND_PATH "/proc/sys/vm/mmap_rnd_bits"
+#define MMAP_RND_COMPAT_PATH "/proc/sys/vm/mmap_rnd_compat_bits"
+
+// __attribute__((unused)) due to lack of mips support: see mips block in SetMmapRndBitsAction
+static bool __attribute__((unused)) SetMmapRndBitsMin(int start, int min, bool compat) {
+ std::string path;
+ if (compat) {
+ path = MMAP_RND_COMPAT_PATH;
+ } else {
+ path = MMAP_RND_PATH;
+ }
+
+ return SetHighestAvailableOptionValue(path, min, start);
+}
+
+// Set /proc/sys/vm/mmap_rnd_bits and potentially
+// /proc/sys/vm/mmap_rnd_compat_bits to the maximum supported values.
+// Returns -1 if unable to set these to an acceptable value.
+//
+// To support this sysctl, the following upstream commits are needed:
+//
+// d07e22597d1d mm: mmap: add new /proc tunable for mmap_base ASLR
+// e0c25d958f78 arm: mm: support ARCH_MMAP_RND_BITS
+// 8f0d3aa9de57 arm64: mm: support ARCH_MMAP_RND_BITS
+// 9e08f57d684a x86: mm: support ARCH_MMAP_RND_BITS
+// ec9ee4acd97c drivers: char: random: add get_random_long()
+// 5ef11c35ce86 mm: ASLR: use get_random_long()
+Result<Success> SetMmapRndBitsAction(const std::vector<std::string>& args) {
+// values are arch-dependent
+#if defined(USER_MODE_LINUX)
+ // uml does not support mmap_rnd_bits
+ return Success();
+#elif defined(__aarch64__)
+ // arm64 supports 18 - 33 bits depending on pagesize and VA_SIZE
+ if (SetMmapRndBitsMin(33, 24, false) && SetMmapRndBitsMin(16, 16, true)) {
+ return Success();
+ }
+#elif defined(__x86_64__)
+ // x86_64 supports 28 - 32 bits
+ if (SetMmapRndBitsMin(32, 32, false) && SetMmapRndBitsMin(16, 16, true)) {
+ return Success();
+ }
+#elif defined(__arm__) || defined(__i386__)
+ // check to see if we're running on 64-bit kernel
+ bool h64 = !access(MMAP_RND_COMPAT_PATH, F_OK);
+ // supported 32-bit architecture must have 16 bits set
+ if (SetMmapRndBitsMin(16, 16, h64)) {
+ return Success();
+ }
+#elif defined(__mips__) || defined(__mips64__)
+ // TODO: add mips support b/27788820
+ return Success();
+#else
+ LOG(ERROR) << "Unknown architecture";
+#endif
+
+ LOG(FATAL) << "Unable to set adequate mmap entropy value!";
+ return Error();
+}
+
+#define KPTR_RESTRICT_PATH "/proc/sys/kernel/kptr_restrict"
+#define KPTR_RESTRICT_MINVALUE 2
+#define KPTR_RESTRICT_MAXVALUE 4
+
+// Set kptr_restrict to the highest available level.
+//
+// Aborts if unable to set this to an acceptable value.
+Result<Success> SetKptrRestrictAction(const std::vector<std::string>& args) {
+ std::string path = KPTR_RESTRICT_PATH;
+
+ if (!SetHighestAvailableOptionValue(path, KPTR_RESTRICT_MINVALUE, KPTR_RESTRICT_MAXVALUE)) {
+ LOG(FATAL) << "Unable to set adequate kptr_restrict value!";
+ return Error();
+ }
+ return Success();
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/security.h b/init/security.h
new file mode 100644
index 0000000..31e5790
--- /dev/null
+++ b/init/security.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2017 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 _INIT_SECURITY_H
+#define _INIT_SECURITY_H
+
+#include <string>
+#include <vector>
+
+#include "result.h"
+
+namespace android {
+namespace init {
+
+Result<Success> MixHwrngIntoLinuxRngAction(const std::vector<std::string>& args);
+Result<Success> SetMmapRndBitsAction(const std::vector<std::string>& args);
+Result<Success> SetKptrRestrictAction(const std::vector<std::string>& args);
+
+} // namespace init
+} // namespace android
+
+#endif
diff --git a/init/selinux.cpp b/init/selinux.cpp
new file mode 100644
index 0000000..ef59164
--- /dev/null
+++ b/init/selinux.cpp
@@ -0,0 +1,460 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+// This file contains the functions that initialize SELinux during boot as well as helper functions
+// for SELinux operation for init.
+
+// When the system boots, there is no SEPolicy present and init is running in the kernel domain.
+// Init loads the SEPolicy from the file system, restores the context of /init based on this
+// SEPolicy, and finally exec()'s itself to run in the proper domain.
+
+// The SEPolicy on Android comes in two variants: monolithic and split.
+
+// The monolithic policy variant is for legacy non-treble devices that contain a single SEPolicy
+// file located at /sepolicy and is directly loaded into the kernel SELinux subsystem.
+
+// The split policy is for supporting treble devices. It splits the SEPolicy across files on
+// /system/etc/selinux (the 'plat' portion of the policy) and /vendor/etc/selinux (the 'nonplat'
+// portion of the policy). This is necessary to allow the system image to be updated independently
+// of the vendor image, while maintaining contributions from both partitions in the SEPolicy. This
+// is especially important for VTS testing, where the SEPolicy on the Google System Image may not be
+// identical to the system image shipped on a vendor's device.
+
+// The split SEPolicy is loaded as described below:
+// 1) There is a precompiled SEPolicy located at /vendor/etc/selinux/precompiled_sepolicy.
+// Stored along with this file is the sha256 hash of the parts of the SEPolicy on /system that
+// were used to compile this precompiled policy. The system partition contains a similar sha256
+// of the parts of the SEPolicy that it currently contains. If these two hashes match, then the
+// system loads this precompiled_sepolicy directly.
+// 2) If these hashes do not match, then /system has been updated out of sync with /vendor and the
+// init needs to compile the SEPolicy. /system contains the SEPolicy compiler, secilc, and it
+// is used by the LoadSplitPolicy() function below to compile the SEPolicy to a temp directory
+// and load it. That function contains even more documentation with the specific implementation
+// details of how the SEPolicy is compiled if needed.
+
+#include "selinux.h"
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <android-base/chrono_utils.h>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <selinux/android.h>
+
+#include "log.h"
+#include "util.h"
+
+using android::base::Timer;
+using android::base::unique_fd;
+
+namespace android {
+namespace init {
+
+namespace {
+
+selabel_handle* sehandle = nullptr;
+
+enum EnforcingStatus { SELINUX_PERMISSIVE, SELINUX_ENFORCING };
+
+EnforcingStatus StatusFromCmdline() {
+ EnforcingStatus status = SELINUX_ENFORCING;
+
+ import_kernel_cmdline(false,
+ [&](const std::string& key, const std::string& value, bool in_qemu) {
+ if (key == "androidboot.selinux" && value == "permissive") {
+ status = SELINUX_PERMISSIVE;
+ }
+ });
+
+ return status;
+}
+
+bool IsEnforcing() {
+ if (ALLOW_PERMISSIVE_SELINUX) {
+ return StatusFromCmdline() == SELINUX_ENFORCING;
+ }
+ return true;
+}
+
+// Forks, executes the provided program in the child, and waits for the completion in the parent.
+// Child's stderr is captured and logged using LOG(ERROR).
+bool ForkExecveAndWaitForCompletion(const char* filename, char* const argv[]) {
+ // Create a pipe used for redirecting child process's output.
+ // * pipe_fds[0] is the FD the parent will use for reading.
+ // * pipe_fds[1] is the FD the child will use for writing.
+ int pipe_fds[2];
+ if (pipe(pipe_fds) == -1) {
+ PLOG(ERROR) << "Failed to create pipe";
+ return false;
+ }
+
+ pid_t child_pid = fork();
+ if (child_pid == -1) {
+ PLOG(ERROR) << "Failed to fork for " << filename;
+ return false;
+ }
+
+ if (child_pid == 0) {
+ // fork succeeded -- this is executing in the child process
+
+ // Close the pipe FD not used by this process
+ TEMP_FAILURE_RETRY(close(pipe_fds[0]));
+
+ // Redirect stderr to the pipe FD provided by the parent
+ if (TEMP_FAILURE_RETRY(dup2(pipe_fds[1], STDERR_FILENO)) == -1) {
+ PLOG(ERROR) << "Failed to redirect stderr of " << filename;
+ _exit(127);
+ return false;
+ }
+ TEMP_FAILURE_RETRY(close(pipe_fds[1]));
+
+ if (execv(filename, argv) == -1) {
+ PLOG(ERROR) << "Failed to execve " << filename;
+ return false;
+ }
+ // Unreachable because execve will have succeeded and replaced this code
+ // with child process's code.
+ _exit(127);
+ return false;
+ } else {
+ // fork succeeded -- this is executing in the original/parent process
+
+ // Close the pipe FD not used by this process
+ TEMP_FAILURE_RETRY(close(pipe_fds[1]));
+
+ // Log the redirected output of the child process.
+ // It's unfortunate that there's no standard way to obtain an istream for a file descriptor.
+ // As a result, we're buffering all output and logging it in one go at the end of the
+ // invocation, instead of logging it as it comes in.
+ const int child_out_fd = pipe_fds[0];
+ std::string child_output;
+ if (!android::base::ReadFdToString(child_out_fd, &child_output)) {
+ PLOG(ERROR) << "Failed to capture full output of " << filename;
+ }
+ TEMP_FAILURE_RETRY(close(child_out_fd));
+ if (!child_output.empty()) {
+ // Log captured output, line by line, because LOG expects to be invoked for each line
+ std::istringstream in(child_output);
+ std::string line;
+ while (std::getline(in, line)) {
+ LOG(ERROR) << filename << ": " << line;
+ }
+ }
+
+ // Wait for child to terminate
+ int status;
+ if (TEMP_FAILURE_RETRY(waitpid(child_pid, &status, 0)) != child_pid) {
+ PLOG(ERROR) << "Failed to wait for " << filename;
+ return false;
+ }
+
+ if (WIFEXITED(status)) {
+ int status_code = WEXITSTATUS(status);
+ if (status_code == 0) {
+ return true;
+ } else {
+ LOG(ERROR) << filename << " exited with status " << status_code;
+ }
+ } else if (WIFSIGNALED(status)) {
+ LOG(ERROR) << filename << " killed by signal " << WTERMSIG(status);
+ } else if (WIFSTOPPED(status)) {
+ LOG(ERROR) << filename << " stopped by signal " << WSTOPSIG(status);
+ } else {
+ LOG(ERROR) << "waitpid for " << filename << " returned unexpected status: " << status;
+ }
+
+ return false;
+ }
+}
+
+bool ReadFirstLine(const char* file, std::string* line) {
+ line->clear();
+
+ std::string contents;
+ if (!android::base::ReadFileToString(file, &contents, true /* follow symlinks */)) {
+ return false;
+ }
+ std::istringstream in(contents);
+ std::getline(in, *line);
+ return true;
+}
+
+bool FindPrecompiledSplitPolicy(std::string* file) {
+ file->clear();
+
+ static constexpr const char precompiled_sepolicy[] = "/vendor/etc/selinux/precompiled_sepolicy";
+ if (access(precompiled_sepolicy, R_OK) == -1) {
+ return false;
+ }
+ std::string actual_plat_id;
+ if (!ReadFirstLine("/system/etc/selinux/plat_and_mapping_sepolicy.cil.sha256", &actual_plat_id)) {
+ PLOG(INFO) << "Failed to read "
+ "/system/etc/selinux/plat_and_mapping_sepolicy.cil.sha256";
+ return false;
+ }
+ std::string precompiled_plat_id;
+ if (!ReadFirstLine("/vendor/etc/selinux/precompiled_sepolicy.plat_and_mapping.sha256",
+ &precompiled_plat_id)) {
+ PLOG(INFO) << "Failed to read "
+ "/vendor/etc/selinux/"
+ "precompiled_sepolicy.plat_and_mapping.sha256";
+ return false;
+ }
+ if ((actual_plat_id.empty()) || (actual_plat_id != precompiled_plat_id)) {
+ return false;
+ }
+
+ *file = precompiled_sepolicy;
+ return true;
+}
+
+bool GetVendorMappingVersion(std::string* plat_vers) {
+ if (!ReadFirstLine("/vendor/etc/selinux/plat_sepolicy_vers.txt", plat_vers)) {
+ PLOG(ERROR) << "Failed to read /vendor/etc/selinux/plat_sepolicy_vers.txt";
+ return false;
+ }
+ if (plat_vers->empty()) {
+ LOG(ERROR) << "No version present in plat_sepolicy_vers.txt";
+ return false;
+ }
+ return true;
+}
+
+constexpr const char plat_policy_cil_file[] = "/system/etc/selinux/plat_sepolicy.cil";
+
+bool IsSplitPolicyDevice() {
+ return access(plat_policy_cil_file, R_OK) != -1;
+}
+
+bool LoadSplitPolicy() {
+ // IMPLEMENTATION NOTE: Split policy consists of three CIL files:
+ // * platform -- policy needed due to logic contained in the system image,
+ // * non-platform -- policy needed due to logic contained in the vendor image,
+ // * mapping -- mapping policy which helps preserve forward-compatibility of non-platform policy
+ // with newer versions of platform policy.
+ //
+ // secilc is invoked to compile the above three policy files into a single monolithic policy
+ // file. This file is then loaded into the kernel.
+
+ // Load precompiled policy from vendor image, if a matching policy is found there. The policy
+ // must match the platform policy on the system image.
+ std::string precompiled_sepolicy_file;
+ if (FindPrecompiledSplitPolicy(&precompiled_sepolicy_file)) {
+ unique_fd fd(open(precompiled_sepolicy_file.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY));
+ if (fd != -1) {
+ if (selinux_android_load_policy_from_fd(fd, precompiled_sepolicy_file.c_str()) < 0) {
+ LOG(ERROR) << "Failed to load SELinux policy from " << precompiled_sepolicy_file;
+ return false;
+ }
+ return true;
+ }
+ }
+ // No suitable precompiled policy could be loaded
+
+ LOG(INFO) << "Compiling SELinux policy";
+
+ // Determine the highest policy language version supported by the kernel
+ set_selinuxmnt("/sys/fs/selinux");
+ int max_policy_version = security_policyvers();
+ if (max_policy_version == -1) {
+ PLOG(ERROR) << "Failed to determine highest policy version supported by kernel";
+ return false;
+ }
+
+ // We store the output of the compilation on /dev because this is the most convenient tmpfs
+ // storage mount available this early in the boot sequence.
+ char compiled_sepolicy[] = "/dev/sepolicy.XXXXXX";
+ unique_fd compiled_sepolicy_fd(mkostemp(compiled_sepolicy, O_CLOEXEC));
+ if (compiled_sepolicy_fd < 0) {
+ PLOG(ERROR) << "Failed to create temporary file " << compiled_sepolicy;
+ return false;
+ }
+
+ // Determine which mapping file to include
+ std::string vend_plat_vers;
+ if (!GetVendorMappingVersion(&vend_plat_vers)) {
+ return false;
+ }
+ std::string mapping_file("/system/etc/selinux/mapping/" + vend_plat_vers + ".cil");
+ const std::string version_as_string = std::to_string(max_policy_version);
+
+ // clang-format off
+ const char* compile_args[] = {
+ "/system/bin/secilc",
+ plat_policy_cil_file,
+ "-M", "true", "-G", "-N",
+ // Target the highest policy language version supported by the kernel
+ "-c", version_as_string.c_str(),
+ mapping_file.c_str(),
+ "/vendor/etc/selinux/nonplat_sepolicy.cil",
+ "-o", compiled_sepolicy,
+ // We don't care about file_contexts output by the compiler
+ "-f", "/sys/fs/selinux/null", // /dev/null is not yet available
+ nullptr};
+ // clang-format on
+
+ if (!ForkExecveAndWaitForCompletion(compile_args[0], (char**)compile_args)) {
+ unlink(compiled_sepolicy);
+ return false;
+ }
+ unlink(compiled_sepolicy);
+
+ LOG(INFO) << "Loading compiled SELinux policy";
+ if (selinux_android_load_policy_from_fd(compiled_sepolicy_fd, compiled_sepolicy) < 0) {
+ LOG(ERROR) << "Failed to load SELinux policy from " << compiled_sepolicy;
+ return false;
+ }
+
+ return true;
+}
+
+bool LoadMonolithicPolicy() {
+ LOG(VERBOSE) << "Loading SELinux policy from monolithic file";
+ if (selinux_android_load_policy() < 0) {
+ PLOG(ERROR) << "Failed to load monolithic SELinux policy";
+ return false;
+ }
+ return true;
+}
+
+bool LoadPolicy() {
+ return IsSplitPolicyDevice() ? LoadSplitPolicy() : LoadMonolithicPolicy();
+}
+
+} // namespace
+
+void SelinuxInitialize() {
+ Timer t;
+
+ LOG(INFO) << "Loading SELinux policy";
+ if (!LoadPolicy()) {
+ LOG(FATAL) << "Unable to load SELinux policy";
+ }
+
+ bool kernel_enforcing = (security_getenforce() == 1);
+ bool is_enforcing = IsEnforcing();
+ if (kernel_enforcing != is_enforcing) {
+ if (security_setenforce(is_enforcing)) {
+ PLOG(FATAL) << "security_setenforce(%s) failed" << (is_enforcing ? "true" : "false");
+ }
+ }
+
+ if (auto result = WriteFile("/sys/fs/selinux/checkreqprot", "0"); !result) {
+ LOG(FATAL) << "Unable to write to /sys/fs/selinux/checkreqprot: " << result.error();
+ }
+
+ // init's first stage can't set properties, so pass the time to the second stage.
+ setenv("INIT_SELINUX_TOOK", std::to_string(t.duration().count()).c_str(), 1);
+}
+
+// The files and directories that were created before initial sepolicy load or
+// files on ramdisk need to have their security context restored to the proper
+// value. This must happen before /dev is populated by ueventd.
+void SelinuxRestoreContext() {
+ LOG(INFO) << "Running restorecon...";
+ selinux_android_restorecon("/dev", 0);
+ selinux_android_restorecon("/dev/kmsg", 0);
+ if constexpr (WORLD_WRITABLE_KMSG) {
+ selinux_android_restorecon("/dev/kmsg_debug", 0);
+ }
+ selinux_android_restorecon("/dev/socket", 0);
+ selinux_android_restorecon("/dev/random", 0);
+ selinux_android_restorecon("/dev/urandom", 0);
+ selinux_android_restorecon("/dev/__properties__", 0);
+
+ selinux_android_restorecon("/file_contexts.bin", 0);
+ selinux_android_restorecon("/plat_file_contexts", 0);
+ selinux_android_restorecon("/nonplat_file_contexts", 0);
+ selinux_android_restorecon("/plat_property_contexts", 0);
+ selinux_android_restorecon("/nonplat_property_contexts", 0);
+ selinux_android_restorecon("/plat_seapp_contexts", 0);
+ selinux_android_restorecon("/nonplat_seapp_contexts", 0);
+ selinux_android_restorecon("/plat_service_contexts", 0);
+ selinux_android_restorecon("/nonplat_service_contexts", 0);
+ selinux_android_restorecon("/plat_hwservice_contexts", 0);
+ selinux_android_restorecon("/nonplat_hwservice_contexts", 0);
+ selinux_android_restorecon("/sepolicy", 0);
+ selinux_android_restorecon("/vndservice_contexts", 0);
+
+ selinux_android_restorecon("/dev/block", SELINUX_ANDROID_RESTORECON_RECURSE);
+ selinux_android_restorecon("/dev/device-mapper", 0);
+
+ selinux_android_restorecon("/sbin/mke2fs_static", 0);
+ selinux_android_restorecon("/sbin/e2fsdroid_static", 0);
+}
+
+// This function sets up SELinux logging to be written to kmsg, to match init's logging.
+void SelinuxSetupKernelLogging() {
+ selinux_callback cb;
+ cb.func_log = selinux_klog_callback;
+ selinux_set_callback(SELINUX_CB_LOG, cb);
+}
+
+// selinux_android_file_context_handle() takes on the order of 10+ms to run, so we want to cache
+// its value. selinux_android_restorecon() also needs an sehandle for file context look up. It
+// will create and store its own copy, but selinux_android_set_sehandle() can be used to provide
+// one, thus eliminating an extra call to selinux_android_file_context_handle().
+void SelabelInitialize() {
+ sehandle = selinux_android_file_context_handle();
+ selinux_android_set_sehandle(sehandle);
+}
+
+// A C++ wrapper around selabel_lookup() using the cached sehandle.
+// If sehandle is null, this returns success with an empty context.
+bool SelabelLookupFileContext(const std::string& key, int type, std::string* result) {
+ result->clear();
+
+ if (!sehandle) return true;
+
+ char* context;
+ if (selabel_lookup(sehandle, &context, key.c_str(), type) != 0) {
+ return false;
+ }
+ *result = context;
+ free(context);
+ return true;
+}
+
+// A C++ wrapper around selabel_lookup_best_match() using the cached sehandle.
+// If sehandle is null, this returns success with an empty context.
+bool SelabelLookupFileContextBestMatch(const std::string& key,
+ const std::vector<std::string>& aliases, int type,
+ std::string* result) {
+ result->clear();
+
+ if (!sehandle) return true;
+
+ std::vector<const char*> c_aliases;
+ for (const auto& alias : aliases) {
+ c_aliases.emplace_back(alias.c_str());
+ }
+ c_aliases.emplace_back(nullptr);
+
+ char* context;
+ if (selabel_lookup_best_match(sehandle, &context, key.c_str(), &c_aliases[0], type) != 0) {
+ return false;
+ }
+ *result = context;
+ free(context);
+ return true;
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/selinux.h b/init/selinux.h
new file mode 100644
index 0000000..7b880ec
--- /dev/null
+++ b/init/selinux.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2017 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 _INIT_SELINUX_H
+#define _INIT_SELINUX_H
+
+#include <string>
+#include <vector>
+
+namespace android {
+namespace init {
+
+void SelinuxInitialize();
+void SelinuxRestoreContext();
+
+void SelinuxSetupKernelLogging();
+
+void SelabelInitialize();
+bool SelabelLookupFileContext(const std::string& key, int type, std::string* result);
+bool SelabelLookupFileContextBestMatch(const std::string& key,
+ const std::vector<std::string>& aliases, int type,
+ std::string* result);
+
+} // namespace init
+} // namespace android
+
+#endif
diff --git a/init/service.cpp b/init/service.cpp
index 7a657c8..d3c9f92 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -57,22 +57,20 @@
namespace android {
namespace init {
-static std::string ComputeContextFromExecutable(std::string& service_name,
- const std::string& service_path) {
+static Result<std::string> ComputeContextFromExecutable(std::string& service_name,
+ const std::string& service_path) {
std::string computed_context;
char* raw_con = nullptr;
char* raw_filecon = nullptr;
if (getcon(&raw_con) == -1) {
- LOG(ERROR) << "could not get context while starting '" << service_name << "'";
- return "";
+ return Error() << "Could not get security context";
}
std::unique_ptr<char> mycon(raw_con);
if (getfilecon(service_path.c_str(), &raw_filecon) == -1) {
- LOG(ERROR) << "could not get file context while starting '" << service_name << "'";
- return "";
+ return Error() << "Could not get file context";
}
std::unique_ptr<char> filecon(raw_filecon);
@@ -84,12 +82,10 @@
free(new_con);
}
if (rc == 0 && computed_context == mycon.get()) {
- LOG(ERROR) << "service " << service_name << " does not have a SELinux domain defined";
- return "";
+ return Error() << "Service does not have an SELinux domain defined";
}
if (rc < 0) {
- LOG(ERROR) << "could not get context while starting '" << service_name << "'";
- return "";
+ return Error() << "Could not get process context";
}
return computed_context;
}
@@ -147,33 +143,11 @@
strs->push_back(nullptr);
}
-ServiceEnvironmentInfo::ServiceEnvironmentInfo() {
-}
-
-ServiceEnvironmentInfo::ServiceEnvironmentInfo(const std::string& name,
- const std::string& value)
- : name(name), value(value) {
-}
+unsigned long Service::next_start_order_ = 1;
+bool Service::is_exec_service_running_ = false;
Service::Service(const std::string& name, const std::vector<std::string>& args)
- : name_(name),
- classnames_({"default"}),
- flags_(0),
- pid_(0),
- crash_count_(0),
- uid_(0),
- gid_(0),
- namespace_flags_(0),
- seclabel_(""),
- onrestart_(false, "<Service '" + name + "' onrestart>", 0),
- keychord_id_(0),
- ioprio_class_(IoSchedClass_NONE),
- ioprio_pri_(0),
- priority_(0),
- oom_score_adjust_(-1000),
- args_(args) {
- onrestart_.InitSingleTrigger("onrestart");
-}
+ : Service(name, 0, 0, 0, {}, 0, 0, "", args) {}
Service::Service(const std::string& name, unsigned flags, uid_t uid, gid_t gid,
const std::vector<gid_t>& supp_gids, const CapSet& capabilities,
@@ -196,6 +170,10 @@
ioprio_pri_(0),
priority_(0),
oom_score_adjust_(-1000),
+ swappiness_(-1),
+ soft_limit_in_bytes_(-1),
+ limit_in_bytes_(-1),
+ start_order_(0),
args_(args) {
onrestart_.InitSingleTrigger("onrestart");
}
@@ -211,7 +189,10 @@
if (new_state == "running") {
uint64_t start_ns = time_started_.time_since_epoch().count();
- property_set("ro.boottime." + name_, std::to_string(start_ns));
+ std::string boottime_property = "ro.boottime." + name_;
+ if (GetProperty(boottime_property, "").empty()) {
+ property_set(boottime_property, std::to_string(start_ns));
+ }
}
}
@@ -239,8 +220,8 @@
if (capabilities_.any() && uid_) {
// If Android is running in a container, some securebits might already
// be locked, so don't change those.
- int64_t securebits = prctl(PR_GET_SECUREBITS);
- if (securebits == -1) {
+ unsigned long securebits = prctl(PR_GET_SECUREBITS);
+ if (securebits == -1UL) {
PLOG(FATAL) << "prctl(PR_GET_SECUREBITS) failed for " << name_;
}
securebits |= SECBIT_KEEP_CAPS | SECBIT_KEEP_CAPS_LOCKED;
@@ -291,12 +272,13 @@
std::for_each(descriptors_.begin(), descriptors_.end(),
std::bind(&DescriptorInfo::Clean, std::placeholders::_1));
- if (flags_ & SVC_TEMPORARY) {
- return;
- }
+ if (flags_ & SVC_EXEC) UnSetExec();
+
+ if (flags_ & SVC_TEMPORARY) return;
pid_ = 0;
flags_ &= (~SVC_RUNNING);
+ start_order_ = 0;
// Oneshot processes go into the disabled state on exit,
// except when manually restarted.
@@ -315,8 +297,7 @@
if ((flags_ & SVC_CRITICAL) && !(flags_ & SVC_RESTART)) {
if (now < time_crashed_ + 4min) {
if (++crash_count_ > 4) {
- LOG(ERROR) << "critical process '" << name_ << "' exited 4 times in 4 minutes";
- panic();
+ LOG(FATAL) << "critical process '" << name_ << "' exited 4 times in 4 minutes";
}
} else {
time_crashed_ = now;
@@ -342,12 +323,12 @@
[] (const auto& info) { LOG(INFO) << *info; });
}
-bool Service::ParseCapabilities(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseCapabilities(const std::vector<std::string>& args) {
capabilities_ = 0;
if (!CapAmbientSupported()) {
- *err = "capabilities requested but the kernel does not support ambient capabilities";
- return false;
+ return Error()
+ << "capabilities requested but the kernel does not support ambient capabilities";
}
unsigned int last_valid_cap = GetLastValidCap();
@@ -359,74 +340,71 @@
const std::string& arg = args[i];
int res = LookupCap(arg);
if (res < 0) {
- *err = StringPrintf("invalid capability '%s'", arg.c_str());
- return false;
+ return Error() << StringPrintf("invalid capability '%s'", arg.c_str());
}
unsigned int cap = static_cast<unsigned int>(res); // |res| is >= 0.
if (cap > last_valid_cap) {
- *err = StringPrintf("capability '%s' not supported by the kernel", arg.c_str());
- return false;
+ return Error() << StringPrintf("capability '%s' not supported by the kernel",
+ arg.c_str());
}
capabilities_[cap] = true;
}
- return true;
+ return Success();
}
-bool Service::ParseClass(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseClass(const std::vector<std::string>& args) {
classnames_ = std::set<std::string>(args.begin() + 1, args.end());
- return true;
+ return Success();
}
-bool Service::ParseConsole(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseConsole(const std::vector<std::string>& args) {
flags_ |= SVC_CONSOLE;
console_ = args.size() > 1 ? "/dev/" + args[1] : "";
- return true;
+ return Success();
}
-bool Service::ParseCritical(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseCritical(const std::vector<std::string>& args) {
flags_ |= SVC_CRITICAL;
- return true;
+ return Success();
}
-bool Service::ParseDisabled(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseDisabled(const std::vector<std::string>& args) {
flags_ |= SVC_DISABLED;
flags_ |= SVC_RC_DISABLED;
- return true;
+ return Success();
}
-bool Service::ParseGroup(const std::vector<std::string>& args, std::string* err) {
- std::string decode_uid_err;
- if (!DecodeUid(args[1], &gid_, &decode_uid_err)) {
- *err = "Unable to find GID for '" + args[1] + "': " + decode_uid_err;
- return false;
+Result<Success> Service::ParseGroup(const std::vector<std::string>& args) {
+ auto gid = DecodeUid(args[1]);
+ if (!gid) {
+ return Error() << "Unable to decode GID for '" << args[1] << "': " << gid.error();
}
+ gid_ = *gid;
+
for (std::size_t n = 2; n < args.size(); n++) {
- gid_t gid;
- if (!DecodeUid(args[n], &gid, &decode_uid_err)) {
- *err = "Unable to find GID for '" + args[n] + "': " + decode_uid_err;
- return false;
+ gid = DecodeUid(args[n]);
+ if (!gid) {
+ return Error() << "Unable to decode GID for '" << args[n] << "': " << gid.error();
}
- supp_gids_.emplace_back(gid);
+ supp_gids_.emplace_back(*gid);
}
- return true;
+ return Success();
}
-bool Service::ParsePriority(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParsePriority(const std::vector<std::string>& args) {
priority_ = 0;
if (!ParseInt(args[1], &priority_,
static_cast<int>(ANDROID_PRIORITY_HIGHEST), // highest is negative
static_cast<int>(ANDROID_PRIORITY_LOWEST))) {
- *err = StringPrintf("process priority value must be range %d - %d",
- ANDROID_PRIORITY_HIGHEST, ANDROID_PRIORITY_LOWEST);
- return false;
+ return Error() << StringPrintf("process priority value must be range %d - %d",
+ ANDROID_PRIORITY_HIGHEST, ANDROID_PRIORITY_LOWEST);
}
- return true;
+ return Success();
}
-bool Service::ParseIoprio(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseIoprio(const std::vector<std::string>& args) {
if (!ParseInt(args[2], &ioprio_pri_, 0, 7)) {
- *err = "priority value must be range 0 - 7";
- return false;
+ return Error() << "priority value must be range 0 - 7";
}
if (args[1] == "rt") {
@@ -436,14 +414,13 @@
} else if (args[1] == "idle") {
ioprio_class_ = IoSchedClass_IDLE;
} else {
- *err = "ioprio option usage: ioprio <rt|be|idle> <0-7>";
- return false;
+ return Error() << "ioprio option usage: ioprio <rt|be|idle> <0-7>";
}
- return true;
+ return Success();
}
-bool Service::ParseKeycodes(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseKeycodes(const std::vector<std::string>& args) {
for (std::size_t i = 1; i < args.size(); i++) {
int code;
if (ParseInt(args[i], &code)) {
@@ -452,22 +429,24 @@
LOG(WARNING) << "ignoring invalid keycode: " << args[i];
}
}
- return true;
+ return Success();
}
-bool Service::ParseOneshot(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseOneshot(const std::vector<std::string>& args) {
flags_ |= SVC_ONESHOT;
- return true;
+ return Success();
}
-bool Service::ParseOnrestart(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseOnrestart(const std::vector<std::string>& args) {
std::vector<std::string> str_args(args.begin() + 1, args.end());
int line = onrestart_.NumCommands() + 1;
- onrestart_.AddCommand(str_args, line, err);
- return true;
+ if (auto result = onrestart_.AddCommand(str_args, line); !result) {
+ return Error() << "cannot add Onrestart command: " << result.error();
+ }
+ return Success();
}
-bool Service::ParseNamespace(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseNamespace(const std::vector<std::string>& args) {
for (size_t i = 1; i < args.size(); i++) {
if (args[i] == "pid") {
namespace_flags_ |= CLONE_NEWPID;
@@ -476,111 +455,125 @@
} else if (args[i] == "mnt") {
namespace_flags_ |= CLONE_NEWNS;
} else {
- *err = "namespace must be 'pid' or 'mnt'";
- return false;
+ return Error() << "namespace must be 'pid' or 'mnt'";
}
}
- return true;
+ return Success();
}
-bool Service::ParseOomScoreAdjust(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseOomScoreAdjust(const std::vector<std::string>& args) {
if (!ParseInt(args[1], &oom_score_adjust_, -1000, 1000)) {
- *err = "oom_score_adjust value must be in range -1000 - +1000";
- return false;
+ return Error() << "oom_score_adjust value must be in range -1000 - +1000";
}
- return true;
+ return Success();
}
-bool Service::ParseSeclabel(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseMemcgSwappiness(const std::vector<std::string>& args) {
+ if (!ParseInt(args[1], &swappiness_, 0)) {
+ return Error() << "swappiness value must be equal or greater than 0";
+ }
+ return Success();
+}
+
+Result<Success> Service::ParseMemcgLimitInBytes(const std::vector<std::string>& args) {
+ if (!ParseInt(args[1], &limit_in_bytes_, 0)) {
+ return Error() << "limit_in_bytes value must be equal or greater than 0";
+ }
+ return Success();
+}
+
+Result<Success> Service::ParseMemcgSoftLimitInBytes(const std::vector<std::string>& args) {
+ if (!ParseInt(args[1], &soft_limit_in_bytes_, 0)) {
+ return Error() << "soft_limit_in_bytes value must be equal or greater than 0";
+ }
+ return Success();
+}
+
+Result<Success> Service::ParseSeclabel(const std::vector<std::string>& args) {
seclabel_ = args[1];
- return true;
+ return Success();
}
-bool Service::ParseSetenv(const std::vector<std::string>& args, std::string* err) {
- envvars_.emplace_back(args[1], args[2]);
- return true;
+Result<Success> Service::ParseSetenv(const std::vector<std::string>& args) {
+ environment_vars_.emplace_back(args[1], args[2]);
+ return Success();
}
-bool Service::ParseShutdown(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseShutdown(const std::vector<std::string>& args) {
if (args[1] == "critical") {
flags_ |= SVC_SHUTDOWN_CRITICAL;
- return true;
+ return Success();
}
- return false;
+ return Error() << "Invalid shutdown option";
}
template <typename T>
-bool Service::AddDescriptor(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::AddDescriptor(const std::vector<std::string>& args) {
int perm = args.size() > 3 ? std::strtoul(args[3].c_str(), 0, 8) : -1;
- uid_t uid = 0;
- gid_t gid = 0;
+ Result<uid_t> uid = 0;
+ Result<gid_t> gid = 0;
std::string context = args.size() > 6 ? args[6] : "";
- std::string decode_uid_err;
if (args.size() > 4) {
- if (!DecodeUid(args[4], &uid, &decode_uid_err)) {
- *err = "Unable to find UID for '" + args[4] + "': " + decode_uid_err;
- return false;
+ uid = DecodeUid(args[4]);
+ if (!uid) {
+ return Error() << "Unable to find UID for '" << args[4] << "': " << uid.error();
}
}
if (args.size() > 5) {
- if (!DecodeUid(args[5], &gid, &decode_uid_err)) {
- *err = "Unable to find GID for '" + args[5] + "': " + decode_uid_err;
- return false;
+ gid = DecodeUid(args[5]);
+ if (!gid) {
+ return Error() << "Unable to find GID for '" << args[5] << "': " << gid.error();
}
}
- auto descriptor = std::make_unique<T>(args[1], args[2], uid, gid, perm, context);
+ auto descriptor = std::make_unique<T>(args[1], args[2], *uid, *gid, perm, context);
auto old =
std::find_if(descriptors_.begin(), descriptors_.end(),
[&descriptor] (const auto& other) { return descriptor.get() == other.get(); });
if (old != descriptors_.end()) {
- *err = "duplicate descriptor " + args[1] + " " + args[2];
- return false;
+ return Error() << "duplicate descriptor " << args[1] << " " << args[2];
}
descriptors_.emplace_back(std::move(descriptor));
- return true;
+ return Success();
}
// name type perm [ uid gid context ]
-bool Service::ParseSocket(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseSocket(const std::vector<std::string>& args) {
if (!StartsWith(args[2], "dgram") && !StartsWith(args[2], "stream") &&
!StartsWith(args[2], "seqpacket")) {
- *err = "socket type must be 'dgram', 'stream' or 'seqpacket'";
- return false;
+ return Error() << "socket type must be 'dgram', 'stream' or 'seqpacket'";
}
- return AddDescriptor<SocketInfo>(args, err);
+ return AddDescriptor<SocketInfo>(args);
}
// name type perm [ uid gid context ]
-bool Service::ParseFile(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseFile(const std::vector<std::string>& args) {
if (args[2] != "r" && args[2] != "w" && args[2] != "rw") {
- *err = "file type must be 'r', 'w' or 'rw'";
- return false;
+ return Error() << "file type must be 'r', 'w' or 'rw'";
}
if ((args[1][0] != '/') || (args[1].find("../") != std::string::npos)) {
- *err = "file name must not be relative";
- return false;
+ return Error() << "file name must not be relative";
}
- return AddDescriptor<FileInfo>(args, err);
+ return AddDescriptor<FileInfo>(args);
}
-bool Service::ParseUser(const std::vector<std::string>& args, std::string* err) {
- std::string decode_uid_err;
- if (!DecodeUid(args[1], &uid_, &decode_uid_err)) {
- *err = "Unable to find UID for '" + args[1] + "': " + decode_uid_err;
- return false;
+Result<Success> Service::ParseUser(const std::vector<std::string>& args) {
+ auto uid = DecodeUid(args[1]);
+ if (!uid) {
+ return Error() << "Unable to find UID for '" << args[1] << "': " << uid.error();
}
- return true;
+ uid_ = *uid;
+ return Success();
}
-bool Service::ParseWritepid(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseWritepid(const std::vector<std::string>& args) {
writepid_files_.assign(args.begin() + 1, args.end());
- return true;
+ return Success();
}
class Service::OptionParserMap : public KeywordMap<OptionParser> {
@@ -609,6 +602,12 @@
{"onrestart", {1, kMax, &Service::ParseOnrestart}},
{"oom_score_adjust",
{1, 1, &Service::ParseOomScoreAdjust}},
+ {"memcg.swappiness",
+ {1, 1, &Service::ParseMemcgSwappiness}},
+ {"memcg.soft_limit_in_bytes",
+ {1, 1, &Service::ParseMemcgSoftLimitInBytes}},
+ {"memcg.limit_in_bytes",
+ {1, 1, &Service::ParseMemcgLimitInBytes}},
{"namespace", {1, 2, &Service::ParseNamespace}},
{"seclabel", {1, 1, &Service::ParseSeclabel}},
{"setenv", {2, 2, &Service::ParseSetenv}},
@@ -622,30 +621,33 @@
return option_parsers;
}
-bool Service::ParseLine(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseLine(const std::vector<std::string>& args) {
static const OptionParserMap parser_map;
- auto parser = parser_map.FindFunction(args, err);
+ auto parser = parser_map.FindFunction(args);
- if (!parser) {
- return false;
- }
+ if (!parser) return parser.error();
- return (this->*parser)(args, err);
+ return std::invoke(*parser, this, args);
}
-bool Service::ExecStart(std::unique_ptr<android::base::Timer>* exec_waiter) {
- flags_ |= SVC_EXEC | SVC_ONESHOT;
+Result<Success> Service::ExecStart() {
+ flags_ |= SVC_ONESHOT;
- exec_waiter->reset(new android::base::Timer);
-
- if (!Start()) {
- exec_waiter->reset();
- return false;
+ if (auto result = Start(); !result) {
+ return result;
}
- return true;
+
+ flags_ |= SVC_EXEC;
+ is_exec_service_running_ = true;
+
+ LOG(INFO) << "SVC_EXEC pid " << pid_ << " (uid " << uid_ << " gid " << gid_ << "+"
+ << supp_gids_.size() << " context " << (!seclabel_.empty() ? seclabel_ : "default")
+ << ") started; waiting...";
+
+ return Success();
}
-bool Service::Start() {
+Result<Success> Service::Start() {
// Starting a service removes it from the disabled or reset state and
// immediately takes it out of the restarting state if it was in there.
flags_ &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START));
@@ -654,7 +656,8 @@
// process of exiting, we've ensured that they will immediately restart
// on exit, unless they are ONESHOT.
if (flags_ & SVC_RUNNING) {
- return false;
+ // It is not an error to try to start a service that is already running.
+ return Success();
}
bool needs_console = (flags_ & SVC_CONSOLE);
@@ -667,28 +670,27 @@
// properly registered for the device node
int console_fd = open(console_.c_str(), O_RDWR | O_CLOEXEC);
if (console_fd < 0) {
- PLOG(ERROR) << "service '" << name_ << "' couldn't open console '" << console_ << "'";
flags_ |= SVC_DISABLED;
- return false;
+ return ErrnoError() << "Couldn't open console '" << console_ << "'";
}
close(console_fd);
}
struct stat sb;
if (stat(args_[0].c_str(), &sb) == -1) {
- PLOG(ERROR) << "cannot find '" << args_[0] << "', disabling '" << name_ << "'";
flags_ |= SVC_DISABLED;
- return false;
+ return ErrnoError() << "Cannot find '" << args_[0] << "'";
}
std::string scon;
if (!seclabel_.empty()) {
scon = seclabel_;
} else {
- scon = ComputeContextFromExecutable(name_, args_[0]);
- if (scon == "") {
- return false;
+ auto result = ComputeContextFromExecutable(name_, args_[0]);
+ if (!result) {
+ return result.error();
}
+ scon = *result;
}
LOG(INFO) << "starting service '" << name_ << "'...";
@@ -709,8 +711,8 @@
SetUpPidNamespace(name_);
}
- for (const auto& ei : envvars_) {
- add_environment(ei.name.c_str(), ei.value.c_str());
+ for (const auto& [key, value] : environment_vars_) {
+ setenv(key.c_str(), value.c_str(), 1);
}
std::for_each(descriptors_.begin(), descriptors_.end(),
@@ -765,7 +767,7 @@
std::vector<char*> strs;
ExpandArgs(args_, &strs);
- if (execve(strs[0], (char**) &strs[0], (char**) ENV) < 0) {
+ if (execv(strs[0], (char**)&strs[0]) < 0) {
PLOG(ERROR) << "cannot execve('" << strs[0] << "')";
}
@@ -773,9 +775,8 @@
}
if (pid < 0) {
- PLOG(ERROR) << "failed to fork for '" << name_ << "'";
pid_ = 0;
- return false;
+ return ErrnoError() << "Failed to fork";
}
if (oom_score_adjust_ != -1000) {
@@ -789,39 +790,52 @@
time_started_ = boot_clock::now();
pid_ = pid;
flags_ |= SVC_RUNNING;
+ start_order_ = next_start_order_++;
process_cgroup_empty_ = false;
errno = -createProcessGroup(uid_, pid_);
if (errno != 0) {
PLOG(ERROR) << "createProcessGroup(" << uid_ << ", " << pid_ << ") failed for service '"
<< name_ << "'";
- }
+ } else {
+ if (swappiness_ != -1) {
+ if (!setProcessGroupSwappiness(uid_, pid_, swappiness_)) {
+ PLOG(ERROR) << "setProcessGroupSwappiness failed";
+ }
+ }
- if ((flags_ & SVC_EXEC) != 0) {
- LOG(INFO) << "SVC_EXEC pid " << pid_ << " (uid " << uid_ << " gid " << gid_ << "+"
- << supp_gids_.size() << " context "
- << (!seclabel_.empty() ? seclabel_ : "default") << ") started; waiting...";
+ if (soft_limit_in_bytes_ != -1) {
+ if (!setProcessGroupSoftLimit(uid_, pid_, soft_limit_in_bytes_)) {
+ PLOG(ERROR) << "setProcessGroupSoftLimit failed";
+ }
+ }
+
+ if (limit_in_bytes_ != -1) {
+ if (!setProcessGroupLimit(uid_, pid_, limit_in_bytes_)) {
+ PLOG(ERROR) << "setProcessGroupLimit failed";
+ }
+ }
}
NotifyStateChange("running");
- return true;
+ return Success();
}
-bool Service::StartIfNotDisabled() {
+Result<Success> Service::StartIfNotDisabled() {
if (!(flags_ & SVC_DISABLED)) {
return Start();
} else {
flags_ |= SVC_DISABLED_START;
}
- return true;
+ return Success();
}
-bool Service::Enable() {
+Result<Success> Service::Enable() {
flags_ &= ~(SVC_DISABLED | SVC_RC_DISABLED);
if (flags_ & SVC_DISABLED_START) {
return Start();
}
- return true;
+ return Success();
}
void Service::Reset() {
@@ -847,26 +861,12 @@
StopOrReset(SVC_RESTART);
} else if (!(flags_ & SVC_RESTARTING)) {
/* Just start the service since it's not running. */
- Start();
+ if (auto result = Start(); !result) {
+ LOG(ERROR) << "Could not restart '" << name_ << "': " << result.error();
+ }
} /* else: Service is restarting anyways. */
}
-void Service::RestartIfNeeded(time_t* process_needs_restart_at) {
- boot_clock::time_point now = boot_clock::now();
- boot_clock::time_point next_start = time_started_ + 5s;
- if (now > next_start) {
- flags_ &= (~SVC_RESTARTING);
- Start();
- return;
- }
-
- time_t next_start_time_t = time(nullptr) +
- time_t(std::chrono::duration_cast<std::chrono::seconds>(next_start - now).count());
- if (next_start_time_t < *process_needs_restart_at || *process_needs_restart_at == 0) {
- *process_needs_restart_at = next_start_time_t;
- }
-}
-
// The how field should be either SVC_DISABLED, SVC_RESET, or SVC_RESTART.
void Service::StopOrReset(int how) {
// The service is still SVC_RUNNING until its process exits, but if it has
@@ -912,50 +912,18 @@
close(fd);
}
-int ServiceManager::exec_count_ = 0;
+ServiceList::ServiceList() {}
-ServiceManager::ServiceManager() {
-}
-
-ServiceManager& ServiceManager::GetInstance() {
- static ServiceManager instance;
+ServiceList& ServiceList::GetInstance() {
+ static ServiceList instance;
return instance;
}
-void ServiceManager::AddService(std::unique_ptr<Service> service) {
+void ServiceList::AddService(std::unique_ptr<Service> service) {
services_.emplace_back(std::move(service));
}
-bool ServiceManager::Exec(const std::vector<std::string>& args) {
- Service* svc = MakeExecOneshotService(args);
- if (!svc) {
- LOG(ERROR) << "Could not create exec service";
- return false;
- }
- if (!svc->ExecStart(&exec_waiter_)) {
- LOG(ERROR) << "Could not start exec service";
- ServiceManager::GetInstance().RemoveService(*svc);
- return false;
- }
- return true;
-}
-
-bool ServiceManager::ExecStart(const std::string& name) {
- Service* svc = FindServiceByName(name);
- if (!svc) {
- LOG(ERROR) << "ExecStart(" << name << "): Service not found";
- return false;
- }
- if (!svc->ExecStart(&exec_waiter_)) {
- LOG(ERROR) << "ExecStart(" << name << "): Could not start Service";
- return false;
- }
- return true;
-}
-
-bool ServiceManager::IsWaitingForExec() const { return exec_waiter_ != nullptr; }
-
-Service* ServiceManager::MakeExecOneshotService(const std::vector<std::string>& args) {
+std::unique_ptr<Service> Service::MakeTemporaryOneshotService(const std::vector<std::string>& args) {
// Parse the arguments: exec [SECLABEL [UID [GID]*] --] COMMAND ARGS...
// SECLABEL can be a - to denote default
std::size_t command_arg = 1;
@@ -976,10 +944,11 @@
}
std::vector<std::string> str_args(args.begin() + command_arg, args.end());
- exec_count_++;
- std::string name = "exec " + std::to_string(exec_count_) + " (" + Join(str_args, " ") + ")";
+ static size_t exec_count = 0;
+ exec_count++;
+ std::string name = "exec " + std::to_string(exec_count) + " (" + Join(str_args, " ") + ")";
- unsigned flags = SVC_EXEC | SVC_ONESHOT | SVC_TEMPORARY;
+ unsigned flags = SVC_ONESHOT | SVC_TEMPORARY;
CapSet no_capabilities;
unsigned namespace_flags = 0;
@@ -987,100 +956,50 @@
if (command_arg > 2 && args[1] != "-") {
seclabel = args[1];
}
- uid_t uid = 0;
+ Result<uid_t> uid = 0;
if (command_arg > 3) {
- std::string decode_uid_err;
- if (!DecodeUid(args[2], &uid, &decode_uid_err)) {
- LOG(ERROR) << "Unable to find UID for '" << args[2] << "': " << decode_uid_err;
+ uid = DecodeUid(args[2]);
+ if (!uid) {
+ LOG(ERROR) << "Unable to decode UID for '" << args[2] << "': " << uid.error();
return nullptr;
}
}
- gid_t gid = 0;
+ Result<gid_t> gid = 0;
std::vector<gid_t> supp_gids;
if (command_arg > 4) {
- std::string decode_uid_err;
- if (!DecodeUid(args[3], &gid, &decode_uid_err)) {
- LOG(ERROR) << "Unable to find GID for '" << args[3] << "': " << decode_uid_err;
+ gid = DecodeUid(args[3]);
+ if (!gid) {
+ LOG(ERROR) << "Unable to decode GID for '" << args[3] << "': " << gid.error();
return nullptr;
}
std::size_t nr_supp_gids = command_arg - 1 /* -- */ - 4 /* exec SECLABEL UID GID */;
for (size_t i = 0; i < nr_supp_gids; ++i) {
- gid_t supp_gid;
- if (!DecodeUid(args[4 + i], &supp_gid, &decode_uid_err)) {
- LOG(ERROR) << "Unable to find UID for '" << args[4 + i] << "': " << decode_uid_err;
+ auto supp_gid = DecodeUid(args[4 + i]);
+ if (!supp_gid) {
+ LOG(ERROR) << "Unable to decode GID for '" << args[4 + i]
+ << "': " << supp_gid.error();
return nullptr;
}
- supp_gids.push_back(supp_gid);
+ supp_gids.push_back(*supp_gid);
}
}
- auto svc_p = std::make_unique<Service>(name, flags, uid, gid, supp_gids, no_capabilities,
- namespace_flags, seclabel, str_args);
- Service* svc = svc_p.get();
- services_.emplace_back(std::move(svc_p));
-
- return svc;
+ return std::make_unique<Service>(name, flags, *uid, *gid, supp_gids, no_capabilities,
+ namespace_flags, seclabel, str_args);
}
-Service* ServiceManager::FindServiceByName(const std::string& name) const {
- auto svc = std::find_if(services_.begin(), services_.end(),
- [&name] (const std::unique_ptr<Service>& s) {
- return name == s->name();
- });
- if (svc != services_.end()) {
- return svc->get();
+// Shutdown services in the opposite order that they were started.
+const std::vector<Service*> ServiceList::services_in_shutdown_order() const {
+ std::vector<Service*> shutdown_services;
+ for (const auto& service : services_) {
+ if (service->start_order() > 0) shutdown_services.emplace_back(service.get());
}
- return nullptr;
+ std::sort(shutdown_services.begin(), shutdown_services.end(),
+ [](const auto& a, const auto& b) { return a->start_order() > b->start_order(); });
+ return shutdown_services;
}
-Service* ServiceManager::FindServiceByPid(pid_t pid) const {
- auto svc = std::find_if(services_.begin(), services_.end(),
- [&pid] (const std::unique_ptr<Service>& s) {
- return s->pid() == pid;
- });
- if (svc != services_.end()) {
- return svc->get();
- }
- return nullptr;
-}
-
-Service* ServiceManager::FindServiceByKeychord(int keychord_id) const {
- auto svc = std::find_if(services_.begin(), services_.end(),
- [&keychord_id] (const std::unique_ptr<Service>& s) {
- return s->keychord_id() == keychord_id;
- });
-
- if (svc != services_.end()) {
- return svc->get();
- }
- return nullptr;
-}
-
-void ServiceManager::ForEachService(const std::function<void(Service*)>& callback) const {
- for (const auto& s : services_) {
- callback(s.get());
- }
-}
-
-void ServiceManager::ForEachServiceInClass(const std::string& classname,
- void (*func)(Service* svc)) const {
- for (const auto& s : services_) {
- if (s->classnames().find(classname) != s->classnames().end()) {
- func(s.get());
- }
- }
-}
-
-void ServiceManager::ForEachServiceWithFlags(unsigned matchflags,
- void (*func)(Service* svc)) const {
- for (const auto& s : services_) {
- if (s->flags() & matchflags) {
- func(s.get());
- }
- }
-}
-
-void ServiceManager::RemoveService(const Service& svc) {
+void ServiceList::RemoveService(const Service& svc) {
auto svc_it = std::find_if(services_.begin(), services_.end(),
[&svc] (const std::unique_ptr<Service>& s) {
return svc.name() == s->name();
@@ -1092,116 +1011,40 @@
services_.erase(svc_it);
}
-void ServiceManager::DumpState() const {
+void ServiceList::DumpState() const {
for (const auto& s : services_) {
s->DumpState();
}
}
-bool ServiceManager::ReapOneProcess() {
- siginfo_t siginfo = {};
- // This returns a zombie pid or informs us that there are no zombies left to be reaped.
- // It does NOT reap the pid; that is done below.
- if (TEMP_FAILURE_RETRY(waitid(P_ALL, 0, &siginfo, WEXITED | WNOHANG | WNOWAIT)) != 0) {
- PLOG(ERROR) << "waitid failed";
- return false;
- }
-
- auto pid = siginfo.si_pid;
- if (pid == 0) return false;
-
- // At this point we know we have a zombie pid, so we use this scopeguard to reap the pid
- // whenever the function returns from this point forward.
- // We do NOT want to reap the zombie earlier as in Service::Reap(), we kill(-pid, ...) and we
- // want the pid to remain valid throughout that (and potentially future) usages.
- auto reaper = make_scope_guard([pid] { TEMP_FAILURE_RETRY(waitpid(pid, nullptr, WNOHANG)); });
-
- if (PropertyChildReap(pid)) {
- return true;
- }
-
- Service* svc = FindServiceByPid(pid);
-
- std::string name;
- std::string wait_string;
- if (svc) {
- name = StringPrintf("Service '%s' (pid %d)", svc->name().c_str(), pid);
- if (svc->flags() & SVC_EXEC) {
- wait_string = StringPrintf(" waiting took %f seconds",
- exec_waiter_->duration().count() / 1000.0f);
- }
- } else {
- name = StringPrintf("Untracked pid %d", pid);
- }
-
- auto status = siginfo.si_status;
- if (WIFEXITED(status)) {
- LOG(INFO) << name << " exited with status " << WEXITSTATUS(status) << wait_string;
- } else if (WIFSIGNALED(status)) {
- LOG(INFO) << name << " killed by signal " << WTERMSIG(status) << wait_string;
- }
-
- if (!svc) {
- return true;
- }
-
- svc->Reap();
-
- if (svc->flags() & SVC_EXEC) {
- exec_waiter_.reset();
- }
- if (svc->flags() & SVC_TEMPORARY) {
- RemoveService(*svc);
- }
-
- return true;
-}
-
-void ServiceManager::ReapAnyOutstandingChildren() {
- while (ReapOneProcess()) {
- }
-}
-
-void ServiceManager::ClearExecWait() {
- // Clear EXEC flag if there is one pending
- // And clear the wait flag
- for (const auto& s : services_) {
- s->UnSetExec();
- }
- exec_waiter_.reset();
-}
-
-bool ServiceParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
- int line, std::string* err) {
+Result<Success> ServiceParser::ParseSection(std::vector<std::string>&& args,
+ const std::string& filename, int line) {
if (args.size() < 3) {
- *err = "services must have a name and a program";
- return false;
+ return Error() << "services must have a name and a program";
}
const std::string& name = args[1];
if (!IsValidName(name)) {
- *err = StringPrintf("invalid service name '%s'", name.c_str());
- return false;
+ return Error() << "invalid service name '" << name << "'";
}
- Service* old_service = service_manager_->FindServiceByName(name);
+ Service* old_service = service_list_->FindService(name);
if (old_service) {
- *err = "ignored duplicate definition of service '" + name + "'";
- return false;
+ return Error() << "ignored duplicate definition of service '" << name << "'";
}
std::vector<std::string> str_args(args.begin() + 2, args.end());
service_ = std::make_unique<Service>(name, str_args);
- return true;
+ return Success();
}
-bool ServiceParser::ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) {
- return service_ ? service_->ParseLine(std::move(args), err) : false;
+Result<Success> ServiceParser::ParseLineSection(std::vector<std::string>&& args, int line) {
+ return service_ ? service_->ParseLine(std::move(args)) : Success();
}
void ServiceParser::EndSection() {
if (service_) {
- service_manager_->AddService(std::move(service_));
+ service_list_->AddService(std::move(service_));
}
}
diff --git a/init/service.h b/init/service.h
index f682abd..1f2c44f 100644
--- a/init/service.h
+++ b/init/service.h
@@ -30,8 +30,8 @@
#include "action.h"
#include "capabilities.h"
#include "descriptors.h"
-#include "init_parser.h"
#include "keyword_map.h"
+#include "parser.h"
#define SVC_DISABLED 0x001 // do not autostart with class
#define SVC_ONESHOT 0x002 // do not restart on exit
@@ -57,13 +57,6 @@
namespace android {
namespace init {
-struct ServiceEnvironmentInfo {
- ServiceEnvironmentInfo();
- ServiceEnvironmentInfo(const std::string& name, const std::string& value);
- std::string name;
- std::string value;
-};
-
class Service {
public:
Service(const std::string& name, const std::vector<std::string>& args);
@@ -73,27 +66,34 @@
unsigned namespace_flags, const std::string& seclabel,
const std::vector<std::string>& args);
+ static std::unique_ptr<Service> MakeTemporaryOneshotService(const std::vector<std::string>& args);
+
bool IsRunning() { return (flags_ & SVC_RUNNING) != 0; }
- bool ParseLine(const std::vector<std::string>& args, std::string* err);
- bool ExecStart(std::unique_ptr<android::base::Timer>* exec_waiter);
- bool Start();
- bool StartIfNotDisabled();
- bool Enable();
+ Result<Success> ParseLine(const std::vector<std::string>& args);
+ Result<Success> ExecStart();
+ Result<Success> Start();
+ Result<Success> StartIfNotDisabled();
+ Result<Success> Enable();
void Reset();
void Stop();
void Terminate();
void Restart();
- void RestartIfNeeded(time_t* process_needs_restart_at);
void Reap();
void DumpState() const;
void SetShutdownCritical() { flags_ |= SVC_SHUTDOWN_CRITICAL; }
bool IsShutdownCritical() const { return (flags_ & SVC_SHUTDOWN_CRITICAL) != 0; }
- void UnSetExec() { flags_ &= ~SVC_EXEC; }
+ void UnSetExec() {
+ is_exec_service_running_ = false;
+ flags_ &= ~SVC_EXEC;
+ }
+
+ static bool is_exec_service_running() { return is_exec_service_running_; }
const std::string& name() const { return name_; }
const std::set<std::string>& classnames() const { return classnames_; }
unsigned flags() const { return flags_; }
pid_t pid() const { return pid_; }
+ android::base::boot_clock::time_point time_started() const { return time_started_; }
int crash_count() const { return crash_count_; }
uid_t uid() const { return uid_; }
gid_t gid() const { return gid_; }
@@ -108,11 +108,11 @@
int priority() const { return priority_; }
int oom_score_adjust() const { return oom_score_adjust_; }
bool process_cgroup_empty() const { return process_cgroup_empty_; }
+ unsigned long start_order() const { return start_order_; }
const std::vector<std::string>& args() const { return args_; }
private:
- using OptionParser = bool (Service::*) (const std::vector<std::string>& args,
- std::string* err);
+ using OptionParser = Result<Success> (Service::*)(const std::vector<std::string>& args);
class OptionParserMap;
void NotifyStateChange(const std::string& new_state) const;
@@ -122,29 +122,35 @@
void KillProcessGroup(int signal);
void SetProcessAttributes();
- bool ParseCapabilities(const std::vector<std::string>& args, std::string *err);
- bool ParseClass(const std::vector<std::string>& args, std::string* err);
- bool ParseConsole(const std::vector<std::string>& args, std::string* err);
- bool ParseCritical(const std::vector<std::string>& args, std::string* err);
- bool ParseDisabled(const std::vector<std::string>& args, std::string* err);
- bool ParseGroup(const std::vector<std::string>& args, std::string* err);
- bool ParsePriority(const std::vector<std::string>& args, std::string* err);
- bool ParseIoprio(const std::vector<std::string>& args, std::string* err);
- bool ParseKeycodes(const std::vector<std::string>& args, std::string* err);
- bool ParseOneshot(const std::vector<std::string>& args, std::string* err);
- bool ParseOnrestart(const std::vector<std::string>& args, std::string* err);
- bool ParseOomScoreAdjust(const std::vector<std::string>& args, std::string* err);
- bool ParseNamespace(const std::vector<std::string>& args, std::string* err);
- bool ParseSeclabel(const std::vector<std::string>& args, std::string* err);
- bool ParseSetenv(const std::vector<std::string>& args, std::string* err);
- bool ParseShutdown(const std::vector<std::string>& args, std::string* err);
- bool ParseSocket(const std::vector<std::string>& args, std::string* err);
- bool ParseFile(const std::vector<std::string>& args, std::string* err);
- bool ParseUser(const std::vector<std::string>& args, std::string* err);
- bool ParseWritepid(const std::vector<std::string>& args, std::string* err);
+ Result<Success> ParseCapabilities(const std::vector<std::string>& args);
+ Result<Success> ParseClass(const std::vector<std::string>& args);
+ Result<Success> ParseConsole(const std::vector<std::string>& args);
+ Result<Success> ParseCritical(const std::vector<std::string>& args);
+ Result<Success> ParseDisabled(const std::vector<std::string>& args);
+ Result<Success> ParseGroup(const std::vector<std::string>& args);
+ Result<Success> ParsePriority(const std::vector<std::string>& args);
+ Result<Success> ParseIoprio(const std::vector<std::string>& args);
+ Result<Success> ParseKeycodes(const std::vector<std::string>& args);
+ Result<Success> ParseOneshot(const std::vector<std::string>& args);
+ Result<Success> ParseOnrestart(const std::vector<std::string>& args);
+ Result<Success> ParseOomScoreAdjust(const std::vector<std::string>& args);
+ Result<Success> ParseMemcgLimitInBytes(const std::vector<std::string>& args);
+ Result<Success> ParseMemcgSoftLimitInBytes(const std::vector<std::string>& args);
+ Result<Success> ParseMemcgSwappiness(const std::vector<std::string>& args);
+ Result<Success> ParseNamespace(const std::vector<std::string>& args);
+ Result<Success> ParseSeclabel(const std::vector<std::string>& args);
+ Result<Success> ParseSetenv(const std::vector<std::string>& args);
+ Result<Success> ParseShutdown(const std::vector<std::string>& args);
+ Result<Success> ParseSocket(const std::vector<std::string>& args);
+ Result<Success> ParseFile(const std::vector<std::string>& args);
+ Result<Success> ParseUser(const std::vector<std::string>& args);
+ Result<Success> ParseWritepid(const std::vector<std::string>& args);
template <typename T>
- bool AddDescriptor(const std::vector<std::string>& args, std::string* err);
+ Result<Success> AddDescriptor(const std::vector<std::string>& args);
+
+ static unsigned long next_start_order_;
+ static bool is_exec_service_running_;
std::string name_;
std::set<std::string> classnames_;
@@ -165,7 +171,7 @@
std::string seclabel_;
std::vector<std::unique_ptr<DescriptorInfo>> descriptors_;
- std::vector<ServiceEnvironmentInfo> envvars_;
+ std::vector<std::pair<std::string, std::string>> environment_vars_;
Action onrestart_; // Commands to execute on restart.
@@ -181,60 +187,62 @@
int oom_score_adjust_;
+ int swappiness_;
+ int soft_limit_in_bytes_;
+ int limit_in_bytes_;
+
bool process_cgroup_empty_ = false;
+ unsigned long start_order_;
+
std::vector<std::string> args_;
};
-class ServiceManager {
+class ServiceList {
public:
- static ServiceManager& GetInstance();
+ static ServiceList& GetInstance();
// Exposed for testing
- ServiceManager();
+ ServiceList();
void AddService(std::unique_ptr<Service> service);
- Service* MakeExecOneshotService(const std::vector<std::string>& args);
- bool Exec(const std::vector<std::string>& args);
- bool ExecStart(const std::string& name);
- bool IsWaitingForExec() const;
- Service* FindServiceByName(const std::string& name) const;
- Service* FindServiceByPid(pid_t pid) const;
- Service* FindServiceByKeychord(int keychord_id) const;
- void ForEachService(const std::function<void(Service*)>& callback) const;
- void ForEachServiceInClass(const std::string& classname,
- void (*func)(Service* svc)) const;
- void ForEachServiceWithFlags(unsigned matchflags,
- void (*func)(Service* svc)) const;
- void ReapAnyOutstandingChildren();
void RemoveService(const Service& svc);
+
+ template <typename T, typename F = decltype(&Service::name)>
+ Service* FindService(T value, F function = &Service::name) const {
+ auto svc = std::find_if(services_.begin(), services_.end(),
+ [&function, &value](const std::unique_ptr<Service>& s) {
+ return std::invoke(function, s) == value;
+ });
+ if (svc != services_.end()) {
+ return svc->get();
+ }
+ return nullptr;
+ }
+
void DumpState() const;
- void ClearExecWait();
+
+ auto begin() const { return services_.begin(); }
+ auto end() const { return services_.end(); }
+ const std::vector<std::unique_ptr<Service>>& services() const { return services_; }
+ const std::vector<Service*> services_in_shutdown_order() const;
private:
- // Cleans up a child process that exited.
- // Returns true iff a children was cleaned up.
- bool ReapOneProcess();
-
- static int exec_count_; // Every service needs a unique name.
- std::unique_ptr<android::base::Timer> exec_waiter_;
-
std::vector<std::unique_ptr<Service>> services_;
};
class ServiceParser : public SectionParser {
public:
- ServiceParser(ServiceManager* service_manager)
- : service_manager_(service_manager), service_(nullptr) {}
- bool ParseSection(std::vector<std::string>&& args, const std::string& filename, int line,
- std::string* err) override;
- bool ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) override;
+ ServiceParser(ServiceList* service_list) : service_list_(service_list), service_(nullptr) {}
+ Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
+ int line) override;
+ Result<Success> ParseLineSection(std::vector<std::string>&& args, int line) override;
void EndSection() override;
private:
bool IsValidName(const std::string& name) const;
- ServiceManager* service_manager_;
+ ServiceList* service_list_;
std::unique_ptr<Service> service_;
};
diff --git a/init/service_test.cpp b/init/service_test.cpp
index 44f28a3..98d876f 100644
--- a/init/service_test.cpp
+++ b/init/service_test.cpp
@@ -23,6 +23,8 @@
#include <gtest/gtest.h>
+#include "util.h"
+
namespace android {
namespace init {
@@ -71,5 +73,120 @@
EXPECT_FALSE(service_in_old_memory->process_cgroup_empty());
}
+TEST(service, make_temporary_oneshot_service_invalid_syntax) {
+ std::vector<std::string> args;
+ // Nothing.
+ ASSERT_EQ(nullptr, Service::MakeTemporaryOneshotService(args));
+
+ // No arguments to 'exec'.
+ args.push_back("exec");
+ ASSERT_EQ(nullptr, Service::MakeTemporaryOneshotService(args));
+
+ // No command in "exec --".
+ args.push_back("--");
+ ASSERT_EQ(nullptr, Service::MakeTemporaryOneshotService(args));
+}
+
+TEST(service, make_temporary_oneshot_service_too_many_supplementary_gids) {
+ std::vector<std::string> args;
+ args.push_back("exec");
+ args.push_back("seclabel");
+ args.push_back("root"); // uid.
+ args.push_back("root"); // gid.
+ for (int i = 0; i < NR_SVC_SUPP_GIDS; ++i) {
+ args.push_back("root"); // Supplementary gid.
+ }
+ args.push_back("--");
+ args.push_back("/system/bin/id");
+ ASSERT_EQ(nullptr, Service::MakeTemporaryOneshotService(args));
+}
+
+static void Test_make_temporary_oneshot_service(bool dash_dash, bool seclabel, bool uid, bool gid,
+ bool supplementary_gids) {
+ std::vector<std::string> args;
+ args.push_back("exec");
+ if (seclabel) {
+ args.push_back("u:r:su:s0"); // seclabel
+ if (uid) {
+ args.push_back("log"); // uid
+ if (gid) {
+ args.push_back("shell"); // gid
+ if (supplementary_gids) {
+ args.push_back("system"); // supplementary gid 0
+ args.push_back("adb"); // supplementary gid 1
+ }
+ }
+ }
+ }
+ if (dash_dash) {
+ args.push_back("--");
+ }
+ args.push_back("/system/bin/toybox");
+ args.push_back("id");
+ auto svc = Service::MakeTemporaryOneshotService(args);
+ ASSERT_NE(nullptr, svc);
+
+ if (seclabel) {
+ ASSERT_EQ("u:r:su:s0", svc->seclabel());
+ } else {
+ ASSERT_EQ("", svc->seclabel());
+ }
+ if (uid) {
+ auto decoded_uid = DecodeUid("log");
+ ASSERT_TRUE(decoded_uid);
+ ASSERT_EQ(*decoded_uid, svc->uid());
+ } else {
+ ASSERT_EQ(0U, svc->uid());
+ }
+ if (gid) {
+ auto decoded_uid = DecodeUid("shell");
+ ASSERT_TRUE(decoded_uid);
+ ASSERT_EQ(*decoded_uid, svc->gid());
+ } else {
+ ASSERT_EQ(0U, svc->gid());
+ }
+ if (supplementary_gids) {
+ ASSERT_EQ(2U, svc->supp_gids().size());
+
+ auto decoded_uid = DecodeUid("system");
+ ASSERT_TRUE(decoded_uid);
+ ASSERT_EQ(*decoded_uid, svc->supp_gids()[0]);
+
+ decoded_uid = DecodeUid("adb");
+ ASSERT_TRUE(decoded_uid);
+ ASSERT_EQ(*decoded_uid, svc->supp_gids()[1]);
+ } else {
+ ASSERT_EQ(0U, svc->supp_gids().size());
+ }
+
+ ASSERT_EQ(static_cast<std::size_t>(2), svc->args().size());
+ ASSERT_EQ("/system/bin/toybox", svc->args()[0]);
+ ASSERT_EQ("id", svc->args()[1]);
+}
+
+TEST(service, make_temporary_oneshot_service_with_everything) {
+ Test_make_temporary_oneshot_service(true, true, true, true, true);
+}
+
+TEST(service, make_temporary_oneshot_service_with_seclabel_uid_gid) {
+ Test_make_temporary_oneshot_service(true, true, true, true, false);
+}
+
+TEST(service, make_temporary_oneshot_service_with_seclabel_uid) {
+ Test_make_temporary_oneshot_service(true, true, true, false, false);
+}
+
+TEST(service, make_temporary_oneshot_service_with_seclabel) {
+ Test_make_temporary_oneshot_service(true, true, false, false, false);
+}
+
+TEST(service, make_temporary_oneshot_service_with_just_command) {
+ Test_make_temporary_oneshot_service(true, false, false, false, false);
+}
+
+TEST(service, make_temporary_oneshot_service_with_just_command_no_dash) {
+ Test_make_temporary_oneshot_service(false, false, false, false, false);
+}
+
} // namespace init
} // namespace android
diff --git a/init/signal_handler.cpp b/init/signal_handler.cpp
index db1bfcf..9e49c48 100644
--- a/init/signal_handler.cpp
+++ b/init/signal_handler.cpp
@@ -14,29 +14,94 @@
* limitations under the License.
*/
+#include "signal_handler.h"
+
#include <signal.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
+#include <sys/wait.h>
#include <unistd.h>
+#include <android-base/chrono_utils.h>
#include <android-base/logging.h>
+#include <android-base/scopeguard.h>
+#include <android-base/stringprintf.h>
#include "init.h"
+#include "property_service.h"
#include "service.h"
+using android::base::StringPrintf;
+using android::base::boot_clock;
+using android::base::make_scope_guard;
+
namespace android {
namespace init {
static int signal_write_fd = -1;
static int signal_read_fd = -1;
+static bool ReapOneProcess() {
+ siginfo_t siginfo = {};
+ // This returns a zombie pid or informs us that there are no zombies left to be reaped.
+ // It does NOT reap the pid; that is done below.
+ if (TEMP_FAILURE_RETRY(waitid(P_ALL, 0, &siginfo, WEXITED | WNOHANG | WNOWAIT)) != 0) {
+ PLOG(ERROR) << "waitid failed";
+ return false;
+ }
+
+ auto pid = siginfo.si_pid;
+ if (pid == 0) return false;
+
+ // At this point we know we have a zombie pid, so we use this scopeguard to reap the pid
+ // whenever the function returns from this point forward.
+ // We do NOT want to reap the zombie earlier as in Service::Reap(), we kill(-pid, ...) and we
+ // want the pid to remain valid throughout that (and potentially future) usages.
+ auto reaper = make_scope_guard([pid] { TEMP_FAILURE_RETRY(waitpid(pid, nullptr, WNOHANG)); });
+
+ if (PropertyChildReap(pid)) return true;
+
+ Service* service = ServiceList::GetInstance().FindService(pid, &Service::pid);
+
+ std::string name;
+ std::string wait_string;
+ if (service) {
+ name = StringPrintf("Service '%s' (pid %d)", service->name().c_str(), pid);
+ if (service->flags() & SVC_EXEC) {
+ auto exec_duration = boot_clock::now() - service->time_started();
+ auto exec_duration_ms =
+ std::chrono::duration_cast<std::chrono::milliseconds>(exec_duration).count();
+ wait_string = StringPrintf(" waiting took %f seconds", exec_duration_ms / 1000.0f);
+ }
+ } else {
+ name = StringPrintf("Untracked pid %d", pid);
+ }
+
+ auto status = siginfo.si_status;
+ if (WIFEXITED(status)) {
+ LOG(INFO) << name << " exited with status " << WEXITSTATUS(status) << wait_string;
+ } else if (WIFSIGNALED(status)) {
+ LOG(INFO) << name << " killed by signal " << WTERMSIG(status) << wait_string;
+ }
+
+ if (!service) return true;
+
+ service->Reap();
+
+ if (service->flags() & SVC_TEMPORARY) {
+ ServiceList::GetInstance().RemoveService(*service);
+ }
+
+ return true;
+}
+
static void handle_signal() {
// Clear outstanding requests.
char buf[32];
read(signal_read_fd, buf, sizeof(buf));
- ServiceManager::GetInstance().ReapAnyOutstandingChildren();
+ ReapAnyOutstandingChildren();
}
static void SIGCHLD_handler(int) {
@@ -45,6 +110,11 @@
}
}
+void ReapAnyOutstandingChildren() {
+ while (ReapOneProcess()) {
+ }
+}
+
void signal_handler_init() {
// Create a signalling mechanism for SIGCHLD.
int s[2];
@@ -63,7 +133,7 @@
act.sa_flags = SA_NOCLDSTOP;
sigaction(SIGCHLD, &act, 0);
- ServiceManager::GetInstance().ReapAnyOutstandingChildren();
+ ReapAnyOutstandingChildren();
register_epoll_handler(signal_read_fd, handle_signal);
}
diff --git a/init/signal_handler.h b/init/signal_handler.h
index f7881ab..9362be5 100644
--- a/init/signal_handler.h
+++ b/init/signal_handler.h
@@ -20,6 +20,8 @@
namespace android {
namespace init {
+void ReapAnyOutstandingChildren();
+
void signal_handler_init(void);
} // namespace init
diff --git a/init/tokenizer.cpp b/init/tokenizer.cpp
new file mode 100644
index 0000000..f8d9b6b
--- /dev/null
+++ b/init/tokenizer.cpp
@@ -0,0 +1,124 @@
+#include "tokenizer.h"
+
+namespace android {
+namespace init {
+
+int next_token(struct parse_state *state)
+{
+ char *x = state->ptr;
+ char *s;
+
+ if (state->nexttoken) {
+ int t = state->nexttoken;
+ state->nexttoken = 0;
+ return t;
+ }
+
+ for (;;) {
+ switch (*x) {
+ case 0:
+ state->ptr = x;
+ return T_EOF;
+ case '\n':
+ x++;
+ state->ptr = x;
+ return T_NEWLINE;
+ case ' ':
+ case '\t':
+ case '\r':
+ x++;
+ continue;
+ case '#':
+ while (*x && (*x != '\n')) x++;
+ if (*x == '\n') {
+ state->ptr = x+1;
+ return T_NEWLINE;
+ } else {
+ state->ptr = x;
+ return T_EOF;
+ }
+ default:
+ goto text;
+ }
+ }
+
+textdone:
+ state->ptr = x;
+ *s = 0;
+ return T_TEXT;
+text:
+ state->text = s = x;
+textresume:
+ for (;;) {
+ switch (*x) {
+ case 0:
+ goto textdone;
+ case ' ':
+ case '\t':
+ case '\r':
+ x++;
+ goto textdone;
+ case '\n':
+ state->nexttoken = T_NEWLINE;
+ x++;
+ goto textdone;
+ case '"':
+ x++;
+ for (;;) {
+ switch (*x) {
+ case 0:
+ /* unterminated quoted thing */
+ state->ptr = x;
+ return T_EOF;
+ case '"':
+ x++;
+ goto textresume;
+ default:
+ *s++ = *x++;
+ }
+ }
+ break;
+ case '\\':
+ x++;
+ switch (*x) {
+ case 0:
+ goto textdone;
+ case 'n':
+ *s++ = '\n';
+ break;
+ case 'r':
+ *s++ = '\r';
+ break;
+ case 't':
+ *s++ = '\t';
+ break;
+ case '\\':
+ *s++ = '\\';
+ break;
+ case '\r':
+ /* \ <cr> <lf> -> line continuation */
+ if (x[1] != '\n') {
+ x++;
+ continue;
+ }
+ case '\n':
+ /* \ <lf> -> line continuation */
+ state->line++;
+ x++;
+ /* eat any extra whitespace */
+ while((*x == ' ') || (*x == '\t')) x++;
+ continue;
+ default:
+ /* unknown escape -- just copy */
+ *s++ = *x++;
+ }
+ continue;
+ default:
+ *s++ = *x++;
+ }
+ }
+ return T_EOF;
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/tokenizer.h b/init/tokenizer.h
new file mode 100644
index 0000000..72c08ef
--- /dev/null
+++ b/init/tokenizer.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2010 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 _INIT_TOKENIZER_H_
+#define _INIT_TOKENIZER_H_
+
+#define T_EOF 0
+#define T_TEXT 1
+#define T_NEWLINE 2
+
+namespace android {
+namespace init {
+
+struct parse_state
+{
+ char *ptr;
+ char *text;
+ int line;
+ int nexttoken;
+};
+
+int next_token(struct parse_state *state);
+
+} // namespace init
+} // namespace android
+
+#endif
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index c0eae1e..1435d82 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -36,6 +36,7 @@
#include "devices.h"
#include "firmware_handler.h"
#include "log.h"
+#include "selinux.h"
#include "uevent_listener.h"
#include "ueventd_parser.h"
#include "util.h"
@@ -222,10 +223,10 @@
using namespace std::placeholders;
std::vector<SysfsPermissions> sysfs_permissions;
std::vector<Permissions> dev_permissions;
- parser.AddSingleLineParser(
- "/sys/", std::bind(ParsePermissionsLine, _1, _2, &sysfs_permissions, nullptr));
+ parser.AddSingleLineParser("/sys/",
+ std::bind(ParsePermissionsLine, _1, &sysfs_permissions, nullptr));
parser.AddSingleLineParser("/dev/",
- std::bind(ParsePermissionsLine, _1, _2, nullptr, &dev_permissions));
+ std::bind(ParsePermissionsLine, _1, nullptr, &dev_permissions));
parser.ParseConfig("/ueventd.rc");
parser.ParseConfig("/vendor/ueventd.rc");
@@ -257,9 +258,8 @@
LOG(INFO) << "ueventd started!";
- selinux_callback cb;
- cb.func_log = selinux_klog_callback;
- selinux_set_callback(SELINUX_CB_LOG, cb);
+ SelinuxSetupKernelLogging();
+ SelabelInitialize();
DeviceHandler device_handler = CreateDeviceHandler();
UeventListener uevent_listener;
diff --git a/init/ueventd_parser.cpp b/init/ueventd_parser.cpp
index 02e0d42..cd7adb4 100644
--- a/init/ueventd_parser.cpp
+++ b/init/ueventd_parser.cpp
@@ -24,18 +24,16 @@
namespace android {
namespace init {
-bool ParsePermissionsLine(std::vector<std::string>&& args, std::string* err,
- std::vector<SysfsPermissions>* out_sysfs_permissions,
- std::vector<Permissions>* out_dev_permissions) {
+Result<Success> ParsePermissionsLine(std::vector<std::string>&& args,
+ std::vector<SysfsPermissions>* out_sysfs_permissions,
+ std::vector<Permissions>* out_dev_permissions) {
bool is_sysfs = out_sysfs_permissions != nullptr;
if (is_sysfs && args.size() != 5) {
- *err = "/sys/ lines must have 5 entries";
- return false;
+ return Error() << "/sys/ lines must have 5 entries";
}
if (!is_sysfs && args.size() != 4) {
- *err = "/dev/ lines must have 4 entries";
- return false;
+ return Error() << "/dev/ lines must have 4 entries";
}
auto it = args.begin();
@@ -49,23 +47,20 @@
char* end_pointer = 0;
mode_t perm = strtol(perm_string.c_str(), &end_pointer, 8);
if (end_pointer == nullptr || *end_pointer != '\0') {
- *err = "invalid mode '" + perm_string + "'";
- return false;
+ return Error() << "invalid mode '" << perm_string << "'";
}
std::string& uid_string = *it++;
passwd* pwd = getpwnam(uid_string.c_str());
if (!pwd) {
- *err = "invalid uid '" + uid_string + "'";
- return false;
+ return Error() << "invalid uid '" << uid_string << "'";
}
uid_t uid = pwd->pw_uid;
std::string& gid_string = *it++;
struct group* grp = getgrnam(gid_string.c_str());
if (!grp) {
- *err = "invalid gid '" + gid_string + "'";
- return false;
+ return Error() << "invalid gid '" << gid_string << "'";
}
gid_t gid = grp->gr_gid;
@@ -74,53 +69,49 @@
} else {
out_dev_permissions->emplace_back(name, perm, uid, gid);
}
- return true;
+ return Success();
}
-bool SubsystemParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
- int line, std::string* err) {
+Result<Success> SubsystemParser::ParseSection(std::vector<std::string>&& args,
+ const std::string& filename, int line) {
if (args.size() != 2) {
- *err = "subsystems must have exactly one name";
- return false;
+ return Error() << "subsystems must have exactly one name";
}
if (std::find(subsystems_->begin(), subsystems_->end(), args[1]) != subsystems_->end()) {
- *err = "ignoring duplicate subsystem entry";
- return false;
+ return Error() << "ignoring duplicate subsystem entry";
}
- subsystem_.name_ = args[1];
+ subsystem_ = Subsystem(std::move(args[1]));
- return true;
+ return Success();
}
-bool SubsystemParser::ParseDevName(std::vector<std::string>&& args, std::string* err) {
+Result<Success> SubsystemParser::ParseDevName(std::vector<std::string>&& args) {
if (args[1] == "uevent_devname") {
subsystem_.devname_source_ = Subsystem::DevnameSource::DEVNAME_UEVENT_DEVNAME;
- return true;
+ return Success();
}
if (args[1] == "uevent_devpath") {
subsystem_.devname_source_ = Subsystem::DevnameSource::DEVNAME_UEVENT_DEVPATH;
- return true;
+ return Success();
}
- *err = "invalid devname '" + args[1] + "'";
- return false;
+ return Error() << "invalid devname '" << args[1] << "'";
}
-bool SubsystemParser::ParseDirName(std::vector<std::string>&& args, std::string* err) {
+Result<Success> SubsystemParser::ParseDirName(std::vector<std::string>&& args) {
if (args[1].front() != '/') {
- *err = "dirname '" + args[1] + " ' does not start with '/'";
- return false;
+ return Error() << "dirname '" << args[1] << " ' does not start with '/'";
}
subsystem_.dir_name_ = args[1];
- return true;
+ return Success();
}
-bool SubsystemParser::ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) {
- using OptionParser =
- bool (SubsystemParser::*)(std::vector<std::string> && args, std::string * err);
+Result<Success> SubsystemParser::ParseLineSection(std::vector<std::string>&& args, int line) {
+ using OptionParser = Result<Success> (SubsystemParser::*)(std::vector<std::string> && args);
+
static class OptionParserMap : public KeywordMap<OptionParser> {
private:
const Map& map() const override {
@@ -134,13 +125,11 @@
}
} parser_map;
- auto parser = parser_map.FindFunction(args, err);
+ auto parser = parser_map.FindFunction(args);
- if (!parser) {
- return false;
- }
+ if (!parser) return Error() << parser.error();
- return (this->*parser)(std::move(args), err);
+ return std::invoke(*parser, this, std::move(args));
}
void SubsystemParser::EndSection() {
diff --git a/init/ueventd_parser.h b/init/ueventd_parser.h
index 592df63..18d1027 100644
--- a/init/ueventd_parser.h
+++ b/init/ueventd_parser.h
@@ -21,7 +21,7 @@
#include <vector>
#include "devices.h"
-#include "init_parser.h"
+#include "parser.h"
namespace android {
namespace init {
@@ -29,22 +29,22 @@
class SubsystemParser : public SectionParser {
public:
SubsystemParser(std::vector<Subsystem>* subsystems) : subsystems_(subsystems) {}
- bool ParseSection(std::vector<std::string>&& args, const std::string& filename, int line,
- std::string* err) override;
- bool ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) override;
+ Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
+ int line) override;
+ Result<Success> ParseLineSection(std::vector<std::string>&& args, int line) override;
void EndSection() override;
private:
- bool ParseDevName(std::vector<std::string>&& args, std::string* err);
- bool ParseDirName(std::vector<std::string>&& args, std::string* err);
+ Result<Success> ParseDevName(std::vector<std::string>&& args);
+ Result<Success> ParseDirName(std::vector<std::string>&& args);
Subsystem subsystem_;
std::vector<Subsystem>* subsystems_;
};
-bool ParsePermissionsLine(std::vector<std::string>&& args, std::string* err,
- std::vector<SysfsPermissions>* out_sysfs_permissions,
- std::vector<Permissions>* out_dev_permissions);
+Result<Success> ParsePermissionsLine(std::vector<std::string>&& args,
+ std::vector<SysfsPermissions>* out_sysfs_permissions,
+ std::vector<Permissions>* out_dev_permissions);
} // namespace init
} // namespace android
diff --git a/init/ueventd_test.cpp b/init/ueventd_test.cpp
index 86d7055..7290051 100644
--- a/init/ueventd_test.cpp
+++ b/init/ueventd_test.cpp
@@ -19,6 +19,8 @@
#include <sys/stat.h>
#include <unistd.h>
+#include <atomic>
+#include <chrono>
#include <string>
#include <thread>
#include <vector>
@@ -27,8 +29,11 @@
#include <android-base/scopeguard.h>
#include <android-base/test_utils.h>
#include <gtest/gtest.h>
+#include <selinux/android.h>
+#include <selinux/label.h>
#include <selinux/selinux.h>
+using namespace std::chrono_literals;
using namespace std::string_literals;
template <typename T, typename F>
@@ -63,7 +68,10 @@
}
TEST(ueventd, setegid_IsPerThread) {
- if (getuid() != 0) return;
+ if (getuid() != 0) {
+ GTEST_LOG_(INFO) << "Skipping test, must be run as root.";
+ return;
+ }
TemporaryDir dir;
@@ -78,13 +86,20 @@
for (const auto& [file, expected_gid] : files_and_gids) {
struct stat info;
- EXPECT_EQ(0, stat(file.c_str(), &info));
+ ASSERT_EQ(0, stat(file.c_str(), &info));
EXPECT_EQ(expected_gid, info.st_gid);
}
}
TEST(ueventd, setfscreatecon_IsPerThread) {
- if (getuid() != 0) return;
+ if (getuid() != 0) {
+ GTEST_LOG_(INFO) << "Skipping test, must be run as root.";
+ return;
+ }
+ if (!is_selinux_enabled() || security_getenforce() == 1) {
+ GTEST_LOG_(INFO) << "Skipping test, SELinux must be enabled and in permissive mode.";
+ return;
+ }
const char* const contexts[] = {
"u:object_r:audio_device:s0",
@@ -105,8 +120,85 @@
for (const auto& [file, expected_context] : files_and_contexts) {
char* file_context;
- EXPECT_GT(getfilecon(file.c_str(), &file_context), 0);
+ ASSERT_GT(getfilecon(file.c_str(), &file_context), 0);
EXPECT_EQ(expected_context, file_context);
freecon(file_context);
}
}
+
+TEST(ueventd, selabel_lookup_MultiThreaded) {
+ if (getuid() != 0) {
+ GTEST_LOG_(INFO) << "Skipping test, must be run as root.";
+ return;
+ }
+
+ // Test parameters
+ constexpr auto num_threads = 10;
+ constexpr auto run_time = 200ms;
+
+ std::unique_ptr<selabel_handle, decltype(&selabel_close)> sehandle(
+ selinux_android_file_context_handle(), &selabel_close);
+
+ ASSERT_TRUE(sehandle);
+
+ struct {
+ const char* file;
+ int mode;
+ std::string expected_context;
+ } files_and_modes[] = {
+ {"/dev/zero", 020666, ""},
+ {"/dev/null", 020666, ""},
+ {"/dev/random", 020666, ""},
+ {"/dev/urandom", 020666, ""},
+ };
+
+ // Precondition, ensure that we can lookup all of these from a single thread, and store the
+ // expected context for each.
+ for (size_t i = 0; i < arraysize(files_and_modes); ++i) {
+ char* secontext;
+ ASSERT_EQ(0, selabel_lookup(sehandle.get(), &secontext, files_and_modes[i].file,
+ files_and_modes[i].mode));
+ files_and_modes[i].expected_context = secontext;
+ freecon(secontext);
+ }
+
+ // Now that we know we can access them, and what their context should be, run in parallel.
+ std::atomic_bool stopped = false;
+ std::atomic_uint num_api_failures = 0;
+ std::atomic_uint num_context_check_failures = 0;
+ std::atomic_uint num_successes = 0;
+
+ auto thread_function = [&]() {
+ while (!stopped) {
+ for (size_t i = 0; i < arraysize(files_and_modes); ++i) {
+ char* secontext;
+ int result = selabel_lookup(sehandle.get(), &secontext, files_and_modes[i].file,
+ files_and_modes[i].mode);
+ if (result != 0) {
+ num_api_failures++;
+ } else {
+ if (files_and_modes[i].expected_context != secontext) {
+ num_context_check_failures++;
+ } else {
+ num_successes++;
+ }
+ freecon(secontext);
+ }
+ }
+ }
+ };
+
+ std::vector<std::thread> threads;
+ std::generate_n(back_inserter(threads), num_threads,
+ [&]() { return std::thread(thread_function); });
+
+ std::this_thread::sleep_for(run_time);
+ stopped = true;
+ for (auto& thread : threads) {
+ thread.join();
+ }
+
+ EXPECT_EQ(0U, num_api_failures);
+ EXPECT_EQ(0U, num_context_check_failures);
+ EXPECT_GT(num_successes, 0U);
+}
diff --git a/init/util.cpp b/init/util.cpp
index 2792794..a19a6f3 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -42,6 +42,7 @@
#include <selinux/android.h>
#include "reboot.h"
+#include "selinux.h"
#ifdef _INIT_INIT_H
#error "Do not include init.h in files used by ueventd or watchdogd; it will expose init's globals"
@@ -53,31 +54,23 @@
namespace android {
namespace init {
-// DecodeUid() - decodes and returns the given string, which can be either the
-// numeric or name representation, into the integer uid or gid. Returns
-// UINT_MAX on error.
-bool DecodeUid(const std::string& name, uid_t* uid, std::string* err) {
- *uid = UINT_MAX;
- *err = "";
+const std::string kDefaultAndroidDtDir("/proc/device-tree/firmware/android/");
+// DecodeUid() - decodes and returns the given string, which can be either the
+// numeric or name representation, into the integer uid or gid.
+Result<uid_t> DecodeUid(const std::string& name) {
if (isalpha(name[0])) {
passwd* pwd = getpwnam(name.c_str());
- if (!pwd) {
- *err = "getpwnam failed: "s + strerror(errno);
- return false;
- }
- *uid = pwd->pw_uid;
- return true;
+ if (!pwd) return ErrnoError() << "getpwnam failed";
+
+ return pwd->pw_uid;
}
errno = 0;
uid_t result = static_cast<uid_t>(strtoul(name.c_str(), 0, 0));
- if (errno) {
- *err = "strtoul failed: "s + strerror(errno);
- return false;
- }
- *uid = result;
- return true;
+ if (errno) return ErrnoError() << "strtoul failed";
+
+ return result;
}
/*
@@ -87,7 +80,7 @@
* variable ANDROID_SOCKET_ENV_PREFIX<name> ("ANDROID_SOCKET_foo").
*/
int CreateSocket(const char* name, int type, bool passcred, mode_t perm, uid_t uid, gid_t gid,
- const char* socketcon, selabel_handle* sehandle) {
+ const char* socketcon) {
if (socketcon) {
if (setsockcreatecon(socketcon) == -1) {
PLOG(ERROR) << "setsockcreatecon(\"" << socketcon << "\") failed";
@@ -114,11 +107,9 @@
return -1;
}
- char *filecon = NULL;
- if (sehandle) {
- if (selabel_lookup(sehandle, &filecon, addr.sun_path, S_IFSOCK) == 0) {
- setfscreatecon(filecon);
- }
+ std::string secontext;
+ if (SelabelLookupFileContext(addr.sun_path, S_IFSOCK, &secontext) && !secontext.empty()) {
+ setfscreatecon(secontext.c_str());
}
if (passcred) {
@@ -132,8 +123,9 @@
int ret = bind(fd, (struct sockaddr *) &addr, sizeof (addr));
int savederrno = errno;
- setfscreatecon(NULL);
- freecon(filecon);
+ if (!secontext.empty()) {
+ setfscreatecon(nullptr);
+ }
if (ret) {
errno = savederrno;
@@ -162,75 +154,68 @@
return -1;
}
-bool ReadFile(const std::string& path, std::string* content, std::string* err) {
- content->clear();
- *err = "";
-
+Result<std::string> ReadFile(const std::string& path) {
android::base::unique_fd fd(
TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
if (fd == -1) {
- *err = "Unable to open '" + path + "': " + strerror(errno);
- return false;
+ return ErrnoError() << "open() failed";
}
// For security reasons, disallow world-writable
// or group-writable files.
struct stat sb;
if (fstat(fd, &sb) == -1) {
- *err = "fstat failed for '" + path + "': " + strerror(errno);
- return false;
+ return ErrnoError() << "fstat failed()";
}
if ((sb.st_mode & (S_IWGRP | S_IWOTH)) != 0) {
- *err = "Skipping insecure file '" + path + "'";
- return false;
+ return Error() << "Skipping insecure file";
}
- if (!android::base::ReadFdToString(fd, content)) {
- *err = "Unable to read '" + path + "': " + strerror(errno);
- return false;
+ std::string content;
+ if (!android::base::ReadFdToString(fd, &content)) {
+ return ErrnoError() << "Unable to read file contents";
}
- return true;
+ return content;
}
-bool WriteFile(const std::string& path, const std::string& content, std::string* err) {
- *err = "";
-
+Result<Success> WriteFile(const std::string& path, const std::string& content) {
android::base::unique_fd fd(TEMP_FAILURE_RETRY(
open(path.c_str(), O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0600)));
if (fd == -1) {
- *err = "Unable to open '" + path + "': " + strerror(errno);
- return false;
+ return ErrnoError() << "open() failed";
}
if (!android::base::WriteStringToFd(content, fd)) {
- *err = "Unable to write to '" + path + "': " + strerror(errno);
- return false;
+ return ErrnoError() << "Unable to write file contents";
}
- return true;
+ return Success();
}
-int mkdir_recursive(const std::string& path, mode_t mode, selabel_handle* sehandle) {
+bool mkdir_recursive(const std::string& path, mode_t mode) {
std::string::size_type slash = 0;
while ((slash = path.find('/', slash + 1)) != std::string::npos) {
auto directory = path.substr(0, slash);
struct stat info;
if (stat(directory.c_str(), &info) != 0) {
- auto ret = make_dir(directory.c_str(), mode, sehandle);
- if (ret && errno != EEXIST) return ret;
+ auto ret = make_dir(directory, mode);
+ if (!ret && errno != EEXIST) return false;
}
}
- auto ret = make_dir(path.c_str(), mode, sehandle);
- if (ret && errno != EEXIST) return ret;
- return 0;
+ auto ret = make_dir(path, mode);
+ if (!ret && errno != EEXIST) return false;
+ return true;
}
int wait_for_file(const char* filename, std::chrono::nanoseconds timeout) {
- boot_clock::time_point timeout_time = boot_clock::now() + timeout;
- while (boot_clock::now() < timeout_time) {
+ android::base::Timer t;
+ while (t.duration() < timeout) {
struct stat sb;
- if (stat(filename, &sb) != -1) return 0;
-
+ if (stat(filename, &sb) != -1) {
+ LOG(INFO) << "wait for '" << filename << "' took " << t;
+ return 0;
+ }
std::this_thread::sleep_for(10ms);
}
+ LOG(WARNING) << "wait for '" << filename << "' timed out and took " << t;
return -1;
}
@@ -247,26 +232,21 @@
}
}
-int make_dir(const char* path, mode_t mode, selabel_handle* sehandle) {
- int rc;
-
- char *secontext = NULL;
-
- if (sehandle) {
- selabel_lookup(sehandle, &secontext, path, mode);
- setfscreatecon(secontext);
+bool make_dir(const std::string& path, mode_t mode) {
+ std::string secontext;
+ if (SelabelLookupFileContext(path, mode, &secontext) && !secontext.empty()) {
+ setfscreatecon(secontext.c_str());
}
- rc = mkdir(path, mode);
+ int rc = mkdir(path.c_str(), mode);
- if (secontext) {
+ if (!secontext.empty()) {
int save_errno = errno;
- freecon(secontext);
- setfscreatecon(NULL);
+ setfscreatecon(nullptr);
errno = save_errno;
}
- return rc;
+ return rc == 0;
}
/*
@@ -368,16 +348,31 @@
return true;
}
-void panic() {
- LOG(ERROR) << "panic: rebooting to bootloader";
- // Do not queue "shutdown" trigger since we want to shutdown immediately
- DoReboot(ANDROID_RB_RESTART2, "reboot", "bootloader", false);
+static std::string init_android_dt_dir() {
+ // Use the standard procfs-based path by default
+ std::string android_dt_dir = kDefaultAndroidDtDir;
+ // The platform may specify a custom Android DT path in kernel cmdline
+ import_kernel_cmdline(false,
+ [&](const std::string& key, const std::string& value, bool in_qemu) {
+ if (key == "androidboot.android_dt_dir") {
+ android_dt_dir = value;
+ }
+ });
+ LOG(INFO) << "Using Android DT directory " << android_dt_dir;
+ return android_dt_dir;
}
-// Reads the content of device tree file under kAndroidDtDir directory.
+// FIXME: The same logic is duplicated in system/core/fs_mgr/
+const std::string& get_android_dt_dir() {
+ // Set once and saves time for subsequent calls to this function
+ static const std::string kAndroidDtDir = init_android_dt_dir();
+ return kAndroidDtDir;
+}
+
+// Reads the content of device tree file under the platform's Android DT directory.
// Returns true if the read is success, false otherwise.
bool read_android_dt_file(const std::string& sub_path, std::string* dt_content) {
- const std::string file_name = kAndroidDtDir + sub_path;
+ const std::string file_name = get_android_dt_dir() + sub_path;
if (android::base::ReadFileToString(file_name, dt_content)) {
if (!dt_content->empty()) {
dt_content->pop_back(); // Trims the trailing '\0' out.
diff --git a/init/util.h b/init/util.h
index 452df2d..2cfcf6c 100644
--- a/init/util.h
+++ b/init/util.h
@@ -28,9 +28,9 @@
#include <android-base/chrono_utils.h>
#include <selinux/label.h>
-#define COLDBOOT_DONE "/dev/.coldboot_done"
+#include "result.h"
-const std::string kAndroidDtDir("/proc/device-tree/firmware/android/");
+#define COLDBOOT_DONE "/dev/.coldboot_done"
using android::base::boot_clock;
using namespace std::chrono_literals;
@@ -39,25 +39,26 @@
namespace init {
int CreateSocket(const char* name, int type, bool passcred, mode_t perm, uid_t uid, gid_t gid,
- const char* socketcon, selabel_handle* sehandle);
+ const char* socketcon);
-bool ReadFile(const std::string& path, std::string* content, std::string* err);
-bool WriteFile(const std::string& path, const std::string& content, std::string* err);
+Result<std::string> ReadFile(const std::string& path);
+Result<Success> WriteFile(const std::string& path, const std::string& content);
-bool DecodeUid(const std::string& name, uid_t* uid, std::string* err);
+Result<uid_t> DecodeUid(const std::string& name);
-int mkdir_recursive(const std::string& pathname, mode_t mode, selabel_handle* sehandle);
+bool mkdir_recursive(const std::string& pathname, mode_t mode);
int wait_for_file(const char *filename, std::chrono::nanoseconds timeout);
void import_kernel_cmdline(bool in_qemu,
const std::function<void(const std::string&, const std::string&, bool)>&);
-int make_dir(const char* path, mode_t mode, selabel_handle* sehandle);
+bool make_dir(const std::string& path, mode_t mode);
std::string bytes_to_hex(const uint8_t *bytes, size_t bytes_len);
bool is_dir(const char* pathname);
bool expand_props(const std::string& src, std::string* dst);
-void panic() __attribute__((__noreturn__));
-
-// Reads or compares the content of device tree file under kAndroidDtDir directory.
+// Returns the platform's Android DT directory as specified in the kernel cmdline.
+// If the platform does not configure a custom DT path, returns the standard one (based in procfs).
+const std::string& get_android_dt_dir();
+// Reads or compares the content of device tree file under the platform's Android DT directory.
bool read_android_dt_file(const std::string& sub_path, std::string* dt_content);
bool is_android_dt_value_expected(const std::string& sub_path, const std::string& expected_content);
diff --git a/init/util_test.cpp b/init/util_test.cpp
index c16ab74..3ae53a4 100644
--- a/init/util_test.cpp
+++ b/init/util_test.cpp
@@ -30,61 +30,51 @@
namespace init {
TEST(util, ReadFile_ENOENT) {
- std::string s("hello");
- std::string err;
errno = 0;
- EXPECT_FALSE(ReadFile("/proc/does-not-exist", &s, &err));
- EXPECT_EQ("Unable to open '/proc/does-not-exist': No such file or directory", err);
+ auto file_contents = ReadFile("/proc/does-not-exist");
EXPECT_EQ(ENOENT, errno);
- EXPECT_EQ("", s); // s was cleared.
+ ASSERT_FALSE(file_contents);
+ EXPECT_EQ("open() failed: No such file or directory", file_contents.error_string());
}
TEST(util, ReadFileGroupWriteable) {
std::string s("hello");
TemporaryFile tf;
- std::string err;
ASSERT_TRUE(tf.fd != -1);
- EXPECT_TRUE(WriteFile(tf.path, s, &err)) << strerror(errno);
- EXPECT_EQ("", err);
+ EXPECT_TRUE(WriteFile(tf.path, s)) << strerror(errno);
EXPECT_NE(-1, fchmodat(AT_FDCWD, tf.path, 0620, AT_SYMLINK_NOFOLLOW)) << strerror(errno);
- EXPECT_FALSE(ReadFile(tf.path, &s, &err)) << strerror(errno);
- EXPECT_EQ("Skipping insecure file '"s + tf.path + "'", err);
- EXPECT_EQ("", s); // s was cleared.
+ auto file_contents = ReadFile(tf.path);
+ ASSERT_FALSE(file_contents) << strerror(errno);
+ EXPECT_EQ("Skipping insecure file", file_contents.error_string());
}
TEST(util, ReadFileWorldWiteable) {
std::string s("hello");
TemporaryFile tf;
- std::string err;
ASSERT_TRUE(tf.fd != -1);
- EXPECT_TRUE(WriteFile(tf.path, s, &err)) << strerror(errno);
- EXPECT_EQ("", err);
+ EXPECT_TRUE(WriteFile(tf.path, s)) << strerror(errno);
EXPECT_NE(-1, fchmodat(AT_FDCWD, tf.path, 0602, AT_SYMLINK_NOFOLLOW)) << strerror(errno);
- EXPECT_FALSE(ReadFile(tf.path, &s, &err)) << strerror(errno);
- EXPECT_EQ("Skipping insecure file '"s + tf.path + "'", err);
- EXPECT_EQ("", s); // s was cleared.
+ auto file_contents = ReadFile(tf.path);
+ ASSERT_FALSE(file_contents) << strerror(errno);
+ EXPECT_EQ("Skipping insecure file", file_contents.error_string());
}
TEST(util, ReadFileSymbolicLink) {
- std::string s("hello");
errno = 0;
// lrwxrwxrwx 1 root root 13 1970-01-01 00:00 charger -> /sbin/healthd
- std::string err;
- EXPECT_FALSE(ReadFile("/charger", &s, &err));
- EXPECT_EQ("Unable to open '/charger': Too many symbolic links encountered", err);
+ auto file_contents = ReadFile("/charger");
EXPECT_EQ(ELOOP, errno);
- EXPECT_EQ("", s); // s was cleared.
+ ASSERT_FALSE(file_contents);
+ EXPECT_EQ("open() failed: Too many symbolic links encountered", file_contents.error_string());
}
TEST(util, ReadFileSuccess) {
- std::string s("hello");
- std::string err;
- EXPECT_TRUE(ReadFile("/proc/version", &s, &err));
- EXPECT_EQ("", err);
- EXPECT_GT(s.length(), 6U);
- EXPECT_EQ('\n', s[s.length() - 1]);
- s[5] = 0;
- EXPECT_STREQ("Linux", s.c_str());
+ auto file_contents = ReadFile("/proc/version");
+ ASSERT_TRUE(file_contents);
+ EXPECT_GT(file_contents->length(), 6U);
+ EXPECT_EQ('\n', file_contents->at(file_contents->length() - 1));
+ (*file_contents)[5] = 0;
+ EXPECT_STREQ("Linux", file_contents->c_str());
}
TEST(util, WriteFileBinary) {
@@ -95,29 +85,23 @@
ASSERT_EQ(10u, contents.size());
TemporaryFile tf;
- std::string err;
ASSERT_TRUE(tf.fd != -1);
- EXPECT_TRUE(WriteFile(tf.path, contents, &err)) << strerror(errno);
- EXPECT_EQ("", err);
+ EXPECT_TRUE(WriteFile(tf.path, contents)) << strerror(errno);
- std::string read_back_contents;
- EXPECT_TRUE(ReadFile(tf.path, &read_back_contents, &err)) << strerror(errno);
- EXPECT_EQ("", err);
- EXPECT_EQ(contents, read_back_contents);
- EXPECT_EQ(10u, read_back_contents.size());
+ auto read_back_contents = ReadFile(tf.path);
+ ASSERT_TRUE(read_back_contents) << strerror(errno);
+ EXPECT_EQ(contents, *read_back_contents);
+ EXPECT_EQ(10u, read_back_contents->size());
}
TEST(util, WriteFileNotExist) {
std::string s("hello");
- std::string s2("hello");
TemporaryDir test_dir;
std::string path = android::base::StringPrintf("%s/does-not-exist", test_dir.path);
- std::string err;
- EXPECT_TRUE(WriteFile(path, s, &err));
- EXPECT_EQ("", err);
- EXPECT_TRUE(ReadFile(path, &s2, &err));
- EXPECT_EQ("", err);
- EXPECT_EQ(s, s2);
+ EXPECT_TRUE(WriteFile(path, s));
+ auto file_contents = ReadFile(path);
+ ASSERT_TRUE(file_contents);
+ EXPECT_EQ(s, *file_contents);
struct stat sb;
int fd = open(path.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
EXPECT_NE(-1, fd);
@@ -127,37 +111,30 @@
}
TEST(util, WriteFileExist) {
- std::string s2("");
TemporaryFile tf;
ASSERT_TRUE(tf.fd != -1);
- std::string err;
- EXPECT_TRUE(WriteFile(tf.path, "1hello1", &err)) << strerror(errno);
- EXPECT_EQ("", err);
- EXPECT_TRUE(ReadFile(tf.path, &s2, &err));
- EXPECT_EQ("", err);
- EXPECT_STREQ("1hello1", s2.c_str());
- EXPECT_TRUE(WriteFile(tf.path, "2ll2", &err));
- EXPECT_EQ("", err);
- EXPECT_TRUE(ReadFile(tf.path, &s2, &err));
- EXPECT_EQ("", err);
- EXPECT_STREQ("2ll2", s2.c_str());
+ EXPECT_TRUE(WriteFile(tf.path, "1hello1")) << strerror(errno);
+ auto file_contents = ReadFile(tf.path);
+ ASSERT_TRUE(file_contents);
+ EXPECT_EQ("1hello1", *file_contents);
+ EXPECT_TRUE(WriteFile(tf.path, "2ll2"));
+ file_contents = ReadFile(tf.path);
+ ASSERT_TRUE(file_contents);
+ EXPECT_EQ("2ll2", *file_contents);
}
TEST(util, DecodeUid) {
- uid_t decoded_uid;
- std::string err;
+ auto decoded_uid = DecodeUid("root");
+ EXPECT_TRUE(decoded_uid);
+ EXPECT_EQ(0U, *decoded_uid);
- EXPECT_TRUE(DecodeUid("root", &decoded_uid, &err));
- EXPECT_EQ("", err);
- EXPECT_EQ(0U, decoded_uid);
+ decoded_uid = DecodeUid("toot");
+ EXPECT_FALSE(decoded_uid);
+ EXPECT_EQ("getpwnam failed: No such file or directory", decoded_uid.error_string());
- EXPECT_FALSE(DecodeUid("toot", &decoded_uid, &err));
- EXPECT_EQ("getpwnam failed: No such file or directory", err);
- EXPECT_EQ(UINT_MAX, decoded_uid);
-
- EXPECT_TRUE(DecodeUid("123", &decoded_uid, &err));
- EXPECT_EQ("", err);
- EXPECT_EQ(123U, decoded_uid);
+ decoded_uid = DecodeUid("123");
+ EXPECT_TRUE(decoded_uid);
+ EXPECT_EQ(123U, *decoded_uid);
}
TEST(util, is_dir) {
@@ -170,7 +147,7 @@
TEST(util, mkdir_recursive) {
TemporaryDir test_dir;
std::string path = android::base::StringPrintf("%s/three/directories/deep", test_dir.path);
- EXPECT_EQ(0, mkdir_recursive(path, 0755, nullptr));
+ EXPECT_TRUE(mkdir_recursive(path, 0755));
std::string path1 = android::base::StringPrintf("%s/three", test_dir.path);
EXPECT_TRUE(is_dir(path1.c_str()));
std::string path2 = android::base::StringPrintf("%s/three/directories", test_dir.path);
@@ -182,7 +159,7 @@
TEST(util, mkdir_recursive_extra_slashes) {
TemporaryDir test_dir;
std::string path = android::base::StringPrintf("%s/three////directories/deep//", test_dir.path);
- EXPECT_EQ(0, mkdir_recursive(path, 0755, nullptr));
+ EXPECT_TRUE(mkdir_recursive(path, 0755));
std::string path1 = android::base::StringPrintf("%s/three", test_dir.path);
EXPECT_TRUE(is_dir(path1.c_str()));
std::string path2 = android::base::StringPrintf("%s/three/directories", test_dir.path);
diff --git a/libappfuse/Android.bp b/libappfuse/Android.bp
index e659f79..29ffe32 100644
--- a/libappfuse/Android.bp
+++ b/libappfuse/Android.bp
@@ -8,7 +8,6 @@
"-Wall",
"-Werror",
],
- clang: true
}
cc_library_shared {
@@ -19,6 +18,7 @@
"FuseAppLoop.cc",
"FuseBuffer.cc",
"FuseBridgeLoop.cc",
+ "EpollController.cc",
]
}
diff --git a/libappfuse/EpollController.cc b/libappfuse/EpollController.cc
new file mode 100644
index 0000000..9daeab8
--- /dev/null
+++ b/libappfuse/EpollController.cc
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2017 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 specic language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+
+#include "libappfuse/EpollController.h"
+
+namespace android {
+namespace fuse {
+
+EpollController::EpollController(base::unique_fd&& poll_fd) : poll_fd_(std::move(poll_fd)) {
+}
+
+bool EpollController::Wait(size_t event_count) {
+ events_.resize(event_count);
+ const int result = TEMP_FAILURE_RETRY(epoll_wait(poll_fd_, events_.data(), event_count, -1));
+ if (result == -1) {
+ PLOG(ERROR) << "Failed to wait for epoll";
+ return false;
+ }
+ events_.resize(result);
+ return true;
+}
+
+bool EpollController::AddFd(int fd, int events, void* data) {
+ return InvokeControl(EPOLL_CTL_ADD, fd, events, data);
+}
+
+bool EpollController::UpdateFd(int fd, int events, void* data) {
+ return InvokeControl(EPOLL_CTL_MOD, fd, events, data);
+}
+
+bool EpollController::RemoveFd(int fd) {
+ return InvokeControl(EPOLL_CTL_DEL, fd, /* events */ 0, nullptr);
+}
+
+const std::vector<epoll_event>& EpollController::events() const {
+ return events_;
+}
+
+bool EpollController::InvokeControl(int op, int fd, int events, void* data) const {
+ epoll_event event;
+ memset(&event, 0, sizeof(event));
+ event.events = events;
+ event.data.ptr = data;
+ if (epoll_ctl(poll_fd_, op, fd, &event) == -1) {
+ PLOG(ERROR) << "epoll_ctl() error op=" << op;
+ return false;
+ }
+ return true;
+}
+}
+}
diff --git a/libappfuse/FuseAppLoop.cc b/libappfuse/FuseAppLoop.cc
index a31880e..b6bc191 100644
--- a/libappfuse/FuseAppLoop.cc
+++ b/libappfuse/FuseAppLoop.cc
@@ -16,205 +16,232 @@
#include "libappfuse/FuseAppLoop.h"
+#include <sys/eventfd.h>
#include <sys/stat.h>
#include <android-base/logging.h>
#include <android-base/unique_fd.h>
+#include "libappfuse/EpollController.h"
+
namespace android {
namespace fuse {
namespace {
-void HandleLookUp(FuseBuffer* buffer, FuseAppLoopCallback* callback) {
- // AppFuse does not support directory structure now.
- // It can lookup only files under the mount point.
- if (buffer->request.header.nodeid != FUSE_ROOT_ID) {
- LOG(ERROR) << "Nodeid is not FUSE_ROOT_ID.";
- buffer->response.Reset(0, -ENOENT, buffer->request.header.unique);
- return;
- }
-
- // Ensure that the filename ends with 0.
- const size_t filename_length =
- buffer->request.header.len - sizeof(fuse_in_header);
- if (buffer->request.lookup_name[filename_length - 1] != 0) {
- LOG(ERROR) << "File name does not end with 0.";
- buffer->response.Reset(0, -ENOENT, buffer->request.header.unique);
- return;
- }
-
- const uint64_t inode =
- static_cast<uint64_t>(atol(buffer->request.lookup_name));
- if (inode == 0 || inode == LONG_MAX) {
- LOG(ERROR) << "Invalid filename";
- buffer->response.Reset(0, -ENOENT, buffer->request.header.unique);
- return;
- }
-
- const int64_t size = callback->OnGetSize(inode);
- if (size < 0) {
- buffer->response.Reset(0, size, buffer->request.header.unique);
- return;
- }
-
- buffer->response.Reset(sizeof(fuse_entry_out), 0,
- buffer->request.header.unique);
- buffer->response.entry_out.nodeid = inode;
- buffer->response.entry_out.attr_valid = 10;
- buffer->response.entry_out.entry_valid = 10;
- buffer->response.entry_out.attr.ino = inode;
- buffer->response.entry_out.attr.mode = S_IFREG | 0777;
- buffer->response.entry_out.attr.size = size;
-}
-
-void HandleGetAttr(FuseBuffer* buffer, FuseAppLoopCallback* callback) {
- const uint64_t nodeid = buffer->request.header.nodeid;
- int64_t size;
- uint32_t mode;
- if (nodeid == FUSE_ROOT_ID) {
- size = 0;
- mode = S_IFDIR | 0777;
- } else {
- size = callback->OnGetSize(buffer->request.header.nodeid);
- if (size < 0) {
- buffer->response.Reset(0, size, buffer->request.header.unique);
- return;
+bool HandleLookUp(FuseAppLoop* loop, FuseBuffer* buffer, FuseAppLoopCallback* callback) {
+ // AppFuse does not support directory structure now.
+ // It can lookup only files under the mount point.
+ if (buffer->request.header.nodeid != FUSE_ROOT_ID) {
+ LOG(ERROR) << "Nodeid is not FUSE_ROOT_ID.";
+ return loop->ReplySimple(buffer->request.header.unique, -ENOENT);
}
- mode = S_IFREG | 0777;
- }
- buffer->response.Reset(sizeof(fuse_attr_out), 0,
- buffer->request.header.unique);
- buffer->response.attr_out.attr_valid = 10;
- buffer->response.attr_out.attr.ino = nodeid;
- buffer->response.attr_out.attr.mode = mode;
- buffer->response.attr_out.attr.size = size;
+ // Ensure that the filename ends with 0.
+ const size_t filename_length = buffer->request.header.len - sizeof(fuse_in_header);
+ if (buffer->request.lookup_name[filename_length - 1] != 0) {
+ LOG(ERROR) << "File name does not end with 0.";
+ return loop->ReplySimple(buffer->request.header.unique, -ENOENT);
+ }
+
+ const uint64_t inode = static_cast<uint64_t>(atol(buffer->request.lookup_name));
+ if (inode == 0 || inode == LONG_MAX) {
+ LOG(ERROR) << "Invalid filename";
+ return loop->ReplySimple(buffer->request.header.unique, -ENOENT);
+ }
+
+ callback->OnLookup(buffer->request.header.unique, inode);
+ return true;
}
-void HandleOpen(FuseBuffer* buffer, FuseAppLoopCallback* callback) {
- const int32_t file_handle = callback->OnOpen(buffer->request.header.nodeid);
- if (file_handle < 0) {
- buffer->response.Reset(0, file_handle, buffer->request.header.unique);
- return;
- }
- buffer->response.Reset(sizeof(fuse_open_out), kFuseSuccess,
- buffer->request.header.unique);
- buffer->response.open_out.fh = file_handle;
+bool HandleGetAttr(FuseAppLoop* loop, FuseBuffer* buffer, FuseAppLoopCallback* callback) {
+ if (buffer->request.header.nodeid == FUSE_ROOT_ID) {
+ return loop->ReplyGetAttr(buffer->request.header.unique, buffer->request.header.nodeid, 0,
+ S_IFDIR | 0777);
+ } else {
+ callback->OnGetAttr(buffer->request.header.unique, buffer->request.header.nodeid);
+ return true;
+ }
}
-void HandleFsync(FuseBuffer* buffer, FuseAppLoopCallback* callback) {
- buffer->response.Reset(0, callback->OnFsync(buffer->request.header.nodeid),
- buffer->request.header.unique);
+bool HandleRead(FuseAppLoop* loop, FuseBuffer* buffer, FuseAppLoopCallback* callback) {
+ if (buffer->request.read_in.size > kFuseMaxRead) {
+ return loop->ReplySimple(buffer->request.header.unique, -EINVAL);
+ }
+
+ callback->OnRead(buffer->request.header.unique, buffer->request.header.nodeid,
+ buffer->request.read_in.offset, buffer->request.read_in.size);
+ return true;
}
-void HandleRelease(FuseBuffer* buffer, FuseAppLoopCallback* callback) {
- buffer->response.Reset(0, callback->OnRelease(buffer->request.header.nodeid),
- buffer->request.header.unique);
+bool HandleWrite(FuseAppLoop* loop, FuseBuffer* buffer, FuseAppLoopCallback* callback) {
+ if (buffer->request.write_in.size > kFuseMaxWrite) {
+ return loop->ReplySimple(buffer->request.header.unique, -EINVAL);
+ }
+
+ callback->OnWrite(buffer->request.header.unique, buffer->request.header.nodeid,
+ buffer->request.write_in.offset, buffer->request.write_in.size,
+ buffer->request.write_data);
+ return true;
}
-void HandleRead(FuseBuffer* buffer, FuseAppLoopCallback* callback) {
- const uint64_t unique = buffer->request.header.unique;
- const uint64_t nodeid = buffer->request.header.nodeid;
- const uint64_t offset = buffer->request.read_in.offset;
- const uint32_t size = buffer->request.read_in.size;
+bool HandleMessage(FuseAppLoop* loop, FuseBuffer* buffer, int fd, FuseAppLoopCallback* callback) {
+ if (!buffer->request.Read(fd)) {
+ return false;
+ }
- if (size > kFuseMaxRead) {
- buffer->response.Reset(0, -EINVAL, buffer->request.header.unique);
- return;
- }
+ const uint32_t opcode = buffer->request.header.opcode;
+ LOG(VERBOSE) << "Read a fuse packet, opcode=" << opcode;
+ switch (opcode) {
+ case FUSE_FORGET:
+ // Do not reply to FUSE_FORGET.
+ return true;
- const int32_t read_size = callback->OnRead(nodeid, offset, size,
- buffer->response.read_data);
- if (read_size < 0) {
- buffer->response.Reset(0, read_size, buffer->request.header.unique);
- return;
- }
+ case FUSE_LOOKUP:
+ return HandleLookUp(loop, buffer, callback);
- buffer->response.ResetHeader(read_size, kFuseSuccess, unique);
-}
+ case FUSE_GETATTR:
+ return HandleGetAttr(loop, buffer, callback);
-void HandleWrite(FuseBuffer* buffer, FuseAppLoopCallback* callback) {
- const uint64_t unique = buffer->request.header.unique;
- const uint64_t nodeid = buffer->request.header.nodeid;
- const uint64_t offset = buffer->request.write_in.offset;
- const uint32_t size = buffer->request.write_in.size;
+ case FUSE_OPEN:
+ callback->OnOpen(buffer->request.header.unique, buffer->request.header.nodeid);
+ return true;
- if (size > kFuseMaxWrite) {
- buffer->response.Reset(0, -EINVAL, buffer->request.header.unique);
- return;
- }
+ case FUSE_READ:
+ return HandleRead(loop, buffer, callback);
- const int32_t write_size = callback->OnWrite(nodeid, offset, size,
- buffer->request.write_data);
- if (write_size < 0) {
- buffer->response.Reset(0, write_size, buffer->request.header.unique);
- return;
- }
+ case FUSE_WRITE:
+ return HandleWrite(loop, buffer, callback);
- buffer->response.Reset(sizeof(fuse_write_out), kFuseSuccess, unique);
- buffer->response.write_out.size = write_size;
+ case FUSE_RELEASE:
+ callback->OnRelease(buffer->request.header.unique, buffer->request.header.nodeid);
+ return true;
+
+ case FUSE_FSYNC:
+ callback->OnFsync(buffer->request.header.unique, buffer->request.header.nodeid);
+ return true;
+
+ default:
+ buffer->HandleNotImpl();
+ return buffer->response.Write(fd);
+ }
}
} // namespace
-bool StartFuseAppLoop(int raw_fd, FuseAppLoopCallback* callback) {
- base::unique_fd fd(raw_fd);
- FuseBuffer buffer;
+FuseAppLoopCallback::~FuseAppLoopCallback() = default;
- LOG(DEBUG) << "Start fuse loop.";
- while (callback->IsActive()) {
- if (!buffer.request.Read(fd)) {
- return false;
+FuseAppLoop::FuseAppLoop(base::unique_fd&& fd) : fd_(std::move(fd)) {}
+
+void FuseAppLoop::Break() {
+ const int64_t value = 1;
+ if (write(break_fd_, &value, sizeof(value)) == -1) {
+ PLOG(ERROR) << "Failed to send a break event";
+ }
+}
+
+bool FuseAppLoop::ReplySimple(uint64_t unique, int32_t result) {
+ if (result == -ENOSYS) {
+ // We should not return -ENOSYS because the kernel stops delivering FUSE
+ // command after receiving -ENOSYS as a result for the command.
+ result = -EBADF;
+ }
+ FuseSimpleResponse response;
+ response.Reset(0, result, unique);
+ return response.Write(fd_);
+}
+
+bool FuseAppLoop::ReplyLookup(uint64_t unique, uint64_t inode, int64_t size) {
+ FuseSimpleResponse response;
+ response.Reset(sizeof(fuse_entry_out), 0, unique);
+ response.entry_out.nodeid = inode;
+ response.entry_out.attr_valid = 10;
+ response.entry_out.entry_valid = 10;
+ response.entry_out.attr.ino = inode;
+ response.entry_out.attr.mode = S_IFREG | 0777;
+ response.entry_out.attr.size = size;
+ return response.Write(fd_);
+}
+
+bool FuseAppLoop::ReplyGetAttr(uint64_t unique, uint64_t inode, int64_t size, int mode) {
+ CHECK(mode == (S_IFREG | 0777) || mode == (S_IFDIR | 0777));
+ FuseSimpleResponse response;
+ response.Reset(sizeof(fuse_attr_out), 0, unique);
+ response.attr_out.attr_valid = 10;
+ response.attr_out.attr.ino = inode;
+ response.attr_out.attr.mode = mode;
+ response.attr_out.attr.size = size;
+ return response.Write(fd_);
+}
+
+bool FuseAppLoop::ReplyOpen(uint64_t unique, uint64_t fh) {
+ FuseSimpleResponse response;
+ response.Reset(sizeof(fuse_open_out), kFuseSuccess, unique);
+ response.open_out.fh = fh;
+ return response.Write(fd_);
+}
+
+bool FuseAppLoop::ReplyWrite(uint64_t unique, uint32_t size) {
+ CHECK(size <= kFuseMaxWrite);
+ FuseSimpleResponse response;
+ response.Reset(sizeof(fuse_write_out), kFuseSuccess, unique);
+ response.write_out.size = size;
+ return response.Write(fd_);
+}
+
+bool FuseAppLoop::ReplyRead(uint64_t unique, uint32_t size, const void* data) {
+ CHECK(size <= kFuseMaxRead);
+ FuseSimpleResponse response;
+ response.ResetHeader(size, kFuseSuccess, unique);
+ return response.WriteWithBody(fd_, sizeof(FuseResponse), data);
+}
+
+void FuseAppLoop::Start(FuseAppLoopCallback* callback) {
+ break_fd_.reset(eventfd(/* initval */ 0, EFD_CLOEXEC));
+ if (break_fd_.get() == -1) {
+ PLOG(ERROR) << "Failed to open FD for break event";
+ return;
}
- const uint32_t opcode = buffer.request.header.opcode;
- LOG(VERBOSE) << "Read a fuse packet, opcode=" << opcode;
- switch (opcode) {
- case FUSE_FORGET:
- // Do not reply to FUSE_FORGET.
- continue;
-
- case FUSE_LOOKUP:
- HandleLookUp(&buffer, callback);
- break;
-
- case FUSE_GETATTR:
- HandleGetAttr(&buffer, callback);
- break;
-
- case FUSE_OPEN:
- HandleOpen(&buffer, callback);
- break;
-
- case FUSE_READ:
- HandleRead(&buffer, callback);
- break;
-
- case FUSE_WRITE:
- HandleWrite(&buffer, callback);
- break;
-
- case FUSE_RELEASE:
- HandleRelease(&buffer, callback);
- break;
-
- case FUSE_FSYNC:
- HandleFsync(&buffer, callback);
- break;
-
- default:
- buffer.HandleNotImpl();
- break;
+ base::unique_fd epoll_fd(epoll_create1(EPOLL_CLOEXEC));
+ if (epoll_fd.get() == -1) {
+ PLOG(ERROR) << "Failed to open FD for epoll";
+ return;
}
- if (!buffer.response.Write(fd)) {
- LOG(ERROR) << "Failed to write a response to the device.";
- return false;
- }
- }
+ int last_event;
+ int break_event;
- return true;
+ std::unique_ptr<EpollController> epoll_controller(new EpollController(std::move(epoll_fd)));
+ if (!epoll_controller->AddFd(fd_, EPOLLIN, &last_event)) {
+ return;
+ }
+ if (!epoll_controller->AddFd(break_fd_, EPOLLIN, &break_event)) {
+ return;
+ }
+
+ last_event = 0;
+ break_event = 0;
+
+ FuseBuffer buffer;
+ while (true) {
+ if (!epoll_controller->Wait(1)) {
+ break;
+ }
+ last_event = 0;
+ *reinterpret_cast<int*>(epoll_controller->events()[0].data.ptr) =
+ epoll_controller->events()[0].events;
+
+ if (break_event != 0 || (last_event & ~EPOLLIN) != 0) {
+ break;
+ }
+
+ if (!HandleMessage(this, &buffer, fd_, callback)) {
+ break;
+ }
+ }
+
+ LOG(VERBOSE) << "FuseAppLoop exit";
}
} // namespace fuse
diff --git a/libappfuse/FuseBridgeLoop.cc b/libappfuse/FuseBridgeLoop.cc
index 2386bf8..3f47066 100644
--- a/libappfuse/FuseBridgeLoop.cc
+++ b/libappfuse/FuseBridgeLoop.cc
@@ -16,85 +16,355 @@
#include "libappfuse/FuseBridgeLoop.h"
+#include <sys/epoll.h>
+#include <sys/socket.h>
+
+#include <unordered_map>
+
#include <android-base/logging.h>
#include <android-base/unique_fd.h>
+#include "libappfuse/EpollController.h"
+
namespace android {
namespace fuse {
+namespace {
-bool StartFuseBridgeLoop(
- int raw_dev_fd, int raw_proxy_fd, FuseBridgeLoopCallback* callback) {
- base::unique_fd dev_fd(raw_dev_fd);
- base::unique_fd proxy_fd(raw_proxy_fd);
- FuseBuffer buffer;
- size_t open_count = 0;
+enum class FuseBridgeState { kWaitToReadEither, kWaitToReadProxy, kWaitToWriteProxy, kClosing };
- LOG(DEBUG) << "Start fuse loop.";
- while (true) {
- if (!buffer.request.Read(dev_fd)) {
- return false;
+struct FuseBridgeEntryEvent {
+ FuseBridgeEntry* entry;
+ int events;
+};
+
+void GetObservedEvents(FuseBridgeState state, int* device_events, int* proxy_events) {
+ switch (state) {
+ case FuseBridgeState::kWaitToReadEither:
+ *device_events = EPOLLIN;
+ *proxy_events = EPOLLIN;
+ return;
+ case FuseBridgeState::kWaitToReadProxy:
+ *device_events = 0;
+ *proxy_events = EPOLLIN;
+ return;
+ case FuseBridgeState::kWaitToWriteProxy:
+ *device_events = 0;
+ *proxy_events = EPOLLOUT;
+ return;
+ case FuseBridgeState::kClosing:
+ *device_events = 0;
+ *proxy_events = 0;
+ return;
+ }
+}
+}
+
+class FuseBridgeEntry {
+ public:
+ FuseBridgeEntry(int mount_id, base::unique_fd&& dev_fd, base::unique_fd&& proxy_fd)
+ : mount_id_(mount_id),
+ device_fd_(std::move(dev_fd)),
+ proxy_fd_(std::move(proxy_fd)),
+ state_(FuseBridgeState::kWaitToReadEither),
+ last_state_(FuseBridgeState::kWaitToReadEither),
+ last_device_events_({this, 0}),
+ last_proxy_events_({this, 0}),
+ open_count_(0) {}
+
+ // Transfer bytes depends on availability of FDs and the internal |state_|.
+ void Transfer(FuseBridgeLoopCallback* callback) {
+ constexpr int kUnexpectedEventMask = ~(EPOLLIN | EPOLLOUT);
+ const bool unexpected_event = (last_device_events_.events & kUnexpectedEventMask) ||
+ (last_proxy_events_.events & kUnexpectedEventMask);
+ const bool device_read_ready = last_device_events_.events & EPOLLIN;
+ const bool proxy_read_ready = last_proxy_events_.events & EPOLLIN;
+ const bool proxy_write_ready = last_proxy_events_.events & EPOLLOUT;
+
+ last_device_events_.events = 0;
+ last_proxy_events_.events = 0;
+
+ LOG(VERBOSE) << "Transfer device_read_ready=" << device_read_ready
+ << " proxy_read_ready=" << proxy_read_ready
+ << " proxy_write_ready=" << proxy_write_ready;
+
+ if (unexpected_event) {
+ LOG(ERROR) << "Invalid epoll event is observed";
+ state_ = FuseBridgeState::kClosing;
+ return;
+ }
+
+ switch (state_) {
+ case FuseBridgeState::kWaitToReadEither:
+ if (proxy_read_ready) {
+ state_ = ReadFromProxy();
+ } else if (device_read_ready) {
+ state_ = ReadFromDevice(callback);
+ }
+ return;
+
+ case FuseBridgeState::kWaitToReadProxy:
+ CHECK(proxy_read_ready);
+ state_ = ReadFromProxy();
+ return;
+
+ case FuseBridgeState::kWaitToWriteProxy:
+ CHECK(proxy_write_ready);
+ state_ = WriteToProxy();
+ return;
+
+ case FuseBridgeState::kClosing:
+ return;
+ }
}
- const uint32_t opcode = buffer.request.header.opcode;
- LOG(VERBOSE) << "Read a fuse packet, opcode=" << opcode;
- switch (opcode) {
- case FUSE_FORGET:
- // Do not reply to FUSE_FORGET.
- continue;
+ bool IsClosing() const { return state_ == FuseBridgeState::kClosing; }
- case FUSE_LOOKUP:
- case FUSE_GETATTR:
- case FUSE_OPEN:
- case FUSE_READ:
- case FUSE_WRITE:
- case FUSE_RELEASE:
- case FUSE_FSYNC:
- if (!buffer.request.Write(proxy_fd)) {
- LOG(ERROR) << "Failed to write a request to the proxy.";
- return false;
+ int mount_id() const { return mount_id_; }
+
+ private:
+ friend class BridgeEpollController;
+
+ FuseBridgeState ReadFromProxy() {
+ switch (buffer_.response.ReadOrAgain(proxy_fd_)) {
+ case ResultOrAgain::kSuccess:
+ break;
+ case ResultOrAgain::kFailure:
+ return FuseBridgeState::kClosing;
+ case ResultOrAgain::kAgain:
+ return FuseBridgeState::kWaitToReadProxy;
}
- if (!buffer.response.Read(proxy_fd)) {
- LOG(ERROR) << "Failed to read a response from the proxy.";
- return false;
+
+ if (!buffer_.response.Write(device_fd_)) {
+ return FuseBridgeState::kClosing;
}
- break;
- case FUSE_INIT:
- buffer.HandleInit();
- break;
+ auto it = opcode_map_.find(buffer_.response.header.unique);
+ if (it != opcode_map_.end()) {
+ switch (it->second) {
+ case FUSE_OPEN:
+ if (buffer_.response.header.error == fuse::kFuseSuccess) {
+ open_count_++;
+ }
+ break;
- default:
- buffer.HandleNotImpl();
- break;
+ case FUSE_RELEASE:
+ if (open_count_ > 0) {
+ open_count_--;
+ } else {
+ LOG(WARNING) << "Unexpected FUSE_RELEASE before opening a file.";
+ break;
+ }
+ if (open_count_ == 0) {
+ return FuseBridgeState::kClosing;
+ }
+ break;
+ }
+ opcode_map_.erase(it);
+ }
+
+ return FuseBridgeState::kWaitToReadEither;
}
- if (!buffer.response.Write(dev_fd)) {
- LOG(ERROR) << "Failed to write a response to the device.";
- return false;
+ FuseBridgeState ReadFromDevice(FuseBridgeLoopCallback* callback) {
+ LOG(VERBOSE) << "ReadFromDevice";
+ if (!buffer_.request.Read(device_fd_)) {
+ return FuseBridgeState::kClosing;
+ }
+
+ const uint32_t opcode = buffer_.request.header.opcode;
+ LOG(VERBOSE) << "Read a fuse packet, opcode=" << opcode;
+ switch (opcode) {
+ case FUSE_FORGET:
+ // Do not reply to FUSE_FORGET.
+ return FuseBridgeState::kWaitToReadEither;
+
+ case FUSE_LOOKUP:
+ case FUSE_GETATTR:
+ case FUSE_OPEN:
+ case FUSE_READ:
+ case FUSE_WRITE:
+ case FUSE_RELEASE:
+ case FUSE_FSYNC:
+ if (opcode == FUSE_OPEN || opcode == FUSE_RELEASE) {
+ opcode_map_.emplace(buffer_.request.header.unique, opcode);
+ }
+ return WriteToProxy();
+
+ case FUSE_INIT:
+ buffer_.HandleInit();
+ break;
+
+ default:
+ buffer_.HandleNotImpl();
+ break;
+ }
+
+ if (!buffer_.response.Write(device_fd_)) {
+ return FuseBridgeState::kClosing;
+ }
+
+ if (opcode == FUSE_INIT) {
+ callback->OnMount(mount_id_);
+ }
+
+ return FuseBridgeState::kWaitToReadEither;
}
- switch (opcode) {
- case FUSE_INIT:
- callback->OnMount();
- break;
- case FUSE_OPEN:
- if (buffer.response.header.error == fuse::kFuseSuccess) {
- open_count++;
+ FuseBridgeState WriteToProxy() {
+ switch (buffer_.request.WriteOrAgain(proxy_fd_)) {
+ case ResultOrAgain::kSuccess:
+ return FuseBridgeState::kWaitToReadEither;
+ case ResultOrAgain::kFailure:
+ return FuseBridgeState::kClosing;
+ case ResultOrAgain::kAgain:
+ return FuseBridgeState::kWaitToWriteProxy;
}
- break;
- case FUSE_RELEASE:
- if (open_count != 0) {
- open_count--;
- } else {
- LOG(WARNING) << "Unexpected FUSE_RELEASE before opening a file.";
- break;
- }
- if (open_count == 0) {
- return true;
- }
- break;
}
- }
+
+ const int mount_id_;
+ base::unique_fd device_fd_;
+ base::unique_fd proxy_fd_;
+ FuseBuffer buffer_;
+ FuseBridgeState state_;
+ FuseBridgeState last_state_;
+ FuseBridgeEntryEvent last_device_events_;
+ FuseBridgeEntryEvent last_proxy_events_;
+
+ // Remember map between unique and opcode in fuse_in_header so that we can
+ // refer the opcode later.
+ std::unordered_map<uint64_t, uint32_t> opcode_map_;
+
+ int open_count_;
+
+ DISALLOW_COPY_AND_ASSIGN(FuseBridgeEntry);
+};
+
+class BridgeEpollController : private EpollController {
+ public:
+ BridgeEpollController(base::unique_fd&& poll_fd) : EpollController(std::move(poll_fd)) {}
+
+ bool AddBridgePoll(FuseBridgeEntry* bridge) const {
+ return InvokeControl(EPOLL_CTL_ADD, bridge);
+ }
+
+ bool UpdateOrDeleteBridgePoll(FuseBridgeEntry* bridge) const {
+ return InvokeControl(
+ bridge->state_ != FuseBridgeState::kClosing ? EPOLL_CTL_MOD : EPOLL_CTL_DEL, bridge);
+ }
+
+ bool Wait(size_t bridge_count, std::unordered_set<FuseBridgeEntry*>* entries_out) {
+ CHECK(entries_out);
+ const size_t event_count = std::max<size_t>(bridge_count * 2, 1);
+ if (!EpollController::Wait(event_count)) {
+ return false;
+ }
+ entries_out->clear();
+ for (const auto& event : events()) {
+ FuseBridgeEntryEvent* const entry_event =
+ reinterpret_cast<FuseBridgeEntryEvent*>(event.data.ptr);
+ entry_event->events = event.events;
+ entries_out->insert(entry_event->entry);
+ }
+ return true;
+ }
+
+ private:
+ bool InvokeControl(int op, FuseBridgeEntry* bridge) const {
+ LOG(VERBOSE) << "InvokeControl op=" << op << " bridge=" << bridge->mount_id_
+ << " state=" << static_cast<int>(bridge->state_)
+ << " last_state=" << static_cast<int>(bridge->last_state_);
+
+ int last_device_events;
+ int last_proxy_events;
+ int device_events;
+ int proxy_events;
+ GetObservedEvents(bridge->last_state_, &last_device_events, &last_proxy_events);
+ GetObservedEvents(bridge->state_, &device_events, &proxy_events);
+ bool result = true;
+ if (op != EPOLL_CTL_MOD || last_device_events != device_events) {
+ result &= EpollController::InvokeControl(op, bridge->device_fd_, device_events,
+ &bridge->last_device_events_);
+ }
+ if (op != EPOLL_CTL_MOD || last_proxy_events != proxy_events) {
+ result &= EpollController::InvokeControl(op, bridge->proxy_fd_, proxy_events,
+ &bridge->last_proxy_events_);
+ }
+ return result;
+ }
+};
+
+FuseBridgeLoop::FuseBridgeLoop() : opened_(true) {
+ base::unique_fd epoll_fd(epoll_create1(/* no flag */ 0));
+ if (epoll_fd.get() == -1) {
+ PLOG(ERROR) << "Failed to open FD for epoll";
+ opened_ = false;
+ return;
+ }
+ epoll_controller_.reset(new BridgeEpollController(std::move(epoll_fd)));
+}
+
+FuseBridgeLoop::~FuseBridgeLoop() { CHECK(bridges_.empty()); }
+
+bool FuseBridgeLoop::AddBridge(int mount_id, base::unique_fd dev_fd, base::unique_fd proxy_fd) {
+ LOG(VERBOSE) << "Adding bridge " << mount_id;
+
+ std::unique_ptr<FuseBridgeEntry> bridge(
+ new FuseBridgeEntry(mount_id, std::move(dev_fd), std::move(proxy_fd)));
+ std::lock_guard<std::mutex> lock(mutex_);
+ if (!opened_) {
+ LOG(ERROR) << "Tried to add a mount to a closed bridge";
+ return false;
+ }
+ if (bridges_.count(mount_id)) {
+ LOG(ERROR) << "Tried to add a mount point that has already been added";
+ return false;
+ }
+ if (!epoll_controller_->AddBridgePoll(bridge.get())) {
+ return false;
+ }
+
+ bridges_.emplace(mount_id, std::move(bridge));
+ return true;
+}
+
+bool FuseBridgeLoop::ProcessEventLocked(const std::unordered_set<FuseBridgeEntry*>& entries,
+ FuseBridgeLoopCallback* callback) {
+ for (auto entry : entries) {
+ entry->Transfer(callback);
+ if (!epoll_controller_->UpdateOrDeleteBridgePoll(entry)) {
+ return false;
+ }
+ if (entry->IsClosing()) {
+ const int mount_id = entry->mount_id();
+ callback->OnClosed(mount_id);
+ bridges_.erase(mount_id);
+ if (bridges_.size() == 0) {
+ // All bridges are now closed.
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+void FuseBridgeLoop::Start(FuseBridgeLoopCallback* callback) {
+ LOG(DEBUG) << "Start fuse bridge loop";
+ std::unordered_set<FuseBridgeEntry*> entries;
+ while (true) {
+ const bool wait_result = epoll_controller_->Wait(bridges_.size(), &entries);
+ LOG(VERBOSE) << "Receive epoll events";
+ {
+ std::lock_guard<std::mutex> lock(mutex_);
+ if (!(wait_result && ProcessEventLocked(entries, callback))) {
+ for (auto it = bridges_.begin(); it != bridges_.end();) {
+ callback->OnClosed(it->second->mount_id());
+ it = bridges_.erase(it);
+ }
+ opened_ = false;
+ return;
+ }
+ }
+ }
}
} // namespace fuse
diff --git a/libappfuse/FuseBuffer.cc b/libappfuse/FuseBuffer.cc
index 8fb2dbc..653e96b 100644
--- a/libappfuse/FuseBuffer.cc
+++ b/libappfuse/FuseBuffer.cc
@@ -23,81 +23,165 @@
#include <algorithm>
#include <type_traits>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/macros.h>
namespace android {
namespace fuse {
+namespace {
-static_assert(
- std::is_standard_layout<FuseBuffer>::value,
- "FuseBuffer must be standard layout union.");
+constexpr useconds_t kRetrySleepForWriting = 1000; // 1 ms
template <typename T>
-bool FuseMessage<T>::CheckHeaderLength(const char* name) const {
- const auto& header = static_cast<const T*>(this)->header;
- if (header.len >= sizeof(header) && header.len <= sizeof(T)) {
+bool CheckHeaderLength(const FuseMessage<T>* self, const char* name, size_t max_size) {
+ const auto& header = static_cast<const T*>(self)->header;
+ if (header.len >= sizeof(header) && header.len <= max_size) {
+ return true;
+ } else {
+ LOG(ERROR) << "Invalid header length is found in " << name << ": " << header.len;
+ return false;
+ }
+}
+
+template <typename T>
+ResultOrAgain ReadInternal(FuseMessage<T>* self, int fd, int sockflag) {
+ char* const buf = reinterpret_cast<char*>(self);
+ const ssize_t result = sockflag ? TEMP_FAILURE_RETRY(recv(fd, buf, sizeof(T), sockflag))
+ : TEMP_FAILURE_RETRY(read(fd, buf, sizeof(T)));
+
+ switch (result) {
+ case 0:
+ // Expected EOF.
+ return ResultOrAgain::kFailure;
+ case -1:
+ if (errno == EAGAIN) {
+ return ResultOrAgain::kAgain;
+ }
+ PLOG(ERROR) << "Failed to read a FUSE message";
+ return ResultOrAgain::kFailure;
+ }
+
+ const auto& header = static_cast<const T*>(self)->header;
+ if (result < static_cast<ssize_t>(sizeof(header))) {
+ LOG(ERROR) << "Read bytes " << result << " are shorter than header size " << sizeof(header);
+ return ResultOrAgain::kFailure;
+ }
+
+ if (!CheckHeaderLength<T>(self, "Read", sizeof(T))) {
+ return ResultOrAgain::kFailure;
+ }
+
+ if (static_cast<uint32_t>(result) != header.len) {
+ LOG(ERROR) << "Read bytes " << result << " are different from header.len " << header.len;
+ return ResultOrAgain::kFailure;
+ }
+
+ return ResultOrAgain::kSuccess;
+}
+
+template <typename T>
+ResultOrAgain WriteInternal(const FuseMessage<T>* self, int fd, int sockflag, const void* data,
+ size_t max_size) {
+ if (!CheckHeaderLength<T>(self, "Write", max_size)) {
+ return ResultOrAgain::kFailure;
+ }
+
+ const char* const buf = reinterpret_cast<const char*>(self);
+ const auto& header = static_cast<const T*>(self)->header;
+
+ while (true) {
+ int result;
+ if (sockflag) {
+ CHECK(data == nullptr);
+ result = TEMP_FAILURE_RETRY(send(fd, buf, header.len, sockflag));
+ } else if (data) {
+ const struct iovec vec[] = {{const_cast<char*>(buf), sizeof(header)},
+ {const_cast<void*>(data), header.len - sizeof(header)}};
+ result = TEMP_FAILURE_RETRY(writev(fd, vec, arraysize(vec)));
+ } else {
+ result = TEMP_FAILURE_RETRY(write(fd, buf, header.len));
+ }
+ if (result == -1) {
+ switch (errno) {
+ case ENOBUFS:
+ // When returning ENOBUFS, epoll still reports the FD is writable. Just usleep
+ // and retry again.
+ usleep(kRetrySleepForWriting);
+ continue;
+ case EAGAIN:
+ return ResultOrAgain::kAgain;
+ default:
+ PLOG(ERROR) << "Failed to write a FUSE message";
+ return ResultOrAgain::kFailure;
+ }
+ }
+
+ if (static_cast<unsigned int>(result) != header.len) {
+ LOG(ERROR) << "Written bytes " << result << " is different from length in header "
+ << header.len;
+ return ResultOrAgain::kFailure;
+ }
+ return ResultOrAgain::kSuccess;
+ }
+}
+}
+
+static_assert(std::is_standard_layout<FuseBuffer>::value,
+ "FuseBuffer must be standard layout union.");
+
+bool SetupMessageSockets(base::unique_fd (*result)[2]) {
+ base::unique_fd fds[2];
+ {
+ int raw_fds[2];
+ if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, raw_fds) == -1) {
+ PLOG(ERROR) << "Failed to create sockets for proxy";
+ return false;
+ }
+ fds[0].reset(raw_fds[0]);
+ fds[1].reset(raw_fds[1]);
+ }
+
+ constexpr int kMaxMessageSize = sizeof(FuseBuffer);
+ if (setsockopt(fds[0], SOL_SOCKET, SO_SNDBUFFORCE, &kMaxMessageSize, sizeof(int)) != 0 ||
+ setsockopt(fds[1], SOL_SOCKET, SO_SNDBUFFORCE, &kMaxMessageSize, sizeof(int)) != 0) {
+ PLOG(ERROR) << "Failed to update buffer size for socket";
+ return false;
+ }
+
+ (*result)[0] = std::move(fds[0]);
+ (*result)[1] = std::move(fds[1]);
return true;
- } else {
- LOG(ERROR) << "Invalid header length is found in " << name << ": " <<
- header.len;
- return false;
- }
}
template <typename T>
bool FuseMessage<T>::Read(int fd) {
- char* const buf = reinterpret_cast<char*>(this);
- const ssize_t result = TEMP_FAILURE_RETRY(::read(fd, buf, sizeof(T)));
- if (result < 0) {
- PLOG(ERROR) << "Failed to read a FUSE message";
- return false;
- }
+ return ReadInternal(this, fd, 0) == ResultOrAgain::kSuccess;
+}
- const auto& header = static_cast<const T*>(this)->header;
- if (result < static_cast<ssize_t>(sizeof(header))) {
- LOG(ERROR) << "Read bytes " << result << " are shorter than header size " <<
- sizeof(header);
- return false;
- }
-
- if (!CheckHeaderLength("Read")) {
- return false;
- }
-
- if (static_cast<uint32_t>(result) > header.len) {
- LOG(ERROR) << "Read bytes " << result << " are longer than header.len " <<
- header.len;
- return false;
- }
-
- if (!base::ReadFully(fd, buf + result, header.len - result)) {
- PLOG(ERROR) << "ReadFully failed";
- return false;
- }
-
- return true;
+template <typename T>
+ResultOrAgain FuseMessage<T>::ReadOrAgain(int fd) {
+ return ReadInternal(this, fd, MSG_DONTWAIT);
}
template <typename T>
bool FuseMessage<T>::Write(int fd) const {
- if (!CheckHeaderLength("Write")) {
- return false;
- }
-
- const char* const buf = reinterpret_cast<const char*>(this);
- const auto& header = static_cast<const T*>(this)->header;
- if (!base::WriteFully(fd, buf, header.len)) {
- PLOG(ERROR) << "WriteFully failed";
- return false;
- }
-
- return true;
+ return WriteInternal(this, fd, 0, nullptr, sizeof(T)) == ResultOrAgain::kSuccess;
}
-template class FuseMessage<FuseRequest>;
-template class FuseMessage<FuseResponse>;
+template <typename T>
+bool FuseMessage<T>::WriteWithBody(int fd, size_t max_size, const void* data) const {
+ CHECK(data != nullptr);
+ return WriteInternal(this, fd, 0, data, max_size) == ResultOrAgain::kSuccess;
+}
+
+template <typename T>
+ResultOrAgain FuseMessage<T>::WriteOrAgain(int fd) const {
+ return WriteInternal(this, fd, MSG_DONTWAIT, nullptr, sizeof(T));
+}
void FuseRequest::Reset(
uint32_t data_length, uint32_t opcode, uint64_t unique) {
@@ -107,17 +191,18 @@
header.unique = unique;
}
-void FuseResponse::ResetHeader(
- uint32_t data_length, int32_t error, uint64_t unique) {
- CHECK_LE(error, 0) << "error should be zero or negative.";
- header.len = sizeof(fuse_out_header) + data_length;
- header.error = error;
- header.unique = unique;
+template <size_t N>
+void FuseResponseBase<N>::ResetHeader(uint32_t data_length, int32_t error, uint64_t unique) {
+ CHECK_LE(error, 0) << "error should be zero or negative.";
+ header.len = sizeof(fuse_out_header) + data_length;
+ header.error = error;
+ header.unique = unique;
}
-void FuseResponse::Reset(uint32_t data_length, int32_t error, uint64_t unique) {
- memset(this, 0, sizeof(fuse_out_header) + data_length);
- ResetHeader(data_length, error, unique);
+template <size_t N>
+void FuseResponseBase<N>::Reset(uint32_t data_length, int32_t error, uint64_t unique) {
+ memset(this, 0, sizeof(fuse_out_header) + data_length);
+ ResetHeader(data_length, error, unique);
}
void FuseBuffer::HandleInit() {
@@ -167,5 +252,11 @@
response.Reset(0, -ENOSYS, unique);
}
+template class FuseMessage<FuseRequest>;
+template class FuseMessage<FuseResponse>;
+template class FuseMessage<FuseSimpleResponse>;
+template struct FuseResponseBase<0u>;
+template struct FuseResponseBase<kFuseMaxRead>;
+
} // namespace fuse
} // namespace android
diff --git a/libappfuse/include/libappfuse/EpollController.h b/libappfuse/include/libappfuse/EpollController.h
new file mode 100644
index 0000000..622bd2c
--- /dev/null
+++ b/libappfuse/include/libappfuse/EpollController.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2017 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 specic language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_LIBAPPFUSE_EPOLLCONTROLLER_H_
+#define ANDROID_LIBAPPFUSE_EPOLLCONTROLLER_H_
+
+#include <sys/epoll.h>
+
+#include <vector>
+
+#include <android-base/macros.h>
+#include <android-base/unique_fd.h>
+
+namespace android {
+namespace fuse {
+
+class EpollController {
+ public:
+ explicit EpollController(base::unique_fd&& poll_fd);
+ bool Wait(size_t event_count);
+ bool AddFd(int fd, int events, void* data);
+ bool UpdateFd(int fd, int events, void* data);
+ bool RemoveFd(int fd);
+
+ const std::vector<epoll_event>& events() const;
+
+ protected:
+ bool InvokeControl(int op, int fd, int events, void* data) const;
+
+ private:
+ base::unique_fd poll_fd_;
+ std::vector<epoll_event> events_;
+
+ DISALLOW_COPY_AND_ASSIGN(EpollController);
+};
+}
+}
+
+#endif // ANDROID_LIBAPPFUSE_EPOLLCONTROLLER_H_
diff --git a/libappfuse/include/libappfuse/FuseAppLoop.h b/libappfuse/include/libappfuse/FuseAppLoop.h
index c3edfcc..f2ef2b5 100644
--- a/libappfuse/include/libappfuse/FuseAppLoop.h
+++ b/libappfuse/include/libappfuse/FuseAppLoop.h
@@ -17,23 +17,51 @@
#ifndef ANDROID_LIBAPPFUSE_FUSEAPPLOOP_H_
#define ANDROID_LIBAPPFUSE_FUSEAPPLOOP_H_
+#include <memory>
+#include <mutex>
+
+#include <android-base/unique_fd.h>
+
#include "libappfuse/FuseBuffer.h"
namespace android {
namespace fuse {
+class EpollController;
+
class FuseAppLoopCallback {
public:
- virtual bool IsActive() = 0;
- virtual int64_t OnGetSize(uint64_t inode) = 0;
- virtual int32_t OnFsync(uint64_t inode) = 0;
- virtual int32_t OnWrite(
- uint64_t inode, uint64_t offset, uint32_t size, const void* data) = 0;
- virtual int32_t OnRead(
- uint64_t inode, uint64_t offset, uint32_t size, void* data) = 0;
- virtual int32_t OnOpen(uint64_t inode) = 0;
- virtual int32_t OnRelease(uint64_t inode) = 0;
- virtual ~FuseAppLoopCallback() = default;
+ virtual void OnLookup(uint64_t unique, uint64_t inode) = 0;
+ virtual void OnGetAttr(uint64_t unique, uint64_t inode) = 0;
+ virtual void OnFsync(uint64_t unique, uint64_t inode) = 0;
+ virtual void OnWrite(uint64_t unique, uint64_t inode, uint64_t offset, uint32_t size,
+ const void* data) = 0;
+ virtual void OnRead(uint64_t unique, uint64_t inode, uint64_t offset, uint32_t size) = 0;
+ virtual void OnOpen(uint64_t unique, uint64_t inode) = 0;
+ virtual void OnRelease(uint64_t unique, uint64_t inode) = 0;
+ virtual ~FuseAppLoopCallback();
+};
+
+class FuseAppLoop final {
+ public:
+ FuseAppLoop(base::unique_fd&& fd);
+
+ void Start(FuseAppLoopCallback* callback);
+ void Break();
+
+ bool ReplySimple(uint64_t unique, int32_t result);
+ bool ReplyLookup(uint64_t unique, uint64_t inode, int64_t size);
+ bool ReplyGetAttr(uint64_t unique, uint64_t inode, int64_t size, int mode);
+ bool ReplyOpen(uint64_t unique, uint64_t fh);
+ bool ReplyWrite(uint64_t unique, uint32_t size);
+ bool ReplyRead(uint64_t unique, uint32_t size, const void* data);
+
+ private:
+ base::unique_fd fd_;
+ base::unique_fd break_fd_;
+
+ // Lock for multi-threading.
+ std::mutex mutex_;
};
bool StartFuseAppLoop(int fd, FuseAppLoopCallback* callback);
diff --git a/libappfuse/include/libappfuse/FuseBridgeLoop.h b/libappfuse/include/libappfuse/FuseBridgeLoop.h
index 1f71cf2..6bfda98 100644
--- a/libappfuse/include/libappfuse/FuseBridgeLoop.h
+++ b/libappfuse/include/libappfuse/FuseBridgeLoop.h
@@ -17,6 +17,13 @@
#ifndef ANDROID_LIBAPPFUSE_FUSEBRIDGELOOP_H_
#define ANDROID_LIBAPPFUSE_FUSEBRIDGELOOP_H_
+#include <map>
+#include <mutex>
+#include <queue>
+#include <unordered_set>
+
+#include <android-base/macros.h>
+
#include "libappfuse/FuseBuffer.h"
namespace android {
@@ -24,12 +31,41 @@
class FuseBridgeLoopCallback {
public:
- virtual void OnMount() = 0;
- virtual ~FuseBridgeLoopCallback() = default;
+ virtual void OnMount(int mount_id) = 0;
+ virtual void OnClosed(int mount_id) = 0;
+ virtual ~FuseBridgeLoopCallback() = default;
};
-bool StartFuseBridgeLoop(
- int dev_fd, int proxy_fd, FuseBridgeLoopCallback* callback);
+class FuseBridgeEntry;
+class BridgeEpollController;
+
+class FuseBridgeLoop final {
+ public:
+ FuseBridgeLoop();
+ ~FuseBridgeLoop();
+
+ void Start(FuseBridgeLoopCallback* callback);
+
+ // Add bridge to the loop. It's OK to invoke the method from a different
+ // thread from one which invokes |Start|.
+ bool AddBridge(int mount_id, base::unique_fd dev_fd, base::unique_fd proxy_fd);
+
+ private:
+ bool ProcessEventLocked(const std::unordered_set<FuseBridgeEntry*>& entries,
+ FuseBridgeLoopCallback* callback);
+
+ std::unique_ptr<BridgeEpollController> epoll_controller_;
+
+ // Map between |mount_id| and bridge entry.
+ std::map<int, std::unique_ptr<FuseBridgeEntry>> bridges_;
+
+ // Lock for multi-threading.
+ std::mutex mutex_;
+
+ bool opened_;
+
+ DISALLOW_COPY_AND_ASSIGN(FuseBridgeLoop);
+};
} // namespace fuse
} // namespace android
diff --git a/libappfuse/include/libappfuse/FuseBuffer.h b/libappfuse/include/libappfuse/FuseBuffer.h
index 7abd2fa..7a70bf3 100644
--- a/libappfuse/include/libappfuse/FuseBuffer.h
+++ b/libappfuse/include/libappfuse/FuseBuffer.h
@@ -17,6 +17,7 @@
#ifndef ANDROID_LIBAPPFUSE_FUSEBUFFER_H_
#define ANDROID_LIBAPPFUSE_FUSEBUFFER_H_
+#include <android-base/unique_fd.h>
#include <linux/fuse.h>
namespace android {
@@ -28,13 +29,23 @@
constexpr size_t kFuseMaxRead = 128 * 1024;
constexpr int32_t kFuseSuccess = 0;
+// Setup sockets to transfer FuseMessage.
+bool SetupMessageSockets(base::unique_fd (*sockets)[2]);
+
+enum class ResultOrAgain {
+ kSuccess,
+ kFailure,
+ kAgain,
+};
+
template<typename T>
class FuseMessage {
public:
bool Read(int fd);
bool Write(int fd) const;
- private:
- bool CheckHeaderLength(const char* name) const;
+ bool WriteWithBody(int fd, size_t max_size, const void* data) const;
+ ResultOrAgain ReadOrAgain(int fd);
+ ResultOrAgain WriteOrAgain(int fd) const;
};
// FuseRequest represents file operation requests from /dev/fuse. It starts
@@ -54,33 +65,37 @@
// for FUSE_READ
fuse_read_in read_in;
// for FUSE_LOOKUP
- char lookup_name[0];
+ char lookup_name[kFuseMaxWrite];
};
void Reset(uint32_t data_length, uint32_t opcode, uint64_t unique);
};
// FuseResponse represents file operation responses to /dev/fuse. It starts
// from fuse_out_header. The body layout depends on the operation code.
-struct FuseResponse : public FuseMessage<FuseResponse> {
- fuse_out_header header;
- union {
- // for FUSE_INIT
- fuse_init_out init_out;
- // for FUSE_LOOKUP
- fuse_entry_out entry_out;
- // for FUSE_GETATTR
- fuse_attr_out attr_out;
- // for FUSE_OPEN
- fuse_open_out open_out;
- // for FUSE_READ
- char read_data[kFuseMaxRead];
- // for FUSE_WRITE
- fuse_write_out write_out;
- };
- void Reset(uint32_t data_length, int32_t error, uint64_t unique);
- void ResetHeader(uint32_t data_length, int32_t error, uint64_t unique);
+template <size_t N>
+struct FuseResponseBase : public FuseMessage<FuseResponseBase<N>> {
+ fuse_out_header header;
+ union {
+ // for FUSE_INIT
+ fuse_init_out init_out;
+ // for FUSE_LOOKUP
+ fuse_entry_out entry_out;
+ // for FUSE_GETATTR
+ fuse_attr_out attr_out;
+ // for FUSE_OPEN
+ fuse_open_out open_out;
+ // for FUSE_READ
+ char read_data[N];
+ // for FUSE_WRITE
+ fuse_write_out write_out;
+ };
+ void Reset(uint32_t data_length, int32_t error, uint64_t unique);
+ void ResetHeader(uint32_t data_length, int32_t error, uint64_t unique);
};
+using FuseResponse = FuseResponseBase<kFuseMaxRead>;
+using FuseSimpleResponse = FuseResponseBase<0u>;
+
// To reduce memory usage, FuseBuffer shares the memory region for request and
// response.
union FuseBuffer final {
diff --git a/libappfuse/tests/FuseAppLoopTest.cc b/libappfuse/tests/FuseAppLoopTest.cc
index 25906cf..98e3665 100644
--- a/libappfuse/tests/FuseAppLoopTest.cc
+++ b/libappfuse/tests/FuseAppLoopTest.cc
@@ -23,6 +23,9 @@
#include <gtest/gtest.h>
#include <thread>
+#include "libappfuse/EpollController.h"
+#include "libappfuse/FuseBridgeLoop.h"
+
namespace android {
namespace fuse {
namespace {
@@ -37,85 +40,61 @@
class Callback : public FuseAppLoopCallback {
public:
std::vector<CallbackRequest> requests;
+ FuseAppLoop* loop;
- bool IsActive() override {
- return true;
+ void OnGetAttr(uint64_t seq, uint64_t inode) override {
+ EXPECT_NE(FUSE_ROOT_ID, static_cast<int>(inode));
+ EXPECT_TRUE(loop->ReplyGetAttr(seq, inode, kTestFileSize, S_IFREG | 0777));
}
- int64_t OnGetSize(uint64_t inode) override {
- if (inode == FUSE_ROOT_ID) {
- return 0;
- } else {
- return kTestFileSize;
- }
+ void OnLookup(uint64_t unique, uint64_t inode) override {
+ EXPECT_NE(FUSE_ROOT_ID, static_cast<int>(inode));
+ EXPECT_TRUE(loop->ReplyLookup(unique, inode, kTestFileSize));
}
- int32_t OnFsync(uint64_t inode) override {
- requests.push_back({
- .code = FUSE_FSYNC,
- .inode = inode
- });
- return 0;
+ void OnFsync(uint64_t seq, uint64_t inode) override {
+ requests.push_back({.code = FUSE_FSYNC, .inode = inode});
+ loop->ReplySimple(seq, 0);
}
- int32_t OnWrite(uint64_t inode,
- uint64_t offset ATTRIBUTE_UNUSED,
- uint32_t size ATTRIBUTE_UNUSED,
- const void* data ATTRIBUTE_UNUSED) override {
- requests.push_back({
- .code = FUSE_WRITE,
- .inode = inode
- });
- return 0;
+ void OnWrite(uint64_t seq, uint64_t inode, uint64_t offset ATTRIBUTE_UNUSED,
+ uint32_t size ATTRIBUTE_UNUSED, const void* data ATTRIBUTE_UNUSED) override {
+ requests.push_back({.code = FUSE_WRITE, .inode = inode});
+ loop->ReplyWrite(seq, 0);
}
- int32_t OnRead(uint64_t inode,
- uint64_t offset ATTRIBUTE_UNUSED,
- uint32_t size ATTRIBUTE_UNUSED,
- void* data ATTRIBUTE_UNUSED) override {
- requests.push_back({
- .code = FUSE_READ,
- .inode = inode
- });
- return 0;
+ void OnRead(uint64_t seq, uint64_t inode, uint64_t offset ATTRIBUTE_UNUSED,
+ uint32_t size ATTRIBUTE_UNUSED) override {
+ requests.push_back({.code = FUSE_READ, .inode = inode});
+ loop->ReplySimple(seq, 0);
}
- int32_t OnOpen(uint64_t inode) override {
- requests.push_back({
- .code = FUSE_OPEN,
- .inode = inode
- });
- return 0;
+ void OnOpen(uint64_t seq, uint64_t inode) override {
+ requests.push_back({.code = FUSE_OPEN, .inode = inode});
+ loop->ReplyOpen(seq, inode);
}
- int32_t OnRelease(uint64_t inode) override {
- requests.push_back({
- .code = FUSE_RELEASE,
- .inode = inode
- });
- return 0;
+ void OnRelease(uint64_t seq, uint64_t inode) override {
+ requests.push_back({.code = FUSE_RELEASE, .inode = inode});
+ loop->ReplySimple(seq, 0);
}
};
class FuseAppLoopTest : public ::testing::Test {
- private:
- std::thread thread_;
-
protected:
- base::unique_fd sockets_[2];
- Callback callback_;
- FuseRequest request_;
- FuseResponse response_;
+ std::thread thread_;
+ base::unique_fd sockets_[2];
+ Callback callback_;
+ FuseRequest request_;
+ FuseResponse response_;
+ std::unique_ptr<FuseAppLoop> loop_;
- void SetUp() override {
- base::SetMinimumLogSeverity(base::VERBOSE);
- int sockets[2];
- ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets));
- sockets_[0].reset(sockets[0]);
- sockets_[1].reset(sockets[1]);
- thread_ = std::thread([this] {
- StartFuseAppLoop(sockets_[1].release(), &callback_);
- });
+ void SetUp() override {
+ base::SetMinimumLogSeverity(base::VERBOSE);
+ ASSERT_TRUE(SetupMessageSockets(&sockets_));
+ loop_.reset(new FuseAppLoop(std::move(sockets_[1])));
+ callback_.loop = loop_.get();
+ thread_ = std::thread([this] { loop_->Start(&callback_); });
}
void CheckCallback(
@@ -303,5 +282,18 @@
CheckCallback(sizeof(fuse_write_in), FUSE_WRITE, sizeof(fuse_write_out));
}
+TEST_F(FuseAppLoopTest, Break) {
+ // Ensure that the loop started.
+ request_.Reset(sizeof(fuse_open_in), FUSE_OPEN, 1);
+ request_.header.nodeid = 10;
+ ASSERT_TRUE(request_.Write(sockets_[0]));
+ ASSERT_TRUE(response_.Read(sockets_[0]));
+
+ loop_->Break();
+ if (thread_.joinable()) {
+ thread_.join();
+ }
+}
+
} // namespace fuse
} // namespace android
diff --git a/libappfuse/tests/FuseBridgeLoopTest.cc b/libappfuse/tests/FuseBridgeLoopTest.cc
index e74d9e7..51d6051 100644
--- a/libappfuse/tests/FuseBridgeLoopTest.cc
+++ b/libappfuse/tests/FuseBridgeLoopTest.cc
@@ -32,10 +32,12 @@
class Callback : public FuseBridgeLoopCallback {
public:
bool mounted;
- Callback() : mounted(false) {}
- void OnMount() override {
- mounted = true;
- }
+ bool closed;
+ Callback() : mounted(false), closed(false) {}
+
+ void OnMount(int /*mount_id*/) override { mounted = true; }
+
+ void OnClosed(int /* mount_id */) override { closed = true; }
};
class FuseBridgeLoopTest : public ::testing::Test {
@@ -50,18 +52,12 @@
void SetUp() override {
base::SetMinimumLogSeverity(base::VERBOSE);
- int dev_sockets[2];
- int proxy_sockets[2];
- ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, dev_sockets));
- ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, proxy_sockets));
- dev_sockets_[0].reset(dev_sockets[0]);
- dev_sockets_[1].reset(dev_sockets[1]);
- proxy_sockets_[0].reset(proxy_sockets[0]);
- proxy_sockets_[1].reset(proxy_sockets[1]);
-
+ ASSERT_TRUE(SetupMessageSockets(&dev_sockets_));
+ ASSERT_TRUE(SetupMessageSockets(&proxy_sockets_));
thread_ = std::thread([this] {
- StartFuseBridgeLoop(
- dev_sockets_[1].release(), proxy_sockets_[0].release(), &callback_);
+ FuseBridgeLoop loop;
+ loop.AddBridge(1, std::move(dev_sockets_[1]), std::move(proxy_sockets_[0]));
+ loop.Start(&callback_);
});
}
@@ -122,6 +118,7 @@
if (thread_.joinable()) {
thread_.join();
}
+ ASSERT_TRUE(callback_.closed);
}
void TearDown() override {
diff --git a/libappfuse/tests/FuseBufferTest.cc b/libappfuse/tests/FuseBufferTest.cc
index 1a1abd5..ade34ac 100644
--- a/libappfuse/tests/FuseBufferTest.cc
+++ b/libappfuse/tests/FuseBufferTest.cc
@@ -112,30 +112,6 @@
TestWriteInvalidLength(sizeof(fuse_in_header) - 1);
}
-TEST(FuseMessageTest, ShortWriteAndRead) {
- int raw_fds[2];
- ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, raw_fds));
-
- android::base::unique_fd fds[2];
- fds[0].reset(raw_fds[0]);
- fds[1].reset(raw_fds[1]);
-
- const int send_buffer_size = 1024;
- ASSERT_EQ(0, setsockopt(fds[0], SOL_SOCKET, SO_SNDBUF, &send_buffer_size,
- sizeof(int)));
-
- bool succeed = false;
- const int sender_fd = fds[0].get();
- std::thread thread([sender_fd, &succeed] {
- FuseRequest request;
- request.header.len = 1024 * 4;
- succeed = request.Write(sender_fd);
- });
- thread.detach();
- FuseRequest request;
- ASSERT_TRUE(request.Read(fds[1]));
-}
-
TEST(FuseResponseTest, Reset) {
FuseResponse response;
// Write 1 to the first ten bytes.
@@ -211,5 +187,29 @@
EXPECT_EQ(-ENOSYS, buffer.response.header.error);
}
+TEST(SetupMessageSocketsTest, Stress) {
+ constexpr int kCount = 1000;
+
+ FuseRequest request;
+ request.header.len = sizeof(FuseRequest);
+
+ base::unique_fd fds[2];
+ SetupMessageSockets(&fds);
+
+ std::thread thread([&fds] {
+ FuseRequest request;
+ for (int i = 0; i < kCount; ++i) {
+ ASSERT_TRUE(request.Read(fds[1]));
+ usleep(1000);
+ }
+ });
+
+ for (int i = 0; i < kCount; ++i) {
+ ASSERT_TRUE(request.Write(fds[0]));
+ }
+
+ thread.join();
+}
+
} // namespace fuse
} // namespace android
diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp
index e02aaf2..02e0487 100644
--- a/libbacktrace/Android.bp
+++ b/libbacktrace/Android.bp
@@ -53,6 +53,8 @@
"UnwindCurrent.cpp",
"UnwindMap.cpp",
"UnwindPtrace.cpp",
+ "UnwindStack.cpp",
+ "UnwindStackMap.cpp",
]
cc_library_headers {
@@ -64,6 +66,10 @@
cc_library {
name: "libbacktrace",
vendor_available: true,
+ vndk: {
+ enabled: true,
+ support_system_process: true,
+ },
defaults: ["libbacktrace_common"],
host_supported: true,
@@ -84,6 +90,7 @@
"libbase",
"liblog",
"libunwind",
+ "libunwindstack",
],
static_libs: ["libcutils"],
@@ -97,6 +104,7 @@
"libbase",
"liblog",
"libunwind",
+ "libunwindstack",
],
static_libs: ["libcutils"],
@@ -108,6 +116,7 @@
"libbase",
"liblog",
"libunwind",
+ "libunwindstack",
],
static_libs: ["libasync_safe", "libcutils"],
@@ -130,11 +139,13 @@
linux: {
shared_libs: [
"libunwind",
+ "libunwindstack",
],
},
android: {
shared_libs: [
"libunwind",
+ "libunwindstack",
],
},
}
@@ -161,6 +172,7 @@
shared_libs = [
"libbase",
"libunwind",
+ "libunwindstack",
"libziparchive",
],
}
@@ -192,6 +204,7 @@
"libcutils",
"liblog",
"libunwind",
+ "libunwindstack",
],
group_static_libs: true,
diff --git a/libbacktrace/Backtrace.cpp b/libbacktrace/Backtrace.cpp
index e46d353..81f5e32 100644
--- a/libbacktrace/Backtrace.cpp
+++ b/libbacktrace/Backtrace.cpp
@@ -84,10 +84,8 @@
}
std::string Backtrace::FormatFrameData(const backtrace_frame_data_t* frame) {
- uintptr_t relative_pc;
std::string map_name;
if (BacktraceMap::IsValid(frame->map)) {
- relative_pc = BacktraceMap::GetRelativePc(frame->map, frame->pc);
if (!frame->map.name.empty()) {
map_name = frame->map.name.c_str();
if (map_name[0] == '[' && map_name[map_name.size() - 1] == ']') {
@@ -99,10 +97,9 @@
}
} else {
map_name = "<unknown>";
- relative_pc = frame->pc;
}
- std::string line(StringPrintf("#%02zu pc %" PRIPTR " ", frame->num, relative_pc));
+ std::string line(StringPrintf("#%02zu pc %" PRIPTR " ", frame->num, frame->rel_pc));
line += map_name;
// Special handling for non-zero offset maps, we need to print that
// information.
diff --git a/libbacktrace/UnwindCurrent.cpp b/libbacktrace/UnwindCurrent.cpp
index 3c509e6..2c87fa8 100644
--- a/libbacktrace/UnwindCurrent.cpp
+++ b/libbacktrace/UnwindCurrent.cpp
@@ -133,6 +133,11 @@
backtrace_frame_data_t* prev = &frames_.at(num_frames-1);
prev->stack_size = frame->sp - prev->sp;
}
+ if (BacktraceMap::IsValid(frame->map)) {
+ frame->rel_pc = frame->pc - frame->map.start + frame->map.load_bias;
+ } else {
+ frame->rel_pc = frame->pc;
+ }
num_frames++;
} else {
num_ignore_frames--;
diff --git a/libbacktrace/UnwindMap.cpp b/libbacktrace/UnwindMap.cpp
index af79562..0b8232b 100644
--- a/libbacktrace/UnwindMap.cpp
+++ b/libbacktrace/UnwindMap.cpp
@@ -57,7 +57,7 @@
map.start = unw_map.start;
map.end = unw_map.end;
map.offset = unw_map.offset;
- map.load_base = unw_map.load_base;
+ map.load_bias = unw_map.load_base;
map.flags = unw_map.flags;
map.name = unw_map.path;
@@ -106,7 +106,7 @@
map.start = unw_map.start;
map.end = unw_map.end;
map.offset = unw_map.offset;
- map.load_base = unw_map.load_base;
+ map.load_bias = unw_map.load_base;
map.flags = unw_map.flags;
map.name = unw_map.path;
diff --git a/libbacktrace/UnwindPtrace.cpp b/libbacktrace/UnwindPtrace.cpp
index 42ac1bc..87282ef 100644
--- a/libbacktrace/UnwindPtrace.cpp
+++ b/libbacktrace/UnwindPtrace.cpp
@@ -135,6 +135,11 @@
}
FillInMap(frame->pc, &frame->map);
+ if (BacktraceMap::IsValid(frame->map)) {
+ frame->rel_pc = frame->pc - frame->map.start + frame->map.load_bias;
+ } else {
+ frame->rel_pc = frame->pc;
+ }
frame->func_name = GetFunctionName(frame->pc, &frame->func_offset, &frame->map);
diff --git a/libbacktrace/UnwindStack.cpp b/libbacktrace/UnwindStack.cpp
new file mode 100644
index 0000000..e79bca3
--- /dev/null
+++ b/libbacktrace/UnwindStack.cpp
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2017 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 _GNU_SOURCE 1
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ucontext.h>
+
+#include <memory>
+#include <string>
+
+#if !defined(__ANDROID__)
+#include <cutils/threads.h>
+#endif
+
+#include <backtrace/Backtrace.h>
+#include <demangle.h>
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/RegsGetLocal.h>
+
+#include "BacktraceLog.h"
+#include "UnwindStack.h"
+#include "UnwindStackMap.h"
+
+static std::string GetFunctionName(pid_t pid, BacktraceMap* back_map, uintptr_t pc,
+ uintptr_t* offset) {
+ *offset = 0;
+ unwindstack::Maps* maps = reinterpret_cast<UnwindStackMap*>(back_map)->stack_maps();
+
+ // Get the map for this
+ unwindstack::MapInfo* map_info = maps->Find(pc);
+ if (map_info == nullptr || map_info->flags & PROT_DEVICE_MAP) {
+ return "";
+ }
+
+ unwindstack::Elf* elf = map_info->GetElf(pid, true);
+
+ std::string name;
+ uint64_t func_offset;
+ if (!elf->GetFunctionName(elf->GetRelPc(pc, map_info), &name, &func_offset)) {
+ return "";
+ }
+ *offset = func_offset;
+ return name;
+}
+
+static bool IsUnwindLibrary(const std::string& map_name) {
+ const std::string library(basename(map_name.c_str()));
+ return library == "libunwindstack.so" || library == "libbacktrace.so";
+}
+
+static bool Unwind(pid_t pid, unwindstack::Memory* memory, unwindstack::Regs* regs,
+ BacktraceMap* back_map, std::vector<backtrace_frame_data_t>* frames,
+ size_t num_ignore_frames) {
+ unwindstack::Maps* maps = reinterpret_cast<UnwindStackMap*>(back_map)->stack_maps();
+ bool adjust_rel_pc = false;
+ size_t num_frames = 0;
+ frames->clear();
+ while (num_frames < MAX_BACKTRACE_FRAMES) {
+ if (regs->pc() == 0) {
+ break;
+ }
+ unwindstack::MapInfo* map_info = maps->Find(regs->pc());
+ if (map_info == nullptr) {
+ break;
+ }
+
+ unwindstack::Elf* elf = map_info->GetElf(pid, true);
+ uint64_t rel_pc = elf->GetRelPc(regs->pc(), map_info);
+
+ bool skip_frame = num_frames == 0 && IsUnwindLibrary(map_info->name);
+ if (num_ignore_frames == 0 && !skip_frame) {
+ uint64_t adjusted_rel_pc = rel_pc;
+ if (adjust_rel_pc) {
+ adjusted_rel_pc = regs->GetAdjustedPc(rel_pc, elf);
+ }
+ frames->resize(num_frames + 1);
+ backtrace_frame_data_t* frame = &frames->at(num_frames);
+ frame->num = num_frames;
+ // This will point to the adjusted absolute pc. regs->pc() is
+ // unaltered.
+ frame->pc = map_info->start + adjusted_rel_pc;
+ frame->sp = regs->sp();
+ frame->rel_pc = adjusted_rel_pc;
+ frame->stack_size = 0;
+
+ frame->map.start = map_info->start;
+ frame->map.end = map_info->end;
+ frame->map.offset = map_info->offset;
+ frame->map.load_bias = elf->GetLoadBias();
+ frame->map.flags = map_info->flags;
+ frame->map.name = map_info->name;
+
+ uint64_t func_offset = 0;
+ if (elf->GetFunctionName(adjusted_rel_pc, &frame->func_name, &func_offset)) {
+ frame->func_name = demangle(frame->func_name.c_str());
+ } else {
+ frame->func_name = "";
+ }
+ frame->func_offset = func_offset;
+ if (num_frames > 0) {
+ // Set the stack size for the previous frame.
+ backtrace_frame_data_t* prev = &frames->at(num_frames - 1);
+ prev->stack_size = frame->sp - prev->sp;
+ }
+ num_frames++;
+ } else if (!skip_frame && num_ignore_frames > 0) {
+ num_ignore_frames--;
+ }
+ adjust_rel_pc = true;
+
+ // Do not unwind through a device map.
+ if (map_info->flags & PROT_DEVICE_MAP) {
+ break;
+ }
+ unwindstack::MapInfo* sp_info = maps->Find(regs->sp());
+ if (sp_info->flags & PROT_DEVICE_MAP) {
+ break;
+ }
+
+ if (!elf->Step(rel_pc + map_info->elf_offset, regs, memory)) {
+ break;
+ }
+ }
+
+ return true;
+}
+
+UnwindStackCurrent::UnwindStackCurrent(pid_t pid, pid_t tid, BacktraceMap* map)
+ : BacktraceCurrent(pid, tid, map), memory_(new unwindstack::MemoryLocal) {}
+
+std::string UnwindStackCurrent::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) {
+ return ::GetFunctionName(Pid(), GetMap(), pc, offset);
+}
+
+bool UnwindStackCurrent::UnwindFromContext(size_t num_ignore_frames, ucontext_t* ucontext) {
+ std::unique_ptr<unwindstack::Regs> regs;
+ if (ucontext == nullptr) {
+ regs.reset(unwindstack::Regs::CreateFromLocal());
+ // Fill in the registers from this function. Do it here to avoid
+ // one extra function call appearing in the unwind.
+ unwindstack::RegsGetLocal(regs.get());
+ } else {
+ regs.reset(unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::GetMachineType(), ucontext));
+ }
+
+ error_ = BACKTRACE_UNWIND_NO_ERROR;
+ return ::Unwind(getpid(), memory_.get(), regs.get(), GetMap(), &frames_, num_ignore_frames);
+}
+
+UnwindStackPtrace::UnwindStackPtrace(pid_t pid, pid_t tid, BacktraceMap* map)
+ : BacktracePtrace(pid, tid, map), memory_(new unwindstack::MemoryRemote(pid)) {}
+
+std::string UnwindStackPtrace::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) {
+ return ::GetFunctionName(Pid(), GetMap(), pc, offset);
+}
+
+bool UnwindStackPtrace::Unwind(size_t num_ignore_frames, ucontext_t* context) {
+ std::unique_ptr<unwindstack::Regs> regs;
+ if (context == nullptr) {
+ uint32_t machine_type;
+ regs.reset(unwindstack::Regs::RemoteGet(Tid(), &machine_type));
+ } else {
+ regs.reset(unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::GetMachineType(), context));
+ }
+
+ error_ = BACKTRACE_UNWIND_NO_ERROR;
+ return ::Unwind(Pid(), memory_.get(), regs.get(), GetMap(), &frames_, num_ignore_frames);
+}
+
+Backtrace* Backtrace::CreateNew(pid_t pid, pid_t tid, BacktraceMap* map) {
+ if (pid == BACKTRACE_CURRENT_PROCESS) {
+ pid = getpid();
+ if (tid == BACKTRACE_CURRENT_THREAD) {
+ tid = gettid();
+ }
+ } else if (tid == BACKTRACE_CURRENT_THREAD) {
+ tid = pid;
+ }
+
+ if (map == nullptr) {
+// This would cause the wrong type of map object to be created, so disallow.
+#if defined(__ANDROID__)
+ __assert2(__FILE__, __LINE__, __PRETTY_FUNCTION__,
+ "Backtrace::CreateNew() must be called with a real map pointer.");
+#else
+ BACK_LOGE("Backtrace::CreateNew() must be called with a real map pointer.");
+ abort();
+#endif
+ }
+
+ if (pid == getpid()) {
+ return new UnwindStackCurrent(pid, tid, map);
+ } else {
+ return new UnwindStackPtrace(pid, tid, map);
+ }
+}
diff --git a/libbacktrace/UnwindStack.h b/libbacktrace/UnwindStack.h
new file mode 100644
index 0000000..32d1f51
--- /dev/null
+++ b/libbacktrace/UnwindStack.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2017 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 _LIBBACKTRACE_UNWIND_STACK_H
+#define _LIBBACKTRACE_UNWIND_STACK_H
+
+#include <stdint.h>
+
+#include <string>
+
+#include <backtrace/BacktraceMap.h>
+#include <unwindstack/Memory.h>
+
+#include "BacktraceCurrent.h"
+#include "BacktracePtrace.h"
+
+class UnwindStackCurrent : public BacktraceCurrent {
+ public:
+ UnwindStackCurrent(pid_t pid, pid_t tid, BacktraceMap* map);
+ virtual ~UnwindStackCurrent() = default;
+
+ std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) override;
+
+ bool UnwindFromContext(size_t num_ignore_frames, ucontext_t* ucontext) override;
+
+ private:
+ std::unique_ptr<unwindstack::Memory> memory_;
+};
+
+class UnwindStackPtrace : public BacktracePtrace {
+ public:
+ UnwindStackPtrace(pid_t pid, pid_t tid, BacktraceMap* map);
+ virtual ~UnwindStackPtrace() = default;
+
+ bool Unwind(size_t num_ignore_frames, ucontext_t* context) override;
+
+ std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset);
+
+ private:
+ std::unique_ptr<unwindstack::Memory> memory_;
+};
+
+#endif // _LIBBACKTRACE_UNWIND_STACK_H
diff --git a/libbacktrace/UnwindStackMap.cpp b/libbacktrace/UnwindStackMap.cpp
new file mode 100644
index 0000000..ba9fd87
--- /dev/null
+++ b/libbacktrace/UnwindStackMap.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+#include <backtrace/BacktraceMap.h>
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+
+#include "UnwindStackMap.h"
+
+//-------------------------------------------------------------------------
+UnwindStackMap::UnwindStackMap(pid_t pid) : BacktraceMap(pid) {}
+
+bool UnwindStackMap::Build() {
+ if (pid_ == 0) {
+ pid_ = getpid();
+ stack_maps_.reset(new unwindstack::LocalMaps);
+ } else {
+ stack_maps_.reset(new unwindstack::RemoteMaps(pid_));
+ }
+
+ if (!stack_maps_->Parse()) {
+ return false;
+ }
+
+ // Iterate through the maps and fill in the backtrace_map_t structure.
+ for (auto& map_info : *stack_maps_) {
+ backtrace_map_t map;
+ map.start = map_info.start;
+ map.end = map_info.end;
+ map.offset = map_info.offset;
+ // Set to -1 so that it is demand loaded.
+ map.load_bias = static_cast<uintptr_t>(-1);
+ map.flags = map_info.flags;
+ map.name = map_info.name;
+
+ maps_.push_back(map);
+ }
+
+ return true;
+}
+
+void UnwindStackMap::FillIn(uintptr_t addr, backtrace_map_t* map) {
+ BacktraceMap::FillIn(addr, map);
+ if (map->load_bias != static_cast<uintptr_t>(-1)) {
+ return;
+ }
+
+ // Fill in the load_bias.
+ unwindstack::MapInfo* map_info = stack_maps_->Find(addr);
+ if (map_info == nullptr) {
+ return;
+ }
+ unwindstack::Elf* elf = map_info->GetElf(pid_, true);
+ map->load_bias = elf->GetLoadBias();
+}
+
+//-------------------------------------------------------------------------
+// BacktraceMap create function.
+//-------------------------------------------------------------------------
+BacktraceMap* BacktraceMap::CreateNew(pid_t pid, bool uncached) {
+ BacktraceMap* map;
+
+ if (uncached) {
+ // Force use of the base class to parse the maps when this call is made.
+ map = new BacktraceMap(pid);
+ } else if (pid == getpid()) {
+ map = new UnwindStackMap(0);
+ } else {
+ map = new UnwindStackMap(pid);
+ }
+ if (!map->Build()) {
+ delete map;
+ return nullptr;
+ }
+ return map;
+}
diff --git a/libbacktrace/UnwindStackMap.h b/libbacktrace/UnwindStackMap.h
new file mode 100644
index 0000000..7885b74
--- /dev/null
+++ b/libbacktrace/UnwindStackMap.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2017 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 _LIBBACKTRACE_UNWINDSTACK_MAP_H
+#define _LIBBACKTRACE_UNWINDSTACK_MAP_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <backtrace/BacktraceMap.h>
+#include <unwindstack/Maps.h>
+
+class UnwindStackMap : public BacktraceMap {
+ public:
+ explicit UnwindStackMap(pid_t pid);
+ ~UnwindStackMap() = default;
+
+ bool Build() override;
+
+ void FillIn(uintptr_t addr, backtrace_map_t* map) override;
+
+ unwindstack::Maps* stack_maps() { return stack_maps_.get(); }
+
+ protected:
+ std::unique_ptr<unwindstack::Maps> stack_maps_;
+};
+
+#endif // _LIBBACKTRACE_UNWINDSTACK_MAP_H
diff --git a/libbacktrace/backtrace_offline_test.cpp b/libbacktrace/backtrace_offline_test.cpp
index 465b3f9..16b1d79 100644
--- a/libbacktrace/backtrace_offline_test.cpp
+++ b/libbacktrace/backtrace_offline_test.cpp
@@ -167,9 +167,9 @@
// 2. Dump maps
for (auto it = map->begin(); it != map->end(); ++it) {
testdata += android::base::StringPrintf(
- "map: start: %" PRIxPTR " end: %" PRIxPTR " offset: %" PRIxPTR
- " load_base: %" PRIxPTR " flags: %d name: %s\n",
- it->start, it->end, it->offset, it->load_base, it->flags, it->name.c_str());
+ "map: start: %" PRIxPTR " end: %" PRIxPTR " offset: %" PRIxPTR " load_bias: %" PRIxPTR
+ " flags: %d name: %s\n",
+ it->start, it->end, it->offset, it->load_bias, it->flags, it->name.c_str());
}
// 3. Dump registers
testdata += android::base::StringPrintf("registers: %zu ", sizeof(arg.unw_context));
@@ -246,9 +246,9 @@
backtrace_map_t& map = testdata->maps.back();
int pos;
sscanf(line.c_str(),
- "map: start: %" SCNxPTR " end: %" SCNxPTR " offset: %" SCNxPTR
- " load_base: %" SCNxPTR " flags: %d name: %n",
- &map.start, &map.end, &map.offset, &map.load_base, &map.flags, &pos);
+ "map: start: %" SCNxPTR " end: %" SCNxPTR " offset: %" SCNxPTR " load_bias: %" SCNxPTR
+ " flags: %d name: %n",
+ &map.start, &map.end, &map.offset, &map.load_bias, &map.flags, &pos);
map.name = android::base::Trim(line.substr(pos));
} else if (android::base::StartsWith(line, "registers:")) {
size_t size;
@@ -392,8 +392,8 @@
// The last frame is outside of libart.so
ASSERT_EQ(testdata.symbols.size() + 1, backtrace->NumFrames());
for (size_t i = 0; i + 1 < backtrace->NumFrames(); ++i) {
- uintptr_t vaddr_in_file = backtrace->GetFrame(i)->pc - testdata.maps[0].start +
- testdata.maps[0].load_base;
+ uintptr_t vaddr_in_file =
+ backtrace->GetFrame(i)->pc - testdata.maps[0].start + testdata.maps[0].load_bias;
std::string name = FunctionNameForAddress(vaddr_in_file, testdata.symbols);
ASSERT_EQ(name, testdata.symbols[i].name);
}
diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp
index fb463b0..1ec6a45 100644
--- a/libbacktrace/backtrace_test.cpp
+++ b/libbacktrace/backtrace_test.cpp
@@ -784,6 +784,7 @@
backtrace_frame_data_t frame;
frame.num = 1;
frame.pc = 2;
+ frame.rel_pc = 2;
frame.sp = 0;
frame.stack_size = 0;
frame.func_offset = 0;
@@ -799,9 +800,10 @@
// Check map name empty, but exists.
frame.pc = 0xb0020;
+ frame.rel_pc = 0x20;
frame.map.start = 0xb0000;
frame.map.end = 0xbffff;
- frame.map.load_base = 0;
+ frame.map.load_bias = 0;
#if defined(__LP64__)
EXPECT_EQ("#01 pc 0000000000000020 <anonymous:00000000000b0000>",
#else
@@ -813,7 +815,7 @@
frame.pc = 0xc0020;
frame.map.start = 0xc0000;
frame.map.end = 0xcffff;
- frame.map.load_base = 0;
+ frame.map.load_bias = 0;
frame.map.name = "[anon:thread signal stack]";
#if defined(__LP64__)
EXPECT_EQ("#01 pc 0000000000000020 [anon:thread signal stack:00000000000c0000]",
@@ -824,6 +826,7 @@
// Check relative pc is set and map name is set.
frame.pc = 0x12345679;
+ frame.rel_pc = 0x12345678;
frame.map.name = "MapFake";
frame.map.start = 1;
frame.map.end = 1;
@@ -852,9 +855,10 @@
#endif
backtrace->FormatFrameData(&frame));
- // Check func_name is set, func offset is non-zero, and load_base is non-zero.
+ // Check func_name is set, func offset is non-zero, and load_bias is non-zero.
+ frame.rel_pc = 0x123456dc;
frame.func_offset = 645;
- frame.map.load_base = 100;
+ frame.map.load_bias = 100;
#if defined(__LP64__)
EXPECT_EQ("#01 pc 00000000123456dc MapFake (ProcFake+645)",
#else
@@ -1737,9 +1741,13 @@
FinishRemoteProcess(pid);
}
-TEST(libbacktrace, unwind_remote_through_signal_using_handler) { UnwindThroughSignal(false); }
+TEST(libbacktrace, unwind_remote_through_signal_using_handler) {
+ UnwindThroughSignal(false);
+}
-TEST(libbacktrace, unwind_remote_through_signal_using_action) { UnwindThroughSignal(true); }
+TEST(libbacktrace, unwind_remote_through_signal_using_action) {
+ UnwindThroughSignal(true);
+}
#if defined(ENABLE_PSS_TESTS)
#include "GetPss.h"
diff --git a/libbacktrace/include/backtrace/Backtrace.h b/libbacktrace/include/backtrace/Backtrace.h
index 4f73a65..d67ea50 100644
--- a/libbacktrace/include/backtrace/Backtrace.h
+++ b/libbacktrace/include/backtrace/Backtrace.h
@@ -55,6 +55,7 @@
struct backtrace_frame_data_t {
size_t num; // The current fame number.
uintptr_t pc; // The absolute pc.
+ uintptr_t rel_pc; // The relative pc.
uintptr_t sp; // The top of the stack.
size_t stack_size; // The size of the stack, zero indicate an unknown stack size.
backtrace_map_t map; // The map associated with the given pc.
@@ -89,6 +90,8 @@
// If map is NULL, then create the map and manage it internally.
// If map is not NULL, the map is still owned by the caller.
static Backtrace* Create(pid_t pid, pid_t tid, BacktraceMap* map = NULL);
+ // Same as above, but uses a different underlying unwinder.
+ static Backtrace* CreateNew(pid_t pid, pid_t tid, BacktraceMap* map = NULL);
// Create an offline Backtrace object that can be used to do an unwind without a process
// that is still running. If cache_file is set to true, then elf information will be cached
diff --git a/libbacktrace/include/backtrace/BacktraceMap.h b/libbacktrace/include/backtrace/BacktraceMap.h
index 8ab0dfa..963c34b 100644
--- a/libbacktrace/include/backtrace/BacktraceMap.h
+++ b/libbacktrace/include/backtrace/BacktraceMap.h
@@ -41,7 +41,7 @@
uintptr_t start = 0;
uintptr_t end = 0;
uintptr_t offset = 0;
- uintptr_t load_base = 0;
+ uintptr_t load_bias = 0;
int flags = 0;
std::string name;
};
@@ -52,6 +52,8 @@
// Passing a map created with uncached set to true to Backtrace::Create()
// is unsupported.
static BacktraceMap* Create(pid_t pid, bool uncached = false);
+ // Same as above, but is compatible with the new unwinder.
+ static BacktraceMap* CreateNew(pid_t pid, bool uncached = false);
static BacktraceMap* Create(pid_t pid, const std::vector<backtrace_map_t>& maps);
@@ -95,14 +97,6 @@
return map.end > 0;
}
- static uintptr_t GetRelativePc(const backtrace_map_t& map, uintptr_t pc) {
- if (IsValid(map)) {
- return pc - map.start + map.load_base;
- } else {
- return pc;
- }
- }
-
protected:
BacktraceMap(pid_t pid);
diff --git a/libbacktrace/include/backtrace/backtrace_constants.h b/libbacktrace/include/backtrace/backtrace_constants.h
index f8c1575..373a1e5 100644
--- a/libbacktrace/include/backtrace/backtrace_constants.h
+++ b/libbacktrace/include/backtrace/backtrace_constants.h
@@ -20,10 +20,10 @@
// When the pid to be traced is set to this value, then trace the current
// process. If the tid value is not BACKTRACE_NO_TID, then the specified
// thread from the current process will be traced.
-#define BACKTRACE_CURRENT_PROCESS -1
+#define BACKTRACE_CURRENT_PROCESS (-1)
// When the tid to be traced is set to this value, then trace the specified
// current thread of the specified pid.
-#define BACKTRACE_CURRENT_THREAD -1
+#define BACKTRACE_CURRENT_THREAD (-1)
#define MAX_BACKTRACE_FRAMES 64
diff --git a/libcrypto_utils/Android.bp b/libcrypto_utils/Android.bp
index f2560e6..4a5f2a7 100644
--- a/libcrypto_utils/Android.bp
+++ b/libcrypto_utils/Android.bp
@@ -16,6 +16,7 @@
cc_library {
name: "libcrypto_utils",
+ vendor_available: true,
host_supported: true,
srcs: [
"android_pubkey.c",
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index 245deb1..cfe8d29 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -50,6 +50,10 @@
cc_library {
name: "libcutils",
vendor_available: true,
+ vndk: {
+ enabled: true,
+ support_system_process: true,
+ },
host_supported: true,
srcs: [
"config_utils.c",
@@ -150,14 +154,13 @@
"libutils_headers",
],
export_header_lib_headers: ["libcutils_headers"],
+ local_include_dirs: ["include"],
cflags: [
"-Werror",
"-Wall",
"-Wextra",
],
-
- clang: true,
}
subdirs = ["tests"]
diff --git a/libcutils/android_reboot.c b/libcutils/android_reboot.c
index a33e45f..996d89d 100644
--- a/libcutils/android_reboot.c
+++ b/libcutils/android_reboot.c
@@ -35,11 +35,11 @@
restart_cmd = "shutdown";
break;
case ANDROID_RB_THERMOFF:
- restart_cmd = "thermal-shutdown";
+ restart_cmd = "shutdown,thermal";
break;
}
if (!restart_cmd) return -1;
- if (arg) {
+ if (arg && arg[0]) {
ret = asprintf(&prop_value, "%s,%s", restart_cmd, arg);
} else {
ret = asprintf(&prop_value, "%s", restart_cmd);
diff --git a/libcutils/canned_fs_config.c b/libcutils/canned_fs_config.c
index 96ca566..819a846 100644
--- a/libcutils/canned_fs_config.c
+++ b/libcutils/canned_fs_config.c
@@ -23,6 +23,7 @@
#include <string.h>
#include <private/android_filesystem_config.h>
+#include <private/fs_config.h>
#include <private/canned_fs_config.h>
typedef struct {
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index 13c4abf..a1dbd78 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -186,7 +186,7 @@
// Support hostapd administering a network interface.
{ 00755, AID_WIFI, AID_WIFI, CAP_MASK_LONG(CAP_NET_ADMIN) |
CAP_MASK_LONG(CAP_NET_RAW),
- "system/bin/hostapd" },
+ "vendor/bin/hostapd" },
// Support Bluetooth legacy hal accessing /sys/class/rfkill
// Support RT scheduling in Bluetooth
diff --git a/libcutils/include/cutils/android_filesystem_config.h b/libcutils/include/cutils/android_filesystem_config.h
new file mode 120000
index 0000000..d2a92fe
--- /dev/null
+++ b/libcutils/include/cutils/android_filesystem_config.h
@@ -0,0 +1 @@
+../private/android_filesystem_config.h
\ No newline at end of file
diff --git a/libcutils/include/cutils/android_reboot.h b/libcutils/include/cutils/android_reboot.h
index 716567a..a903adb 100644
--- a/libcutils/include/cutils/android_reboot.h
+++ b/libcutils/include/cutils/android_reboot.h
@@ -29,8 +29,8 @@
/* Properties */
#define ANDROID_RB_PROPERTY "sys.powerctl"
-/* Android reboot reason stored in this file */
-#define LAST_REBOOT_REASON_FILE "/data/misc/reboot/last_reboot_reason"
+/* Android reboot reason stored in this property */
+#define LAST_REBOOT_REASON_PROPERTY "persist.sys.boot.reason"
/* Reboot or shutdown the system.
* This call uses ANDROID_RB_PROPERTY to request reboot to init process.
diff --git a/libcutils/include/cutils/list.h b/libcutils/include/cutils/list.h
index 4ba2cfd..dfdc53b 100644
--- a/libcutils/include/cutils/list.h
+++ b/libcutils/include/cutils/list.h
@@ -34,20 +34,20 @@
#define list_declare(name) \
struct listnode name = { \
- .next = &name, \
- .prev = &name, \
+ .next = &(name), \
+ .prev = &(name), \
}
#define list_for_each(node, list) \
- for (node = (list)->next; node != (list); node = node->next)
+ for ((node) = (list)->next; (node) != (list); (node) = (node)->next)
#define list_for_each_reverse(node, list) \
- for (node = (list)->prev; node != (list); node = node->prev)
+ for ((node) = (list)->prev; (node) != (list); (node) = (node)->prev)
#define list_for_each_safe(node, n, list) \
- for (node = (list)->next, n = node->next; \
- node != (list); \
- node = n, n = node->next)
+ for ((node) = (list)->next, (n) = (node)->next; \
+ (node) != (list); \
+ (node) = (n), (n) = (node)->next)
static inline void list_init(struct listnode *node)
{
diff --git a/libcutils/include/cutils/native_handle.h b/libcutils/include/cutils/native_handle.h
index 7d6a988..55754b5 100644
--- a/libcutils/include/cutils/native_handle.h
+++ b/libcutils/include/cutils/native_handle.h
@@ -25,8 +25,8 @@
/* Declare a char array for use with native_handle_init */
#define NATIVE_HANDLE_DECLARE_STORAGE(name, maxFds, maxInts) \
- alignas(native_handle_t) char name[ \
- sizeof(native_handle_t) + sizeof(int) * (maxFds + maxInts)]
+ alignas(native_handle_t) char (name)[ \
+ sizeof(native_handle_t) + sizeof(int) * ((maxFds) + (maxInts))]
typedef struct native_handle
{
diff --git a/libcutils/include/cutils/properties.h b/libcutils/include/cutils/properties.h
index b45f58f..d2e0871 100644
--- a/libcutils/include/cutils/properties.h
+++ b/libcutils/include/cutils/properties.h
@@ -43,12 +43,7 @@
** If the property read fails or returns an empty value, the default
** value is used (if nonnull).
*/
-int property_get(const char *key, char *value, const char *default_value)
-/* Sometimes we use not-Bionic with this, so we need this check. */
-#if defined(__BIONIC_FORTIFY)
- __overloadable __RENAME_CLANG(property_get)
-#endif
- ;
+int property_get(const char* key, char* value, const char* default_value);
/* property_get_bool: returns the value of key coerced into a
** boolean. If the property is not set, then the default value is returned.
@@ -119,27 +114,15 @@
#if defined(__clang__)
-/* Some projects use -Weverything; enable_if is clang-specific.
-** FIXME: This is marked used because we'll otherwise get complaints about an
-** unused static function. This is more robust than marking it unused, since
-** -Wused-but-marked-unused is a thing that will complain if this function is
-** actually used, thus making FORTIFY noisier when an error happens. It's going
-** to go away anyway during our FORTIFY cleanup.
-**/
+/* Some projects use -Weverything; diagnose_if is clang-specific. */
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgcc-compat"
-__BIONIC_ERROR_FUNCTION_VISIBILITY
-int property_get(const char *key, char *value, const char *default_value)
- __overloadable
- __enable_if(__bos(value) != __BIONIC_FORTIFY_UNKNOWN_SIZE &&
- __bos(value) < PROPERTY_VALUE_MAX, __property_get_err_str)
- __errorattr(__property_get_err_str)
- __attribute__((used));
+int property_get(const char* key, char* value, const char* default_value)
+ __clang_error_if(__bos(value) != __BIONIC_FORTIFY_UNKNOWN_SIZE &&
+ __bos(value) < PROPERTY_VALUE_MAX,
+ __property_get_err_str);
#pragma clang diagnostic pop
-/* No object size? No FORTIFY.
-*/
-
#else /* defined(__clang__) */
extern int __property_get_real(const char *, char *, const char *)
diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index 02141d6..55ece54 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -14,11 +14,6 @@
* limitations under the License.
*/
-/* This file is used to define the properties of the filesystem
-** images generated by build tools (mkbootfs and mkyaffs2image) and
-** by the device side of adb.
-*/
-
/*
* This file is consumed by build/tools/fs_config and is used
* for generating various files. Anything #define AID_<name>
@@ -49,18 +44,12 @@
#ifndef _ANDROID_FILESYSTEM_CONFIG_H_
#define _ANDROID_FILESYSTEM_CONFIG_H_
-#include <stdint.h>
-#include <sys/cdefs.h>
#include <sys/types.h>
-#if defined(__BIONIC__)
-#include <linux/capability.h>
-#else
-#include "android_filesystem_capability.h"
+#if !defined(__ANDROID_VNDK__) && !defined(EXCLUDE_FS_CONFIG_STRUCTURES)
+#include <private/fs_config.h>
#endif
-#define CAP_MASK_LONG(cap_name) (1ULL << (cap_name))
-
/* This is the master Users and Groups config for the platform.
* DO NOT EVER RENUMBER
*/
@@ -131,6 +120,8 @@
#define AID_ESE 1060 /* embedded secure element (eSE) subsystem */
#define AID_OTA_UPDATE 1061 /* resource tracking UID for OTA updates */
#define AID_AUTOMOTIVE_EVS 1062 /* Automotive rear and surround view system */
+#define AID_LOWPAN 1063 /* LoWPAN subsystem */
+#define AID_HSM 1064 /* hardware security module subsystem */
/* Changes to this file must be made in AOSP, *not* in internal branches. */
#define AID_SHELL 2000 /* adb and debug shell user */
@@ -193,36 +184,4 @@
* Also see build/tools/fs_config for more details.
*/
-#if !defined(EXCLUDE_FS_CONFIG_STRUCTURES)
-
-struct fs_path_config {
- unsigned mode;
- unsigned uid;
- unsigned gid;
- uint64_t capabilities;
- const char* prefix;
-};
-
-/* Rules for directories and files has moved to system/code/libcutils/fs_config.c */
-
-__BEGIN_DECLS
-
-/*
- * Used in:
- * build/tools/fs_config/fs_config.c
- * build/tools/fs_get_stats/fs_get_stats.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, 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);
-
-__END_DECLS
-
-#endif
#endif
diff --git a/libcutils/include/private/fs_config.h b/libcutils/include/private/fs_config.h
index 7dad668..aab5042 100644
--- a/libcutils/include/private/fs_config.h
+++ b/libcutils/include/private/fs_config.h
@@ -14,10 +14,24 @@
* limitations under the License.
*/
+/* This file is used to define the properties of the filesystem
+** images generated by build tools (mkbootfs and mkyaffs2image) and
+** by the device side of adb.
+*/
+
#ifndef _LIBS_CUTILS_PRIVATE_FS_CONFIG_H
#define _LIBS_CUTILS_PRIVATE_FS_CONFIG_H
#include <stdint.h>
+#include <sys/cdefs.h>
+
+#if defined(__BIONIC__)
+#include <linux/capability.h>
+#else // defined(__BIONIC__)
+#include "android_filesystem_capability.h"
+#endif // defined(__BIONIC__)
+
+#define CAP_MASK_LONG(cap_name) (1ULL << (cap_name))
/*
* binary format for the runtime <partition>/etc/fs_config_(dirs|files)
@@ -34,4 +48,33 @@
char prefix[];
} __attribute__((__aligned__(sizeof(uint64_t))));
+struct fs_path_config {
+ unsigned mode;
+ unsigned uid;
+ unsigned gid;
+ uint64_t capabilities;
+ const char* prefix;
+};
+
+/* Rules for directories and files has moved to system/code/libcutils/fs_config.c */
+
+__BEGIN_DECLS
+
+/*
+ * Used in:
+ * build/tools/fs_config/fs_config.c
+ * build/tools/fs_get_stats/fs_get_stats.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, 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);
+
+__END_DECLS
+
#endif /* _LIBS_CUTILS_PRIVATE_FS_CONFIG_H */
diff --git a/libcutils/include_vndk/cutils/android_filesystem_config.h b/libcutils/include_vndk/cutils/android_filesystem_config.h
new file mode 120000
index 0000000..13a5a08
--- /dev/null
+++ b/libcutils/include_vndk/cutils/android_filesystem_config.h
@@ -0,0 +1 @@
+../../include/private/android_filesystem_config.h
\ No newline at end of file
diff --git a/libcutils/include_vndk/private b/libcutils/include_vndk/private
deleted file mode 120000
index 2245a85..0000000
--- a/libcutils/include_vndk/private
+++ /dev/null
@@ -1 +0,0 @@
-../include/private
\ No newline at end of file
diff --git a/libcutils/socket_network_client_unix.c b/libcutils/socket_network_client_unix.c
index 37851b1..1b87c49 100644
--- a/libcutils/socket_network_client_unix.c
+++ b/libcutils/socket_network_client_unix.c
@@ -63,7 +63,7 @@
for (struct addrinfo* addr = addrs; addr != NULL; addr = addr->ai_next) {
// The Mac doesn't have SOCK_NONBLOCK.
int s = socket(addr->ai_family, type, addr->ai_protocol);
- if (s == -1 || toggle_O_NONBLOCK(s) == -1) return -1;
+ if (s == -1 || toggle_O_NONBLOCK(s) == -1) break;
int rc = connect(s, addr->ai_addr, addr->ai_addrlen);
if (rc == 0) {
diff --git a/libcutils/trace-container.c b/libcutils/trace-container.c
new file mode 100644
index 0000000..03e91b1
--- /dev/null
+++ b/libcutils/trace-container.c
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2017 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 "trace-dev.inc"
+
+#include <cutils/sockets.h>
+#include <sys/stat.h>
+#include <time.h>
+
+/**
+ * For tracing in container, tags are written into a socket
+ * instead of ftrace. Additional data is appended so we need extra space.
+ */
+#define CONTAINER_ATRACE_MESSAGE_LENGTH (ATRACE_MESSAGE_LENGTH + 512)
+
+static pthread_once_t atrace_once_control = PTHREAD_ONCE_INIT;
+
+// Variables used for tracing in container with socket.
+// Note that we need to manually close and reopen socket when Zygote is forking. This requires
+// writing and closing sockets on multiple threads. A rwlock is used for avoiding concurrent
+// operation on the file descriptor.
+static bool atrace_use_container_sock = false;
+static int atrace_container_sock_fd = -1;
+static pthread_mutex_t atrace_enabling_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_rwlock_t atrace_container_sock_rwlock = PTHREAD_RWLOCK_INITIALIZER;
+
+static bool atrace_init_container_sock()
+{
+ pthread_rwlock_wrlock(&atrace_container_sock_rwlock);
+ atrace_container_sock_fd =
+ socket_local_client("trace", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET);
+ if (atrace_container_sock_fd < 0) {
+ ALOGE("Error opening container trace socket: %s (%d)", strerror(errno), errno);
+ }
+ pthread_rwlock_unlock(&atrace_container_sock_rwlock);
+ return atrace_container_sock_fd != -1;
+}
+
+static void atrace_close_container_sock()
+{
+ pthread_rwlock_wrlock(&atrace_container_sock_rwlock);
+ if (atrace_container_sock_fd != -1) close(atrace_container_sock_fd);
+ atrace_container_sock_fd = -1;
+ pthread_rwlock_unlock(&atrace_container_sock_rwlock);
+}
+
+// Set whether tracing is enabled in this process. This is used to prevent
+// the Zygote process from tracing. We need to close the socket in the container when tracing is
+// disabled, and reopen it again after Zygote forking.
+void atrace_set_tracing_enabled(bool enabled)
+{
+ pthread_mutex_lock(&atrace_enabling_mutex);
+ if (atrace_use_container_sock) {
+ bool already_enabled = atomic_load_explicit(&atrace_is_enabled, memory_order_acquire);
+ if (enabled && !already_enabled) {
+ // Trace was disabled previously. Re-initialize container socket.
+ atrace_init_container_sock();
+ } else if (!enabled && already_enabled) {
+ // Trace was enabled previously. Close container socket.
+ atrace_close_container_sock();
+ }
+ }
+ atomic_store_explicit(&atrace_is_enabled, enabled, memory_order_release);
+ pthread_mutex_unlock(&atrace_enabling_mutex);
+ atrace_update_tags();
+}
+
+static void atrace_init_once()
+{
+ atrace_marker_fd = open("/sys/kernel/debug/tracing/trace_marker", O_WRONLY | O_CLOEXEC);
+ if (atrace_marker_fd < 0) {
+ // We're in container, ftrace may be disabled. In such case, we use the
+ // socket to write trace event.
+
+ // Protect the initialization of container socket from
+ // atrace_set_tracing_enabled.
+ pthread_mutex_lock(&atrace_enabling_mutex);
+ atrace_use_container_sock = true;
+ bool success = false;
+ if (atomic_load_explicit(&atrace_is_enabled, memory_order_acquire)) {
+ success = atrace_init_container_sock();
+ }
+ pthread_mutex_unlock(&atrace_enabling_mutex);
+
+ if (!success) {
+ atrace_enabled_tags = 0;
+ goto done;
+ }
+ }
+ atrace_enabled_tags = atrace_get_property();
+
+done:
+ atomic_store_explicit(&atrace_is_ready, true, memory_order_release);
+}
+
+void atrace_setup()
+{
+ pthread_once(&atrace_once_control, atrace_init_once);
+}
+
+static inline uint64_t gettime(clockid_t clk_id)
+{
+ struct timespec ts;
+ clock_gettime(clk_id, &ts);
+ return ts.tv_sec * 1000000 + ts.tv_nsec / 1000;
+}
+
+// Write trace events to container trace file. Note that we need to amend tid and time information
+// here comparing to normal ftrace, where those informations are added by kernel.
+#define WRITE_MSG_IN_CONTAINER_LOCKED(ph, sep_before_name, value_format, name, value) { \
+ char buf[CONTAINER_ATRACE_MESSAGE_LENGTH]; \
+ int pid = getpid(); \
+ int tid = gettid(); \
+ uint64_t ts = gettime(CLOCK_MONOTONIC); \
+ uint64_t tts = gettime(CLOCK_THREAD_CPUTIME_ID); \
+ int len = snprintf( \
+ buf, sizeof(buf), \
+ ph "|%d|%d|%" PRIu64 "|%" PRIu64 sep_before_name "%s" value_format, \
+ pid, tid, ts, tts, name, value); \
+ if (len >= (int) sizeof(buf)) { \
+ int name_len = strlen(name) - (len - sizeof(buf)) - 1; \
+ /* Truncate the name to make the message fit. */ \
+ if (name_len > 0) { \
+ ALOGW("Truncated name in %s: %s\n", __FUNCTION__, name); \
+ len = snprintf( \
+ buf, sizeof(buf), \
+ ph "|%d|%d|%" PRIu64 "|%" PRIu64 sep_before_name "%.*s" value_format, \
+ pid, tid, ts, tts, name_len, name, value); \
+ } else { \
+ /* Data is still too long. Drop it. */ \
+ ALOGW("Data is too long in %s: %s\n", __FUNCTION__, name); \
+ len = 0; \
+ } \
+ } \
+ if (len > 0) { \
+ write(atrace_container_sock_fd, buf, len); \
+ } \
+}
+
+#define WRITE_MSG_IN_CONTAINER(ph, sep_before_name, value_format, name, value) { \
+ pthread_rwlock_rdlock(&atrace_container_sock_rwlock); \
+ if (atrace_container_sock_fd != -1) { \
+ WRITE_MSG_IN_CONTAINER_LOCKED(ph, sep_before_name, value_format, name, value); \
+ } \
+ pthread_rwlock_unlock(&atrace_container_sock_rwlock); \
+}
+
+void atrace_begin_body(const char* name)
+{
+ if (CC_LIKELY(atrace_use_container_sock)) {
+ WRITE_MSG_IN_CONTAINER("B", "|", "%s", name, "");
+ return;
+ }
+
+ if (atrace_marker_fd < 0) return;
+
+ WRITE_MSG("B|%d|", "%s", name, "");
+}
+
+void atrace_end_body()
+{
+ if (CC_LIKELY(atrace_use_container_sock)) {
+ WRITE_MSG_IN_CONTAINER("E", "", "%s", "", "");
+ return;
+ }
+
+ if (atrace_marker_fd < 0) return;
+
+ WRITE_MSG("E|%d", "%s", "", "");
+}
+
+void atrace_async_begin_body(const char* name, int32_t cookie)
+{
+ if (CC_LIKELY(atrace_use_container_sock)) {
+ WRITE_MSG_IN_CONTAINER("S", "|", "|%d", name, cookie);
+ return;
+ }
+
+ if (atrace_marker_fd < 0) return;
+
+ WRITE_MSG("S|%d|", "|%" PRId32, name, cookie);
+}
+
+void atrace_async_end_body(const char* name, int32_t cookie)
+{
+ if (CC_LIKELY(atrace_use_container_sock)) {
+ WRITE_MSG_IN_CONTAINER("F", "|", "|%d", name, cookie);
+ return;
+ }
+
+ if (atrace_marker_fd < 0) return;
+
+ WRITE_MSG("F|%d|", "|%" PRId32, name, cookie);
+}
+
+void atrace_int_body(const char* name, int32_t value)
+{
+ if (CC_LIKELY(atrace_use_container_sock)) {
+ WRITE_MSG_IN_CONTAINER("C", "|", "|%" PRId32, name, value);
+ return;
+ }
+
+ if (atrace_marker_fd < 0) return;
+
+ WRITE_MSG("C|%d|", "|%" PRId32, name, value);
+}
+
+void atrace_int64_body(const char* name, int64_t value)
+{
+ if (CC_LIKELY(atrace_use_container_sock)) {
+ WRITE_MSG_IN_CONTAINER("C", "|", "|%" PRId64, name, value);
+ return;
+ }
+
+ if (atrace_marker_fd < 0) return;
+
+ WRITE_MSG("C|%d|", "|%" PRId64, name, value);
+}
diff --git a/libcutils/trace-dev.c b/libcutils/trace-dev.c
index d45e5a9..4468e83 100644
--- a/libcutils/trace-dev.c
+++ b/libcutils/trace-dev.c
@@ -14,47 +14,9 @@
* limitations under the License.
*/
-#define LOG_TAG "cutils-trace"
+#include "trace-dev.inc"
-#include <errno.h>
-#include <fcntl.h>
-#include <limits.h>
-#include <pthread.h>
-#include <stdatomic.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-
-#include <cutils/compiler.h>
-#include <cutils/properties.h>
-#include <cutils/trace.h>
-#include <log/log.h>
-#include <log/log_properties.h>
-
-/**
- * Maximum size of a message that can be logged to the trace buffer.
- * Note this message includes a tag, the pid, and the string given as the name.
- * Names should be kept short to get the most use of the trace buffer.
- */
-#define ATRACE_MESSAGE_LENGTH 1024
-
-atomic_bool atrace_is_ready = ATOMIC_VAR_INIT(false);
-int atrace_marker_fd = -1;
-uint64_t atrace_enabled_tags = ATRACE_TAG_NOT_READY;
-static bool atrace_is_debuggable = false;
-static atomic_bool atrace_is_enabled = ATOMIC_VAR_INIT(true);
-static pthread_once_t atrace_once_control = PTHREAD_ONCE_INIT;
-static pthread_mutex_t atrace_tags_mutex = PTHREAD_MUTEX_INITIALIZER;
-
-// Set whether this process is debuggable, which determines whether
-// application-level tracing is allowed when the ro.debuggable system property
-// is not set to '1'.
-void atrace_set_debuggable(bool debuggable)
-{
- atrace_is_debuggable = debuggable;
- atrace_update_tags();
-}
+static pthread_once_t atrace_once_control = PTHREAD_ONCE_INIT;
// Set whether tracing is enabled in this process. This is used to prevent
// the Zygote process from tracing.
@@ -64,101 +26,6 @@
atrace_update_tags();
}
-// Check whether the given command line matches one of the comma-separated
-// values listed in the app_cmdlines property.
-static bool atrace_is_cmdline_match(const char* cmdline)
-{
- int count = property_get_int32("debug.atrace.app_number", 0);
-
- char buf[PROPERTY_KEY_MAX];
- char value[PROPERTY_VALUE_MAX];
-
- for (int i = 0; i < count; i++) {
- snprintf(buf, sizeof(buf), "debug.atrace.app_%d", i);
- property_get(buf, value, "");
- if (strcmp(value, cmdline) == 0) {
- return true;
- }
- }
-
- return false;
-}
-
-// Determine whether application-level tracing is enabled for this process.
-static bool atrace_is_app_tracing_enabled()
-{
- bool sys_debuggable = __android_log_is_debuggable();
- bool result = false;
-
- if (sys_debuggable || atrace_is_debuggable) {
- // Check whether tracing is enabled for this process.
- FILE * file = fopen("/proc/self/cmdline", "re");
- if (file) {
- char cmdline[4096];
- if (fgets(cmdline, sizeof(cmdline), file)) {
- result = atrace_is_cmdline_match(cmdline);
- } else {
- ALOGE("Error reading cmdline: %s (%d)", strerror(errno), errno);
- }
- fclose(file);
- } else {
- ALOGE("Error opening /proc/self/cmdline: %s (%d)", strerror(errno),
- errno);
- }
- }
-
- return result;
-}
-
-// Read the sysprop and return the value tags should be set to
-static uint64_t atrace_get_property()
-{
- char value[PROPERTY_VALUE_MAX];
- char *endptr;
- uint64_t tags;
-
- property_get("debug.atrace.tags.enableflags", value, "0");
- errno = 0;
- tags = strtoull(value, &endptr, 0);
- if (value[0] == '\0' || *endptr != '\0') {
- ALOGE("Error parsing trace property: Not a number: %s", value);
- return 0;
- } else if (errno == ERANGE || tags == ULLONG_MAX) {
- ALOGE("Error parsing trace property: Number too large: %s", value);
- return 0;
- }
-
- // Only set the "app" tag if this process was selected for app-level debug
- // tracing.
- if (atrace_is_app_tracing_enabled()) {
- tags |= ATRACE_TAG_APP;
- } else {
- tags &= ~ATRACE_TAG_APP;
- }
-
- return (tags | ATRACE_TAG_ALWAYS) & ATRACE_TAG_VALID_MASK;
-}
-
-// Update tags if tracing is ready. Useful as a sysprop change callback.
-void atrace_update_tags()
-{
- uint64_t tags;
- if (CC_UNLIKELY(atomic_load_explicit(&atrace_is_ready, memory_order_acquire))) {
- if (atomic_load_explicit(&atrace_is_enabled, memory_order_acquire)) {
- tags = atrace_get_property();
- pthread_mutex_lock(&atrace_tags_mutex);
- atrace_enabled_tags = tags;
- pthread_mutex_unlock(&atrace_tags_mutex);
- } else {
- // Tracing is disabled for this process, so we simply don't
- // initialize the tags.
- pthread_mutex_lock(&atrace_tags_mutex);
- atrace_enabled_tags = ATRACE_TAG_NOT_READY;
- pthread_mutex_unlock(&atrace_tags_mutex);
- }
- }
-}
-
static void atrace_init_once()
{
atrace_marker_fd = open("/sys/kernel/debug/tracing/trace_marker", O_WRONLY | O_CLOEXEC);
@@ -181,54 +48,30 @@
void atrace_begin_body(const char* name)
{
- char buf[ATRACE_MESSAGE_LENGTH];
-
- int len = snprintf(buf, sizeof(buf), "B|%d|%s", getpid(), name);
- if (len >= (int) sizeof(buf)) {
- ALOGW("Truncated name in %s: %s\n", __FUNCTION__, name);
- len = sizeof(buf) - 1;
- }
- write(atrace_marker_fd, buf, len);
+ WRITE_MSG("B|%d|", "%s", name, "");
}
void atrace_end_body()
{
- char c = 'E';
- write(atrace_marker_fd, &c, 1);
-}
-
-#define WRITE_MSG(format_begin, format_end, pid, name, value) { \
- char buf[ATRACE_MESSAGE_LENGTH]; \
- int len = snprintf(buf, sizeof(buf), format_begin "%s" format_end, pid, \
- name, value); \
- if (len >= (int) sizeof(buf)) { \
- /* Given the sizeof(buf), and all of the current format buffers, \
- * it is impossible for name_len to be < 0 if len >= sizeof(buf). */ \
- int name_len = strlen(name) - (len - sizeof(buf)) - 1; \
- /* Truncate the name to make the message fit. */ \
- ALOGW("Truncated name in %s: %s\n", __FUNCTION__, name); \
- len = snprintf(buf, sizeof(buf), format_begin "%.*s" format_end, pid, \
- name_len, name, value); \
- } \
- write(atrace_marker_fd, buf, len); \
+ WRITE_MSG("E|%d", "%s", "", "");
}
void atrace_async_begin_body(const char* name, int32_t cookie)
{
- WRITE_MSG("S|%d|", "|%" PRId32, getpid(), name, cookie);
+ WRITE_MSG("S|%d|", "|%" PRId32, name, cookie);
}
void atrace_async_end_body(const char* name, int32_t cookie)
{
- WRITE_MSG("F|%d|", "|%" PRId32, getpid(), name, cookie);
+ WRITE_MSG("F|%d|", "|%" PRId32, name, cookie);
}
void atrace_int_body(const char* name, int32_t value)
{
- WRITE_MSG("C|%d|", "|%" PRId32, getpid(), name, value);
+ WRITE_MSG("C|%d|", "|%" PRId32, name, value);
}
void atrace_int64_body(const char* name, int64_t value)
{
- WRITE_MSG("C|%d|", "|%" PRId64, getpid(), name, value);
+ WRITE_MSG("C|%d|", "|%" PRId64, name, value);
}
diff --git a/libcutils/trace-dev.inc b/libcutils/trace-dev.inc
new file mode 100644
index 0000000..f32330a
--- /dev/null
+++ b/libcutils/trace-dev.inc
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2017 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 __TRACE_DEV_INC
+#define __TRACE_DEV_INC
+
+#define LOG_TAG "cutils-trace"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <pthread.h>
+#include <stdatomic.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include <cutils/compiler.h>
+#include <cutils/properties.h>
+#include <cutils/trace.h>
+#include <log/log.h>
+#include <log/log_properties.h>
+
+/**
+ * Maximum size of a message that can be logged to the trace buffer.
+ * Note this message includes a tag, the pid, and the string given as the name.
+ * Names should be kept short to get the most use of the trace buffer.
+ */
+#define ATRACE_MESSAGE_LENGTH 1024
+
+atomic_bool atrace_is_ready = ATOMIC_VAR_INIT(false);
+int atrace_marker_fd = -1;
+uint64_t atrace_enabled_tags = ATRACE_TAG_NOT_READY;
+static bool atrace_is_debuggable = false;
+static atomic_bool atrace_is_enabled = ATOMIC_VAR_INIT(true);
+static pthread_mutex_t atrace_tags_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+// Set whether this process is debuggable, which determines whether
+// application-level tracing is allowed when the ro.debuggable system property
+// is not set to '1'.
+void atrace_set_debuggable(bool debuggable)
+{
+ atrace_is_debuggable = debuggable;
+ atrace_update_tags();
+}
+
+// Check whether the given command line matches one of the comma-separated
+// values listed in the app_cmdlines property.
+static bool atrace_is_cmdline_match(const char* cmdline)
+{
+ int count = property_get_int32("debug.atrace.app_number", 0);
+
+ char buf[PROPERTY_KEY_MAX];
+ char value[PROPERTY_VALUE_MAX];
+
+ for (int i = 0; i < count; i++) {
+ snprintf(buf, sizeof(buf), "debug.atrace.app_%d", i);
+ property_get(buf, value, "");
+ if (strcmp(value, cmdline) == 0) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// Determine whether application-level tracing is enabled for this process.
+static bool atrace_is_app_tracing_enabled()
+{
+ bool sys_debuggable = __android_log_is_debuggable();
+ bool result = false;
+
+ if (sys_debuggable || atrace_is_debuggable) {
+ // Check whether tracing is enabled for this process.
+ FILE * file = fopen("/proc/self/cmdline", "re");
+ if (file) {
+ char cmdline[4096];
+ if (fgets(cmdline, sizeof(cmdline), file)) {
+ result = atrace_is_cmdline_match(cmdline);
+ } else {
+ ALOGE("Error reading cmdline: %s (%d)", strerror(errno), errno);
+ }
+ fclose(file);
+ } else {
+ ALOGE("Error opening /proc/self/cmdline: %s (%d)", strerror(errno),
+ errno);
+ }
+ }
+
+ return result;
+}
+
+// Read the sysprop and return the value tags should be set to
+static uint64_t atrace_get_property()
+{
+ char value[PROPERTY_VALUE_MAX];
+ char *endptr;
+ uint64_t tags;
+
+ property_get("debug.atrace.tags.enableflags", value, "0");
+ errno = 0;
+ tags = strtoull(value, &endptr, 0);
+ if (value[0] == '\0' || *endptr != '\0') {
+ ALOGE("Error parsing trace property: Not a number: %s", value);
+ return 0;
+ } else if (errno == ERANGE || tags == ULLONG_MAX) {
+ ALOGE("Error parsing trace property: Number too large: %s", value);
+ return 0;
+ }
+
+ // Only set the "app" tag if this process was selected for app-level debug
+ // tracing.
+ if (atrace_is_app_tracing_enabled()) {
+ tags |= ATRACE_TAG_APP;
+ } else {
+ tags &= ~ATRACE_TAG_APP;
+ }
+
+ return (tags | ATRACE_TAG_ALWAYS) & ATRACE_TAG_VALID_MASK;
+}
+
+// Update tags if tracing is ready. Useful as a sysprop change callback.
+void atrace_update_tags()
+{
+ uint64_t tags;
+ if (CC_UNLIKELY(atomic_load_explicit(&atrace_is_ready, memory_order_acquire))) {
+ if (atomic_load_explicit(&atrace_is_enabled, memory_order_acquire)) {
+ tags = atrace_get_property();
+ pthread_mutex_lock(&atrace_tags_mutex);
+ atrace_enabled_tags = tags;
+ pthread_mutex_unlock(&atrace_tags_mutex);
+ } else {
+ // Tracing is disabled for this process, so we simply don't
+ // initialize the tags.
+ pthread_mutex_lock(&atrace_tags_mutex);
+ atrace_enabled_tags = ATRACE_TAG_NOT_READY;
+ pthread_mutex_unlock(&atrace_tags_mutex);
+ }
+ }
+}
+
+#define WRITE_MSG(format_begin, format_end, name, value) { \
+ char buf[ATRACE_MESSAGE_LENGTH]; \
+ int pid = getpid(); \
+ int len = snprintf(buf, sizeof(buf), format_begin "%s" format_end, pid, \
+ name, value); \
+ if (len >= (int) sizeof(buf)) { \
+ /* Given the sizeof(buf), and all of the current format buffers, \
+ * it is impossible for name_len to be < 0 if len >= sizeof(buf). */ \
+ int name_len = strlen(name) - (len - sizeof(buf)) - 1; \
+ /* Truncate the name to make the message fit. */ \
+ ALOGW("Truncated name in %s: %s\n", __FUNCTION__, name); \
+ len = snprintf(buf, sizeof(buf), format_begin "%.*s" format_end, pid, \
+ name_len, name, value); \
+ } \
+ write(atrace_marker_fd, buf, len); \
+}
+
+#endif // __TRACE_DEV_INC
diff --git a/libgrallocusage/Android.bp b/libgrallocusage/Android.bp
new file mode 100644
index 0000000..54bfee5
--- /dev/null
+++ b/libgrallocusage/Android.bp
@@ -0,0 +1,29 @@
+// Copyright 2017 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.
+
+cc_library_static {
+ name: "libgrallocusage",
+ cppflags: [
+ "-Weverything",
+ "-Werror",
+ "-Wno-c++98-compat-pedantic",
+ // Hide errors in headers we include
+ "-Wno-global-constructors",
+ "-Wno-exit-time-destructors",
+ "-Wno-padded",
+ ],
+ srcs: ["GrallocUsageConversion.cpp"],
+ export_include_dirs: ["include"],
+ shared_libs: ["android.hardware.graphics.allocator@2.0"],
+}
diff --git a/libgrallocusage/GrallocUsageConversion.cpp b/libgrallocusage/GrallocUsageConversion.cpp
new file mode 100644
index 0000000..05c8ec4
--- /dev/null
+++ b/libgrallocusage/GrallocUsageConversion.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2017 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 <grallocusage/GrallocUsageConversion.h>
+
+#include <hardware/gralloc.h>
+#include <hardware/gralloc1.h>
+
+void android_convertGralloc0To1Usage(int32_t usage, uint64_t* producerUsage,
+ uint64_t* consumerUsage) {
+ constexpr uint64_t PRODUCER_MASK =
+ GRALLOC1_PRODUCER_USAGE_CPU_READ |
+ /* GRALLOC1_PRODUCER_USAGE_CPU_READ_OFTEN | */
+ GRALLOC1_PRODUCER_USAGE_CPU_WRITE |
+ /* GRALLOC1_PRODUCER_USAGE_CPU_WRITE_OFTEN | */
+ GRALLOC1_PRODUCER_USAGE_GPU_RENDER_TARGET | GRALLOC1_PRODUCER_USAGE_PROTECTED |
+ GRALLOC1_PRODUCER_USAGE_CAMERA | GRALLOC1_PRODUCER_USAGE_VIDEO_DECODER |
+ GRALLOC1_PRODUCER_USAGE_SENSOR_DIRECT_DATA;
+ constexpr uint64_t CONSUMER_MASK =
+ GRALLOC1_CONSUMER_USAGE_CPU_READ |
+ /* GRALLOC1_CONSUMER_USAGE_CPU_READ_OFTEN | */
+ GRALLOC1_CONSUMER_USAGE_GPU_TEXTURE | GRALLOC1_CONSUMER_USAGE_HWCOMPOSER |
+ GRALLOC1_CONSUMER_USAGE_CLIENT_TARGET | GRALLOC1_CONSUMER_USAGE_CURSOR |
+ GRALLOC1_CONSUMER_USAGE_VIDEO_ENCODER | GRALLOC1_CONSUMER_USAGE_CAMERA |
+ GRALLOC1_CONSUMER_USAGE_RENDERSCRIPT | GRALLOC1_CONSUMER_USAGE_GPU_DATA_BUFFER;
+ *producerUsage = static_cast<uint64_t>(usage) & PRODUCER_MASK;
+ *consumerUsage = static_cast<uint64_t>(usage) & CONSUMER_MASK;
+ if ((static_cast<uint32_t>(usage) & GRALLOC_USAGE_SW_READ_OFTEN) == GRALLOC_USAGE_SW_READ_OFTEN) {
+ *producerUsage |= GRALLOC1_PRODUCER_USAGE_CPU_READ_OFTEN;
+ *consumerUsage |= GRALLOC1_CONSUMER_USAGE_CPU_READ_OFTEN;
+ }
+ if ((static_cast<uint32_t>(usage) & GRALLOC_USAGE_SW_WRITE_OFTEN) ==
+ GRALLOC_USAGE_SW_WRITE_OFTEN) {
+ *producerUsage |= GRALLOC1_PRODUCER_USAGE_CPU_WRITE_OFTEN;
+ }
+}
+
+int32_t android_convertGralloc1To0Usage(uint64_t producerUsage, uint64_t consumerUsage) {
+ static_assert(uint64_t(GRALLOC1_CONSUMER_USAGE_CPU_READ_OFTEN) ==
+ uint64_t(GRALLOC1_PRODUCER_USAGE_CPU_READ_OFTEN),
+ "expected ConsumerUsage and ProducerUsage CPU_READ_OFTEN bits to match");
+ uint64_t merged = producerUsage | consumerUsage;
+ if ((merged & (GRALLOC1_CONSUMER_USAGE_CPU_READ_OFTEN)) ==
+ GRALLOC1_CONSUMER_USAGE_CPU_READ_OFTEN) {
+ merged &= ~uint64_t(GRALLOC1_CONSUMER_USAGE_CPU_READ_OFTEN);
+ merged |= GRALLOC_USAGE_SW_READ_OFTEN;
+ }
+ if ((merged & (GRALLOC1_PRODUCER_USAGE_CPU_WRITE_OFTEN)) ==
+ GRALLOC1_PRODUCER_USAGE_CPU_WRITE_OFTEN) {
+ merged &= ~uint64_t(GRALLOC1_PRODUCER_USAGE_CPU_WRITE_OFTEN);
+ merged |= GRALLOC_USAGE_SW_WRITE_OFTEN;
+ }
+ return static_cast<int32_t>(merged);
+}
diff --git a/libgrallocusage/MODULE_LICENSE_APACHE2 b/libgrallocusage/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libgrallocusage/MODULE_LICENSE_APACHE2
diff --git a/libgrallocusage/NOTICE b/libgrallocusage/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/libgrallocusage/NOTICE
@@ -0,0 +1,190 @@
+
+ Copyright (c) 2005-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.
+
+ 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.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
diff --git a/libgrallocusage/include/grallocusage/GrallocUsageConversion.h b/libgrallocusage/include/grallocusage/GrallocUsageConversion.h
new file mode 100644
index 0000000..5c94343
--- /dev/null
+++ b/libgrallocusage/include/grallocusage/GrallocUsageConversion.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2017 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_GRALLOCUSAGE_GRALLOC_USAGE_CONVERSION_H
+#define ANDROID_GRALLOCUSAGE_GRALLOC_USAGE_CONVERSION_H 1
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Conversion functions are out-of-line so that users don't have to be exposed to
+// android/hardware/graphics/allocator/2.0/types.h and link against
+// android.hardware.graphics.allocator@2.0 to get that in their search path.
+
+// Convert a 32-bit gralloc0 usage mask to a producer/consumer pair of 64-bit usage masks as used
+// by android.hardware.graphics.allocator@2.0 (and gralloc1). This conversion properly handles the
+// mismatch between a.h.g.allocator@2.0's CPU_{READ,WRITE}_OFTEN and gralloc0's
+// SW_{READ,WRITE}_OFTEN.
+void android_convertGralloc0To1Usage(int32_t usage, uint64_t* producerUsage,
+ uint64_t* consumerUsage);
+
+// Convert a producer/consumer pair of 64-bit usage masks as used by
+// android.hardware.graphics.allocator@2.0 (and gralloc1) to a 32-bit gralloc0 usage mask. This
+// conversion properly handles the mismatch between a.h.g.allocator@2.0's CPU_{READ,WRITE}_OFTEN
+// and gralloc0's SW_{READ,WRITE}_OFTEN.
+int32_t android_convertGralloc1To0Usage(uint64_t producerUsage, uint64_t consumerUsage);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // ANDROID_GRALLOCUSAGE_GRALLOC_USAGE_CONVERSION_H
diff --git a/libion/Android.bp b/libion/Android.bp
index 6f267e4..6d9fae0 100644
--- a/libion/Android.bp
+++ b/libion/Android.bp
@@ -1,7 +1,11 @@
cc_library {
name: "libion",
- vendor_available: true,
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ support_system_process: true,
+ },
srcs: ["ion.c"],
shared_libs: ["liblog"],
local_include_dirs: [
diff --git a/libion/tests/Android.bp b/libion/tests/Android.bp
index 4428848..b3fcb3b 100644
--- a/libion/tests/Android.bp
+++ b/libion/tests/Android.bp
@@ -16,7 +16,6 @@
cc_test {
name: "ion-unit-tests",
- clang: true,
cflags: [
"-g",
"-Wall",
diff --git a/liblog/Android.bp b/liblog/Android.bp
index b98d18f..e74aa82 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -42,24 +42,6 @@
"logd_writer.c",
]
-cc_library_headers {
- name: "liblog_headers",
- host_supported: true,
- vendor_available: true,
- export_include_dirs: ["include"],
- target: {
- windows: {
- enabled: true,
- },
- linux_bionic: {
- enabled: true,
- },
- vendor: {
- export_include_dirs: ["include_vndk"],
- },
- },
-}
-
// Shared and static library for host and device
// ========================================================
cc_library {
@@ -99,8 +81,7 @@
},
},
- header_libs: ["liblog_headers"],
- export_header_lib_headers: ["liblog_headers"],
+ export_include_dirs: ["include"],
cflags: [
"-Werror",
@@ -119,7 +100,7 @@
}
ndk_headers {
- name: "liblog_ndk_headers",
+ name: "liblog_headers",
from: "include/android",
to: "android",
srcs: ["include/android/log.h"],
diff --git a/liblog/include/log/log_main.h b/liblog/include/log/log_main.h
index da16158..5a3f04c 100644
--- a/liblog/include/log/log_main.h
+++ b/liblog/include/log/log_main.h
@@ -355,11 +355,11 @@
#if LOG_NDEBUG /* Production */
#define android_testLog(prio, tag) \
- (__android_log_is_loggable_len(prio, tag, (tag && *tag) ? strlen(tag) : 0, \
+ (__android_log_is_loggable_len(prio, tag, ((tag) && *(tag)) ? strlen(tag) : 0, \
ANDROID_LOG_DEBUG) != 0)
#else
#define android_testLog(prio, tag) \
- (__android_log_is_loggable_len(prio, tag, (tag && *tag) ? strlen(tag) : 0, \
+ (__android_log_is_loggable_len(prio, tag, ((tag) && *(tag)) ? strlen(tag) : 0, \
ANDROID_LOG_VERBOSE) != 0)
#endif
diff --git a/liblog/logd_reader.c b/liblog/logd_reader.c
index 600f4bb..603ba24 100644
--- a/liblog/logd_reader.c
+++ b/liblog/logd_reader.c
@@ -590,20 +590,30 @@
memset(log_msg, 0, sizeof(*log_msg));
+ unsigned int new_alarm = 0;
if (logger_list->mode & ANDROID_LOG_NONBLOCK) {
+ if ((logger_list->mode & ANDROID_LOG_WRAP) &&
+ (logger_list->start.tv_sec || logger_list->start.tv_nsec)) {
+ /* b/64143705 */
+ new_alarm = (ANDROID_LOG_WRAP_DEFAULT_TIMEOUT * 11) / 10 + 10;
+ logger_list->mode &= ~ANDROID_LOG_WRAP;
+ } else {
+ new_alarm = 30;
+ }
+
memset(&ignore, 0, sizeof(ignore));
ignore.sa_handler = caught_signal;
sigemptyset(&ignore.sa_mask);
/* particularily useful if tombstone is reporting for logd */
sigaction(SIGALRM, &ignore, &old_sigaction);
- old_alarm = alarm(30);
+ old_alarm = alarm(new_alarm);
}
/* NOTE: SOCK_SEQPACKET guarantees we read exactly one full entry */
ret = recv(ret, log_msg, LOGGER_ENTRY_MAX_LEN, 0);
e = errno;
- if (logger_list->mode & ANDROID_LOG_NONBLOCK) {
+ if (new_alarm) {
if ((ret == 0) || (e == EINTR)) {
e = EAGAIN;
ret = -1;
diff --git a/liblog/tests/Android.mk b/liblog/tests/Android.mk
index ab96429..275a2d6 100644
--- a/liblog/tests/Android.mk
+++ b/liblog/tests/Android.mk
@@ -64,7 +64,8 @@
log_radio_test.cpp \
log_read_test.cpp \
log_system_test.cpp \
- log_time_test.cpp
+ log_time_test.cpp \
+ log_wrap_test.cpp
# Build tests for the device (with .so). Run with:
# adb shell /data/nativetest/liblog-unit-tests/liblog-unit-tests
diff --git a/liblog/tests/AndroidTest.xml b/liblog/tests/AndroidTest.xml
index b8d87e6..427f2b4 100644
--- a/liblog/tests/AndroidTest.xml
+++ b/liblog/tests/AndroidTest.xml
@@ -14,6 +14,7 @@
limitations under the License.
-->
<configuration description="Config for CTS Logging Library test cases">
+ <option name="config-descriptor:metadata" key="component" value="systems" />
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
<option name="cleanup" value="true" />
<option name="push" value="CtsLiblogTestCases->/data/local/tmp/CtsLiblogTestCases" />
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 46ec5ef..e2d5aeb 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -105,7 +105,7 @@
}
#if (defined(__ANDROID__) && defined(USING_LOGGER_DEFAULT))
-static std::string popenToString(std::string command) {
+static std::string popenToString(const std::string& command) {
std::string ret;
FILE* fp = popen(command.c_str(), "r");
@@ -129,17 +129,17 @@
static bool isLogdwActive() {
std::string logdwSignature =
popenToString("grep /dev/socket/logdw /proc/net/unix");
- size_t beginning = logdwSignature.find(" ");
+ size_t beginning = logdwSignature.find(' ');
if (beginning == std::string::npos) return true;
- beginning = logdwSignature.find(" ", beginning + 1);
+ beginning = logdwSignature.find(' ', beginning + 1);
if (beginning == std::string::npos) return true;
- size_t end = logdwSignature.find(" ", beginning + 1);
+ size_t end = logdwSignature.find(' ', beginning + 1);
if (end == std::string::npos) return true;
- end = logdwSignature.find(" ", end + 1);
+ end = logdwSignature.find(' ', end + 1);
if (end == std::string::npos) return true;
- end = logdwSignature.find(" ", end + 1);
+ end = logdwSignature.find(' ', end + 1);
if (end == std::string::npos) return true;
- end = logdwSignature.find(" ", end + 1);
+ end = logdwSignature.find(' ', end + 1);
if (end == std::string::npos) return true;
std::string allLogdwEndpoints = popenToString(
"grep ' 00000002" + logdwSignature.substr(beginning, end - beginning) +
@@ -159,7 +159,7 @@
// NB: fgrep with multiple strings is broken in Android
for (beginning = 0;
- (end = allLogdwEndpoints.find("\n", beginning)) != std::string::npos;
+ (end = allLogdwEndpoints.find('\n', beginning)) != std::string::npos;
beginning = end + 1) {
if (myPidFds.find(allLogdwEndpoints.substr(beginning, end - beginning)) !=
std::string::npos)
@@ -3170,7 +3170,7 @@
return (offset != std::string::npos) &&
((offset = content.find_first_not_of(" \t", offset + strlen(needle))) !=
std::string::npos) &&
- (content.find_first_not_of("0", offset) != offset);
+ (content.find_first_not_of('0', offset) != offset);
}
// must not be: '<needle:> 0 kB'
@@ -3239,7 +3239,7 @@
filename = android::base::StringPrintf("/proc/%d/comm", pid);
android::base::ReadFileToString(filename, &content);
content = android::base::StringPrintf(
- "%d:%s", pid, content.substr(0, content.find("\n")).c_str());
+ "%d:%s", pid, content.substr(0, content.find('\n')).c_str());
EXPECT_TRUE(IsOk(shared_ok, content));
EXPECT_TRUE(IsOk(private_ok, content));
diff --git a/liblog/tests/log_id_test.cpp b/liblog/tests/log_id_test.cpp
index c56fa8b..9fb5a2c 100644
--- a/liblog/tests/log_id_test.cpp
+++ b/liblog/tests/log_id_test.cpp
@@ -89,12 +89,12 @@
ASSERT_EQ(0, pthread_create(&t[i], NULL, ConcurrentPrintFn,
reinterpret_cast<void*>(i)));
}
- int ret = 0;
+ int ret = 1;
for (i = 0; i < NUM_CONCURRENT; i++) {
void* result;
ASSERT_EQ(0, pthread_join(t[i], &result));
int this_result = reinterpret_cast<uintptr_t>(result);
- if ((0 == ret) && (0 != this_result)) {
+ if ((0 < ret) && (ret != this_result)) {
ret = this_result;
}
}
diff --git a/liblog/tests/log_wrap_test.cpp b/liblog/tests/log_wrap_test.cpp
new file mode 100644
index 0000000..ebf0b15
--- /dev/null
+++ b/liblog/tests/log_wrap_test.cpp
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2013-2017 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 <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/chrono_utils.h>
+#include <android-base/stringprintf.h>
+#include <android/log.h> // minimal logging API
+#include <gtest/gtest.h>
+#include <log/log_properties.h>
+#include <log/log_read.h>
+#include <log/log_time.h>
+#include <log/log_transport.h>
+
+#ifdef __ANDROID__
+static void read_with_wrap() {
+ android_set_log_transport(LOGGER_LOGD);
+
+ // Read the last line in the log to get a starting timestamp. We're assuming
+ // the log is not empty.
+ const int mode = ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK;
+ struct logger_list* logger_list =
+ android_logger_list_open(LOG_ID_MAIN, mode, 1000, 0);
+
+ ASSERT_NE(logger_list, nullptr);
+
+ log_msg log_msg;
+ int ret = android_logger_list_read(logger_list, &log_msg);
+ android_logger_list_close(logger_list);
+ ASSERT_GT(ret, 0);
+
+ log_time start(log_msg.entry.sec, log_msg.entry.nsec);
+ ASSERT_NE(start, log_time());
+
+ logger_list =
+ android_logger_list_alloc_time(mode | ANDROID_LOG_WRAP, start, 0);
+ ASSERT_NE(logger_list, nullptr);
+
+ struct logger* logger = android_logger_open(logger_list, LOG_ID_MAIN);
+ EXPECT_NE(logger, nullptr);
+ if (logger) {
+ android_logger_list_read(logger_list, &log_msg);
+ }
+
+ android_logger_list_close(logger_list);
+}
+
+static void caught_signal(int /* signum */) {
+}
+#endif
+
+// b/64143705 confirm fixed
+TEST(liblog, wrap_mode_blocks) {
+#ifdef __ANDROID__
+
+ android::base::Timer timer;
+
+ // The read call is expected to take up to 2 hours in the happy case.
+ // We only want to make sure it waits for longer than 30s, but we can't
+ // use an alarm as the implementation uses it. So we run the test in
+ // a separate process.
+ pid_t pid = fork();
+
+ if (pid == 0) {
+ // child
+ read_with_wrap();
+ _exit(0);
+ }
+
+ struct sigaction ignore, old_sigaction;
+ memset(&ignore, 0, sizeof(ignore));
+ ignore.sa_handler = caught_signal;
+ sigemptyset(&ignore.sa_mask);
+ sigaction(SIGALRM, &ignore, &old_sigaction);
+ alarm(45);
+
+ bool killed = false;
+ for (;;) {
+ siginfo_t info = {};
+ // This wait will succeed if the child exits, or fail with EINTR if the
+ // alarm goes off first - a loose approximation to a timed wait.
+ int ret = waitid(P_PID, pid, &info, WEXITED);
+ if (ret >= 0 || errno != EINTR) {
+ EXPECT_EQ(ret, 0);
+ if (!killed) {
+ EXPECT_EQ(info.si_status, 0);
+ }
+ break;
+ }
+ unsigned int alarm_left = alarm(0);
+ if (alarm_left > 0) {
+ alarm(alarm_left);
+ } else {
+ kill(pid, SIGTERM);
+ killed = true;
+ }
+ }
+
+ alarm(0);
+ EXPECT_GT(timer.duration(), std::chrono::seconds(40));
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
diff --git a/libmemtrack/Android.bp b/libmemtrack/Android.bp
index 98413dd..68c580a 100644
--- a/libmemtrack/Android.bp
+++ b/libmemtrack/Android.bp
@@ -2,13 +2,19 @@
cc_library_shared {
name: "libmemtrack",
- srcs: ["memtrack.c"],
+ srcs: ["memtrack.cpp"],
export_include_dirs: ["include"],
local_include_dirs: ["include"],
include_dirs: ["hardware/libhardware/include"],
shared_libs: [
"libhardware",
"liblog",
+ "libbase",
+ "libhidlbase",
+ "libhidltransport",
+ "libhwbinder",
+ "libutils",
+ "android.hardware.memtrack@1.0",
],
cflags: [
"-Wall",
diff --git a/libmemtrack/include/memtrack/memtrack.h b/libmemtrack/include/memtrack/memtrack.h
index 8c0ab89..2134a6f 100644
--- a/libmemtrack/include/memtrack/memtrack.h
+++ b/libmemtrack/include/memtrack/memtrack.h
@@ -35,16 +35,6 @@
struct memtrack_proc;
/**
- * memtrack_init
- *
- * Must be called once before calling any other functions. After this function
- * is called, everything else is thread-safe.
- *
- * Returns 0 on success, -errno on error.
- */
-int memtrack_init(void);
-
-/**
* memtrack_proc_new
*
* Return a new handle to hold process memory stats.
diff --git a/libmemtrack/memtrack.c b/libmemtrack/memtrack.c
deleted file mode 100644
index 9ed9451..0000000
--- a/libmemtrack/memtrack.c
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * Copyright (C) 2013 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 "memtrack"
-
-#include <memtrack/memtrack.h>
-
-#include <errno.h>
-#include <malloc.h>
-#include <string.h>
-
-#include <hardware/memtrack.h>
-#include <log/log.h>
-
-#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0]))
-
-static const memtrack_module_t *module;
-
-struct memtrack_proc {
- pid_t pid;
- struct memtrack_proc_type {
- enum memtrack_type type;
- size_t num_records;
- size_t allocated_records;
- struct memtrack_record *records;
- } types[MEMTRACK_NUM_TYPES];
-};
-
-int memtrack_init(void)
-{
- int err;
-
- if (module) {
- return 0;
- }
-
- err = hw_get_module(MEMTRACK_HARDWARE_MODULE_ID,
- (hw_module_t const**)&module);
- if (err) {
- ALOGE("Couldn't load %s module (%s)", MEMTRACK_HARDWARE_MODULE_ID,
- strerror(-err));
- return err;
- }
-
- return module->init(module);
-}
-
-struct memtrack_proc *memtrack_proc_new(void)
-{
- if (!module) {
- return NULL;
- }
-
- return calloc(sizeof(struct memtrack_proc), 1);
-}
-
-void memtrack_proc_destroy(struct memtrack_proc *p)
-{
- enum memtrack_type i;
-
- if (p) {
- for (i = 0; i < MEMTRACK_NUM_TYPES; i++) {
- free(p->types[i].records);
- }
- }
- free(p);
-}
-
-static int memtrack_proc_get_type(struct memtrack_proc_type *t,
- pid_t pid, enum memtrack_type type)
-{
- size_t num_records = t->num_records;
- int ret;
-
-retry:
- ret = module->getMemory(module, pid, type, t->records, &num_records);
- if (ret) {
- t->num_records = 0;
- return ret;
- }
- if (num_records > t->allocated_records) {
- /* Need more records than allocated */
- free(t->records);
- t->records = calloc(sizeof(*t->records), num_records);
- if (!t->records) {
- return -ENOMEM;
- }
- t->allocated_records = num_records;
- goto retry;
- }
- t->num_records = num_records;
-
- return 0;
-}
-
-/* TODO: sanity checks on return values from HALs:
- * make sure no records have invalid flags set
- * - unknown flags
- * - too many flags of a single category
- * - missing ACCOUNTED/UNACCOUNTED
- * make sure there are not overlapping SHARED and SHARED_PSS records
- */
-static int memtrack_proc_sanity_check(struct memtrack_proc *p)
-{
- (void)p;
- return 0;
-}
-
-int memtrack_proc_get(struct memtrack_proc *p, pid_t pid)
-{
- enum memtrack_type i;
-
- if (!module) {
- return -EINVAL;
- }
-
- if (!p) {
- return -EINVAL;
- }
-
- p->pid = pid;
- for (i = 0; i < MEMTRACK_NUM_TYPES; i++) {
- memtrack_proc_get_type(&p->types[i], pid, i);
- }
-
- return memtrack_proc_sanity_check(p);
-}
-
-static ssize_t memtrack_proc_sum(struct memtrack_proc *p,
- enum memtrack_type types[], size_t num_types,
- unsigned int flags)
-{
- ssize_t sum = 0;
- size_t i;
- size_t j;
-
- for (i = 0; i < num_types; i++) {
- enum memtrack_type type = types[i];
- for (j = 0; j < p->types[type].num_records; j++) {
- if ((p->types[type].records[j].flags & flags) == flags) {
- sum += p->types[type].records[j].size_in_bytes;
- }
- }
- }
-
- return sum;
-}
-
-ssize_t memtrack_proc_graphics_total(struct memtrack_proc *p)
-{
- enum memtrack_type types[] = { MEMTRACK_TYPE_GRAPHICS };
- return memtrack_proc_sum(p, types, ARRAY_SIZE(types), 0);
-}
-
-ssize_t memtrack_proc_graphics_pss(struct memtrack_proc *p)
-{
- enum memtrack_type types[] = { MEMTRACK_TYPE_GRAPHICS };
- return memtrack_proc_sum(p, types, ARRAY_SIZE(types),
- MEMTRACK_FLAG_SMAPS_UNACCOUNTED);
-}
-
-ssize_t memtrack_proc_gl_total(struct memtrack_proc *p)
-{
- enum memtrack_type types[] = { MEMTRACK_TYPE_GL };
- return memtrack_proc_sum(p, types, ARRAY_SIZE(types), 0);
-}
-
-ssize_t memtrack_proc_gl_pss(struct memtrack_proc *p)
-{
- enum memtrack_type types[] = { MEMTRACK_TYPE_GL };
- return memtrack_proc_sum(p, types, ARRAY_SIZE(types),
- MEMTRACK_FLAG_SMAPS_UNACCOUNTED);
-}
-
-ssize_t memtrack_proc_other_total(struct memtrack_proc *p)
-{
- enum memtrack_type types[] = { MEMTRACK_TYPE_MULTIMEDIA,
- MEMTRACK_TYPE_CAMERA,
- MEMTRACK_TYPE_OTHER };
- return memtrack_proc_sum(p, types, ARRAY_SIZE(types), 0);
-}
-
-ssize_t memtrack_proc_other_pss(struct memtrack_proc *p)
-{
- enum memtrack_type types[] = { MEMTRACK_TYPE_MULTIMEDIA,
- MEMTRACK_TYPE_CAMERA,
- MEMTRACK_TYPE_OTHER };
- return memtrack_proc_sum(p, types, ARRAY_SIZE(types),
- MEMTRACK_FLAG_SMAPS_UNACCOUNTED);
-}
diff --git a/libmemtrack/memtrack.cpp b/libmemtrack/memtrack.cpp
new file mode 100644
index 0000000..c5e74c1
--- /dev/null
+++ b/libmemtrack/memtrack.cpp
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2013 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 "memtrack"
+#include <android/hardware/memtrack/1.0/IMemtrack.h>
+#include <memtrack/memtrack.h>
+
+#include <errno.h>
+#include <malloc.h>
+#include <vector>
+#include <string.h>
+#include <mutex>
+
+#include <log/log.h>
+
+using android::hardware::memtrack::V1_0::IMemtrack;
+using android::hardware::memtrack::V1_0::MemtrackType;
+using android::hardware::memtrack::V1_0::MemtrackRecord;
+using android::hardware::memtrack::V1_0::MemtrackFlag;
+using android::hardware::memtrack::V1_0::MemtrackStatus;
+using android::hardware::hidl_vec;
+using android::hardware::Return;
+
+struct memtrack_proc_type {
+ MemtrackType type;
+ std::vector<MemtrackRecord> records;
+};
+
+struct memtrack_proc {
+ pid_t pid;
+ memtrack_proc_type types[static_cast<int>(MemtrackType::NUM_TYPES)];
+};
+
+//TODO(b/31632518)
+static android::sp<IMemtrack> get_instance() {
+ static android::sp<IMemtrack> module = IMemtrack::getService();
+ if (module == nullptr) {
+ ALOGE("Couldn't load memtrack module");
+ }
+ return module;
+}
+
+memtrack_proc *memtrack_proc_new(void)
+{
+ return new memtrack_proc();
+}
+
+void memtrack_proc_destroy(memtrack_proc *p)
+{
+ delete(p);
+}
+
+static int memtrack_proc_get_type(memtrack_proc_type *t,
+ pid_t pid, MemtrackType type)
+{
+ int err = 0;
+ android::sp<IMemtrack> memtrack = get_instance();
+ if (memtrack == nullptr)
+ return -1;
+
+ Return<void> ret = memtrack->getMemory(pid, type,
+ [&t, &err](MemtrackStatus status, hidl_vec<MemtrackRecord> records) {
+ if (status != MemtrackStatus::SUCCESS) {
+ err = -1;
+ t->records.resize(0);
+ }
+ t->records.resize(records.size());
+ for (size_t i = 0; i < records.size(); i++) {
+ t->records[i].sizeInBytes = records[i].sizeInBytes;
+ t->records[i].flags = records[i].flags;
+ }
+ });
+ return ret.isOk() ? err : -1;
+}
+
+/* TODO: sanity checks on return values from HALs:
+ * make sure no records have invalid flags set
+ * - unknown flags
+ * - too many flags of a single category
+ * - missing ACCOUNTED/UNACCOUNTED
+ * make sure there are not overlapping SHARED and SHARED_PSS records
+ */
+static int memtrack_proc_sanity_check(memtrack_proc* /*p*/)
+{
+ return 0;
+}
+
+int memtrack_proc_get(memtrack_proc *p, pid_t pid)
+{
+ if (!p) {
+ return -EINVAL;
+ }
+
+ p->pid = pid;
+ for (uint32_t i = 0; i < (uint32_t)MemtrackType::NUM_TYPES; i++) {
+ int ret = memtrack_proc_get_type(&p->types[i], pid, (MemtrackType)i);
+ if (ret != 0)
+ return ret;
+ }
+
+ return memtrack_proc_sanity_check(p);
+}
+
+static ssize_t memtrack_proc_sum(memtrack_proc *p,
+ const std::vector<MemtrackType>& types, uint32_t flags)
+{
+ ssize_t sum = 0;
+
+ for (size_t i = 0; i < types.size(); i++) {
+ memtrack_proc_type type = p->types[static_cast<int>(types[i])];
+ std::vector<MemtrackRecord> records = type.records;
+ for (size_t j = 0; j < records.size(); j++) {
+ if ((records[j].flags & flags) == flags) {
+ sum += records[j].sizeInBytes;
+ }
+ }
+ }
+
+ return sum;
+}
+
+ssize_t memtrack_proc_graphics_total(memtrack_proc *p)
+{
+ std::vector<MemtrackType> types = {MemtrackType::GRAPHICS};
+ return memtrack_proc_sum(p, types, 0);
+}
+
+ssize_t memtrack_proc_graphics_pss(memtrack_proc *p)
+{
+ std::vector<MemtrackType> types = { MemtrackType::GRAPHICS };
+ return memtrack_proc_sum(p, types,
+ (uint32_t)MemtrackFlag::SMAPS_UNACCOUNTED);
+}
+
+ssize_t memtrack_proc_gl_total(memtrack_proc *p)
+{
+ std::vector<MemtrackType> types = { MemtrackType::GL };
+ return memtrack_proc_sum(p, types, 0);
+}
+
+ssize_t memtrack_proc_gl_pss(memtrack_proc *p)
+{
+ std::vector<MemtrackType> types = { MemtrackType::GL };
+ return memtrack_proc_sum(p, types,
+ (uint32_t)MemtrackFlag::SMAPS_UNACCOUNTED);
+}
+
+ssize_t memtrack_proc_other_total(memtrack_proc *p)
+{
+ std::vector<MemtrackType> types = { MemtrackType::MULTIMEDIA,
+ MemtrackType::CAMERA, MemtrackType::OTHER };
+ return memtrack_proc_sum(p, types, 0);
+}
+
+ssize_t memtrack_proc_other_pss(memtrack_proc *p)
+{
+ std::vector<MemtrackType> types = { MemtrackType::MULTIMEDIA,
+ MemtrackType::CAMERA, MemtrackType::OTHER };
+ return memtrack_proc_sum(p, types,
+ (uint32_t)MemtrackFlag::SMAPS_UNACCOUNTED);
+}
diff --git a/libmemtrack/memtrack_test.c b/libmemtrack/memtrack_test.c
index eaadfa7..77c935e 100644
--- a/libmemtrack/memtrack_test.c
+++ b/libmemtrack/memtrack_test.c
@@ -82,12 +82,6 @@
(void)argc;
(void)argv;
- ret = memtrack_init();
- if (ret < 0) {
- fprintf(stderr, "failed to initialize HAL: %s (%d)\n", strerror(-ret), ret);
- exit(EXIT_FAILURE);
- }
-
ret = pm_kernel_create(&ker);
if (ret) {
fprintf(stderr, "Error creating kernel interface -- "
diff --git a/libmemunreachable/Android.bp b/libmemunreachable/Android.bp
index 826a576..8b76a65 100644
--- a/libmemunreachable/Android.bp
+++ b/libmemunreachable/Android.bp
@@ -6,7 +6,6 @@
"-Wextra",
"-Werror",
],
- clang: true,
shared_libs: [
"libbase",
],
diff --git a/libmemunreachable/MemUnreachable.cpp b/libmemunreachable/MemUnreachable.cpp
index 5e062fd..24fdc7f 100644
--- a/libmemunreachable/MemUnreachable.cpp
+++ b/libmemunreachable/MemUnreachable.cpp
@@ -479,23 +479,6 @@
return oss.str();
}
-// Figure out the abi based on defined macros.
-#if defined(__arm__)
-#define ABI_STRING "arm"
-#elif defined(__aarch64__)
-#define ABI_STRING "arm64"
-#elif defined(__mips__) && !defined(__LP64__)
-#define ABI_STRING "mips"
-#elif defined(__mips__) && defined(__LP64__)
-#define ABI_STRING "mips64"
-#elif defined(__i386__)
-#define ABI_STRING "x86"
-#elif defined(__x86_64__)
-#define ABI_STRING "x86_64"
-#else
-#error "Unsupported ABI"
-#endif
-
std::string UnreachableMemoryInfo::ToString(bool log_contents) const {
std::ostringstream oss;
oss << " " << leak_bytes << " bytes in ";
diff --git a/libmetricslogger/Android.bp b/libmetricslogger/Android.bp
index 26a041a..c692d1f 100644
--- a/libmetricslogger/Android.bp
+++ b/libmetricslogger/Android.bp
@@ -7,7 +7,6 @@
cc_defaults {
name: "metricslogger_defaults",
- clang: true,
host_supported: true,
export_include_dirs: ["include"],
diff --git a/libnativebridge/Android.bp b/libnativebridge/Android.bp
index 1cea4cd..b3c42f0 100644
--- a/libnativebridge/Android.bp
+++ b/libnativebridge/Android.bp
@@ -11,8 +11,7 @@
host_supported: true,
srcs: ["native_bridge.cc"],
- shared_libs: ["liblog"],
- clang: true,
+ shared_libs: ["liblog", "libbase"],
export_include_dirs=["include"],
diff --git a/libnativebridge/native_bridge.cc b/libnativebridge/native_bridge.cc
index 02b4fe7..e24307a 100644
--- a/libnativebridge/native_bridge.cc
+++ b/libnativebridge/native_bridge.cc
@@ -28,6 +28,7 @@
#include <cstring>
+#include <android-base/macros.h>
#include <log/log.h>
namespace android {
@@ -243,29 +244,12 @@
}
}
-#if defined(__arm__)
-static const char* kRuntimeISA = "arm";
-#elif defined(__aarch64__)
-static const char* kRuntimeISA = "arm64";
-#elif defined(__mips__) && !defined(__LP64__)
-static const char* kRuntimeISA = "mips";
-#elif defined(__mips__) && defined(__LP64__)
-static const char* kRuntimeISA = "mips64";
-#elif defined(__i386__)
-static const char* kRuntimeISA = "x86";
-#elif defined(__x86_64__)
-static const char* kRuntimeISA = "x86_64";
-#else
-static const char* kRuntimeISA = "unknown";
-#endif
-
-
bool NeedsNativeBridge(const char* instruction_set) {
if (instruction_set == nullptr) {
ALOGE("Null instruction set in NeedsNativeBridge.");
return false;
}
- return strncmp(instruction_set, kRuntimeISA, strlen(kRuntimeISA) + 1) != 0;
+ return strncmp(instruction_set, ABI_STRING, strlen(ABI_STRING) + 1) != 0;
}
#ifdef __APPLE__
diff --git a/libnativebridge/tests/Android.mk b/libnativebridge/tests/Android.mk
index 70b3fcc..b3861e0 100644
--- a/libnativebridge/tests/Android.mk
+++ b/libnativebridge/tests/Android.mk
@@ -29,12 +29,12 @@
shared_libraries := \
liblog \
+ libbase \
libnativebridge \
libnativebridge-dummy
$(foreach file,$(test_src_files), \
$(eval include $(CLEAR_VARS)) \
- $(eval LOCAL_CLANG := true) \
$(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \
$(eval LOCAL_SRC_FILES := $(file)) \
$(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
@@ -43,7 +43,6 @@
$(foreach file,$(test_src_files), \
$(eval include $(CLEAR_VARS)) \
- $(eval LOCAL_CLANG := true) \
$(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \
$(eval LOCAL_SRC_FILES := $(file)) \
$(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
diff --git a/libnativebridge/tests/NeedsNativeBridge_test.cpp b/libnativebridge/tests/NeedsNativeBridge_test.cpp
index 2067ed2..c8ff743 100644
--- a/libnativebridge/tests/NeedsNativeBridge_test.cpp
+++ b/libnativebridge/tests/NeedsNativeBridge_test.cpp
@@ -16,34 +16,20 @@
#include "NativeBridgeTest.h"
+#include <android-base/macros.h>
+
namespace android {
static const char* kISAs[] = { "arm", "arm64", "mips", "mips64", "x86", "x86_64", "random", "64arm",
"64_x86", "64_x86_64", "", "reallylongstringabcd", nullptr };
-#if defined(__arm__)
-static const char* kRuntimeISA = "arm";
-#elif defined(__aarch64__)
-static const char* kRuntimeISA = "arm64";
-#elif defined(__mips__) && !defined(__LP64__)
-static const char* kRuntimeISA = "mips";
-#elif defined(__mips__) && defined(__LP64__)
-static const char* kRuntimeISA = "mips64";
-#elif defined(__i386__)
-static const char* kRuntimeISA = "x86";
-#elif defined(__x86_64__)
-static const char* kRuntimeISA = "x86_64";
-#else
-static const char* kRuntimeISA = "unknown";
-#endif
-
TEST_F(NativeBridgeTest, NeedsNativeBridge) {
- EXPECT_EQ(false, NeedsNativeBridge(kRuntimeISA));
+ EXPECT_EQ(false, NeedsNativeBridge(ABI_STRING));
- const size_t kISACount = sizeof(kISAs)/sizeof(kISAs[0]);
- for (size_t i = 0; i < kISACount; i++) {
- EXPECT_EQ(kISAs[i] == nullptr ? false : strcmp(kISAs[i], kRuntimeISA) != 0,
- NeedsNativeBridge(kISAs[i]));
+ const size_t kISACount = sizeof(kISAs) / sizeof(kISAs[0]);
+ for (size_t i = 0; i < kISACount; i++) {
+ EXPECT_EQ(kISAs[i] == nullptr ? false : strcmp(kISAs[i], ABI_STRING) != 0,
+ NeedsNativeBridge(kISAs[i]));
}
}
diff --git a/libnativeloader/Android.bp b/libnativeloader/Android.bp
index c1133fb..13f9744 100644
--- a/libnativeloader/Android.bp
+++ b/libnativeloader/Android.bp
@@ -19,7 +19,6 @@
host_ldlibs: ["-ldl"],
},
},
- clang: true,
cflags: [
"-Werror",
"-Wall",
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index 36a2e44..7ccd7db 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -15,7 +15,7 @@
*/
#include "nativeloader/native_loader.h"
-#include "ScopedUtfChars.h"
+#include <nativehelper/ScopedUtfChars.h>
#include <dlfcn.h>
#ifdef __ANDROID__
diff --git a/libpackagelistparser/Android.bp b/libpackagelistparser/Android.bp
index 70ff528..a9fec7d 100644
--- a/libpackagelistparser/Android.bp
+++ b/libpackagelistparser/Android.bp
@@ -6,7 +6,6 @@
local_include_dirs: ["include"],
export_include_dirs: ["include"],
- clang: true,
sanitize: {
misc_undefined: ["integer"],
},
diff --git a/libprocessgroup/include/processgroup/processgroup.h b/libprocessgroup/include/processgroup/processgroup.h
index f0c3795..9fa4154 100644
--- a/libprocessgroup/include/processgroup/processgroup.h
+++ b/libprocessgroup/include/processgroup/processgroup.h
@@ -33,6 +33,10 @@
int createProcessGroup(uid_t uid, int initialPid);
+bool setProcessGroupSwappiness(uid_t uid, int initialPid, int swappiness);
+bool setProcessGroupSoftLimit(uid_t uid, int initialPid, int64_t softLimitInBytes);
+bool setProcessGroupLimit(uid_t uid, int initialPid, int64_t limitInBytes);
+
void removeAllProcessGroups(void);
__END_DECLS
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 9b8248e..8526b3a 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -35,12 +35,15 @@
#include <set>
#include <thread>
+#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/unique_fd.h>
#include <private/android_filesystem_config.h>
#include <processgroup/processgroup.h>
+using android::base::WriteStringToFile;
+
using namespace std::chrono_literals;
#define MEM_CGROUP_PATH "/dev/memcg/apps"
@@ -402,22 +405,40 @@
strlcat(path, PROCESSGROUP_CGROUP_PROCS_FILE, sizeof(path));
- int fd = open(path, O_WRONLY);
- if (fd == -1) {
- int ret = -errno;
- PLOG(ERROR) << "Failed to open " << path;
- return ret;
- }
-
- char pid[PROCESSGROUP_MAX_PID_LEN + 1] = {0};
- int len = snprintf(pid, sizeof(pid), "%d", initialPid);
-
int ret = 0;
- if (write(fd, pid, len) < 0) {
+ if (!WriteStringToFile(std::to_string(initialPid), path)) {
ret = -errno;
- PLOG(ERROR) << "Failed to write '" << pid << "' to " << path;
+ PLOG(ERROR) << "Failed to write '" << initialPid << "' to " << path;
}
- close(fd);
return ret;
}
+
+static bool setProcessGroupValue(uid_t uid, int pid, const char* fileName, int64_t value) {
+ char path[PROCESSGROUP_MAX_PATH_LEN] = {0};
+ if (strcmp(getCgroupRootPath(), MEM_CGROUP_PATH)) {
+ PLOG(ERROR) << "Memcg is not mounted." << path;
+ return false;
+ }
+
+ convertUidPidToPath(path, sizeof(path), uid, pid);
+ strlcat(path, fileName, sizeof(path));
+
+ if (!WriteStringToFile(std::to_string(value), path)) {
+ PLOG(ERROR) << "Failed to write '" << value << "' to " << path;
+ return false;
+ }
+ return true;
+}
+
+bool setProcessGroupSwappiness(uid_t uid, int pid, int swappiness) {
+ return setProcessGroupValue(uid, pid, "/memory.swappiness", swappiness);
+}
+
+bool setProcessGroupSoftLimit(uid_t uid, int pid, int64_t soft_limit_in_bytes) {
+ return setProcessGroupValue(uid, pid, "/memory.soft_limit_in_bytes", soft_limit_in_bytes);
+}
+
+bool setProcessGroupLimit(uid_t uid, int pid, int64_t limit_in_bytes) {
+ return setProcessGroupValue(uid, pid, "/memory.limit_in_bytes", limit_in_bytes);
+}
diff --git a/libsparse/simg_dump.py b/libsparse/simg_dump.py
index c70d45f..82a03ad 100755
--- a/libsparse/simg_dump.py
+++ b/libsparse/simg_dump.py
@@ -15,43 +15,64 @@
# limitations under the License.
from __future__ import print_function
-import getopt, posixpath, signal, struct, sys
+import csv
+import getopt
+import hashlib
+import posixpath
+import signal
+import struct
+import sys
+
def usage(argv0):
print("""
-Usage: %s [-v] sparse_image_file ...
+Usage: %s [-v] [-s] [-c <filename>] sparse_image_file ...
-v verbose output
-""" % ( argv0 ))
+ -s show sha1sum of data blocks
+ -c <filename> save .csv file of blocks
+""" % (argv0))
sys.exit(2)
-def main():
+def main():
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
me = posixpath.basename(sys.argv[0])
# Parse the command line
- verbose = 0 # -v
+ verbose = 0 # -v
+ showhash = 0 # -s
+ csvfilename = None # -c
try:
opts, args = getopt.getopt(sys.argv[1:],
- "v",
- ["verbose"])
+ "vsc:",
+ ["verbose", "showhash", "csvfile"])
except getopt.GetoptError, e:
print(e)
usage(me)
for o, a in opts:
if o in ("-v", "--verbose"):
verbose += 1
+ elif o in ("-s", "--showhash"):
+ showhash = True
+ elif o in ("-c", "--csvfile"):
+ csvfilename = a
else:
print("Unrecognized option \"%s\"" % (o))
usage(me)
- if len(args) == 0:
+ if not args:
print("No sparse_image_file specified")
usage(me)
+ if csvfilename:
+ csvfile = open(csvfilename, "wb")
+ csvwriter = csv.writer(csvfile)
+
+ output = verbose or csvfilename or showhash
+
for path in args:
- FH = open(path, 'rb')
+ FH = open(path, "rb")
header_bin = FH.read(28)
header = struct.unpack("<I4H4I", header_bin)
@@ -88,71 +109,99 @@
if image_checksum != 0:
print("checksum=0x%08X" % (image_checksum))
- if not verbose:
+ if not output:
continue
- print(" input_bytes output_blocks")
- print("chunk offset number offset number")
+
+ if verbose > 0:
+ print(" input_bytes output_blocks")
+ print("chunk offset number offset number")
+
+ if csvfilename:
+ csvwriter.writerow(["chunk", "input offset", "input bytes",
+ "output offset", "output blocks", "type", "hash"])
+
offset = 0
- for i in xrange(1,total_chunks+1):
+ for i in xrange(1, total_chunks + 1):
header_bin = FH.read(12)
header = struct.unpack("<2H2I", header_bin)
chunk_type = header[0]
- reserved1 = header[1]
chunk_sz = header[2]
total_sz = header[3]
data_sz = total_sz - 12
+ curhash = ""
+ curtype = ""
+ curpos = FH.tell()
- print("%4u %10u %10u %7u %7u" % (i, FH.tell(), data_sz, offset, chunk_sz),
- end=" ")
+ if verbose > 0:
+ print("%4u %10u %10u %7u %7u" % (i, curpos, data_sz, offset, chunk_sz),
+ end=" ")
if chunk_type == 0xCAC1:
if data_sz != (chunk_sz * blk_sz):
print("Raw chunk input size (%u) does not match output size (%u)"
% (data_sz, chunk_sz * blk_sz))
- break;
+ break
else:
- print("Raw data", end="")
- FH.read(data_sz)
+ curtype = "Raw data"
+ data = FH.read(data_sz)
+ if showhash:
+ h = hashlib.sha1()
+ h.update(data)
+ curhash = h.hexdigest()
elif chunk_type == 0xCAC2:
if data_sz != 4:
print("Fill chunk should have 4 bytes of fill, but this has %u"
- % (data_sz), end="")
- break;
+ % (data_sz))
+ break
else:
fill_bin = FH.read(4)
fill = struct.unpack("<I", fill_bin)
- print("Fill with 0x%08X" % (fill))
+ curtype = format("Fill with 0x%08X" % (fill))
+ if showhash:
+ h = hashlib.sha1()
+ data = fill_bin * (blk_sz / 4);
+ for block in xrange(chunk_sz):
+ h.update(data)
+ curhash = h.hexdigest()
elif chunk_type == 0xCAC3:
if data_sz != 0:
print("Don't care chunk input size is non-zero (%u)" % (data_sz))
- break;
+ break
else:
- print("Don't care", end="")
+ curtype = "Don't care"
elif chunk_type == 0xCAC4:
if data_sz != 4:
print("CRC32 chunk should have 4 bytes of CRC, but this has %u"
- % (data_sz), end="")
- break;
+ % (data_sz))
+ break
else:
crc_bin = FH.read(4)
crc = struct.unpack("<I", crc_bin)
- print("Unverified CRC32 0x%08X" % (crc))
+ curtype = format("Unverified CRC32 0x%08X" % (crc))
else:
- print("Unknown chunk type 0x%04X" % (chunk_type), end="")
- break;
+ print("Unknown chunk type 0x%04X" % (chunk_type))
+ break
- if verbose > 1:
- header = struct.unpack("<12B", header_bin)
- print(" (%02X%02X %02X%02X %02X%02X%02X%02X %02X%02X%02X%02X)"
- % (header[0], header[1], header[2], header[3],
- header[4], header[5], header[6], header[7],
- header[8], header[9], header[10], header[11]))
- else:
- print()
+ if verbose > 0:
+ print("%-18s" % (curtype), end=" ")
+
+ if verbose > 1:
+ header = struct.unpack("<12B", header_bin)
+ print(" (%02X%02X %02X%02X %02X%02X%02X%02X %02X%02X%02X%02X)"
+ % (header[0], header[1], header[2], header[3],
+ header[4], header[5], header[6], header[7],
+ header[8], header[9], header[10], header[11]), end=" ")
+
+ print(curhash)
+
+ if csvfilename:
+ csvwriter.writerow([i, curpos, data_sz, offset, chunk_sz, curtype,
+ curhash])
offset += chunk_sz
- print(" %10u %7u End" % (FH.tell(), offset))
+ if verbose > 0:
+ print(" %10u %7u End" % (FH.tell(), offset))
if total_blks != offset:
print("The header said we should have %u output blocks, but we saw %u"
@@ -163,6 +212,9 @@
print("There were %u bytes of extra data at the end of the file."
% (junk_len))
+ if csvfilename:
+ csvfile.close()
+
sys.exit(0)
if __name__ == "__main__":
diff --git a/libsuspend/Android.bp b/libsuspend/Android.bp
index 7883151..130800e 100644
--- a/libsuspend/Android.bp
+++ b/libsuspend/Android.bp
@@ -6,8 +6,6 @@
srcs: [
"autosuspend.c",
- "autosuspend_autosleep.c",
- "autosuspend_earlysuspend.c",
"autosuspend_wakeup_count.c",
],
export_include_dirs: ["include"],
diff --git a/libsuspend/autosuspend.c b/libsuspend/autosuspend.c
index 54730c2..96e1c10 100644
--- a/libsuspend/autosuspend.c
+++ b/libsuspend/autosuspend.c
@@ -34,19 +34,6 @@
return 0;
}
- autosuspend_ops = autosuspend_earlysuspend_init();
- if (autosuspend_ops) {
- goto out;
- }
-
-/* Remove autosleep so userspace can manager suspend/resume and keep stats */
-#if 0
- autosuspend_ops = autosuspend_autosleep_init();
- if (autosuspend_ops) {
- goto out;
- }
-#endif
-
autosuspend_ops = autosuspend_wakeup_count_init();
if (autosuspend_ops) {
goto out;
diff --git a/libsuspend/autosuspend_autosleep.c b/libsuspend/autosuspend_autosleep.c
deleted file mode 100644
index 77d8db0..0000000
--- a/libsuspend/autosuspend_autosleep.c
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2012 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 "libsuspend"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <stddef.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <log/log.h>
-
-#include "autosuspend_ops.h"
-
-#define SYS_POWER_AUTOSLEEP "/sys/power/autosleep"
-
-static int autosleep_fd;
-static const char *sleep_state = "mem";
-static const char *on_state = "off";
-
-static int autosuspend_autosleep_enable(void)
-{
- char buf[80];
- int ret;
-
- ALOGV("autosuspend_autosleep_enable\n");
-
- 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);
- goto err;
- }
-
- ALOGV("autosuspend_autosleep_enable done\n");
-
- return 0;
-
-err:
- return ret;
-}
-
-static int autosuspend_autosleep_disable(void)
-{
- char buf[80];
- int ret;
-
- ALOGV("autosuspend_autosleep_disable\n");
-
- 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);
- goto err;
- }
-
- ALOGV("autosuspend_autosleep_disable done\n");
-
- return 0;
-
-err:
- return ret;
-}
-
-struct autosuspend_ops autosuspend_autosleep_ops = {
- .enable = autosuspend_autosleep_enable,
- .disable = autosuspend_autosleep_disable,
-};
-
-struct autosuspend_ops *autosuspend_autosleep_init(void)
-{
- char buf[80];
-
- 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);
- return NULL;
- }
-
- ALOGI("Selected autosleep\n");
-
- autosuspend_autosleep_disable();
-
- return &autosuspend_autosleep_ops;
-}
diff --git a/libsuspend/autosuspend_earlysuspend.c b/libsuspend/autosuspend_earlysuspend.c
deleted file mode 100644
index 809ee82..0000000
--- a/libsuspend/autosuspend_earlysuspend.c
+++ /dev/null
@@ -1,219 +0,0 @@
-/*
- * Copyright (C) 2012 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 "libsuspend"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <pthread.h>
-#include <stdbool.h>
-#include <stddef.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <log/log.h>
-
-#include "autosuspend_ops.h"
-
-#define EARLYSUSPEND_SYS_POWER_STATE "/sys/power/state"
-#define EARLYSUSPEND_WAIT_FOR_FB_SLEEP "/sys/power/wait_for_fb_sleep"
-#define EARLYSUSPEND_WAIT_FOR_FB_WAKE "/sys/power/wait_for_fb_wake"
-
-static int sPowerStatefd;
-static const char *pwr_state_mem = "mem";
-static const char *pwr_state_on = "on";
-static pthread_t earlysuspend_thread;
-static pthread_mutex_t earlysuspend_mutex = PTHREAD_MUTEX_INITIALIZER;
-static pthread_cond_t earlysuspend_cond = PTHREAD_COND_INITIALIZER;
-static bool wait_for_earlysuspend;
-static enum {
- EARLYSUSPEND_ON,
- EARLYSUSPEND_MEM,
-} earlysuspend_state = EARLYSUSPEND_ON;
-
-int wait_for_fb_wake(void)
-{
- int err = 0;
- char buf;
- 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
- err = TEMP_FAILURE_RETRY(read(fd, &buf, 1));
- ALOGE_IF(err < 0,
- "*** ANDROID_WAIT_FOR_FB_WAKE failed (%s)", strerror(errno));
- close(fd);
- return err < 0 ? err : 0;
-}
-
-static int wait_for_fb_sleep(void)
-{
- int err = 0;
- char buf;
- 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
- err = TEMP_FAILURE_RETRY(read(fd, &buf, 1));
- ALOGE_IF(err < 0,
- "*** ANDROID_WAIT_FOR_FB_SLEEP failed (%s)", strerror(errno));
- close(fd);
- return err < 0 ? err : 0;
-}
-
-static void *earlysuspend_thread_func(void __unused *arg)
-{
- while (1) {
- if (wait_for_fb_sleep()) {
- ALOGE("Failed reading wait_for_fb_sleep, exiting earlysuspend thread\n");
- return NULL;
- }
- pthread_mutex_lock(&earlysuspend_mutex);
- earlysuspend_state = EARLYSUSPEND_MEM;
- pthread_cond_signal(&earlysuspend_cond);
- pthread_mutex_unlock(&earlysuspend_mutex);
-
- if (wait_for_fb_wake()) {
- ALOGE("Failed reading wait_for_fb_wake, exiting earlysuspend thread\n");
- return NULL;
- }
- pthread_mutex_lock(&earlysuspend_mutex);
- earlysuspend_state = EARLYSUSPEND_ON;
- pthread_cond_signal(&earlysuspend_cond);
- pthread_mutex_unlock(&earlysuspend_mutex);
- }
-}
-static int autosuspend_earlysuspend_enable(void)
-{
- char buf[80];
- int ret;
-
- ALOGV("autosuspend_earlysuspend_enable\n");
-
- ret = write(sPowerStatefd, pwr_state_mem, strlen(pwr_state_mem));
- if (ret < 0) {
- strerror_r(errno, buf, sizeof(buf));
- ALOGE("Error writing to %s: %s\n", EARLYSUSPEND_SYS_POWER_STATE, buf);
- goto err;
- }
-
- if (wait_for_earlysuspend) {
- pthread_mutex_lock(&earlysuspend_mutex);
- while (earlysuspend_state != EARLYSUSPEND_MEM) {
- pthread_cond_wait(&earlysuspend_cond, &earlysuspend_mutex);
- }
- pthread_mutex_unlock(&earlysuspend_mutex);
- }
-
- ALOGV("autosuspend_earlysuspend_enable done\n");
-
- return 0;
-
-err:
- return ret;
-}
-
-static int autosuspend_earlysuspend_disable(void)
-{
- char buf[80];
- int ret;
-
- ALOGV("autosuspend_earlysuspend_disable\n");
-
- 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);
- goto err;
- }
-
- if (wait_for_earlysuspend) {
- pthread_mutex_lock(&earlysuspend_mutex);
- while (earlysuspend_state != EARLYSUSPEND_ON) {
- pthread_cond_wait(&earlysuspend_cond, &earlysuspend_mutex);
- }
- pthread_mutex_unlock(&earlysuspend_mutex);
- }
-
- ALOGV("autosuspend_earlysuspend_disable done\n");
-
- return 0;
-
-err:
- return ret;
-}
-
-struct autosuspend_ops autosuspend_earlysuspend_ops = {
- .enable = autosuspend_earlysuspend_enable,
- .disable = autosuspend_earlysuspend_disable,
-};
-
-void start_earlysuspend_thread(void)
-{
- char buf[80];
- int ret;
-
- ret = access(EARLYSUSPEND_WAIT_FOR_FB_SLEEP, F_OK);
- if (ret < 0) {
- return;
- }
-
- ret = access(EARLYSUSPEND_WAIT_FOR_FB_WAKE, F_OK);
- if (ret < 0) {
- return;
- }
-
- wait_for_fb_wake();
-
- ALOGI("Starting early suspend unblocker thread\n");
- ret = pthread_create(&earlysuspend_thread, NULL, earlysuspend_thread_func, NULL);
- if (ret) {
- strerror_r(errno, buf, sizeof(buf));
- ALOGE("Error creating thread: %s\n", buf);
- return;
- }
-
- wait_for_earlysuspend = true;
-}
-
-struct autosuspend_ops *autosuspend_earlysuspend_init(void)
-{
- char buf[80];
- int ret;
-
- sPowerStatefd = TEMP_FAILURE_RETRY(open(EARLYSUSPEND_SYS_POWER_STATE, O_RDWR));
-
- if (sPowerStatefd < 0) {
- strerror_r(errno, buf, sizeof(buf));
- ALOGW("Error opening %s: %s\n", EARLYSUSPEND_SYS_POWER_STATE, buf);
- return NULL;
- }
-
- 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);
- goto err_write;
- }
-
- ALOGI("Selected early suspend\n");
-
- start_earlysuspend_thread();
-
- return &autosuspend_earlysuspend_ops;
-
-err_write:
- close(sPowerStatefd);
- return NULL;
-}
diff --git a/libsync/Android.bp b/libsync/Android.bp
index 4bafb08..1646348 100644
--- a/libsync/Android.bp
+++ b/libsync/Android.bp
@@ -1,3 +1,17 @@
+ndk_headers {
+ name: "libsync_headers",
+ from: "include/ndk",
+ to: "android",
+ srcs: ["include/ndk/sync.h"],
+ license: "NOTICE",
+}
+
+ndk_library {
+ name: "libsync",
+ symbol_file: "libsync.map.txt",
+ first_version: "26",
+}
+
cc_defaults {
name: "libsync_defaults",
srcs: ["sync.c"],
@@ -38,5 +52,4 @@
"-Wno-missing-field-initializers",
"-Wno-sign-compare",
],
- clang: true,
}
diff --git a/libsync/NOTICE b/libsync/NOTICE
new file mode 100644
index 0000000..2c8db73
--- /dev/null
+++ b/libsync/NOTICE
@@ -0,0 +1,190 @@
+
+ Copyright (c) 2012-2017, 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.
+
+ 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.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
diff --git a/libsync/include/android/sync.h b/libsync/include/android/sync.h
new file mode 100644
index 0000000..68f74a0
--- /dev/null
+++ b/libsync/include/android/sync.h
@@ -0,0 +1,69 @@
+/*
+ * sync.h
+ *
+ * Copyright 2012 Google, Inc
+ *
+ * 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 __SYS_CORE_SYNC_H
+#define __SYS_CORE_SYNC_H
+
+/* This file contains the legacy sync interface used by Android platform and
+ * device code. The direct contents will be removed over time as code
+ * transitions to using the updated interface in ndk/sync.h. When this file is
+ * empty other than the ndk/sync.h include, that file will be renamed to
+ * replace this one.
+ *
+ * New code should continue to include this file (#include <android/sync.h>)
+ * instead of ndk/sync.h so the eventual rename is seamless, but should only
+ * use the things declared in ndk/sync.h.
+ *
+ * This file used to be called sync/sync.h, but we renamed to that both the
+ * platform and NDK call it android/sync.h. A symlink from the old name to this
+ * one exists temporarily to avoid having to change all sync clients
+ * simultaneously. It will be removed when they've been updated, and probably
+ * after this change has been delivered to AOSP so that integrations don't
+ * break builds.
+ */
+
+#include "../ndk/sync.h"
+
+__BEGIN_DECLS
+
+struct sync_fence_info_data {
+ uint32_t len;
+ char name[32];
+ int32_t status;
+ uint8_t pt_info[0];
+};
+
+struct sync_pt_info {
+ uint32_t len;
+ char obj_name[32];
+ char driver_name[32];
+ int32_t status;
+ uint64_t timestamp_ns;
+ uint8_t driver_data[0];
+};
+
+/* timeout in msecs */
+int sync_wait(int fd, int timeout);
+struct sync_fence_info_data *sync_fence_info(int fd);
+struct sync_pt_info *sync_pt_info(struct sync_fence_info_data *info,
+ struct sync_pt_info *itr);
+void sync_fence_info_free(struct sync_fence_info_data *info);
+
+__END_DECLS
+
+#endif /* __SYS_CORE_SYNC_H */
diff --git a/libsync/include/ndk/sync.h b/libsync/include/ndk/sync.h
new file mode 100644
index 0000000..3c55783
--- /dev/null
+++ b/libsync/include/ndk/sync.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2017 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_SYNC_H
+#define ANDROID_SYNC_H
+
+#include <stdint.h>
+
+#include <linux/sync_file.h>
+
+__BEGIN_DECLS
+
+#if __ANDROID_API__ >= __ANDROID_API_O__
+
+/* Fences indicate the status of an asynchronous task. They are initially
+ * in unsignaled state (0), and make a one-time transition to either signaled
+ * (1) or error (< 0) state. A sync file is a collection of one or more fences;
+ * the sync file's status is error if any of its fences are in error state,
+ * signaled if all of the child fences are signaled, or unsignaled otherwise.
+ *
+ * Sync files are created by various device APIs in response to submitting
+ * tasks to the device. Standard file descriptor lifetime syscalls like dup()
+ * and close() are used to manage sync file lifetime.
+ *
+ * The poll(), ppoll(), or select() syscalls can be used to wait for the sync
+ * file to change status, or (with a timeout of zero) to check its status.
+ *
+ * The functions below provide a few additional sync-specific operations.
+ */
+
+/**
+ * Merge two sync files.
+ *
+ * This produces a new sync file with the given name which has the union of the
+ * two original sync file's fences; redundant fences may be removed.
+ *
+ * If one of the input sync files is signaled or invalid, then this function
+ * may behave like dup(): the new file descriptor refers to the valid/unsignaled
+ * sync file with its original name, rather than a new sync file.
+ *
+ * The original fences remain valid, and the caller is responsible for closing
+ * them.
+ */
+int32_t sync_merge(const char *name, int32_t fd1, int32_t fd2);
+
+/**
+ * Retrieve detailed information about a sync file and its fences.
+ *
+ * The returned sync_file_info must be freed by calling sync_file_info_free().
+ */
+struct sync_file_info *sync_file_info(int32_t fd);
+
+/**
+ * Get the array of fence infos from the sync file's info.
+ *
+ * The returned array is owned by the parent sync file info, and has
+ * info->num_fences entries.
+ */
+static inline struct sync_fence_info* sync_get_fence_info(const struct sync_file_info* info) {
+// This header should compile in C, but some C++ projects enable
+// warnings-as-error for C-style casts.
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wold-style-cast"
+ return (struct sync_fence_info *)(uintptr_t)(info->sync_fence_info);
+#pragma GCC diagnostic pop
+}
+
+/** Free a struct sync_file_info structure */
+void sync_file_info_free(struct sync_file_info *info);
+
+#endif // __ANDROID_API__ >= __ANDROID_API_O__
+
+__END_DECLS
+
+#endif /* ANDROID_SYNC_H */
diff --git a/libsync/include/sync/sync.h b/libsync/include/sync/sync.h
deleted file mode 100644
index 50ed0ac..0000000
--- a/libsync/include/sync/sync.h
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- * sync.h
- *
- * Copyright 2012 Google, Inc
- *
- * 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 __SYS_CORE_SYNC_H
-#define __SYS_CORE_SYNC_H
-
-#include <sys/cdefs.h>
-#include <stdint.h>
-
-#include <linux/types.h>
-
-__BEGIN_DECLS
-
-struct sync_legacy_merge_data {
- int32_t fd2;
- char name[32];
- int32_t fence;
-};
-
-struct sync_fence_info_data {
- uint32_t len;
- char name[32];
- int32_t status;
- uint8_t pt_info[0];
-};
-
-struct sync_pt_info {
- uint32_t len;
- char obj_name[32];
- char driver_name[32];
- int32_t status;
- uint64_t timestamp_ns;
- uint8_t driver_data[0];
-};
-
-#define SYNC_IOC_MAGIC '>'
-
-/**
- * DOC: SYNC_IOC_LEGACY_WAIT - wait for a fence to signal
- *
- * pass timeout in milliseconds. Waits indefinitely timeout < 0.
- *
- * This is the legacy version of the Sync API before the de-stage that happened
- * on Linux kernel 4.7.
- */
-#define SYNC_IOC_LEGACY_WAIT _IOW(SYNC_IOC_MAGIC, 0, __s32)
-
-/**
- * DOC: SYNC_IOC_MERGE - merge two fences
- *
- * Takes a struct sync_merge_data. Creates a new fence containing copies of
- * the sync_pts in both the calling fd and sync_merge_data.fd2. Returns the
- * new fence's fd in sync_merge_data.fence
- *
- * This is the legacy version of the Sync API before the de-stage that happened
- * on Linux kernel 4.7.
- */
-#define SYNC_IOC_LEGACY_MERGE _IOWR(SYNC_IOC_MAGIC, 1, \
- struct sync_legacy_merge_data)
-
-/**
- * DOC: SYNC_IOC_LEGACY_FENCE_INFO - get detailed information on a fence
- *
- * Takes a struct sync_fence_info_data with extra space allocated for pt_info.
- * Caller should write the size of the buffer into len. On return, len is
- * updated to reflect the total size of the sync_fence_info_data including
- * pt_info.
- *
- * pt_info is a buffer containing sync_pt_infos for every sync_pt in the fence.
- * To iterate over the sync_pt_infos, use the sync_pt_info.len field.
- *
- * This is the legacy version of the Sync API before the de-stage that happened
- * on Linux kernel 4.7.
- */
-#define SYNC_IOC_LEGACY_FENCE_INFO _IOWR(SYNC_IOC_MAGIC, 2,\
- struct sync_fence_info_data)
-
-struct sync_merge_data {
- char name[32];
- int32_t fd2;
- int32_t fence;
- uint32_t flags;
- uint32_t pad;
-};
-
-struct sync_file_info {
- char name[32];
- int32_t status;
- uint32_t flags;
- uint32_t num_fences;
- uint32_t pad;
-
- uint64_t sync_fence_info;
-};
-
-struct sync_fence_info {
- char obj_name[32];
- char driver_name[32];
- int32_t status;
- uint32_t flags;
- uint64_t timestamp_ns;
-};
-
-/**
- * Mainline API:
- *
- * Opcodes 0, 1 and 2 were burned during a API change to avoid users of the
- * old API to get weird errors when trying to handling sync_files. The API
- * change happened during the de-stage of the Sync Framework when there was
- * no upstream users available.
- */
-
-/**
- * DOC: SYNC_IOC_MERGE - merge two fences
- *
- * Takes a struct sync_merge_data. Creates a new fence containing copies of
- * the sync_pts in both the calling fd and sync_merge_data.fd2. Returns the
- * new fence's fd in sync_merge_data.fence
- *
- * This is the new version of the Sync API after the de-stage that happened
- * on Linux kernel 4.7.
- */
-#define SYNC_IOC_MERGE _IOWR(SYNC_IOC_MAGIC, 3, struct sync_merge_data)
-
-/**
- * DOC: SYNC_IOC_FILE_INFO - get detailed information on a sync_file
- *
- * Takes a struct sync_file_info. If num_fences is 0, the field is updated
- * with the actual number of fences. If num_fences is > 0, the system will
- * use the pointer provided on sync_fence_info to return up to num_fences of
- * struct sync_fence_info, with detailed fence information.
- *
- * This is the new version of the Sync API after the de-stage that happened
- * on Linux kernel 4.7.
- */
-#define SYNC_IOC_FILE_INFO _IOWR(SYNC_IOC_MAGIC, 4, struct sync_file_info)
-
-/* timeout in msecs */
-int sync_wait(int fd, int timeout);
-int sync_merge(const char *name, int fd1, int fd2);
-struct sync_fence_info_data *sync_fence_info(int fd);
-struct sync_pt_info *sync_pt_info(struct sync_fence_info_data *info,
- struct sync_pt_info *itr);
-void sync_fence_info_free(struct sync_fence_info_data *info);
-
-__END_DECLS
-
-#endif /* __SYS_CORE_SYNC_H */
diff --git a/libsync/include/sync/sync.h b/libsync/include/sync/sync.h
new file mode 120000
index 0000000..3b17e48
--- /dev/null
+++ b/libsync/include/sync/sync.h
@@ -0,0 +1 @@
+../android/sync.h
\ No newline at end of file
diff --git a/libsync/libsync.map.txt b/libsync/libsync.map.txt
new file mode 100644
index 0000000..daa28ae
--- /dev/null
+++ b/libsync/libsync.map.txt
@@ -0,0 +1,32 @@
+#
+# Copyright 2017 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.
+#
+
+LIBSYNC {
+ global:
+ sync_merge; # introduced=26
+ sync_get_fence_info; # introduced=26
+ sync_free_fence_info; # introduced=26
+ local:
+ *;
+};
+
+LIBSYNC_PLATFORM {
+ global:
+ sync_wait;
+ sync_fence_info;
+ sync_pt_info;
+ sync_fence_info_free;
+} LIBSYNC_PLATFORM;
diff --git a/libsync/sync.c b/libsync/sync.c
index 9ed03db..baeccda 100644
--- a/libsync/sync.c
+++ b/libsync/sync.c
@@ -16,19 +16,59 @@
* limitations under the License.
*/
+#include <errno.h>
#include <fcntl.h>
#include <malloc.h>
+#include <poll.h>
+#include <stdatomic.h>
#include <stdint.h>
#include <string.h>
-#include <errno.h>
-#include <poll.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
-#include <sync/sync.h>
+#include <android/sync.h>
+/* Legacy Sync API */
+
+struct sync_legacy_merge_data {
+ int32_t fd2;
+ char name[32];
+ int32_t fence;
+};
+
+/**
+ * DOC: SYNC_IOC_MERGE - merge two fences
+ *
+ * Takes a struct sync_merge_data. Creates a new fence containing copies of
+ * the sync_pts in both the calling fd and sync_merge_data.fd2. Returns the
+ * new fence's fd in sync_merge_data.fence
+ *
+ * This is the legacy version of the Sync API before the de-stage that happened
+ * on Linux kernel 4.7.
+ */
+#define SYNC_IOC_LEGACY_MERGE _IOWR(SYNC_IOC_MAGIC, 1, \
+ struct sync_legacy_merge_data)
+
+/**
+ * DOC: SYNC_IOC_LEGACY_FENCE_INFO - get detailed information on a fence
+ *
+ * Takes a struct sync_fence_info_data with extra space allocated for pt_info.
+ * Caller should write the size of the buffer into len. On return, len is
+ * updated to reflect the total size of the sync_fence_info_data including
+ * pt_info.
+ *
+ * pt_info is a buffer containing sync_pt_infos for every sync_pt in the fence.
+ * To iterate over the sync_pt_infos, use the sync_pt_info.len field.
+ *
+ * This is the legacy version of the Sync API before the de-stage that happened
+ * on Linux kernel 4.7.
+ */
+#define SYNC_IOC_LEGACY_FENCE_INFO _IOWR(SYNC_IOC_MAGIC, 2,\
+ struct sync_fence_info_data)
+
+/* SW Sync API */
struct sw_sync_create_fence_data {
__u32 value;
@@ -40,6 +80,24 @@
#define SW_SYNC_IOC_CREATE_FENCE _IOWR(SW_SYNC_IOC_MAGIC, 0, struct sw_sync_create_fence_data)
#define SW_SYNC_IOC_INC _IOW(SW_SYNC_IOC_MAGIC, 1, __u32)
+// ---------------------------------------------------------------------------
+// Support for caching the sync uapi version.
+//
+// This library supports both legacy (android/staging) uapi and modern
+// (mainline) sync uapi. Library calls first try one uapi, and if that fails,
+// try the other. Since any given kernel only supports one uapi version, after
+// the first successful syscall we know what the kernel supports and can skip
+// trying the other.
+
+enum uapi_version {
+ UAPI_UNKNOWN,
+ UAPI_MODERN,
+ UAPI_LEGACY
+};
+static atomic_int g_uapi_version = ATOMIC_VAR_INIT(UAPI_UNKNOWN);
+
+// ---------------------------------------------------------------------------
+
int sync_wait(int fd, int timeout)
{
struct pollfd fds;
@@ -70,9 +128,21 @@
return ret;
}
-int sync_merge(const char *name, int fd1, int fd2)
+static int legacy_sync_merge(const char *name, int fd1, int fd2)
{
- struct sync_legacy_merge_data legacy_data;
+ struct sync_legacy_merge_data data;
+ int ret;
+
+ data.fd2 = fd2;
+ strlcpy(data.name, name, sizeof(data.name));
+ ret = ioctl(fd1, SYNC_IOC_LEGACY_MERGE, &data);
+ if (ret < 0)
+ return ret;
+ return data.fence;
+}
+
+static int modern_sync_merge(const char *name, int fd1, int fd2)
+{
struct sync_merge_data data;
int ret;
@@ -82,29 +152,42 @@
data.pad = 0;
ret = ioctl(fd1, SYNC_IOC_MERGE, &data);
- if (ret < 0 && errno == ENOTTY) {
- legacy_data.fd2 = fd2;
- strlcpy(legacy_data.name, name, sizeof(legacy_data.name));
-
- ret = ioctl(fd1, SYNC_IOC_LEGACY_MERGE, &legacy_data);
- if (ret < 0)
- return ret;
-
- return legacy_data.fence;
- } else if (ret < 0) {
+ if (ret < 0)
return ret;
- }
-
return data.fence;
}
-struct sync_fence_info_data *sync_fence_info(int fd)
+int sync_merge(const char *name, int fd1, int fd2)
+{
+ int uapi;
+ int ret;
+
+ uapi = atomic_load_explicit(&g_uapi_version, memory_order_acquire);
+
+ if (uapi == UAPI_MODERN || uapi == UAPI_UNKNOWN) {
+ ret = modern_sync_merge(name, fd1, fd2);
+ if (ret >= 0 || errno != ENOTTY) {
+ if (ret >= 0 && uapi == UAPI_UNKNOWN) {
+ atomic_store_explicit(&g_uapi_version, UAPI_MODERN,
+ memory_order_release);
+ }
+ return ret;
+ }
+ }
+
+ ret = legacy_sync_merge(name, fd1, fd2);
+ if (ret >= 0 && uapi == UAPI_UNKNOWN) {
+ atomic_store_explicit(&g_uapi_version, UAPI_LEGACY,
+ memory_order_release);
+ }
+ return ret;
+}
+
+static struct sync_fence_info_data *legacy_sync_fence_info(int fd)
{
struct sync_fence_info_data *legacy_info;
struct sync_pt_info *legacy_pt_info;
- struct sync_file_info *info;
- struct sync_fence_info *fence_info;
- int err, num_fences, i;
+ int err;
legacy_info = malloc(4096);
if (legacy_info == NULL)
@@ -112,46 +195,57 @@
legacy_info->len = 4096;
err = ioctl(fd, SYNC_IOC_LEGACY_FENCE_INFO, legacy_info);
- if (err < 0 && errno != ENOTTY) {
+ if (err < 0) {
free(legacy_info);
return NULL;
- } else if (err == 0) {
- return legacy_info;
}
+ return legacy_info;
+}
- info = calloc(1, sizeof(*info));
- if (info == NULL)
- goto free;
+static struct sync_file_info *modern_sync_file_info(int fd)
+{
+ struct sync_file_info local_info;
+ struct sync_file_info *info;
+ int err;
+
+ memset(&local_info, 0, sizeof(local_info));
+ err = ioctl(fd, SYNC_IOC_FILE_INFO, &local_info);
+ if (err < 0)
+ return NULL;
+
+ info = calloc(1, sizeof(struct sync_file_info) +
+ local_info.num_fences * sizeof(struct sync_fence_info));
+ if (!info)
+ return NULL;
+ info->sync_fence_info = (__u64)(uintptr_t)(info + 1);
err = ioctl(fd, SYNC_IOC_FILE_INFO, info);
- if (err < 0)
- goto free;
-
- num_fences = info->num_fences;
-
- if (num_fences) {
- info->flags = 0;
- info->num_fences = num_fences;
- info->sync_fence_info = (uint64_t) calloc(num_fences,
- sizeof(struct sync_fence_info));
- if ((void *)info->sync_fence_info == NULL)
- goto free;
-
- err = ioctl(fd, SYNC_IOC_FILE_INFO, info);
- if (err < 0) {
- free((void *)info->sync_fence_info);
- goto free;
- }
+ if (err < 0) {
+ free(info);
+ return NULL;
}
+ return info;
+}
+
+static struct sync_fence_info_data *sync_file_info_to_legacy_fence_info(
+ const struct sync_file_info *info)
+{
+ struct sync_fence_info_data *legacy_info;
+ struct sync_pt_info *legacy_pt_info;
+ const struct sync_fence_info *fence_info = sync_get_fence_info(info);
+ const uint32_t num_fences = info->num_fences;
+
+ legacy_info = malloc(4096);
+ if (legacy_info == NULL)
+ return NULL;
legacy_info->len = sizeof(*legacy_info) +
- num_fences * sizeof(struct sync_fence_info);
+ num_fences * sizeof(struct sync_pt_info);
strlcpy(legacy_info->name, info->name, sizeof(legacy_info->name));
legacy_info->status = info->status;
legacy_pt_info = (struct sync_pt_info *)legacy_info->pt_info;
- fence_info = (struct sync_fence_info *)info->sync_fence_info;
- for (i = 0 ; i < num_fences ; i++) {
+ for (uint32_t i = 0; i < num_fences; i++) {
legacy_pt_info[i].len = sizeof(*legacy_pt_info);
strlcpy(legacy_pt_info[i].obj_name, fence_info[i].obj_name,
sizeof(legacy_pt_info->obj_name));
@@ -161,14 +255,109 @@
legacy_pt_info[i].timestamp_ns = fence_info[i].timestamp_ns;
}
- free((void *)info->sync_fence_info);
- free(info);
return legacy_info;
+}
-free:
- free(legacy_info);
- free(info);
- return NULL;
+static struct sync_file_info* legacy_fence_info_to_sync_file_info(
+ struct sync_fence_info_data *legacy_info)
+{
+ struct sync_file_info *info;
+ struct sync_pt_info *pt;
+ struct sync_fence_info *fence;
+ size_t num_fences;
+ int err;
+
+ pt = NULL;
+ num_fences = 0;
+ while ((pt = sync_pt_info(legacy_info, pt)) != NULL)
+ num_fences++;
+
+ info = calloc(1, sizeof(struct sync_file_info) +
+ num_fences * sizeof(struct sync_fence_info));
+ if (!info) {
+ free(legacy_info);
+ return NULL;
+ }
+ info->sync_fence_info = (__u64)(uintptr_t)(info + 1);
+
+ strlcpy(info->name, legacy_info->name, sizeof(info->name));
+ info->status = legacy_info->status;
+ info->num_fences = num_fences;
+
+ pt = NULL;
+ fence = sync_get_fence_info(info);
+ while ((pt = sync_pt_info(legacy_info, pt)) != NULL) {
+ strlcpy(fence->obj_name, pt->obj_name, sizeof(fence->obj_name));
+ strlcpy(fence->driver_name, pt->driver_name,
+ sizeof(fence->driver_name));
+ fence->status = pt->status;
+ fence->timestamp_ns = pt->timestamp_ns;
+ fence++;
+ }
+
+ return info;
+}
+
+struct sync_fence_info_data *sync_fence_info(int fd)
+{
+ struct sync_fence_info_data *legacy_info;
+ int uapi;
+
+ uapi = atomic_load_explicit(&g_uapi_version, memory_order_acquire);
+
+ if (uapi == UAPI_LEGACY || uapi == UAPI_UNKNOWN) {
+ legacy_info = legacy_sync_fence_info(fd);
+ if (legacy_info || errno != ENOTTY) {
+ if (legacy_info && uapi == UAPI_UNKNOWN) {
+ atomic_store_explicit(&g_uapi_version, UAPI_LEGACY,
+ memory_order_release);
+ }
+ return legacy_info;
+ }
+ }
+
+ struct sync_file_info* file_info;
+ file_info = modern_sync_file_info(fd);
+ if (!file_info)
+ return NULL;
+ if (uapi == UAPI_UNKNOWN) {
+ atomic_store_explicit(&g_uapi_version, UAPI_MODERN,
+ memory_order_release);
+ }
+ legacy_info = sync_file_info_to_legacy_fence_info(file_info);
+ sync_file_info_free(file_info);
+ return legacy_info;
+}
+
+struct sync_file_info* sync_file_info(int32_t fd)
+{
+ struct sync_file_info *info;
+ int uapi;
+
+ uapi = atomic_load_explicit(&g_uapi_version, memory_order_acquire);
+
+ if (uapi == UAPI_MODERN || uapi == UAPI_UNKNOWN) {
+ info = modern_sync_file_info(fd);
+ if (info || errno != ENOTTY) {
+ if (info && uapi == UAPI_UNKNOWN) {
+ atomic_store_explicit(&g_uapi_version, UAPI_MODERN,
+ memory_order_release);
+ }
+ return info;
+ }
+ }
+
+ struct sync_fence_info_data *legacy_info;
+ legacy_info = legacy_sync_fence_info(fd);
+ if (!legacy_info)
+ return NULL;
+ if (uapi == UAPI_UNKNOWN) {
+ atomic_store_explicit(&g_uapi_version, UAPI_LEGACY,
+ memory_order_release);
+ }
+ info = legacy_fence_info_to_sync_file_info(legacy_info);
+ sync_fence_info_free(legacy_info);
+ return info;
}
struct sync_pt_info *sync_pt_info(struct sync_fence_info_data *info,
@@ -190,6 +379,11 @@
free(info);
}
+void sync_file_info_free(struct sync_file_info *info)
+{
+ free(info);
+}
+
int sw_sync_timeline_create(void)
{
diff --git a/libsync/sync_test.c b/libsync/sync_test.c
index 9a5f7d8..f1ffdcf 100644
--- a/libsync/sync_test.c
+++ b/libsync/sync_test.c
@@ -22,7 +22,7 @@
#include <string.h>
#include <unistd.h>
-#include <sync/sync.h>
+#include <android/sync.h>
#include "sw_sync.h"
pthread_mutex_t printf_mutex = PTHREAD_MUTEX_INITIALIZER;
diff --git a/libsync/tests/sync_test.cpp b/libsync/tests/sync_test.cpp
index 401aaee..f08e97e 100644
--- a/libsync/tests/sync_test.cpp
+++ b/libsync/tests/sync_test.cpp
@@ -1,5 +1,5 @@
#include <gtest/gtest.h>
-#include <sync/sync.h>
+#include <android/sync.h>
#include <sw_sync.h>
#include <fcntl.h>
#include <vector>
@@ -172,20 +172,20 @@
return sync_wait(m_fd, timeout);
}
vector<SyncPointInfo> getInfo() const {
- struct sync_pt_info *pointInfo = nullptr;
vector<SyncPointInfo> fenceInfo;
- sync_fence_info_data *info = sync_fence_info(getFd());
+ struct sync_file_info *info = sync_file_info(getFd());
if (!info) {
return fenceInfo;
}
- while ((pointInfo = sync_pt_info(info, pointInfo))) {
+ const auto fences = sync_get_fence_info(info);
+ for (uint32_t i = 0; i < info->num_fences; i++) {
fenceInfo.push_back(SyncPointInfo{
- pointInfo->driver_name,
- pointInfo->obj_name,
- pointInfo->timestamp_ns,
- pointInfo->status});
+ fences[i].driver_name,
+ fences[i].obj_name,
+ fences[i].timestamp_ns,
+ fences[i].status});
}
- sync_fence_info_free(info);
+ sync_file_info_free(info);
return fenceInfo;
}
int getSize() const {
@@ -212,6 +212,32 @@
}
};
+static void CheckModernLegacyInfoMatch(const SyncFence& f) {
+ struct sync_file_info* modern = sync_file_info(f.getFd());
+ struct sync_fence_info_data* legacy = sync_fence_info(f.getFd());
+
+ ASSERT_TRUE(modern != NULL);
+ ASSERT_TRUE(legacy != NULL);
+
+ EXPECT_STREQ(modern->name, legacy->name);
+ EXPECT_EQ(modern->status, legacy->status);
+
+ uint32_t fenceIdx = 0;
+ struct sync_pt_info* pt = sync_pt_info(legacy, NULL);
+ const struct sync_fence_info* fences = sync_get_fence_info(modern);
+ while (fenceIdx < modern->num_fences && pt != NULL) {
+ EXPECT_STREQ(fences[fenceIdx].obj_name, pt->obj_name);
+ EXPECT_STREQ(fences[fenceIdx].driver_name, pt->driver_name);
+ EXPECT_EQ(fences[fenceIdx].status, pt->status);
+ EXPECT_EQ(fences[fenceIdx].timestamp_ns, pt->timestamp_ns);
+
+ fenceIdx++;
+ pt = sync_pt_info(legacy, pt);
+ }
+ EXPECT_EQ(fenceIdx, modern->num_fences);
+ EXPECT_EQ(NULL, pt);
+}
+
int SyncFence::s_fenceCount = 0;
TEST(AllocTest, Timeline) {
@@ -225,6 +251,7 @@
SyncFence fence(timeline, 1);
ASSERT_TRUE(fence.isValid());
+ CheckModernLegacyInfoMatch(fence);
}
TEST(AllocTest, FenceNegative) {
@@ -321,15 +348,21 @@
timeline.inc(1);
ASSERT_EQ(a.getSignaledCount(), 1);
ASSERT_EQ(d.getActiveCount(), 1);
+ CheckModernLegacyInfoMatch(a);
+ CheckModernLegacyInfoMatch(d);
timeline.inc(1);
ASSERT_EQ(b.getSignaledCount(), 1);
ASSERT_EQ(d.getActiveCount(), 1);
+ CheckModernLegacyInfoMatch(b);
+ CheckModernLegacyInfoMatch(d);
timeline.inc(1);
ASSERT_EQ(c.getSignaledCount(), 1);
ASSERT_EQ(d.getActiveCount(), 0);
ASSERT_EQ(d.getSignaledCount(), 1);
+ CheckModernLegacyInfoMatch(c);
+ CheckModernLegacyInfoMatch(d);
}
TEST(FenceTest, MergeSameFence) {
@@ -343,9 +376,11 @@
ASSERT_TRUE(selfMergeFence.isValid());
ASSERT_EQ(selfMergeFence.getSignaledCount(), 0);
+ CheckModernLegacyInfoMatch(selfMergeFence);
timeline.inc(5);
ASSERT_EQ(selfMergeFence.getSignaledCount(), 1);
+ CheckModernLegacyInfoMatch(selfMergeFence);
}
TEST(FenceTest, PollOnDestroyedTimeline) {
@@ -397,14 +432,17 @@
timelineA.inc(5);
ASSERT_EQ(mergedFence.getActiveCount(), 2);
ASSERT_EQ(mergedFence.getSignaledCount(), 1);
+ CheckModernLegacyInfoMatch(mergedFence);
timelineB.inc(5);
ASSERT_EQ(mergedFence.getActiveCount(), 1);
ASSERT_EQ(mergedFence.getSignaledCount(), 2);
+ CheckModernLegacyInfoMatch(mergedFence);
timelineC.inc(5);
ASSERT_EQ(mergedFence.getActiveCount(), 0);
ASSERT_EQ(mergedFence.getSignaledCount(), 3);
+ CheckModernLegacyInfoMatch(mergedFence);
// confirm you can successfully wait.
ASSERT_EQ(mergedFence.wait(100), 0);
@@ -560,6 +598,7 @@
// Merge.
fence = SyncFence(fence, SyncFence(timeline, syncPoint));
ASSERT_TRUE(fence.isValid());
+ CheckModernLegacyInfoMatch(fence);
}
// Confirm our map matches the fence.
diff --git a/libsystem/include/system/graphics-base.h b/libsystem/include/system/graphics-base.h
new file mode 100644
index 0000000..2a44faf
--- /dev/null
+++ b/libsystem/include/system/graphics-base.h
@@ -0,0 +1,141 @@
+// This file is autogenerated by hidl-gen. Do not edit manually.
+// Source: android.hardware.graphics.common@1.0
+// Root: android.hardware:hardware/interfaces
+
+#ifndef HIDL_GENERATED_ANDROID_HARDWARE_GRAPHICS_COMMON_V1_0_EXPORTED_CONSTANTS_H_
+#define HIDL_GENERATED_ANDROID_HARDWARE_GRAPHICS_COMMON_V1_0_EXPORTED_CONSTANTS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+ HAL_PIXEL_FORMAT_RGBA_8888 = 1,
+ HAL_PIXEL_FORMAT_RGBX_8888 = 2,
+ HAL_PIXEL_FORMAT_RGB_888 = 3,
+ HAL_PIXEL_FORMAT_RGB_565 = 4,
+ HAL_PIXEL_FORMAT_BGRA_8888 = 5,
+ HAL_PIXEL_FORMAT_RGBA_1010102 = 43, // 0x2B
+ HAL_PIXEL_FORMAT_RGBA_FP16 = 22, // 0x16
+ HAL_PIXEL_FORMAT_YV12 = 842094169, // 0x32315659
+ HAL_PIXEL_FORMAT_Y8 = 538982489, // 0x20203859
+ HAL_PIXEL_FORMAT_Y16 = 540422489, // 0x20363159
+ HAL_PIXEL_FORMAT_RAW16 = 32, // 0x20
+ HAL_PIXEL_FORMAT_RAW10 = 37, // 0x25
+ HAL_PIXEL_FORMAT_RAW12 = 38, // 0x26
+ HAL_PIXEL_FORMAT_RAW_OPAQUE = 36, // 0x24
+ HAL_PIXEL_FORMAT_BLOB = 33, // 0x21
+ HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED = 34, // 0x22
+ HAL_PIXEL_FORMAT_YCBCR_420_888 = 35, // 0x23
+ HAL_PIXEL_FORMAT_YCBCR_422_888 = 39, // 0x27
+ HAL_PIXEL_FORMAT_YCBCR_444_888 = 40, // 0x28
+ HAL_PIXEL_FORMAT_FLEX_RGB_888 = 41, // 0x29
+ HAL_PIXEL_FORMAT_FLEX_RGBA_8888 = 42, // 0x2A
+ HAL_PIXEL_FORMAT_YCBCR_422_SP = 16, // 0x10
+ HAL_PIXEL_FORMAT_YCRCB_420_SP = 17, // 0x11
+ HAL_PIXEL_FORMAT_YCBCR_422_I = 20, // 0x14
+ HAL_PIXEL_FORMAT_JPEG = 256, // 0x100
+} android_pixel_format_t;
+
+typedef enum {
+ HAL_TRANSFORM_FLIP_H = 1, // 0x01
+ HAL_TRANSFORM_FLIP_V = 2, // 0x02
+ HAL_TRANSFORM_ROT_90 = 4, // 0x04
+ HAL_TRANSFORM_ROT_180 = 3, // 0x03
+ HAL_TRANSFORM_ROT_270 = 7, // 0x07
+} android_transform_t;
+
+typedef enum {
+ HAL_DATASPACE_UNKNOWN = 0, // 0x0
+ HAL_DATASPACE_ARBITRARY = 1, // 0x1
+ HAL_DATASPACE_STANDARD_SHIFT = 16,
+ HAL_DATASPACE_STANDARD_MASK = 4128768, // (63 << STANDARD_SHIFT)
+ HAL_DATASPACE_STANDARD_UNSPECIFIED = 0, // (0 << STANDARD_SHIFT)
+ HAL_DATASPACE_STANDARD_BT709 = 65536, // (1 << STANDARD_SHIFT)
+ HAL_DATASPACE_STANDARD_BT601_625 = 131072, // (2 << STANDARD_SHIFT)
+ HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED = 196608, // (3 << STANDARD_SHIFT)
+ HAL_DATASPACE_STANDARD_BT601_525 = 262144, // (4 << STANDARD_SHIFT)
+ HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED = 327680, // (5 << STANDARD_SHIFT)
+ HAL_DATASPACE_STANDARD_BT2020 = 393216, // (6 << STANDARD_SHIFT)
+ HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE = 458752, // (7 << STANDARD_SHIFT)
+ HAL_DATASPACE_STANDARD_BT470M = 524288, // (8 << STANDARD_SHIFT)
+ HAL_DATASPACE_STANDARD_FILM = 589824, // (9 << STANDARD_SHIFT)
+ HAL_DATASPACE_STANDARD_DCI_P3 = 655360, // (10 << STANDARD_SHIFT)
+ HAL_DATASPACE_STANDARD_ADOBE_RGB = 720896, // (11 << STANDARD_SHIFT)
+ HAL_DATASPACE_TRANSFER_SHIFT = 22,
+ HAL_DATASPACE_TRANSFER_MASK = 130023424, // (31 << TRANSFER_SHIFT)
+ HAL_DATASPACE_TRANSFER_UNSPECIFIED = 0, // (0 << TRANSFER_SHIFT)
+ HAL_DATASPACE_TRANSFER_LINEAR = 4194304, // (1 << TRANSFER_SHIFT)
+ HAL_DATASPACE_TRANSFER_SRGB = 8388608, // (2 << TRANSFER_SHIFT)
+ HAL_DATASPACE_TRANSFER_SMPTE_170M = 12582912, // (3 << TRANSFER_SHIFT)
+ HAL_DATASPACE_TRANSFER_GAMMA2_2 = 16777216, // (4 << TRANSFER_SHIFT)
+ HAL_DATASPACE_TRANSFER_GAMMA2_6 = 20971520, // (5 << TRANSFER_SHIFT)
+ HAL_DATASPACE_TRANSFER_GAMMA2_8 = 25165824, // (6 << TRANSFER_SHIFT)
+ HAL_DATASPACE_TRANSFER_ST2084 = 29360128, // (7 << TRANSFER_SHIFT)
+ HAL_DATASPACE_TRANSFER_HLG = 33554432, // (8 << TRANSFER_SHIFT)
+ HAL_DATASPACE_RANGE_SHIFT = 27,
+ HAL_DATASPACE_RANGE_MASK = 939524096, // (7 << RANGE_SHIFT)
+ HAL_DATASPACE_RANGE_UNSPECIFIED = 0, // (0 << RANGE_SHIFT)
+ HAL_DATASPACE_RANGE_FULL = 134217728, // (1 << RANGE_SHIFT)
+ HAL_DATASPACE_RANGE_LIMITED = 268435456, // (2 << RANGE_SHIFT)
+ HAL_DATASPACE_RANGE_EXTENDED = 402653184, // (3 << RANGE_SHIFT)
+ HAL_DATASPACE_SRGB_LINEAR = 512, // 0x200
+ HAL_DATASPACE_V0_SRGB_LINEAR = 138477568, // ((STANDARD_BT709 | TRANSFER_LINEAR) | RANGE_FULL)
+ HAL_DATASPACE_V0_SCRGB_LINEAR = 406913024, // ((STANDARD_BT709 | TRANSFER_LINEAR) | RANGE_EXTENDED)
+ HAL_DATASPACE_SRGB = 513, // 0x201
+ HAL_DATASPACE_V0_SRGB = 142671872, // ((STANDARD_BT709 | TRANSFER_SRGB) | RANGE_FULL)
+ HAL_DATASPACE_V0_SCRGB = 411107328, // ((STANDARD_BT709 | TRANSFER_SRGB) | RANGE_EXTENDED)
+ HAL_DATASPACE_JFIF = 257, // 0x101
+ HAL_DATASPACE_V0_JFIF = 146931712, // ((STANDARD_BT601_625 | TRANSFER_SMPTE_170M) | RANGE_FULL)
+ HAL_DATASPACE_BT601_625 = 258, // 0x102
+ HAL_DATASPACE_V0_BT601_625 = 281149440, // ((STANDARD_BT601_625 | TRANSFER_SMPTE_170M) | RANGE_LIMITED)
+ HAL_DATASPACE_BT601_525 = 259, // 0x103
+ HAL_DATASPACE_V0_BT601_525 = 281280512, // ((STANDARD_BT601_525 | TRANSFER_SMPTE_170M) | RANGE_LIMITED)
+ HAL_DATASPACE_BT709 = 260, // 0x104
+ HAL_DATASPACE_V0_BT709 = 281083904, // ((STANDARD_BT709 | TRANSFER_SMPTE_170M) | RANGE_LIMITED)
+ HAL_DATASPACE_DCI_P3_LINEAR = 139067392, // ((STANDARD_DCI_P3 | TRANSFER_LINEAR) | RANGE_FULL)
+ HAL_DATASPACE_DCI_P3 = 155844608, // ((STANDARD_DCI_P3 | TRANSFER_GAMMA2_6) | RANGE_FULL)
+ HAL_DATASPACE_DISPLAY_P3_LINEAR = 139067392, // ((STANDARD_DCI_P3 | TRANSFER_LINEAR) | RANGE_FULL)
+ HAL_DATASPACE_DISPLAY_P3 = 143261696, // ((STANDARD_DCI_P3 | TRANSFER_SRGB) | RANGE_FULL)
+ HAL_DATASPACE_ADOBE_RGB = 151715840, // ((STANDARD_ADOBE_RGB | TRANSFER_GAMMA2_2) | RANGE_FULL)
+ HAL_DATASPACE_BT2020_LINEAR = 138805248, // ((STANDARD_BT2020 | TRANSFER_LINEAR) | RANGE_FULL)
+ HAL_DATASPACE_BT2020 = 147193856, // ((STANDARD_BT2020 | TRANSFER_SMPTE_170M) | RANGE_FULL)
+ HAL_DATASPACE_BT2020_PQ = 163971072, // ((STANDARD_BT2020 | TRANSFER_ST2084) | RANGE_FULL)
+ HAL_DATASPACE_DEPTH = 4096, // 0x1000
+ HAL_DATASPACE_SENSOR = 4097, // 0x1001
+} android_dataspace_t;
+
+typedef enum {
+ HAL_COLOR_MODE_NATIVE = 0,
+ HAL_COLOR_MODE_STANDARD_BT601_625 = 1,
+ HAL_COLOR_MODE_STANDARD_BT601_625_UNADJUSTED = 2,
+ HAL_COLOR_MODE_STANDARD_BT601_525 = 3,
+ HAL_COLOR_MODE_STANDARD_BT601_525_UNADJUSTED = 4,
+ HAL_COLOR_MODE_STANDARD_BT709 = 5,
+ HAL_COLOR_MODE_DCI_P3 = 6,
+ HAL_COLOR_MODE_SRGB = 7,
+ HAL_COLOR_MODE_ADOBE_RGB = 8,
+ HAL_COLOR_MODE_DISPLAY_P3 = 9,
+} android_color_mode_t;
+
+typedef enum {
+ HAL_COLOR_TRANSFORM_IDENTITY = 0,
+ HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX = 1,
+ HAL_COLOR_TRANSFORM_VALUE_INVERSE = 2,
+ HAL_COLOR_TRANSFORM_GRAYSCALE = 3,
+ HAL_COLOR_TRANSFORM_CORRECT_PROTANOPIA = 4,
+ HAL_COLOR_TRANSFORM_CORRECT_DEUTERANOPIA = 5,
+ HAL_COLOR_TRANSFORM_CORRECT_TRITANOPIA = 6,
+} android_color_transform_t;
+
+typedef enum {
+ HAL_HDR_DOLBY_VISION = 1,
+ HAL_HDR_HDR10 = 2,
+ HAL_HDR_HLG = 3,
+} android_hdr_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // HIDL_GENERATED_ANDROID_HARDWARE_GRAPHICS_COMMON_V1_0_EXPORTED_CONSTANTS_H_
diff --git a/libsystem/include/system/graphics.h b/libsystem/include/system/graphics.h
index ae10fa0..1a99187 100644
--- a/libsystem/include/system/graphics.h
+++ b/libsystem/include/system/graphics.h
@@ -20,10 +20,30 @@
#include <stddef.h>
#include <stdint.h>
+/*
+ * Some of the enums are now defined in HIDL in hardware/interfaces and are
+ * generated.
+ */
+#include "graphics-base.h"
+
#ifdef __cplusplus
extern "C" {
#endif
+/* for compatibility */
+#define HAL_PIXEL_FORMAT_YCbCr_420_888 HAL_PIXEL_FORMAT_YCBCR_420_888
+#define HAL_PIXEL_FORMAT_YCbCr_422_888 HAL_PIXEL_FORMAT_YCBCR_422_888
+#define HAL_PIXEL_FORMAT_YCbCr_444_888 HAL_PIXEL_FORMAT_YCBCR_444_888
+#define HAL_PIXEL_FORMAT_YCbCr_422_SP HAL_PIXEL_FORMAT_YCBCR_422_SP
+#define HAL_PIXEL_FORMAT_YCrCb_420_SP HAL_PIXEL_FORMAT_YCRCB_420_SP
+#define HAL_PIXEL_FORMAT_YCbCr_422_I HAL_PIXEL_FORMAT_YCBCR_422_I
+typedef android_pixel_format_t android_pixel_format;
+typedef android_transform_t android_transform;
+typedef android_dataspace_t android_dataspace;
+typedef android_color_mode_t android_color_mode;
+typedef android_color_transform_t android_color_transform;
+typedef android_hdr_t android_hdr;
+
/*
* If the HAL needs to create service threads to handle graphics related
* tasks, these threads need to run at HAL_PRIORITY_URGENT_DISPLAY priority
@@ -38,411 +58,6 @@
#define HAL_PRIORITY_URGENT_DISPLAY (-8)
-/**
- * pixel format definitions
- */
-
-typedef enum android_pixel_format {
- /*
- * "linear" color pixel formats:
- *
- * When used with ANativeWindow, the dataSpace field describes the color
- * space of the buffer.
- *
- * The color space determines, for example, if the formats are linear or
- * gamma-corrected; or whether any special operations are performed when
- * reading or writing into a buffer in one of these formats.
- */
- HAL_PIXEL_FORMAT_RGBA_8888 = 1,
- HAL_PIXEL_FORMAT_RGBX_8888 = 2,
- HAL_PIXEL_FORMAT_RGB_888 = 3,
- HAL_PIXEL_FORMAT_RGB_565 = 4,
- HAL_PIXEL_FORMAT_BGRA_8888 = 5,
-
- /*
- * 0x100 - 0x1FF
- *
- * This range is reserved for pixel formats that are specific to the HAL
- * implementation. Implementations can use any value in this range to
- * communicate video pixel formats between their HAL modules. These formats
- * must not have an alpha channel. Additionally, an EGLimage created from a
- * gralloc buffer of one of these formats must be supported for use with the
- * GL_OES_EGL_image_external OpenGL ES extension.
- */
-
- /*
- * Android YUV format:
- *
- * This format is exposed outside of the HAL to software decoders and
- * applications. EGLImageKHR must support it in conjunction with the
- * OES_EGL_image_external extension.
- *
- * YV12 is a 4:2:0 YCrCb planar format comprised of a WxH Y plane followed
- * by (W/2) x (H/2) Cr and Cb planes.
- *
- * This format assumes
- * - an even width
- * - an even height
- * - a horizontal stride multiple of 16 pixels
- * - a vertical stride equal to the height
- *
- * y_size = stride * height
- * c_stride = ALIGN(stride/2, 16)
- * c_size = c_stride * height/2
- * size = y_size + c_size * 2
- * cr_offset = y_size
- * cb_offset = y_size + c_size
- *
- * When used with ANativeWindow, the dataSpace field describes the color
- * space of the buffer.
- */
- HAL_PIXEL_FORMAT_YV12 = 0x32315659, // YCrCb 4:2:0 Planar
-
-
- /*
- * Android Y8 format:
- *
- * This format is exposed outside of the HAL to the framework.
- * The expected gralloc usage flags are SW_* and HW_CAMERA_*,
- * and no other HW_ flags will be used.
- *
- * Y8 is a YUV planar format comprised of a WxH Y plane,
- * with each pixel being represented by 8 bits.
- *
- * It is equivalent to just the Y plane from YV12.
- *
- * This format assumes
- * - an even width
- * - an even height
- * - a horizontal stride multiple of 16 pixels
- * - a vertical stride equal to the height
- *
- * size = stride * height
- *
- * When used with ANativeWindow, the dataSpace field describes the color
- * space of the buffer.
- */
- HAL_PIXEL_FORMAT_Y8 = 0x20203859,
-
- /*
- * Android Y16 format:
- *
- * This format is exposed outside of the HAL to the framework.
- * The expected gralloc usage flags are SW_* and HW_CAMERA_*,
- * and no other HW_ flags will be used.
- *
- * Y16 is a YUV planar format comprised of a WxH Y plane,
- * with each pixel being represented by 16 bits.
- *
- * It is just like Y8, but has double the bits per pixel (little endian).
- *
- * This format assumes
- * - an even width
- * - an even height
- * - a horizontal stride multiple of 16 pixels
- * - a vertical stride equal to the height
- * - strides are specified in pixels, not in bytes
- *
- * size = stride * height * 2
- *
- * 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,
- * plus an associated confidence value.
- */
- HAL_PIXEL_FORMAT_Y16 = 0x20363159,
-
- /*
- * Android RAW sensor format:
- *
- * This format is exposed outside of the camera HAL to applications.
- *
- * RAW16 is a single-channel, 16-bit, little endian format, typically
- * representing raw Bayer-pattern images from an image sensor, with minimal
- * processing.
- *
- * The exact pixel layout of the data in the buffer is sensor-dependent, and
- * needs to be queried from the camera device.
- *
- * Generally, not all 16 bits are used; more common values are 10 or 12
- * bits. If not all bits are used, the lower-order bits are filled first.
- * All parameters to interpret the raw data (black and white points,
- * color space, etc) must be queried from the camera device.
- *
- * This format assumes
- * - an even width
- * - an even height
- * - a horizontal stride multiple of 16 pixels
- * - a vertical stride equal to the height
- * - strides are specified in pixels, not in bytes
- *
- * size = stride * height * 2
- *
- * 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 should be
- * HAL_DATASPACE_ARBITRARY, as raw image sensor buffers require substantial
- * extra metadata to define.
- */
- HAL_PIXEL_FORMAT_RAW16 = 0x20,
-
- /*
- * Android RAW10 format:
- *
- * This format is exposed outside of the camera HAL to applications.
- *
- * RAW10 is a single-channel, 10-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 4 consecutive pixels are packed into 5 bytes (40 bits). Each one
- * of the first 4 bytes contains the top 8 bits of each pixel, The fifth byte
- * contains the 2 least significant bits of the 4 pixels, the exact layout data
- * for each 4 consecutive pixels is illustrated below (Pi[j] stands for the jth
- * bit of the ith pixel):
- *
- * bit 7 bit 0
- * =====|=====|=====|=====|=====|=====|=====|=====|
- * Byte 0: |P0[9]|P0[8]|P0[7]|P0[6]|P0[5]|P0[4]|P0[3]|P0[2]|
- * |-----|-----|-----|-----|-----|-----|-----|-----|
- * Byte 1: |P1[9]|P1[8]|P1[7]|P1[6]|P1[5]|P1[4]|P1[3]|P1[2]|
- * |-----|-----|-----|-----|-----|-----|-----|-----|
- * Byte 2: |P2[9]|P2[8]|P2[7]|P2[6]|P2[5]|P2[4]|P2[3]|P2[2]|
- * |-----|-----|-----|-----|-----|-----|-----|-----|
- * Byte 3: |P3[9]|P3[8]|P3[7]|P3[6]|P3[5]|P3[4]|P3[3]|P3[2]|
- * |-----|-----|-----|-----|-----|-----|-----|-----|
- * Byte 4: |P3[1]|P3[0]|P2[1]|P2[0]|P1[1]|P1[0]|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 * (10 / 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 * (10 / 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_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.
- *
- * RAW_OPAQUE is a format for unprocessed raw image buffers coming from an
- * image sensor. The actual structure of buffers of this format is
- * implementation-dependent.
- *
- * 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_RAW_OPAQUE = 0x24,
-
- /*
- * Android binary blob graphics buffer format:
- *
- * This format is used to carry task-specific data which does not have a
- * standard image structure. The details of the format are left to the two
- * endpoints.
- *
- * A typical use case is for transporting JPEG-compressed images from the
- * Camera HAL to the framework or to applications.
- *
- * Buffers of this format must have a height of 1, and width equal to their
- * size in bytes.
- *
- * When used with ANativeWindow, the mapping of the dataSpace field to
- * buffer contents for BLOB is as follows:
- *
- * dataSpace value | Buffer contents
- * -------------------------------+-----------------------------------------
- * HAL_DATASPACE_JFIF | An encoded JPEG image
- * HAL_DATASPACE_DEPTH | An android_depth_points buffer
- * Other | Unsupported
- *
- */
- HAL_PIXEL_FORMAT_BLOB = 0x21,
-
- /*
- * Android format indicating that the choice of format is entirely up to the
- * device-specific Gralloc implementation.
- *
- * The Gralloc implementation should examine the usage bits passed in when
- * allocating a buffer with this format, and it should derive the pixel
- * format from those usage flags. This format will never be used with any
- * of the GRALLOC_USAGE_SW_* usage flags.
- *
- * If a buffer of this format is to be used as an OpenGL ES texture, the
- * framework will assume that sampling the texture will always return an
- * alpha value of 1.0 (i.e. the buffer contains only opaque pixel values).
- *
- * When used with ANativeWindow, the dataSpace field describes the color
- * space of the buffer.
- */
- HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED = 0x22,
-
- /*
- * Android flexible YCbCr 4:2:0 formats
- *
- * 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_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.
- *
- * When used with ANativeWindow, the dataSpace field describes the color
- * space of the buffer.
- */
- 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
- HAL_PIXEL_FORMAT_YCbCr_422_I = 0x14, // YUY2
-} android_pixel_format_t;
-
/*
* Structure for describing YCbCr formats for consumption by applications.
* This is used with HAL_PIXEL_FORMAT_YCbCr_*_888.
@@ -623,795 +238,24 @@
};
/**
- * Transformation definitions
- *
- * IMPORTANT NOTE:
- * HAL_TRANSFORM_ROT_90 is applied CLOCKWISE and AFTER HAL_TRANSFORM_FLIP_{H|V}.
- *
- */
+ * These structures are used to define the reference display's
+ * capabilities for HDR content. Display engine can use this
+ * to better tone map content to user's display.
+ * Color is defined in CIE XYZ coordinates
+ */
+struct android_xy_color {
+ float x;
+ float y;
+};
-typedef enum android_transform {
- /* flip source image horizontally (around the vertical axis) */
- HAL_TRANSFORM_FLIP_H = 0x01,
- /* flip source image vertically (around the horizontal axis)*/
- HAL_TRANSFORM_FLIP_V = 0x02,
- /* rotate source image 90 degrees clockwise */
- HAL_TRANSFORM_ROT_90 = 0x04,
- /* rotate source image 180 degrees */
- HAL_TRANSFORM_ROT_180 = 0x03,
- /* rotate source image 270 degrees clockwise */
- HAL_TRANSFORM_ROT_270 = 0x07,
- /* don't use. see system/window.h */
- HAL_TRANSFORM_RESERVED = 0x08,
-} android_transform_t;
-
-/**
- * Dataspace Definitions
- * ======================
- *
- * Dataspace is the definition of how pixel values should be interpreted.
- *
- * For many formats, this is the colorspace of the image data, which includes
- * primaries (including white point) and the transfer characteristic function,
- * which describes both gamma curve and numeric range (within the bit depth).
- *
- * Other dataspaces include depth measurement data from a depth camera.
- *
- * A dataspace is comprised of a number of fields.
- *
- * Version
- * --------
- * The top 2 bits represent the revision of the field specification. This is
- * currently always 0.
- *
- *
- * bits 31-30 29 - 0
- * +-----+----------------------------------------------------+
- * fields | Rev | Revision specific fields |
- * +-----+----------------------------------------------------+
- *
- * Field layout for version = 0:
- * ----------------------------
- *
- * A dataspace is comprised of the following fields:
- * Standard
- * Transfer function
- * Range
- *
- * bits 31-30 29-27 26 - 22 21 - 16 15 - 0
- * +-----+-----+--------+--------+----------------------------+
- * fields | 0 |Range|Transfer|Standard| Legacy and custom |
- * +-----+-----+--------+--------+----------------------------+
- * VV RRR TTTTT SSSSSS LLLLLLLL LLLLLLLL
- *
- * If range, transfer and standard fields are all 0 (e.g. top 16 bits are
- * all zeroes), the bottom 16 bits contain either a legacy dataspace value,
- * or a custom value.
- */
-
-typedef enum android_dataspace {
- /*
- * Default-assumption data space, when not explicitly specified.
- *
- * It is safest to assume the buffer is an image with sRGB primaries and
- * encoding ranges, but the consumer and/or the producer of the data may
- * simply be using defaults. No automatic gamma transform should be
- * expected, except for a possible display gamma transform when drawn to a
- * screen.
- */
- HAL_DATASPACE_UNKNOWN = 0x0,
-
- /*
- * Arbitrary dataspace with manually defined characteristics. Definition
- * for colorspaces or other meaning must be communicated separately.
- *
- * This is used when specifying primaries, transfer characteristics,
- * etc. separately.
- *
- * A typical use case is in video encoding parameters (e.g. for H.264),
- * where a colorspace can have separately defined primaries, transfer
- * characteristics, etc.
- */
- HAL_DATASPACE_ARBITRARY = 0x1,
-
- /*
- * Color-description aspects
- *
- * The following aspects define various characteristics of the color
- * specification. These represent bitfields, so that a data space value
- * can specify each of them independently.
- */
-
- HAL_DATASPACE_STANDARD_SHIFT = 16,
-
- /*
- * Standard aspect
- *
- * Defines the chromaticity coordinates of the source primaries in terms of
- * the CIE 1931 definition of x and y specified in ISO 11664-1.
- */
- HAL_DATASPACE_STANDARD_MASK = 63 << HAL_DATASPACE_STANDARD_SHIFT, // 0x3F
-
- /*
- * Chromacity coordinates are unknown or are determined by the application.
- * Implementations shall use the following suggested standards:
- *
- * All YCbCr formats: BT709 if size is 720p or larger (since most video
- * content is letterboxed this corresponds to width is
- * 1280 or greater, or height is 720 or greater).
- * BT601_625 if size is smaller than 720p or is JPEG.
- * All RGB formats: BT709.
- *
- * For all other formats standard is undefined, and implementations should use
- * an appropriate standard for the data represented.
- */
- HAL_DATASPACE_STANDARD_UNSPECIFIED = 0 << HAL_DATASPACE_STANDARD_SHIFT,
-
- /*
- * Primaries: x y
- * green 0.300 0.600
- * blue 0.150 0.060
- * red 0.640 0.330
- * white (D65) 0.3127 0.3290
- *
- * Use the unadjusted KR = 0.2126, KB = 0.0722 luminance interpretation
- * for RGB conversion.
- */
- HAL_DATASPACE_STANDARD_BT709 = 1 << HAL_DATASPACE_STANDARD_SHIFT,
-
- /*
- * Primaries: x y
- * green 0.290 0.600
- * blue 0.150 0.060
- * red 0.640 0.330
- * white (D65) 0.3127 0.3290
- *
- * KR = 0.299, KB = 0.114. This adjusts the luminance interpretation
- * for RGB conversion from the one purely determined by the primaries
- * to minimize the color shift into RGB space that uses BT.709
- * primaries.
- */
- HAL_DATASPACE_STANDARD_BT601_625 = 2 << HAL_DATASPACE_STANDARD_SHIFT,
-
- /*
- * Primaries: x y
- * green 0.290 0.600
- * blue 0.150 0.060
- * red 0.640 0.330
- * white (D65) 0.3127 0.3290
- *
- * Use the unadjusted KR = 0.222, KB = 0.071 luminance interpretation
- * for RGB conversion.
- */
- HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED = 3 << HAL_DATASPACE_STANDARD_SHIFT,
-
- /*
- * Primaries: x y
- * green 0.310 0.595
- * blue 0.155 0.070
- * red 0.630 0.340
- * white (D65) 0.3127 0.3290
- *
- * KR = 0.299, KB = 0.114. This adjusts the luminance interpretation
- * for RGB conversion from the one purely determined by the primaries
- * to minimize the color shift into RGB space that uses BT.709
- * primaries.
- */
- HAL_DATASPACE_STANDARD_BT601_525 = 4 << HAL_DATASPACE_STANDARD_SHIFT,
-
- /*
- * Primaries: x y
- * green 0.310 0.595
- * blue 0.155 0.070
- * red 0.630 0.340
- * white (D65) 0.3127 0.3290
- *
- * Use the unadjusted KR = 0.212, KB = 0.087 luminance interpretation
- * for RGB conversion (as in SMPTE 240M).
- */
- HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED = 5 << HAL_DATASPACE_STANDARD_SHIFT,
-
- /*
- * Primaries: x y
- * green 0.170 0.797
- * blue 0.131 0.046
- * red 0.708 0.292
- * white (D65) 0.3127 0.3290
- *
- * Use the unadjusted KR = 0.2627, KB = 0.0593 luminance interpretation
- * for RGB conversion.
- */
- HAL_DATASPACE_STANDARD_BT2020 = 6 << HAL_DATASPACE_STANDARD_SHIFT,
-
- /*
- * Primaries: x y
- * green 0.170 0.797
- * blue 0.131 0.046
- * red 0.708 0.292
- * white (D65) 0.3127 0.3290
- *
- * Use the unadjusted KR = 0.2627, KB = 0.0593 luminance interpretation
- * for RGB conversion using the linear domain.
- */
- HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE = 7 << HAL_DATASPACE_STANDARD_SHIFT,
-
- /*
- * Primaries: x y
- * green 0.21 0.71
- * blue 0.14 0.08
- * red 0.67 0.33
- * white (C) 0.310 0.316
- *
- * Use the unadjusted KR = 0.30, KB = 0.11 luminance interpretation
- * for RGB conversion.
- */
- HAL_DATASPACE_STANDARD_BT470M = 8 << HAL_DATASPACE_STANDARD_SHIFT,
-
- /*
- * Primaries: x y
- * green 0.243 0.692
- * blue 0.145 0.049
- * red 0.681 0.319
- * white (C) 0.310 0.316
- *
- * Use the unadjusted KR = 0.254, KB = 0.068 luminance interpretation
- * for RGB conversion.
- */
- HAL_DATASPACE_STANDARD_FILM = 9 << HAL_DATASPACE_STANDARD_SHIFT,
-
- HAL_DATASPACE_TRANSFER_SHIFT = 22,
-
- /*
- * Transfer aspect
- *
- * Transfer characteristics are the opto-electronic transfer characteristic
- * at the source as a function of linear optical intensity (luminance).
- *
- * For digital signals, E corresponds to the recorded value. Normally, the
- * transfer function is applied in RGB space to each of the R, G and B
- * components independently. This may result in color shift that can be
- * minized by applying the transfer function in Lab space only for the L
- * component. Implementation may apply the transfer function in RGB space
- * for all pixel formats if desired.
- */
-
- HAL_DATASPACE_TRANSFER_MASK = 31 << HAL_DATASPACE_TRANSFER_SHIFT, // 0x1F
-
- /*
- * Transfer characteristics are unknown or are determined by the
- * application.
- *
- * Implementations should use the following transfer functions:
- *
- * For YCbCr formats: use HAL_DATASPACE_TRANSFER_SMPTE_170M
- * For RGB formats: use HAL_DATASPACE_TRANSFER_SRGB
- *
- * For all other formats transfer function is undefined, and implementations
- * should use an appropriate standard for the data represented.
- */
- HAL_DATASPACE_TRANSFER_UNSPECIFIED = 0 << HAL_DATASPACE_TRANSFER_SHIFT,
-
- /*
- * Transfer characteristic curve:
- * E = L
- * L - luminance of image 0 <= L <= 1 for conventional colorimetry
- * E - corresponding electrical signal
- */
- HAL_DATASPACE_TRANSFER_LINEAR = 1 << HAL_DATASPACE_TRANSFER_SHIFT,
-
- /*
- * Transfer characteristic curve:
- *
- * E = 1.055 * L^(1/2.4) - 0.055 for 0.0031308 <= L <= 1
- * = 12.92 * L for 0 <= L < 0.0031308
- * L - luminance of image 0 <= L <= 1 for conventional colorimetry
- * E - corresponding electrical signal
- */
- HAL_DATASPACE_TRANSFER_SRGB = 2 << HAL_DATASPACE_TRANSFER_SHIFT,
-
- /*
- * BT.601 525, BT.601 625, BT.709, BT.2020
- *
- * Transfer characteristic curve:
- * E = 1.099 * L ^ 0.45 - 0.099 for 0.018 <= L <= 1
- * = 4.500 * L for 0 <= L < 0.018
- * L - luminance of image 0 <= L <= 1 for conventional colorimetry
- * E - corresponding electrical signal
- */
- HAL_DATASPACE_TRANSFER_SMPTE_170M = 3 << HAL_DATASPACE_TRANSFER_SHIFT,
-
- /*
- * Assumed display gamma 2.2.
- *
- * Transfer characteristic curve:
- * E = L ^ (1/2.2)
- * L - luminance of image 0 <= L <= 1 for conventional colorimetry
- * E - corresponding electrical signal
- */
- HAL_DATASPACE_TRANSFER_GAMMA2_2 = 4 << HAL_DATASPACE_TRANSFER_SHIFT,
-
- /*
- * display gamma 2.8.
- *
- * Transfer characteristic curve:
- * E = L ^ (1/2.8)
- * L - luminance of image 0 <= L <= 1 for conventional colorimetry
- * E - corresponding electrical signal
- */
- HAL_DATASPACE_TRANSFER_GAMMA2_8 = 5 << HAL_DATASPACE_TRANSFER_SHIFT,
-
- /*
- * SMPTE ST 2084
- *
- * Transfer characteristic curve:
- * E = ((c1 + c2 * L^n) / (1 + c3 * L^n)) ^ m
- * c1 = c3 - c2 + 1 = 3424 / 4096 = 0.8359375
- * c2 = 32 * 2413 / 4096 = 18.8515625
- * c3 = 32 * 2392 / 4096 = 18.6875
- * m = 128 * 2523 / 4096 = 78.84375
- * n = 0.25 * 2610 / 4096 = 0.1593017578125
- * L - luminance of image 0 <= L <= 1 for HDR colorimetry.
- * L = 1 corresponds to 10000 cd/m2
- * E - corresponding electrical signal
- */
- HAL_DATASPACE_TRANSFER_ST2084 = 6 << HAL_DATASPACE_TRANSFER_SHIFT,
-
- /*
- * ARIB STD-B67 Hybrid Log Gamma
- *
- * Transfer characteristic curve:
- * E = r * L^0.5 for 0 <= L <= 1
- * = a * ln(L - b) + c for 1 < L
- * a = 0.17883277
- * b = 0.28466892
- * c = 0.55991073
- * r = 0.5
- * L - luminance of image 0 <= L for HDR colorimetry. L = 1 corresponds
- * to reference white level of 100 cd/m2
- * E - corresponding electrical signal
- */
- HAL_DATASPACE_TRANSFER_HLG = 7 << HAL_DATASPACE_TRANSFER_SHIFT,
-
- HAL_DATASPACE_RANGE_SHIFT = 27,
-
- /*
- * Range aspect
- *
- * Defines the range of values corresponding to the unit range of 0-1.
- * This is defined for YCbCr only, but can be expanded to RGB space.
- */
- HAL_DATASPACE_RANGE_MASK = 7 << HAL_DATASPACE_RANGE_SHIFT, // 0x7
-
- /*
- * Range is unknown or are determined by the application. Implementations
- * shall use the following suggested ranges:
- *
- * All YCbCr formats: limited range.
- * All RGB or RGBA formats (including RAW and Bayer): full range.
- * All Y formats: full range
- *
- * For all other formats range is undefined, and implementations should use
- * an appropriate range for the data represented.
- */
- HAL_DATASPACE_RANGE_UNSPECIFIED = 0 << HAL_DATASPACE_RANGE_SHIFT,
-
- /*
- * Full range uses all values for Y, Cb and Cr from
- * 0 to 2^b-1, where b is the bit depth of the color format.
- */
- HAL_DATASPACE_RANGE_FULL = 1 << HAL_DATASPACE_RANGE_SHIFT,
-
- /*
- * Limited range uses values 16/256*2^b to 235/256*2^b for Y, and
- * 1/16*2^b to 15/16*2^b for Cb, Cr, R, G and B, where b is the bit depth of
- * the color format.
- *
- * E.g. For 8-bit-depth formats:
- * Luma (Y) samples should range from 16 to 235, inclusive
- * Chroma (Cb, Cr) samples should range from 16 to 240, inclusive
- *
- * For 10-bit-depth formats:
- * Luma (Y) samples should range from 64 to 940, inclusive
- * Chroma (Cb, Cr) samples should range from 64 to 960, inclusive
- */
- HAL_DATASPACE_RANGE_LIMITED = 2 << HAL_DATASPACE_RANGE_SHIFT,
-
- /*
- * Legacy dataspaces
- */
-
- /*
- * sRGB linear encoding:
- *
- * The red, green, and blue components are stored in sRGB space, but
- * are linear, not gamma-encoded.
- * The RGB primaries and the white point are the same as BT.709.
- *
- * The values are encoded using the full range ([0,255] for 8-bit) for all
- * components.
- */
- HAL_DATASPACE_SRGB_LINEAR = 0x200, // deprecated, use HAL_DATASPACE_V0_SRGB_LINEAR
-
- HAL_DATASPACE_V0_SRGB_LINEAR = HAL_DATASPACE_STANDARD_BT709 |
- HAL_DATASPACE_TRANSFER_LINEAR | HAL_DATASPACE_RANGE_FULL,
-
-
- /*
- * sRGB gamma encoding:
- *
- * The red, green and blue components are stored in sRGB space, and
- * converted to linear space when read, using the SRGB transfer function
- * for each of the R, G and B components. When written, the inverse
- * transformation is performed.
- *
- * The alpha component, if present, is always stored in linear space and
- * is left unmodified when read or written.
- *
- * Use full range and BT.709 standard.
- */
- HAL_DATASPACE_SRGB = 0x201, // deprecated, use HAL_DATASPACE_V0_SRGB
-
- HAL_DATASPACE_V0_SRGB = HAL_DATASPACE_STANDARD_BT709 |
- HAL_DATASPACE_TRANSFER_SRGB | HAL_DATASPACE_RANGE_FULL,
-
-
- /*
- * YCbCr Colorspaces
- * -----------------
- *
- * Primaries are given using (x,y) coordinates in the CIE 1931 definition
- * of x and y specified by ISO 11664-1.
- *
- * Transfer characteristics are the opto-electronic transfer characteristic
- * at the source as a function of linear optical intensity (luminance).
- */
-
- /*
- * JPEG File Interchange Format (JFIF)
- *
- * Same model as BT.601-625, but all values (Y, Cb, Cr) range from 0 to 255
- *
- * Use full range, BT.601 transfer and BT.601_625 standard.
- */
- HAL_DATASPACE_JFIF = 0x101, // deprecated, use HAL_DATASPACE_V0_JFIF
-
- HAL_DATASPACE_V0_JFIF = HAL_DATASPACE_STANDARD_BT601_625 |
- HAL_DATASPACE_TRANSFER_SMPTE_170M | HAL_DATASPACE_RANGE_FULL,
-
- /*
- * ITU-R Recommendation 601 (BT.601) - 625-line
- *
- * Standard-definition television, 625 Lines (PAL)
- *
- * Use limited range, BT.601 transfer and BT.601_625 standard.
- */
- HAL_DATASPACE_BT601_625 = 0x102, // deprecated, use HAL_DATASPACE_V0_BT601_625
-
- HAL_DATASPACE_V0_BT601_625 = HAL_DATASPACE_STANDARD_BT601_625 |
- HAL_DATASPACE_TRANSFER_SMPTE_170M | HAL_DATASPACE_RANGE_LIMITED,
-
-
- /*
- * ITU-R Recommendation 601 (BT.601) - 525-line
- *
- * Standard-definition television, 525 Lines (NTSC)
- *
- * Use limited range, BT.601 transfer and BT.601_525 standard.
- */
- HAL_DATASPACE_BT601_525 = 0x103, // deprecated, use HAL_DATASPACE_V0_BT601_525
-
- HAL_DATASPACE_V0_BT601_525 = HAL_DATASPACE_STANDARD_BT601_525 |
- HAL_DATASPACE_TRANSFER_SMPTE_170M | HAL_DATASPACE_RANGE_LIMITED,
-
- /*
- * ITU-R Recommendation 709 (BT.709)
- *
- * High-definition television
- *
- * Use limited range, BT.709 transfer and BT.709 standard.
- */
- HAL_DATASPACE_BT709 = 0x104, // deprecated, use HAL_DATASPACE_V0_BT709
-
- HAL_DATASPACE_V0_BT709 = HAL_DATASPACE_STANDARD_BT709 |
- HAL_DATASPACE_TRANSFER_SMPTE_170M | HAL_DATASPACE_RANGE_LIMITED,
-
- /*
- * Data spaces for non-color formats
- */
-
- /*
- * The buffer contains depth ranging measurements from a depth camera.
- * This value is valid with formats:
- * 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, confidence) coordinate point list.
- * The point cloud will be represented with the android_depth_points
- * structure.
- */
- HAL_DATASPACE_DEPTH = 0x1000
-
-} android_dataspace_t;
-
-/*
- * Color modes that may be supported by a display.
- *
- * Definitions:
- * Rendering intent generally defines the goal in mapping a source (input)
- * color to a destination device color for a given color mode.
- *
- * It is important to keep in mind three cases where mapping may be applied:
- * 1. The source gamut is much smaller than the destination (display) gamut
- * 2. The source gamut is much larger than the destination gamut (this will
- * ordinarily be handled using colorimetric rendering, below)
- * 3. The source and destination gamuts are roughly equal, although not
- * completely overlapping
- * Also, a common requirement for mappings is that skin tones should be
- * preserved, or at least remain natural in appearance.
- *
- * Colorimetric Rendering Intent (All cases):
- * Colorimetric indicates that colors should be preserved. In the case
- * that the source gamut lies wholly within the destination gamut or is
- * about the same (#1, #3), this will simply mean that no manipulations
- * (no saturation boost, for example) are applied. In the case where some
- * source colors lie outside the destination gamut (#2, #3), those will
- * need to be mapped to colors that are within the destination gamut,
- * while the already in-gamut colors remain unchanged.
- *
- * Non-colorimetric transforms can take many forms. There are no hard
- * rules and it's left to the implementation to define.
- * Two common intents are described below.
- *
- * Stretched-Gamut Enhancement Intent (Source < Destination):
- * When the destination gamut is much larger than the source gamut (#1), the
- * source primaries may be redefined to reflect the full extent of the
- * destination space, or to reflect an intermediate gamut.
- * Skin-tone preservation would likely be applied. An example might be sRGB
- * input displayed on a DCI-P3 capable device, with skin-tone preservation.
- *
- * Within-Gamut Enhancement Intent (Source >= Destination):
- * When the device (destination) gamut is not larger than the source gamut
- * (#2 or #3), but the appearance of a larger gamut is desired, techniques
- * such as saturation boost may be applied to the source colors. Skin-tone
- * preservation may be applied. There is no unique method for within-gamut
- * enhancement; it would be defined within a flexible color mode.
- *
- */
-typedef enum android_color_mode {
-
- /*
- * HAL_COLOR_MODE_DEFAULT is the "native" gamut of the display.
- * White Point: Vendor/OEM defined
- * Panel Gamma: Vendor/OEM defined (typically 2.2)
- * Rendering Intent: Vendor/OEM defined (typically 'enhanced')
- */
- HAL_COLOR_MODE_NATIVE = 0,
-
- /*
- * HAL_COLOR_MODE_STANDARD_BT601_625 corresponds with display
- * settings that implement the ITU-R Recommendation BT.601
- * or Rec 601. Using 625 line version
- * Rendering Intent: Colorimetric
- * Primaries:
- * x y
- * green 0.290 0.600
- * blue 0.150 0.060
- * red 0.640 0.330
- * white (D65) 0.3127 0.3290
- *
- * KR = 0.299, KB = 0.114. This adjusts the luminance interpretation
- * for RGB conversion from the one purely determined by the primaries
- * to minimize the color shift into RGB space that uses BT.709
- * primaries.
- *
- * Gamma Correction (GC):
- *
- * if Vlinear < 0.018
- * Vnonlinear = 4.500 * Vlinear
- * else
- * Vnonlinear = 1.099 * (Vlinear)^(0.45) – 0.099
- */
- HAL_COLOR_MODE_STANDARD_BT601_625 = 1,
-
- /*
- * Primaries:
- * x y
- * green 0.290 0.600
- * blue 0.150 0.060
- * red 0.640 0.330
- * white (D65) 0.3127 0.3290
- *
- * Use the unadjusted KR = 0.222, KB = 0.071 luminance interpretation
- * for RGB conversion.
- *
- * Gamma Correction (GC):
- *
- * if Vlinear < 0.018
- * Vnonlinear = 4.500 * Vlinear
- * else
- * Vnonlinear = 1.099 * (Vlinear)^(0.45) – 0.099
- */
- HAL_COLOR_MODE_STANDARD_BT601_625_UNADJUSTED = 2,
-
- /*
- * Primaries:
- * x y
- * green 0.310 0.595
- * blue 0.155 0.070
- * red 0.630 0.340
- * white (D65) 0.3127 0.3290
- *
- * KR = 0.299, KB = 0.114. This adjusts the luminance interpretation
- * for RGB conversion from the one purely determined by the primaries
- * to minimize the color shift into RGB space that uses BT.709
- * primaries.
- *
- * Gamma Correction (GC):
- *
- * if Vlinear < 0.018
- * Vnonlinear = 4.500 * Vlinear
- * else
- * Vnonlinear = 1.099 * (Vlinear)^(0.45) – 0.099
- */
- HAL_COLOR_MODE_STANDARD_BT601_525 = 3,
-
- /*
- * Primaries:
- * x y
- * green 0.310 0.595
- * blue 0.155 0.070
- * red 0.630 0.340
- * white (D65) 0.3127 0.3290
- *
- * Use the unadjusted KR = 0.212, KB = 0.087 luminance interpretation
- * for RGB conversion (as in SMPTE 240M).
- *
- * Gamma Correction (GC):
- *
- * if Vlinear < 0.018
- * Vnonlinear = 4.500 * Vlinear
- * else
- * Vnonlinear = 1.099 * (Vlinear)^(0.45) – 0.099
- */
- HAL_COLOR_MODE_STANDARD_BT601_525_UNADJUSTED = 4,
-
- /*
- * HAL_COLOR_MODE_REC709 corresponds with display settings that implement
- * the ITU-R Recommendation BT.709 / Rec. 709 for high-definition television.
- * Rendering Intent: Colorimetric
- * Primaries:
- * x y
- * green 0.300 0.600
- * blue 0.150 0.060
- * red 0.640 0.330
- * white (D65) 0.3127 0.3290
- *
- * HDTV REC709 Inverse Gamma Correction (IGC): V represents normalized
- * (with [0 to 1] range) value of R, G, or B.
- *
- * if Vnonlinear < 0.081
- * Vlinear = Vnonlinear / 4.5
- * else
- * Vlinear = ((Vnonlinear + 0.099) / 1.099) ^ (1/0.45)
- *
- * HDTV REC709 Gamma Correction (GC):
- *
- * if Vlinear < 0.018
- * Vnonlinear = 4.5 * Vlinear
- * else
- * Vnonlinear = 1.099 * (Vlinear) ^ 0.45 – 0.099
- */
- HAL_COLOR_MODE_STANDARD_BT709 = 5,
-
- /*
- * HAL_COLOR_MODE_DCI_P3 corresponds with display settings that implement
- * SMPTE EG 432-1 and SMPTE RP 431-2
- * Rendering Intent: Colorimetric
- * Primaries:
- * x y
- * green 0.265 0.690
- * blue 0.150 0.060
- * red 0.680 0.320
- * white (D65) 0.3127 0.3290
- *
- * Gamma: 2.2
- */
- HAL_COLOR_MODE_DCI_P3 = 6,
-
- /*
- * HAL_COLOR_MODE_SRGB corresponds with display settings that implement
- * the sRGB color space. Uses the same primaries as ITU-R Recommendation
- * BT.709
- * Rendering Intent: Colorimetric
- * Primaries:
- * x y
- * green 0.300 0.600
- * blue 0.150 0.060
- * red 0.640 0.330
- * white (D65) 0.3127 0.3290
- *
- * PC/Internet (sRGB) Inverse Gamma Correction (IGC):
- *
- * if Vnonlinear ≤ 0.03928
- * Vlinear = Vnonlinear / 12.92
- * else
- * Vlinear = ((Vnonlinear + 0.055)/1.055) ^ 2.4
- *
- * PC/Internet (sRGB) Gamma Correction (GC):
- *
- * if Vlinear ≤ 0.0031308
- * Vnonlinear = 12.92 * Vlinear
- * else
- * Vnonlinear = 1.055 * (Vlinear)^(1/2.4) – 0.055
- */
- HAL_COLOR_MODE_SRGB = 7,
-
- /*
- * HAL_COLOR_MODE_ADOBE_RGB corresponds with the RGB color space developed
- * by Adobe Systems, Inc. in 1998.
- * Rendering Intent: Colorimetric
- * Primaries:
- * x y
- * green 0.210 0.710
- * blue 0.150 0.060
- * red 0.640 0.330
- * white (D65) 0.3127 0.3290
- *
- * Gamma: 2.2
- */
- HAL_COLOR_MODE_ADOBE_RGB = 8
-
-} android_color_mode_t;
-
-/*
- * Color transforms that may be applied by hardware composer to the whole
- * display.
- */
-typedef enum android_color_transform {
- /* Applies no transform to the output color */
- HAL_COLOR_TRANSFORM_IDENTITY = 0,
-
- /* Applies an arbitrary transform defined by a 4x4 affine matrix */
- HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX = 1,
-
- /* Applies a transform that inverts the value or luminance of the color, but
- * does not modify hue or saturation */
- HAL_COLOR_TRANSFORM_VALUE_INVERSE = 2,
-
- /* Applies a transform that maps all colors to shades of gray */
- HAL_COLOR_TRANSFORM_GRAYSCALE = 3,
-
- /* Applies a transform which corrects for protanopic color blindness */
- HAL_COLOR_TRANSFORM_CORRECT_PROTANOPIA = 4,
-
- /* Applies a transform which corrects for deuteranopic color blindness */
- HAL_COLOR_TRANSFORM_CORRECT_DEUTERANOPIA = 5,
-
- /* Applies a transform which corrects for tritanopic color blindness */
- HAL_COLOR_TRANSFORM_CORRECT_TRITANOPIA = 6
-} android_color_transform_t;
-
-/*
- * Supported HDR formats. Must be kept in sync with equivalents in Display.java.
- */
-typedef enum android_hdr {
- /* Device supports Dolby Vision HDR */
- HAL_HDR_DOLBY_VISION = 1,
-
- /* Device supports HDR10 */
- HAL_HDR_HDR10 = 2,
-
- /* Device supports hybrid log-gamma HDR */
- HAL_HDR_HLG = 3
-} android_hdr_t;
+struct android_smpte2086_metadata {
+ struct android_xy_color displayPrimaryRed;
+ struct android_xy_color displayPrimaryGreen;
+ struct android_xy_color displayPrimaryBlue;
+ struct android_xy_color whitePoint;
+ float maxLuminance;
+ float minLuminance;
+};
#ifdef __cplusplus
}
diff --git a/libsystem/include/system/radio.h b/libsystem/include/system/radio.h
index 03b252e..acf3ea7 100644
--- a/libsystem/include/system/radio.h
+++ b/libsystem/include/system/radio.h
@@ -81,7 +81,7 @@
} radio_direction_t;
/* unique handle allocated to a radio module */
-typedef unsigned int radio_handle_t;
+typedef uint32_t radio_handle_t;
/* Opaque meta data structure used by radio meta data API (see system/radio_metadata.h) */
typedef struct radio_metadata radio_metadata_t;
@@ -109,10 +109,10 @@
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];
+ uint32_t lower_limit;
+ uint32_t upper_limit;
+ uint32_t num_spacings;
+ uint32_t spacings[RADIO_NUM_SPACINGS_MAX];
union {
radio_hal_fm_band_config_t fm;
radio_hal_am_band_config_t am;
@@ -137,10 +137,10 @@
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 */
+ uint32_t num_tuners; /* number of tuners controllable independently */
+ uint32_t 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 */
+ uint32_t num_bands; /* number of band descriptors */
radio_hal_band_config_t bands[RADIO_NUM_BANDS_MAX]; /* band descriptors */
} radio_hal_properties_t;
@@ -153,10 +153,10 @@
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;
+ uint32_t num_tuners;
+ uint32_t num_audio_sources;
bool supports_capture;
- unsigned int num_bands;
+ uint32_t num_bands;
radio_band_config_t bands[RADIO_NUM_BANDS_MAX];
} radio_properties_t;
@@ -164,12 +164,12 @@
* 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) */
+ uint32_t channel; /* current channel. (e.g kHz for band type RADIO_BAND_FM) */
+ uint32_t 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 */
+ uint32_t signal_strength; /* signal strength from 0 to 100 */
/* meta data (e.g PTY, song title ...), must not be NULL */
__attribute__((aligned(8))) radio_metadata_t *metadata;
} radio_program_info_t;
@@ -196,7 +196,7 @@
/* 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 */
+ int32_t status; /* used by RADIO_EVENT_CONFIG, RADIO_EVENT_TUNED */
union {
/* RADIO_EVENT_ANTENNA, RADIO_EVENT_TA, RADIO_EVENT_EA */
bool on;
@@ -209,12 +209,13 @@
/* Used internally by the framework. Same information as in struct radio_hal_event */
typedef struct radio_event {
radio_event_type_t type;
- int status;
+ int32_t 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 */
+ /* meta data (e.g PTY, song title ...), must not be NULL */
+ __attribute__((aligned(8))) radio_metadata_t *metadata;
};
} radio_event_t;
diff --git a/libsystem/include/system/window-deprecated.h b/libsystem/include/system/window-deprecated.h
new file mode 100644
index 0000000..e836aea
--- /dev/null
+++ b/libsystem/include/system/window-deprecated.h
@@ -0,0 +1,1114 @@
+/*
+ * 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.
+ */
+
+/**************************************************************************************************
+ **************************************************************************************************
+ **** ****
+ **** DEPRECATED ****
+ **** ****
+ **** THIS FILE EXISTS ONLY FOR BACKWARD SOURCE COMPATIBILITY. ****
+ **** ****
+ **** DO NOT ADD TO THIS FILE. ****
+ **** ****
+ **** Driver implementors (vendors) should use vndk/window.h ****
+ **** (frameworks/native/libs/nativewindow/include/vndk/window.h) ****
+ **** ****
+ **** Internal definition can be found here: ****
+ **** frameworks/native/libs/nativewindow/include/system/window.h ****
+ **** ****
+ **************************************************************************************************
+ **************************************************************************************************/
+
+#pragma once
+
+#include <cutils/native_handle.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/cdefs.h>
+#include <system/graphics.h>
+#include <unistd.h>
+#include <stdbool.h>
+
+#ifndef __UNUSED
+#define __UNUSED __attribute__((__unused__))
+#endif
+#ifndef __deprecated
+#define __deprecated __attribute__((__deprecated__))
+#endif
+
+__BEGIN_DECLS
+
+/*****************************************************************************/
+
+#ifdef __cplusplus
+#define ANDROID_NATIVE_UNSIGNED_CAST(x) static_cast<unsigned int>(x)
+#else
+#define ANDROID_NATIVE_UNSIGNED_CAST(x) ((unsigned int)(x))
+#endif
+
+#define ANDROID_NATIVE_MAKE_CONSTANT(a,b,c,d) \
+ ((ANDROID_NATIVE_UNSIGNED_CAST(a) << 24) | \
+ (ANDROID_NATIVE_UNSIGNED_CAST(b) << 16) | \
+ (ANDROID_NATIVE_UNSIGNED_CAST(c) << 8) | \
+ (ANDROID_NATIVE_UNSIGNED_CAST(d)))
+
+#define ANDROID_NATIVE_WINDOW_MAGIC \
+ ANDROID_NATIVE_MAKE_CONSTANT('_','w','n','d')
+
+#define ANDROID_NATIVE_BUFFER_MAGIC \
+ ANDROID_NATIVE_MAKE_CONSTANT('_','b','f','r')
+
+// ---------------------------------------------------------------------------
+
+// This #define may be used to conditionally compile device-specific code to
+// support either the prior ANativeWindow interface, which did not pass libsync
+// fences around, or the new interface that does. This #define is only present
+// when the ANativeWindow interface does include libsync support.
+#define ANDROID_NATIVE_WINDOW_HAS_SYNC 1
+
+// ---------------------------------------------------------------------------
+
+typedef const native_handle_t* buffer_handle_t;
+
+// ---------------------------------------------------------------------------
+
+typedef struct android_native_rect_t
+{
+ int32_t left;
+ int32_t top;
+ int32_t right;
+ int32_t bottom;
+} android_native_rect_t;
+
+// ---------------------------------------------------------------------------
+
+typedef struct android_native_base_t
+{
+ /* a magic value defined by the actual EGL native type */
+ int magic;
+
+ /* the sizeof() of the actual EGL native type */
+ int version;
+
+ void* reserved[4];
+
+ /* reference-counting interface */
+ void (*incRef)(struct android_native_base_t* base);
+ void (*decRef)(struct android_native_base_t* base);
+} android_native_base_t;
+
+typedef struct ANativeWindowBuffer
+{
+#ifdef __cplusplus
+ ANativeWindowBuffer() {
+ common.magic = ANDROID_NATIVE_BUFFER_MAGIC;
+ common.version = sizeof(ANativeWindowBuffer);
+ memset(common.reserved, 0, sizeof(common.reserved));
+ }
+
+ // Implement the methods that sp<ANativeWindowBuffer> expects so that it
+ // can be used to automatically refcount ANativeWindowBuffer's.
+ void incStrong(const void* /*id*/) const {
+ common.incRef(const_cast<android_native_base_t*>(&common));
+ }
+ void decStrong(const void* /*id*/) const {
+ common.decRef(const_cast<android_native_base_t*>(&common));
+ }
+#endif
+
+ struct android_native_base_t common;
+
+ int width;
+ int height;
+ int stride;
+ int format;
+ int usage;
+ uintptr_t layerCount;
+
+ void* reserved[1];
+
+ buffer_handle_t handle;
+
+ void* reserved_proc[8];
+} ANativeWindowBuffer_t;
+
+// Old typedef for backwards compatibility.
+typedef ANativeWindowBuffer_t android_native_buffer_t;
+
+// ---------------------------------------------------------------------------
+
+/* attributes queriable with query() */
+enum {
+ NATIVE_WINDOW_WIDTH = 0,
+ NATIVE_WINDOW_HEIGHT = 1,
+ NATIVE_WINDOW_FORMAT = 2,
+
+ /* The minimum number of buffers that must remain un-dequeued after a buffer
+ * has been queued. This value applies only if set_buffer_count was used to
+ * override the number of buffers and if a buffer has since been queued.
+ * Users of the set_buffer_count ANativeWindow method should query this
+ * value before calling set_buffer_count. If it is necessary to have N
+ * buffers simultaneously dequeued as part of the steady-state operation,
+ * and this query returns M then N+M buffers should be requested via
+ * native_window_set_buffer_count.
+ *
+ * Note that this value does NOT apply until a single buffer has been
+ * queued. In particular this means that it is possible to:
+ *
+ * 1. Query M = min undequeued buffers
+ * 2. Set the buffer count to N + M
+ * 3. Dequeue all N + M buffers
+ * 4. Cancel M buffers
+ * 5. Queue, dequeue, queue, dequeue, ad infinitum
+ */
+ NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS = 3,
+
+ /* Check whether queueBuffer operations on the ANativeWindow send the buffer
+ * to the window compositor. The query sets the returned 'value' argument
+ * to 1 if the ANativeWindow DOES send queued buffers directly to the window
+ * compositor and 0 if the buffers do not go directly to the window
+ * compositor.
+ *
+ * This can be used to determine whether protected buffer content should be
+ * sent to the ANativeWindow. Note, however, that a result of 1 does NOT
+ * indicate that queued buffers will be protected from applications or users
+ * capturing their contents. If that behavior is desired then some other
+ * mechanism (e.g. the GRALLOC_USAGE_PROTECTED flag) should be used in
+ * conjunction with this query.
+ */
+ NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER = 4,
+
+ /* Get the concrete type of a ANativeWindow. See below for the list of
+ * possible return values.
+ *
+ * This query should not be used outside the Android framework and will
+ * likely be removed in the near future.
+ */
+ NATIVE_WINDOW_CONCRETE_TYPE = 5,
+
+
+ /*
+ * Default width and height of ANativeWindow buffers, these are the
+ * dimensions of the window buffers irrespective of the
+ * NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS call and match the native window
+ * size unless overridden by NATIVE_WINDOW_SET_BUFFERS_USER_DIMENSIONS.
+ */
+ NATIVE_WINDOW_DEFAULT_WIDTH = 6,
+ NATIVE_WINDOW_DEFAULT_HEIGHT = 7,
+
+ /*
+ * transformation that will most-likely be applied to buffers. This is only
+ * a hint, the actual transformation applied might be different.
+ *
+ * INTENDED USE:
+ *
+ * The transform hint can be used by a producer, for instance the GLES
+ * driver, to pre-rotate the rendering such that the final transformation
+ * in the composer is identity. This can be very useful when used in
+ * conjunction with the h/w composer HAL, in situations where it
+ * cannot handle arbitrary rotations.
+ *
+ * 1. Before dequeuing a buffer, the GL driver (or any other ANW client)
+ * queries the ANW for NATIVE_WINDOW_TRANSFORM_HINT.
+ *
+ * 2. The GL driver overrides the width and height of the ANW to
+ * account for NATIVE_WINDOW_TRANSFORM_HINT. This is done by querying
+ * NATIVE_WINDOW_DEFAULT_{WIDTH | HEIGHT}, swapping the dimensions
+ * according to NATIVE_WINDOW_TRANSFORM_HINT and calling
+ * native_window_set_buffers_dimensions().
+ *
+ * 3. The GL driver dequeues a buffer of the new pre-rotated size.
+ *
+ * 4. The GL driver renders to the buffer such that the image is
+ * already transformed, that is applying NATIVE_WINDOW_TRANSFORM_HINT
+ * to the rendering.
+ *
+ * 5. The GL driver calls native_window_set_transform to apply
+ * inverse transformation to the buffer it just rendered.
+ * In order to do this, the GL driver needs
+ * to calculate the inverse of NATIVE_WINDOW_TRANSFORM_HINT, this is
+ * done easily:
+ *
+ * int hintTransform, inverseTransform;
+ * query(..., NATIVE_WINDOW_TRANSFORM_HINT, &hintTransform);
+ * inverseTransform = hintTransform;
+ * if (hintTransform & HAL_TRANSFORM_ROT_90)
+ * inverseTransform ^= HAL_TRANSFORM_ROT_180;
+ *
+ *
+ * 6. The GL driver queues the pre-transformed buffer.
+ *
+ * 7. The composer combines the buffer transform with the display
+ * transform. If the buffer transform happens to cancel out the
+ * display transform then no rotation is needed.
+ *
+ */
+ NATIVE_WINDOW_TRANSFORM_HINT = 8,
+
+ /*
+ * Boolean that indicates whether the consumer is running more than
+ * one buffer behind the producer.
+ */
+ NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND = 9,
+
+ /*
+ * The consumer gralloc usage bits currently set by the consumer.
+ * The values are defined in hardware/libhardware/include/gralloc.h.
+ */
+ NATIVE_WINDOW_CONSUMER_USAGE_BITS = 10,
+
+ /**
+ * Transformation that will by applied to buffers by the hwcomposer.
+ * This must not be set or checked by producer endpoints, and will
+ * disable the transform hint set in SurfaceFlinger (see
+ * NATIVE_WINDOW_TRANSFORM_HINT).
+ *
+ * INTENDED USE:
+ * Temporary - Please do not use this. This is intended only to be used
+ * by the camera's LEGACY mode.
+ *
+ * In situations where a SurfaceFlinger client wishes to set a transform
+ * that is not visible to the producer, and will always be applied in the
+ * hardware composer, the client can set this flag with
+ * native_window_set_buffers_sticky_transform. This can be used to rotate
+ * and flip buffers consumed by hardware composer without actually changing
+ * the aspect ratio of the buffers produced.
+ */
+ NATIVE_WINDOW_STICKY_TRANSFORM = 11,
+
+ /**
+ * The default data space for the buffers as set by the consumer.
+ * The values are defined in graphics.h.
+ */
+ NATIVE_WINDOW_DEFAULT_DATASPACE = 12,
+
+ /*
+ * Returns the age of the contents of the most recently dequeued buffer as
+ * the number of frames that have elapsed since it was last queued. For
+ * example, if the window is double-buffered, the age of any given buffer in
+ * steady state will be 2. If the dequeued buffer has never been queued, its
+ * age will be 0.
+ */
+ NATIVE_WINDOW_BUFFER_AGE = 13,
+
+ /*
+ * Returns the duration of the last dequeueBuffer call in microseconds
+ */
+ NATIVE_WINDOW_LAST_DEQUEUE_DURATION = 14,
+
+ /*
+ * Returns the duration of the last queueBuffer call in microseconds
+ */
+ NATIVE_WINDOW_LAST_QUEUE_DURATION = 15,
+
+ /*
+ * Returns the number of image layers that the ANativeWindow buffer
+ * contains. By default this is 1, unless a buffer is explicitly allocated
+ * to contain multiple layers.
+ */
+ NATIVE_WINDOW_LAYER_COUNT = 16,
+
+ /*
+ * Returns 1 if the native window is valid, 0 otherwise. native window is valid
+ * if it is safe (i.e. no crash will occur) to call any method on it.
+ */
+ NATIVE_WINDOW_IS_VALID = 17,
+
+ /*
+ * Returns 1 if NATIVE_WINDOW_GET_FRAME_TIMESTAMPS will return display
+ * present info, 0 if it won't.
+ */
+ NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_PRESENT = 18,
+
+ /*
+ * The consumer end is capable of handling protected buffers, i.e. buffer
+ * with GRALLOC_USAGE_PROTECTED usage bits on.
+ */
+ NATIVE_WINDOW_CONSUMER_IS_PROTECTED = 19,
+};
+
+/* Valid operations for the (*perform)() hook.
+ *
+ * Values marked as 'deprecated' are supported, but have been superceded by
+ * other functionality.
+ *
+ * Values marked as 'private' should be considered private to the framework.
+ * HAL implementation code with access to an ANativeWindow should not use these,
+ * as it may not interact properly with the framework's use of the
+ * ANativeWindow.
+ */
+enum {
+// clang-format off
+ NATIVE_WINDOW_SET_USAGE = 0,
+ NATIVE_WINDOW_CONNECT = 1, /* deprecated */
+ NATIVE_WINDOW_DISCONNECT = 2, /* deprecated */
+ NATIVE_WINDOW_SET_CROP = 3, /* private */
+ NATIVE_WINDOW_SET_BUFFER_COUNT = 4,
+ NATIVE_WINDOW_SET_BUFFERS_GEOMETRY = 5, /* deprecated */
+ NATIVE_WINDOW_SET_BUFFERS_TRANSFORM = 6,
+ NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP = 7,
+ NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS = 8,
+ NATIVE_WINDOW_SET_BUFFERS_FORMAT = 9,
+ NATIVE_WINDOW_SET_SCALING_MODE = 10, /* private */
+ NATIVE_WINDOW_LOCK = 11, /* private */
+ NATIVE_WINDOW_UNLOCK_AND_POST = 12, /* private */
+ NATIVE_WINDOW_API_CONNECT = 13, /* private */
+ NATIVE_WINDOW_API_DISCONNECT = 14, /* private */
+ NATIVE_WINDOW_SET_BUFFERS_USER_DIMENSIONS = 15, /* private */
+ NATIVE_WINDOW_SET_POST_TRANSFORM_CROP = 16, /* private */
+ NATIVE_WINDOW_SET_BUFFERS_STICKY_TRANSFORM = 17,/* private */
+ NATIVE_WINDOW_SET_SIDEBAND_STREAM = 18,
+ NATIVE_WINDOW_SET_BUFFERS_DATASPACE = 19,
+ NATIVE_WINDOW_SET_SURFACE_DAMAGE = 20, /* private */
+ NATIVE_WINDOW_SET_SHARED_BUFFER_MODE = 21,
+ NATIVE_WINDOW_SET_AUTO_REFRESH = 22,
+ NATIVE_WINDOW_GET_REFRESH_CYCLE_DURATION= 23,
+ NATIVE_WINDOW_GET_NEXT_FRAME_ID = 24,
+ NATIVE_WINDOW_ENABLE_FRAME_TIMESTAMPS = 25,
+ NATIVE_WINDOW_GET_COMPOSITOR_TIMING = 26,
+ NATIVE_WINDOW_GET_FRAME_TIMESTAMPS = 27,
+ NATIVE_WINDOW_GET_WIDE_COLOR_SUPPORT = 28,
+ NATIVE_WINDOW_GET_HDR_SUPPORT = 29,
+// clang-format on
+};
+
+/* parameter for NATIVE_WINDOW_[API_][DIS]CONNECT */
+enum {
+ /* Buffers will be queued by EGL via eglSwapBuffers after being filled using
+ * OpenGL ES.
+ */
+ NATIVE_WINDOW_API_EGL = 1,
+
+ /* Buffers will be queued after being filled using the CPU
+ */
+ NATIVE_WINDOW_API_CPU = 2,
+
+ /* Buffers will be queued by Stagefright after being filled by a video
+ * decoder. The video decoder can either be a software or hardware decoder.
+ */
+ NATIVE_WINDOW_API_MEDIA = 3,
+
+ /* Buffers will be queued by the the camera HAL.
+ */
+ NATIVE_WINDOW_API_CAMERA = 4,
+};
+
+/* parameter for NATIVE_WINDOW_SET_BUFFERS_TRANSFORM */
+enum {
+ /* flip source image horizontally */
+ NATIVE_WINDOW_TRANSFORM_FLIP_H = HAL_TRANSFORM_FLIP_H ,
+ /* flip source image vertically */
+ NATIVE_WINDOW_TRANSFORM_FLIP_V = HAL_TRANSFORM_FLIP_V,
+ /* rotate source image 90 degrees clock-wise, and is applied after TRANSFORM_FLIP_{H|V} */
+ NATIVE_WINDOW_TRANSFORM_ROT_90 = HAL_TRANSFORM_ROT_90,
+ /* rotate source image 180 degrees */
+ NATIVE_WINDOW_TRANSFORM_ROT_180 = HAL_TRANSFORM_ROT_180,
+ /* rotate source image 270 degrees clock-wise */
+ NATIVE_WINDOW_TRANSFORM_ROT_270 = HAL_TRANSFORM_ROT_270,
+ /* transforms source by the inverse transform of the screen it is displayed onto. This
+ * transform is applied last */
+ NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY = 0x08
+};
+
+/* parameter for NATIVE_WINDOW_SET_SCALING_MODE
+ * keep in sync with Surface.java in frameworks/base */
+enum {
+ /* the window content is not updated (frozen) until a buffer of
+ * the window size is received (enqueued)
+ */
+ NATIVE_WINDOW_SCALING_MODE_FREEZE = 0,
+ /* the buffer is scaled in both dimensions to match the window size */
+ NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW = 1,
+ /* the buffer is scaled uniformly such that the smaller dimension
+ * of the buffer matches the window size (cropping in the process)
+ */
+ NATIVE_WINDOW_SCALING_MODE_SCALE_CROP = 2,
+ /* the window is clipped to the size of the buffer's crop rectangle; pixels
+ * outside the crop rectangle are treated as if they are completely
+ * transparent.
+ */
+ NATIVE_WINDOW_SCALING_MODE_NO_SCALE_CROP = 3,
+};
+
+/* values returned by the NATIVE_WINDOW_CONCRETE_TYPE query */
+enum {
+ NATIVE_WINDOW_FRAMEBUFFER = 0, /* FramebufferNativeWindow */
+ NATIVE_WINDOW_SURFACE = 1, /* Surface */
+};
+
+/* parameter for NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP
+ *
+ * Special timestamp value to indicate that timestamps should be auto-generated
+ * by the native window when queueBuffer is called. This is equal to INT64_MIN,
+ * defined directly to avoid problems with C99/C++ inclusion of stdint.h.
+ */
+static const int64_t NATIVE_WINDOW_TIMESTAMP_AUTO = (-9223372036854775807LL-1);
+
+/* parameter for NATIVE_WINDOW_GET_FRAME_TIMESTAMPS
+ *
+ * Special timestamp value to indicate the timestamps aren't yet known or
+ * that they are invalid.
+ */
+static const int64_t NATIVE_WINDOW_TIMESTAMP_PENDING = -2;
+static const int64_t NATIVE_WINDOW_TIMESTAMP_INVALID = -1;
+
+struct ANativeWindow
+{
+#ifdef __cplusplus
+ ANativeWindow()
+ : flags(0), minSwapInterval(0), maxSwapInterval(0), xdpi(0), ydpi(0)
+ {
+ common.magic = ANDROID_NATIVE_WINDOW_MAGIC;
+ common.version = sizeof(ANativeWindow);
+ memset(common.reserved, 0, sizeof(common.reserved));
+ }
+
+ /* Implement the methods that sp<ANativeWindow> expects so that it
+ can be used to automatically refcount ANativeWindow's. */
+ void incStrong(const void* /*id*/) const {
+ common.incRef(const_cast<android_native_base_t*>(&common));
+ }
+ void decStrong(const void* /*id*/) const {
+ common.decRef(const_cast<android_native_base_t*>(&common));
+ }
+#endif
+
+ struct android_native_base_t common;
+
+ /* flags describing some attributes of this surface or its updater */
+ const uint32_t flags;
+
+ /* min swap interval supported by this updated */
+ const int minSwapInterval;
+
+ /* max swap interval supported by this updated */
+ const int maxSwapInterval;
+
+ /* horizontal and vertical resolution in DPI */
+ const float xdpi;
+ const float ydpi;
+
+ /* Some storage reserved for the OEM's driver. */
+ intptr_t oem[4];
+
+ /*
+ * Set the swap interval for this surface.
+ *
+ * Returns 0 on success or -errno on error.
+ */
+ int (*setSwapInterval)(struct ANativeWindow* window,
+ int interval);
+
+ /*
+ * Hook called by EGL to acquire a buffer. After this call, the buffer
+ * is not locked, so its content cannot be modified. This call may block if
+ * no buffers are available.
+ *
+ * The window holds a reference to the buffer between dequeueBuffer and
+ * either queueBuffer or cancelBuffer, so clients only need their own
+ * reference if they might use the buffer after queueing or canceling it.
+ * Holding a reference to a buffer after queueing or canceling it is only
+ * allowed if a specific buffer count has been set.
+ *
+ * Returns 0 on success or -errno on error.
+ *
+ * XXX: This function is deprecated. It will continue to work for some
+ * time for binary compatibility, but the new dequeueBuffer function that
+ * outputs a fence file descriptor should be used in its place.
+ */
+ int (*dequeueBuffer_DEPRECATED)(struct ANativeWindow* window,
+ struct ANativeWindowBuffer** buffer);
+
+ /*
+ * hook called by EGL to lock a buffer. This MUST be called before modifying
+ * the content of a buffer. The buffer must have been acquired with
+ * dequeueBuffer first.
+ *
+ * Returns 0 on success or -errno on error.
+ *
+ * XXX: This function is deprecated. It will continue to work for some
+ * time for binary compatibility, but it is essentially a no-op, and calls
+ * to it should be removed.
+ */
+ int (*lockBuffer_DEPRECATED)(struct ANativeWindow* window,
+ struct ANativeWindowBuffer* buffer);
+
+ /*
+ * Hook called by EGL when modifications to the render buffer are done.
+ * This unlocks and post the buffer.
+ *
+ * The window holds a reference to the buffer between dequeueBuffer and
+ * either queueBuffer or cancelBuffer, so clients only need their own
+ * reference if they might use the buffer after queueing or canceling it.
+ * Holding a reference to a buffer after queueing or canceling it is only
+ * allowed if a specific buffer count has been set.
+ *
+ * Buffers MUST be queued in the same order than they were dequeued.
+ *
+ * Returns 0 on success or -errno on error.
+ *
+ * XXX: This function is deprecated. It will continue to work for some
+ * time for binary compatibility, but the new queueBuffer function that
+ * takes a fence file descriptor should be used in its place (pass a value
+ * of -1 for the fence file descriptor if there is no valid one to pass).
+ */
+ int (*queueBuffer_DEPRECATED)(struct ANativeWindow* window,
+ struct ANativeWindowBuffer* buffer);
+
+ /*
+ * hook used to retrieve information about the native window.
+ *
+ * Returns 0 on success or -errno on error.
+ */
+ int (*query)(const struct ANativeWindow* window,
+ int what, int* value);
+
+ /*
+ * hook used to perform various operations on the surface.
+ * (*perform)() is a generic mechanism to add functionality to
+ * ANativeWindow while keeping backward binary compatibility.
+ *
+ * DO NOT CALL THIS HOOK DIRECTLY. Instead, use the helper functions
+ * defined below.
+ *
+ * (*perform)() returns -ENOENT if the 'what' parameter is not supported
+ * by the surface's implementation.
+ *
+ * See above for a list of valid operations, such as
+ * NATIVE_WINDOW_SET_USAGE or NATIVE_WINDOW_CONNECT
+ */
+ int (*perform)(struct ANativeWindow* window,
+ int operation, ... );
+
+ /*
+ * Hook used to cancel a buffer that has been dequeued.
+ * No synchronization is performed between dequeue() and cancel(), so
+ * either external synchronization is needed, or these functions must be
+ * called from the same thread.
+ *
+ * The window holds a reference to the buffer between dequeueBuffer and
+ * either queueBuffer or cancelBuffer, so clients only need their own
+ * reference if they might use the buffer after queueing or canceling it.
+ * Holding a reference to a buffer after queueing or canceling it is only
+ * allowed if a specific buffer count has been set.
+ *
+ * XXX: This function is deprecated. It will continue to work for some
+ * time for binary compatibility, but the new cancelBuffer function that
+ * takes a fence file descriptor should be used in its place (pass a value
+ * of -1 for the fence file descriptor if there is no valid one to pass).
+ */
+ int (*cancelBuffer_DEPRECATED)(struct ANativeWindow* window,
+ struct ANativeWindowBuffer* buffer);
+
+ /*
+ * Hook called by EGL to acquire a buffer. This call may block if no
+ * buffers are available.
+ *
+ * The window holds a reference to the buffer between dequeueBuffer and
+ * either queueBuffer or cancelBuffer, so clients only need their own
+ * reference if they might use the buffer after queueing or canceling it.
+ * Holding a reference to a buffer after queueing or canceling it is only
+ * allowed if a specific buffer count has been set.
+ *
+ * The libsync fence file descriptor returned in the int pointed to by the
+ * fenceFd argument will refer to the fence that must signal before the
+ * dequeued buffer may be written to. A value of -1 indicates that the
+ * caller may access the buffer immediately without waiting on a fence. If
+ * a valid file descriptor is returned (i.e. any value except -1) then the
+ * caller is responsible for closing the file descriptor.
+ *
+ * Returns 0 on success or -errno on error.
+ */
+ int (*dequeueBuffer)(struct ANativeWindow* window,
+ struct ANativeWindowBuffer** buffer, int* fenceFd);
+
+ /*
+ * Hook called by EGL when modifications to the render buffer are done.
+ * This unlocks and post the buffer.
+ *
+ * The window holds a reference to the buffer between dequeueBuffer and
+ * either queueBuffer or cancelBuffer, so clients only need their own
+ * reference if they might use the buffer after queueing or canceling it.
+ * Holding a reference to a buffer after queueing or canceling it is only
+ * allowed if a specific buffer count has been set.
+ *
+ * The fenceFd argument specifies a libsync fence file descriptor for a
+ * fence that must signal before the buffer can be accessed. If the buffer
+ * can be accessed immediately then a value of -1 should be used. The
+ * caller must not use the file descriptor after it is passed to
+ * queueBuffer, and the ANativeWindow implementation is responsible for
+ * closing it.
+ *
+ * Returns 0 on success or -errno on error.
+ */
+ int (*queueBuffer)(struct ANativeWindow* window,
+ struct ANativeWindowBuffer* buffer, int fenceFd);
+
+ /*
+ * Hook used to cancel a buffer that has been dequeued.
+ * No synchronization is performed between dequeue() and cancel(), so
+ * either external synchronization is needed, or these functions must be
+ * called from the same thread.
+ *
+ * The window holds a reference to the buffer between dequeueBuffer and
+ * either queueBuffer or cancelBuffer, so clients only need their own
+ * reference if they might use the buffer after queueing or canceling it.
+ * Holding a reference to a buffer after queueing or canceling it is only
+ * allowed if a specific buffer count has been set.
+ *
+ * The fenceFd argument specifies a libsync fence file decsriptor for a
+ * fence that must signal before the buffer can be accessed. If the buffer
+ * can be accessed immediately then a value of -1 should be used.
+ *
+ * Note that if the client has not waited on the fence that was returned
+ * from dequeueBuffer, that same fence should be passed to cancelBuffer to
+ * ensure that future uses of the buffer are preceded by a wait on that
+ * fence. The caller must not use the file descriptor after it is passed
+ * to cancelBuffer, and the ANativeWindow implementation is responsible for
+ * closing it.
+ *
+ * Returns 0 on success or -errno on error.
+ */
+ int (*cancelBuffer)(struct ANativeWindow* window,
+ struct ANativeWindowBuffer* buffer, int fenceFd);
+};
+
+ /* Backwards compatibility: use ANativeWindow (struct ANativeWindow in C).
+ * android_native_window_t is deprecated.
+ */
+typedef struct ANativeWindow ANativeWindow;
+typedef struct ANativeWindow android_native_window_t __deprecated;
+
+/*
+ * native_window_set_usage(..., usage)
+ * Sets the intended usage flags for the next buffers
+ * acquired with (*lockBuffer)() and on.
+ * By default (if this function is never called), a usage of
+ * GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE
+ * is assumed.
+ * Calling this function will usually cause following buffers to be
+ * reallocated.
+ */
+
+static inline int native_window_set_usage(
+ struct ANativeWindow* window, int usage)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_USAGE, usage);
+}
+
+/* deprecated. Always returns 0. Don't call. */
+static inline int native_window_connect(
+ struct ANativeWindow* window __UNUSED, int api __UNUSED) __deprecated;
+
+static inline int native_window_connect(
+ struct ANativeWindow* window __UNUSED, int api __UNUSED) {
+ return 0;
+}
+
+/* deprecated. Always returns 0. Don't call. */
+static inline int native_window_disconnect(
+ struct ANativeWindow* window __UNUSED, int api __UNUSED) __deprecated;
+
+static inline int native_window_disconnect(
+ struct ANativeWindow* window __UNUSED, int api __UNUSED) {
+ return 0;
+}
+
+/*
+ * native_window_set_crop(..., crop)
+ * Sets which region of the next queued buffers needs to be considered.
+ * Depending on the scaling mode, a buffer's crop region is scaled and/or
+ * cropped to match the surface's size. This function sets the crop in
+ * pre-transformed buffer pixel coordinates.
+ *
+ * The specified crop region applies to all buffers queued after it is called.
+ *
+ * If 'crop' is NULL, subsequently queued buffers won't be cropped.
+ *
+ * An error is returned if for instance the crop region is invalid, out of the
+ * buffer's bound or if the window is invalid.
+ */
+static inline int native_window_set_crop(
+ struct ANativeWindow* window,
+ android_native_rect_t const * crop)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_CROP, crop);
+}
+
+/*
+ * native_window_set_post_transform_crop(..., crop)
+ * Sets which region of the next queued buffers needs to be considered.
+ * Depending on the scaling mode, a buffer's crop region is scaled and/or
+ * cropped to match the surface's size. This function sets the crop in
+ * post-transformed pixel coordinates.
+ *
+ * The specified crop region applies to all buffers queued after it is called.
+ *
+ * If 'crop' is NULL, subsequently queued buffers won't be cropped.
+ *
+ * An error is returned if for instance the crop region is invalid, out of the
+ * buffer's bound or if the window is invalid.
+ */
+static inline int native_window_set_post_transform_crop(
+ struct ANativeWindow* window,
+ android_native_rect_t const * crop)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_POST_TRANSFORM_CROP, crop);
+}
+
+/*
+ * native_window_set_active_rect(..., active_rect)
+ *
+ * This function is deprecated and will be removed soon. For now it simply
+ * sets the post-transform crop for compatibility while multi-project commits
+ * get checked.
+ */
+static inline int native_window_set_active_rect(
+ struct ANativeWindow* window,
+ android_native_rect_t const * active_rect) __deprecated;
+
+static inline int native_window_set_active_rect(
+ struct ANativeWindow* window,
+ android_native_rect_t const * active_rect)
+{
+ return native_window_set_post_transform_crop(window, active_rect);
+}
+
+/*
+ * native_window_set_buffer_count(..., count)
+ * Sets the number of buffers associated with this native window.
+ */
+static inline int native_window_set_buffer_count(
+ struct ANativeWindow* window,
+ size_t bufferCount)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_BUFFER_COUNT, bufferCount);
+}
+
+/*
+ * native_window_set_buffers_geometry(..., int w, int h, int format)
+ * All buffers dequeued after this call will have the dimensions and format
+ * specified. A successful call to this function has the same effect as calling
+ * native_window_set_buffers_size and native_window_set_buffers_format.
+ *
+ * XXX: This function is deprecated. The native_window_set_buffers_dimensions
+ * and native_window_set_buffers_format functions should be used instead.
+ */
+static inline int native_window_set_buffers_geometry(
+ struct ANativeWindow* window,
+ int w, int h, int format) __deprecated;
+
+static inline int native_window_set_buffers_geometry(
+ struct ANativeWindow* window,
+ int w, int h, int format)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_GEOMETRY,
+ w, h, format);
+}
+
+/*
+ * native_window_set_buffers_dimensions(..., int w, int h)
+ * All buffers dequeued after this call will have the dimensions specified.
+ * In particular, all buffers will have a fixed-size, independent from the
+ * native-window size. They will be scaled according to the scaling mode
+ * (see native_window_set_scaling_mode) upon window composition.
+ *
+ * If w and h are 0, the normal behavior is restored. That is, dequeued buffers
+ * following this call will be sized to match the window's size.
+ *
+ * Calling this function will reset the window crop to a NULL value, which
+ * disables cropping of the buffers.
+ */
+static inline int native_window_set_buffers_dimensions(
+ struct ANativeWindow* window,
+ int w, int h)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS,
+ w, h);
+}
+
+/*
+ * native_window_set_buffers_user_dimensions(..., int w, int h)
+ *
+ * Sets the user buffer size for the window, which overrides the
+ * window's size. All buffers dequeued after this call will have the
+ * dimensions specified unless overridden by
+ * native_window_set_buffers_dimensions. All buffers will have a
+ * fixed-size, independent from the native-window size. They will be
+ * scaled according to the scaling mode (see
+ * native_window_set_scaling_mode) upon window composition.
+ *
+ * If w and h are 0, the normal behavior is restored. That is, the
+ * default buffer size will match the windows's size.
+ *
+ * Calling this function will reset the window crop to a NULL value, which
+ * disables cropping of the buffers.
+ */
+static inline int native_window_set_buffers_user_dimensions(
+ struct ANativeWindow* window,
+ int w, int h)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_USER_DIMENSIONS,
+ w, h);
+}
+
+/*
+ * native_window_set_buffers_format(..., int format)
+ * All buffers dequeued after this call will have the format specified.
+ *
+ * If the specified format is 0, the default buffer format will be used.
+ */
+static inline int native_window_set_buffers_format(
+ struct ANativeWindow* window,
+ int format)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_FORMAT, format);
+}
+
+/*
+ * native_window_set_buffers_data_space(..., int dataSpace)
+ * All buffers queued after this call will be associated with the dataSpace
+ * parameter specified.
+ *
+ * dataSpace specifies additional information about the buffer that's dependent
+ * on the buffer format and the endpoints. For example, it can be used to convey
+ * the color space of the image data in the buffer, or it can be used to
+ * indicate that the buffers contain depth measurement data instead of color
+ * images. The default dataSpace is 0, HAL_DATASPACE_UNKNOWN, unless it has been
+ * overridden by the consumer.
+ */
+static inline int native_window_set_buffers_data_space(
+ struct ANativeWindow* window,
+ android_dataspace_t dataSpace)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_DATASPACE,
+ dataSpace);
+}
+
+/*
+ * native_window_set_buffers_transform(..., int transform)
+ * All buffers queued after this call will be displayed transformed according
+ * to the transform parameter specified.
+ */
+static inline int native_window_set_buffers_transform(
+ struct ANativeWindow* window,
+ int transform)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_TRANSFORM,
+ transform);
+}
+
+/*
+ * native_window_set_buffers_sticky_transform(..., int transform)
+ * All buffers queued after this call will be displayed transformed according
+ * to the transform parameter specified applied on top of the regular buffer
+ * transform. Setting this transform will disable the transform hint.
+ *
+ * Temporary - This is only intended to be used by the LEGACY camera mode, do
+ * not use this for anything else.
+ */
+static inline int native_window_set_buffers_sticky_transform(
+ struct ANativeWindow* window,
+ int transform)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_STICKY_TRANSFORM,
+ transform);
+}
+
+/*
+ * native_window_set_buffers_timestamp(..., int64_t timestamp)
+ * All buffers queued after this call will be associated with the timestamp
+ * parameter specified. If the timestamp is set to NATIVE_WINDOW_TIMESTAMP_AUTO
+ * (the default), timestamps will be generated automatically when queueBuffer is
+ * called. The timestamp is measured in nanoseconds, and is normally monotonically
+ * increasing. The timestamp should be unaffected by time-of-day adjustments,
+ * and for a camera should be strictly monotonic but for a media player may be
+ * reset when the position is set.
+ */
+static inline int native_window_set_buffers_timestamp(
+ struct ANativeWindow* window,
+ int64_t timestamp)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP,
+ timestamp);
+}
+
+/*
+ * native_window_set_scaling_mode(..., int mode)
+ * All buffers queued after this call will be associated with the scaling mode
+ * specified.
+ */
+static inline int native_window_set_scaling_mode(
+ struct ANativeWindow* window,
+ int mode)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_SCALING_MODE,
+ mode);
+}
+
+/*
+ * native_window_api_connect(..., int api)
+ * connects an API to this window. only one API can be connected at a time.
+ * Returns -EINVAL if for some reason the window cannot be connected, which
+ * can happen if it's connected to some other API.
+ */
+static inline int native_window_api_connect(
+ struct ANativeWindow* window, int api)
+{
+ return window->perform(window, NATIVE_WINDOW_API_CONNECT, api);
+}
+
+/*
+ * native_window_api_disconnect(..., int api)
+ * disconnect the API from this window.
+ * An error is returned if for instance the window wasn't connected in the
+ * first place.
+ */
+static inline int native_window_api_disconnect(
+ struct ANativeWindow* window, int api)
+{
+ return window->perform(window, NATIVE_WINDOW_API_DISCONNECT, api);
+}
+
+/*
+ * native_window_dequeue_buffer_and_wait(...)
+ * Dequeue a buffer and wait on the fence associated with that buffer. The
+ * buffer may safely be accessed immediately upon this function returning. An
+ * error is returned if either of the dequeue or the wait operations fail.
+ */
+static inline int native_window_dequeue_buffer_and_wait(ANativeWindow *anw,
+ struct ANativeWindowBuffer** anb) {
+ return anw->dequeueBuffer_DEPRECATED(anw, anb);
+}
+
+/*
+ * native_window_set_sideband_stream(..., native_handle_t*)
+ * Attach a sideband buffer stream to a native window.
+ */
+static inline int native_window_set_sideband_stream(
+ struct ANativeWindow* window,
+ native_handle_t* sidebandHandle)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_SIDEBAND_STREAM,
+ sidebandHandle);
+}
+
+/*
+ * native_window_set_surface_damage(..., android_native_rect_t* rects, int numRects)
+ * Set the surface damage (i.e., the region of the surface that has changed
+ * since the previous frame). The damage set by this call will be reset (to the
+ * default of full-surface damage) after calling queue, so this must be called
+ * prior to every frame with damage that does not cover the whole surface if the
+ * caller desires downstream consumers to use this optimization.
+ *
+ * The damage region is specified as an array of rectangles, with the important
+ * caveat that the origin of the surface is considered to be the bottom-left
+ * corner, as in OpenGL ES.
+ *
+ * If numRects is set to 0, rects may be NULL, and the surface damage will be
+ * set to the full surface (the same as if this function had not been called for
+ * this frame).
+ */
+static inline int native_window_set_surface_damage(
+ struct ANativeWindow* window,
+ const android_native_rect_t* rects, size_t numRects)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_SURFACE_DAMAGE,
+ rects, numRects);
+}
+
+/*
+ * native_window_set_shared_buffer_mode(..., bool sharedBufferMode)
+ * Enable/disable shared buffer mode
+ */
+static inline int native_window_set_shared_buffer_mode(
+ struct ANativeWindow* window,
+ bool sharedBufferMode)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_SHARED_BUFFER_MODE,
+ sharedBufferMode);
+}
+
+/*
+ * native_window_set_auto_refresh(..., autoRefresh)
+ * Enable/disable auto refresh when in shared buffer mode
+ */
+static inline int native_window_set_auto_refresh(
+ struct ANativeWindow* window,
+ bool autoRefresh)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_AUTO_REFRESH, autoRefresh);
+}
+
+static inline int native_window_get_refresh_cycle_duration(
+ struct ANativeWindow* window,
+ int64_t* outRefreshDuration)
+{
+ return window->perform(window, NATIVE_WINDOW_GET_REFRESH_CYCLE_DURATION,
+ outRefreshDuration);
+}
+
+static inline int native_window_get_next_frame_id(
+ struct ANativeWindow* window, uint64_t* frameId)
+{
+ return window->perform(window, NATIVE_WINDOW_GET_NEXT_FRAME_ID, frameId);
+}
+
+static inline int native_window_enable_frame_timestamps(
+ struct ANativeWindow* window, bool enable)
+{
+ return window->perform(window, NATIVE_WINDOW_ENABLE_FRAME_TIMESTAMPS,
+ enable);
+}
+
+static inline int native_window_get_compositor_timing(
+ struct ANativeWindow* window,
+ int64_t* compositeDeadline, int64_t* compositeInterval,
+ int64_t* compositeToPresentLatency)
+{
+ return window->perform(window, NATIVE_WINDOW_GET_COMPOSITOR_TIMING,
+ compositeDeadline, compositeInterval, compositeToPresentLatency);
+}
+
+static inline int native_window_get_frame_timestamps(
+ struct ANativeWindow* window, uint64_t frameId,
+ int64_t* outRequestedPresentTime, int64_t* outAcquireTime,
+ int64_t* outLatchTime, int64_t* outFirstRefreshStartTime,
+ int64_t* outLastRefreshStartTime, int64_t* outGpuCompositionDoneTime,
+ int64_t* outDisplayPresentTime, int64_t* outDequeueReadyTime,
+ int64_t* outReleaseTime)
+{
+ return window->perform(window, NATIVE_WINDOW_GET_FRAME_TIMESTAMPS,
+ frameId, outRequestedPresentTime, outAcquireTime, outLatchTime,
+ outFirstRefreshStartTime, outLastRefreshStartTime,
+ outGpuCompositionDoneTime, outDisplayPresentTime,
+ outDequeueReadyTime, outReleaseTime);
+}
+
+static inline int native_window_get_wide_color_support(
+ struct ANativeWindow* window, bool* outSupport) {
+ return window->perform(window, NATIVE_WINDOW_GET_WIDE_COLOR_SUPPORT,
+ outSupport);
+}
+
+static inline int native_window_get_hdr_support(struct ANativeWindow* window,
+ bool* outSupport) {
+ return window->perform(window, NATIVE_WINDOW_GET_HDR_SUPPORT, outSupport);
+}
+
+__END_DECLS
diff --git a/libsystem/include/system/window.h b/libsystem/include/system/window.h
index f439705..efa10d6 100644
--- a/libsystem/include/system/window.h
+++ b/libsystem/include/system/window.h
@@ -17,997 +17,6 @@
#ifndef SYSTEM_CORE_INCLUDE_ANDROID_WINDOW_H
#define SYSTEM_CORE_INCLUDE_ANDROID_WINDOW_H
-#include <cutils/native_handle.h>
-#include <errno.h>
-#include <limits.h>
-#include <stdint.h>
-#include <string.h>
-#include <sys/cdefs.h>
-#include <system/graphics.h>
-#include <unistd.h>
-#include <stdbool.h>
-
-#ifndef __UNUSED
-#define __UNUSED __attribute__((__unused__))
-#endif
-#ifndef __deprecated
-#define __deprecated __attribute__((__deprecated__))
-#endif
-
-__BEGIN_DECLS
-
-/*****************************************************************************/
-
-#ifdef __cplusplus
-#define ANDROID_NATIVE_UNSIGNED_CAST(x) static_cast<unsigned int>(x)
-#else
-#define ANDROID_NATIVE_UNSIGNED_CAST(x) ((unsigned int)(x))
-#endif
-
-#define ANDROID_NATIVE_MAKE_CONSTANT(a,b,c,d) \
- ((ANDROID_NATIVE_UNSIGNED_CAST(a) << 24) | \
- (ANDROID_NATIVE_UNSIGNED_CAST(b) << 16) | \
- (ANDROID_NATIVE_UNSIGNED_CAST(c) << 8) | \
- (ANDROID_NATIVE_UNSIGNED_CAST(d)))
-
-#define ANDROID_NATIVE_WINDOW_MAGIC \
- ANDROID_NATIVE_MAKE_CONSTANT('_','w','n','d')
-
-#define ANDROID_NATIVE_BUFFER_MAGIC \
- ANDROID_NATIVE_MAKE_CONSTANT('_','b','f','r')
-
-// ---------------------------------------------------------------------------
-
-// This #define may be used to conditionally compile device-specific code to
-// support either the prior ANativeWindow interface, which did not pass libsync
-// fences around, or the new interface that does. This #define is only present
-// when the ANativeWindow interface does include libsync support.
-#define ANDROID_NATIVE_WINDOW_HAS_SYNC 1
-
-// ---------------------------------------------------------------------------
-
-typedef const native_handle_t* buffer_handle_t;
-
-// ---------------------------------------------------------------------------
-
-typedef struct android_native_rect_t
-{
- int32_t left;
- int32_t top;
- int32_t right;
- int32_t bottom;
-} android_native_rect_t;
-
-// ---------------------------------------------------------------------------
-
-typedef struct android_native_base_t
-{
- /* a magic value defined by the actual EGL native type */
- int magic;
-
- /* the sizeof() of the actual EGL native type */
- int version;
-
- void* reserved[4];
-
- /* reference-counting interface */
- void (*incRef)(struct android_native_base_t* base);
- void (*decRef)(struct android_native_base_t* base);
-} android_native_base_t;
-
-typedef struct ANativeWindowBuffer
-{
-#ifdef __cplusplus
- ANativeWindowBuffer() {
- common.magic = ANDROID_NATIVE_BUFFER_MAGIC;
- common.version = sizeof(ANativeWindowBuffer);
- memset(common.reserved, 0, sizeof(common.reserved));
- }
-
- // Implement the methods that sp<ANativeWindowBuffer> expects so that it
- // can be used to automatically refcount ANativeWindowBuffer's.
- void incStrong(const void* /*id*/) const {
- common.incRef(const_cast<android_native_base_t*>(&common));
- }
- void decStrong(const void* /*id*/) const {
- common.decRef(const_cast<android_native_base_t*>(&common));
- }
-#endif
-
- struct android_native_base_t common;
-
- int width;
- int height;
- int stride;
- int format;
- int usage;
-
- void* reserved[2];
-
- buffer_handle_t handle;
-
- void* reserved_proc[8];
-} ANativeWindowBuffer_t;
-
-// Old typedef for backwards compatibility.
-typedef ANativeWindowBuffer_t android_native_buffer_t;
-
-// ---------------------------------------------------------------------------
-
-/* attributes queriable with query() */
-enum {
- NATIVE_WINDOW_WIDTH = 0,
- NATIVE_WINDOW_HEIGHT = 1,
- NATIVE_WINDOW_FORMAT = 2,
-
- /* The minimum number of buffers that must remain un-dequeued after a buffer
- * has been queued. This value applies only if set_buffer_count was used to
- * override the number of buffers and if a buffer has since been queued.
- * Users of the set_buffer_count ANativeWindow method should query this
- * value before calling set_buffer_count. If it is necessary to have N
- * buffers simultaneously dequeued as part of the steady-state operation,
- * and this query returns M then N+M buffers should be requested via
- * native_window_set_buffer_count.
- *
- * Note that this value does NOT apply until a single buffer has been
- * queued. In particular this means that it is possible to:
- *
- * 1. Query M = min undequeued buffers
- * 2. Set the buffer count to N + M
- * 3. Dequeue all N + M buffers
- * 4. Cancel M buffers
- * 5. Queue, dequeue, queue, dequeue, ad infinitum
- */
- NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS = 3,
-
- /* Check whether queueBuffer operations on the ANativeWindow send the buffer
- * to the window compositor. The query sets the returned 'value' argument
- * to 1 if the ANativeWindow DOES send queued buffers directly to the window
- * compositor and 0 if the buffers do not go directly to the window
- * compositor.
- *
- * This can be used to determine whether protected buffer content should be
- * sent to the ANativeWindow. Note, however, that a result of 1 does NOT
- * indicate that queued buffers will be protected from applications or users
- * capturing their contents. If that behavior is desired then some other
- * mechanism (e.g. the GRALLOC_USAGE_PROTECTED flag) should be used in
- * conjunction with this query.
- */
- NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER = 4,
-
- /* Get the concrete type of a ANativeWindow. See below for the list of
- * possible return values.
- *
- * This query should not be used outside the Android framework and will
- * likely be removed in the near future.
- */
- NATIVE_WINDOW_CONCRETE_TYPE = 5,
-
-
- /*
- * Default width and height of ANativeWindow buffers, these are the
- * dimensions of the window buffers irrespective of the
- * NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS call and match the native window
- * size unless overridden by NATIVE_WINDOW_SET_BUFFERS_USER_DIMENSIONS.
- */
- NATIVE_WINDOW_DEFAULT_WIDTH = 6,
- NATIVE_WINDOW_DEFAULT_HEIGHT = 7,
-
- /*
- * transformation that will most-likely be applied to buffers. This is only
- * a hint, the actual transformation applied might be different.
- *
- * INTENDED USE:
- *
- * The transform hint can be used by a producer, for instance the GLES
- * driver, to pre-rotate the rendering such that the final transformation
- * in the composer is identity. This can be very useful when used in
- * conjunction with the h/w composer HAL, in situations where it
- * cannot handle arbitrary rotations.
- *
- * 1. Before dequeuing a buffer, the GL driver (or any other ANW client)
- * queries the ANW for NATIVE_WINDOW_TRANSFORM_HINT.
- *
- * 2. The GL driver overrides the width and height of the ANW to
- * account for NATIVE_WINDOW_TRANSFORM_HINT. This is done by querying
- * NATIVE_WINDOW_DEFAULT_{WIDTH | HEIGHT}, swapping the dimensions
- * according to NATIVE_WINDOW_TRANSFORM_HINT and calling
- * native_window_set_buffers_dimensions().
- *
- * 3. The GL driver dequeues a buffer of the new pre-rotated size.
- *
- * 4. The GL driver renders to the buffer such that the image is
- * already transformed, that is applying NATIVE_WINDOW_TRANSFORM_HINT
- * to the rendering.
- *
- * 5. The GL driver calls native_window_set_transform to apply
- * inverse transformation to the buffer it just rendered.
- * In order to do this, the GL driver needs
- * to calculate the inverse of NATIVE_WINDOW_TRANSFORM_HINT, this is
- * done easily:
- *
- * int hintTransform, inverseTransform;
- * query(..., NATIVE_WINDOW_TRANSFORM_HINT, &hintTransform);
- * inverseTransform = hintTransform;
- * if (hintTransform & HAL_TRANSFORM_ROT_90)
- * inverseTransform ^= HAL_TRANSFORM_ROT_180;
- *
- *
- * 6. The GL driver queues the pre-transformed buffer.
- *
- * 7. The composer combines the buffer transform with the display
- * transform. If the buffer transform happens to cancel out the
- * display transform then no rotation is needed.
- *
- */
- NATIVE_WINDOW_TRANSFORM_HINT = 8,
-
- /*
- * Boolean that indicates whether the consumer is running more than
- * one buffer behind the producer.
- */
- NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND = 9,
-
- /*
- * The consumer gralloc usage bits currently set by the consumer.
- * The values are defined in hardware/libhardware/include/gralloc.h.
- */
- NATIVE_WINDOW_CONSUMER_USAGE_BITS = 10,
-
- /**
- * Transformation that will by applied to buffers by the hwcomposer.
- * This must not be set or checked by producer endpoints, and will
- * disable the transform hint set in SurfaceFlinger (see
- * NATIVE_WINDOW_TRANSFORM_HINT).
- *
- * INTENDED USE:
- * Temporary - Please do not use this. This is intended only to be used
- * by the camera's LEGACY mode.
- *
- * In situations where a SurfaceFlinger client wishes to set a transform
- * that is not visible to the producer, and will always be applied in the
- * hardware composer, the client can set this flag with
- * native_window_set_buffers_sticky_transform. This can be used to rotate
- * and flip buffers consumed by hardware composer without actually changing
- * the aspect ratio of the buffers produced.
- */
- NATIVE_WINDOW_STICKY_TRANSFORM = 11,
-
- /**
- * The default data space for the buffers as set by the consumer.
- * The values are defined in graphics.h.
- */
- NATIVE_WINDOW_DEFAULT_DATASPACE = 12,
-
- /*
- * Returns the age of the contents of the most recently dequeued buffer as
- * the number of frames that have elapsed since it was last queued. For
- * example, if the window is double-buffered, the age of any given buffer in
- * steady state will be 2. If the dequeued buffer has never been queued, its
- * age will be 0.
- */
- NATIVE_WINDOW_BUFFER_AGE = 13,
-
- /*
- * Returns the duration of the last dequeueBuffer call in microseconds
- */
- NATIVE_WINDOW_LAST_DEQUEUE_DURATION = 14,
-
- /*
- * Returns the duration of the last queueBuffer call in microseconds
- */
- NATIVE_WINDOW_LAST_QUEUE_DURATION = 15,
-};
-
-/* Valid operations for the (*perform)() hook.
- *
- * Values marked as 'deprecated' are supported, but have been superceded by
- * other functionality.
- *
- * Values marked as 'private' should be considered private to the framework.
- * HAL implementation code with access to an ANativeWindow should not use these,
- * as it may not interact properly with the framework's use of the
- * ANativeWindow.
- */
-enum {
- NATIVE_WINDOW_SET_USAGE = 0,
- NATIVE_WINDOW_CONNECT = 1, /* deprecated */
- NATIVE_WINDOW_DISCONNECT = 2, /* deprecated */
- NATIVE_WINDOW_SET_CROP = 3, /* private */
- NATIVE_WINDOW_SET_BUFFER_COUNT = 4,
- NATIVE_WINDOW_SET_BUFFERS_GEOMETRY = 5, /* deprecated */
- NATIVE_WINDOW_SET_BUFFERS_TRANSFORM = 6,
- NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP = 7,
- NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS = 8,
- NATIVE_WINDOW_SET_BUFFERS_FORMAT = 9,
- NATIVE_WINDOW_SET_SCALING_MODE = 10, /* private */
- NATIVE_WINDOW_LOCK = 11, /* private */
- NATIVE_WINDOW_UNLOCK_AND_POST = 12, /* private */
- NATIVE_WINDOW_API_CONNECT = 13, /* private */
- NATIVE_WINDOW_API_DISCONNECT = 14, /* private */
- NATIVE_WINDOW_SET_BUFFERS_USER_DIMENSIONS = 15, /* private */
- NATIVE_WINDOW_SET_POST_TRANSFORM_CROP = 16, /* private */
- NATIVE_WINDOW_SET_BUFFERS_STICKY_TRANSFORM = 17,/* private */
- NATIVE_WINDOW_SET_SIDEBAND_STREAM = 18,
- NATIVE_WINDOW_SET_BUFFERS_DATASPACE = 19,
- NATIVE_WINDOW_SET_SURFACE_DAMAGE = 20, /* private */
- NATIVE_WINDOW_SET_SHARED_BUFFER_MODE = 21,
- NATIVE_WINDOW_SET_AUTO_REFRESH = 22,
- NATIVE_WINDOW_GET_FRAME_TIMESTAMPS = 23,
-};
-
-/* parameter for NATIVE_WINDOW_[API_][DIS]CONNECT */
-enum {
- /* Buffers will be queued by EGL via eglSwapBuffers after being filled using
- * OpenGL ES.
- */
- NATIVE_WINDOW_API_EGL = 1,
-
- /* Buffers will be queued after being filled using the CPU
- */
- NATIVE_WINDOW_API_CPU = 2,
-
- /* Buffers will be queued by Stagefright after being filled by a video
- * decoder. The video decoder can either be a software or hardware decoder.
- */
- NATIVE_WINDOW_API_MEDIA = 3,
-
- /* Buffers will be queued by the the camera HAL.
- */
- NATIVE_WINDOW_API_CAMERA = 4,
-};
-
-/* parameter for NATIVE_WINDOW_SET_BUFFERS_TRANSFORM */
-enum {
- /* flip source image horizontally */
- NATIVE_WINDOW_TRANSFORM_FLIP_H = HAL_TRANSFORM_FLIP_H ,
- /* flip source image vertically */
- NATIVE_WINDOW_TRANSFORM_FLIP_V = HAL_TRANSFORM_FLIP_V,
- /* rotate source image 90 degrees clock-wise, and is applied after TRANSFORM_FLIP_{H|V} */
- NATIVE_WINDOW_TRANSFORM_ROT_90 = HAL_TRANSFORM_ROT_90,
- /* rotate source image 180 degrees */
- NATIVE_WINDOW_TRANSFORM_ROT_180 = HAL_TRANSFORM_ROT_180,
- /* rotate source image 270 degrees clock-wise */
- NATIVE_WINDOW_TRANSFORM_ROT_270 = HAL_TRANSFORM_ROT_270,
- /* transforms source by the inverse transform of the screen it is displayed onto. This
- * transform is applied last */
- NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY = 0x08
-};
-
-/* parameter for NATIVE_WINDOW_SET_SCALING_MODE
- * keep in sync with Surface.java in frameworks/base */
-enum {
- /* the window content is not updated (frozen) until a buffer of
- * the window size is received (enqueued)
- */
- NATIVE_WINDOW_SCALING_MODE_FREEZE = 0,
- /* the buffer is scaled in both dimensions to match the window size */
- NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW = 1,
- /* the buffer is scaled uniformly such that the smaller dimension
- * of the buffer matches the window size (cropping in the process)
- */
- NATIVE_WINDOW_SCALING_MODE_SCALE_CROP = 2,
- /* the window is clipped to the size of the buffer's crop rectangle; pixels
- * outside the crop rectangle are treated as if they are completely
- * transparent.
- */
- NATIVE_WINDOW_SCALING_MODE_NO_SCALE_CROP = 3,
-};
-
-/* values returned by the NATIVE_WINDOW_CONCRETE_TYPE query */
-enum {
- NATIVE_WINDOW_FRAMEBUFFER = 0, /* FramebufferNativeWindow */
- NATIVE_WINDOW_SURFACE = 1, /* Surface */
-};
-
-/* parameter for NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP
- *
- * Special timestamp value to indicate that timestamps should be auto-generated
- * by the native window when queueBuffer is called. This is equal to INT64_MIN,
- * defined directly to avoid problems with C99/C++ inclusion of stdint.h.
- */
-static const int64_t NATIVE_WINDOW_TIMESTAMP_AUTO = (-9223372036854775807LL-1);
-
-struct ANativeWindow
-{
-#ifdef __cplusplus
- ANativeWindow()
- : flags(0), minSwapInterval(0), maxSwapInterval(0), xdpi(0), ydpi(0)
- {
- common.magic = ANDROID_NATIVE_WINDOW_MAGIC;
- common.version = sizeof(ANativeWindow);
- memset(common.reserved, 0, sizeof(common.reserved));
- }
-
- /* Implement the methods that sp<ANativeWindow> expects so that it
- can be used to automatically refcount ANativeWindow's. */
- void incStrong(const void* /*id*/) const {
- common.incRef(const_cast<android_native_base_t*>(&common));
- }
- void decStrong(const void* /*id*/) const {
- common.decRef(const_cast<android_native_base_t*>(&common));
- }
-#endif
-
- struct android_native_base_t common;
-
- /* flags describing some attributes of this surface or its updater */
- const uint32_t flags;
-
- /* min swap interval supported by this updated */
- const int minSwapInterval;
-
- /* max swap interval supported by this updated */
- const int maxSwapInterval;
-
- /* horizontal and vertical resolution in DPI */
- const float xdpi;
- const float ydpi;
-
- /* Some storage reserved for the OEM's driver. */
- intptr_t oem[4];
-
- /*
- * Set the swap interval for this surface.
- *
- * Returns 0 on success or -errno on error.
- */
- int (*setSwapInterval)(struct ANativeWindow* window,
- int interval);
-
- /*
- * Hook called by EGL to acquire a buffer. After this call, the buffer
- * is not locked, so its content cannot be modified. This call may block if
- * no buffers are available.
- *
- * The window holds a reference to the buffer between dequeueBuffer and
- * either queueBuffer or cancelBuffer, so clients only need their own
- * reference if they might use the buffer after queueing or canceling it.
- * Holding a reference to a buffer after queueing or canceling it is only
- * allowed if a specific buffer count has been set.
- *
- * Returns 0 on success or -errno on error.
- *
- * XXX: This function is deprecated. It will continue to work for some
- * time for binary compatibility, but the new dequeueBuffer function that
- * outputs a fence file descriptor should be used in its place.
- */
- int (*dequeueBuffer_DEPRECATED)(struct ANativeWindow* window,
- struct ANativeWindowBuffer** buffer);
-
- /*
- * hook called by EGL to lock a buffer. This MUST be called before modifying
- * the content of a buffer. The buffer must have been acquired with
- * dequeueBuffer first.
- *
- * Returns 0 on success or -errno on error.
- *
- * XXX: This function is deprecated. It will continue to work for some
- * time for binary compatibility, but it is essentially a no-op, and calls
- * to it should be removed.
- */
- int (*lockBuffer_DEPRECATED)(struct ANativeWindow* window,
- struct ANativeWindowBuffer* buffer);
-
- /*
- * Hook called by EGL when modifications to the render buffer are done.
- * This unlocks and post the buffer.
- *
- * The window holds a reference to the buffer between dequeueBuffer and
- * either queueBuffer or cancelBuffer, so clients only need their own
- * reference if they might use the buffer after queueing or canceling it.
- * Holding a reference to a buffer after queueing or canceling it is only
- * allowed if a specific buffer count has been set.
- *
- * Buffers MUST be queued in the same order than they were dequeued.
- *
- * Returns 0 on success or -errno on error.
- *
- * XXX: This function is deprecated. It will continue to work for some
- * time for binary compatibility, but the new queueBuffer function that
- * takes a fence file descriptor should be used in its place (pass a value
- * of -1 for the fence file descriptor if there is no valid one to pass).
- */
- int (*queueBuffer_DEPRECATED)(struct ANativeWindow* window,
- struct ANativeWindowBuffer* buffer);
-
- /*
- * hook used to retrieve information about the native window.
- *
- * Returns 0 on success or -errno on error.
- */
- int (*query)(const struct ANativeWindow* window,
- int what, int* value);
-
- /*
- * hook used to perform various operations on the surface.
- * (*perform)() is a generic mechanism to add functionality to
- * ANativeWindow while keeping backward binary compatibility.
- *
- * DO NOT CALL THIS HOOK DIRECTLY. Instead, use the helper functions
- * defined below.
- *
- * (*perform)() returns -ENOENT if the 'what' parameter is not supported
- * by the surface's implementation.
- *
- * See above for a list of valid operations, such as
- * NATIVE_WINDOW_SET_USAGE or NATIVE_WINDOW_CONNECT
- */
- int (*perform)(struct ANativeWindow* window,
- int operation, ... );
-
- /*
- * Hook used to cancel a buffer that has been dequeued.
- * No synchronization is performed between dequeue() and cancel(), so
- * either external synchronization is needed, or these functions must be
- * called from the same thread.
- *
- * The window holds a reference to the buffer between dequeueBuffer and
- * either queueBuffer or cancelBuffer, so clients only need their own
- * reference if they might use the buffer after queueing or canceling it.
- * Holding a reference to a buffer after queueing or canceling it is only
- * allowed if a specific buffer count has been set.
- *
- * XXX: This function is deprecated. It will continue to work for some
- * time for binary compatibility, but the new cancelBuffer function that
- * takes a fence file descriptor should be used in its place (pass a value
- * of -1 for the fence file descriptor if there is no valid one to pass).
- */
- int (*cancelBuffer_DEPRECATED)(struct ANativeWindow* window,
- struct ANativeWindowBuffer* buffer);
-
- /*
- * Hook called by EGL to acquire a buffer. This call may block if no
- * buffers are available.
- *
- * The window holds a reference to the buffer between dequeueBuffer and
- * either queueBuffer or cancelBuffer, so clients only need their own
- * reference if they might use the buffer after queueing or canceling it.
- * Holding a reference to a buffer after queueing or canceling it is only
- * allowed if a specific buffer count has been set.
- *
- * The libsync fence file descriptor returned in the int pointed to by the
- * fenceFd argument will refer to the fence that must signal before the
- * dequeued buffer may be written to. A value of -1 indicates that the
- * caller may access the buffer immediately without waiting on a fence. If
- * a valid file descriptor is returned (i.e. any value except -1) then the
- * caller is responsible for closing the file descriptor.
- *
- * Returns 0 on success or -errno on error.
- */
- int (*dequeueBuffer)(struct ANativeWindow* window,
- struct ANativeWindowBuffer** buffer, int* fenceFd);
-
- /*
- * Hook called by EGL when modifications to the render buffer are done.
- * This unlocks and post the buffer.
- *
- * The window holds a reference to the buffer between dequeueBuffer and
- * either queueBuffer or cancelBuffer, so clients only need their own
- * reference if they might use the buffer after queueing or canceling it.
- * Holding a reference to a buffer after queueing or canceling it is only
- * allowed if a specific buffer count has been set.
- *
- * The fenceFd argument specifies a libsync fence file descriptor for a
- * fence that must signal before the buffer can be accessed. If the buffer
- * can be accessed immediately then a value of -1 should be used. The
- * caller must not use the file descriptor after it is passed to
- * queueBuffer, and the ANativeWindow implementation is responsible for
- * closing it.
- *
- * Returns 0 on success or -errno on error.
- */
- int (*queueBuffer)(struct ANativeWindow* window,
- struct ANativeWindowBuffer* buffer, int fenceFd);
-
- /*
- * Hook used to cancel a buffer that has been dequeued.
- * No synchronization is performed between dequeue() and cancel(), so
- * either external synchronization is needed, or these functions must be
- * called from the same thread.
- *
- * The window holds a reference to the buffer between dequeueBuffer and
- * either queueBuffer or cancelBuffer, so clients only need their own
- * reference if they might use the buffer after queueing or canceling it.
- * Holding a reference to a buffer after queueing or canceling it is only
- * allowed if a specific buffer count has been set.
- *
- * The fenceFd argument specifies a libsync fence file decsriptor for a
- * fence that must signal before the buffer can be accessed. If the buffer
- * can be accessed immediately then a value of -1 should be used.
- *
- * Note that if the client has not waited on the fence that was returned
- * from dequeueBuffer, that same fence should be passed to cancelBuffer to
- * ensure that future uses of the buffer are preceded by a wait on that
- * fence. The caller must not use the file descriptor after it is passed
- * to cancelBuffer, and the ANativeWindow implementation is responsible for
- * closing it.
- *
- * Returns 0 on success or -errno on error.
- */
- int (*cancelBuffer)(struct ANativeWindow* window,
- struct ANativeWindowBuffer* buffer, int fenceFd);
-};
-
- /* Backwards compatibility: use ANativeWindow (struct ANativeWindow in C).
- * android_native_window_t is deprecated.
- */
-typedef struct ANativeWindow ANativeWindow;
-typedef struct ANativeWindow android_native_window_t __deprecated;
-
-/*
- * native_window_set_usage(..., usage)
- * Sets the intended usage flags for the next buffers
- * acquired with (*lockBuffer)() and on.
- * By default (if this function is never called), a usage of
- * GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE
- * is assumed.
- * Calling this function will usually cause following buffers to be
- * reallocated.
- */
-
-static inline int native_window_set_usage(
- struct ANativeWindow* window, int usage)
-{
- return window->perform(window, NATIVE_WINDOW_SET_USAGE, usage);
-}
-
-/* deprecated. Always returns 0. Don't call. */
-static inline int native_window_connect(
- struct ANativeWindow* window __UNUSED, int api __UNUSED) __deprecated;
-
-static inline int native_window_connect(
- struct ANativeWindow* window __UNUSED, int api __UNUSED) {
- return 0;
-}
-
-/* deprecated. Always returns 0. Don't call. */
-static inline int native_window_disconnect(
- struct ANativeWindow* window __UNUSED, int api __UNUSED) __deprecated;
-
-static inline int native_window_disconnect(
- struct ANativeWindow* window __UNUSED, int api __UNUSED) {
- return 0;
-}
-
-/*
- * native_window_set_crop(..., crop)
- * Sets which region of the next queued buffers needs to be considered.
- * Depending on the scaling mode, a buffer's crop region is scaled and/or
- * cropped to match the surface's size. This function sets the crop in
- * pre-transformed buffer pixel coordinates.
- *
- * The specified crop region applies to all buffers queued after it is called.
- *
- * If 'crop' is NULL, subsequently queued buffers won't be cropped.
- *
- * An error is returned if for instance the crop region is invalid, out of the
- * buffer's bound or if the window is invalid.
- */
-static inline int native_window_set_crop(
- struct ANativeWindow* window,
- android_native_rect_t const * crop)
-{
- return window->perform(window, NATIVE_WINDOW_SET_CROP, crop);
-}
-
-/*
- * native_window_set_post_transform_crop(..., crop)
- * Sets which region of the next queued buffers needs to be considered.
- * Depending on the scaling mode, a buffer's crop region is scaled and/or
- * cropped to match the surface's size. This function sets the crop in
- * post-transformed pixel coordinates.
- *
- * The specified crop region applies to all buffers queued after it is called.
- *
- * If 'crop' is NULL, subsequently queued buffers won't be cropped.
- *
- * An error is returned if for instance the crop region is invalid, out of the
- * buffer's bound or if the window is invalid.
- */
-static inline int native_window_set_post_transform_crop(
- struct ANativeWindow* window,
- android_native_rect_t const * crop)
-{
- return window->perform(window, NATIVE_WINDOW_SET_POST_TRANSFORM_CROP, crop);
-}
-
-/*
- * native_window_set_active_rect(..., active_rect)
- *
- * This function is deprecated and will be removed soon. For now it simply
- * sets the post-transform crop for compatibility while multi-project commits
- * get checked.
- */
-static inline int native_window_set_active_rect(
- struct ANativeWindow* window,
- android_native_rect_t const * active_rect) __deprecated;
-
-static inline int native_window_set_active_rect(
- struct ANativeWindow* window,
- android_native_rect_t const * active_rect)
-{
- return native_window_set_post_transform_crop(window, active_rect);
-}
-
-/*
- * native_window_set_buffer_count(..., count)
- * Sets the number of buffers associated with this native window.
- */
-static inline int native_window_set_buffer_count(
- struct ANativeWindow* window,
- size_t bufferCount)
-{
- return window->perform(window, NATIVE_WINDOW_SET_BUFFER_COUNT, bufferCount);
-}
-
-/*
- * native_window_set_buffers_geometry(..., int w, int h, int format)
- * All buffers dequeued after this call will have the dimensions and format
- * specified. A successful call to this function has the same effect as calling
- * native_window_set_buffers_size and native_window_set_buffers_format.
- *
- * XXX: This function is deprecated. The native_window_set_buffers_dimensions
- * and native_window_set_buffers_format functions should be used instead.
- */
-static inline int native_window_set_buffers_geometry(
- struct ANativeWindow* window,
- int w, int h, int format) __deprecated;
-
-static inline int native_window_set_buffers_geometry(
- struct ANativeWindow* window,
- int w, int h, int format)
-{
- return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_GEOMETRY,
- w, h, format);
-}
-
-/*
- * native_window_set_buffers_dimensions(..., int w, int h)
- * All buffers dequeued after this call will have the dimensions specified.
- * In particular, all buffers will have a fixed-size, independent from the
- * native-window size. They will be scaled according to the scaling mode
- * (see native_window_set_scaling_mode) upon window composition.
- *
- * If w and h are 0, the normal behavior is restored. That is, dequeued buffers
- * following this call will be sized to match the window's size.
- *
- * Calling this function will reset the window crop to a NULL value, which
- * disables cropping of the buffers.
- */
-static inline int native_window_set_buffers_dimensions(
- struct ANativeWindow* window,
- int w, int h)
-{
- return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS,
- w, h);
-}
-
-/*
- * native_window_set_buffers_user_dimensions(..., int w, int h)
- *
- * Sets the user buffer size for the window, which overrides the
- * window's size. All buffers dequeued after this call will have the
- * dimensions specified unless overridden by
- * native_window_set_buffers_dimensions. All buffers will have a
- * fixed-size, independent from the native-window size. They will be
- * scaled according to the scaling mode (see
- * native_window_set_scaling_mode) upon window composition.
- *
- * If w and h are 0, the normal behavior is restored. That is, the
- * default buffer size will match the windows's size.
- *
- * Calling this function will reset the window crop to a NULL value, which
- * disables cropping of the buffers.
- */
-static inline int native_window_set_buffers_user_dimensions(
- struct ANativeWindow* window,
- int w, int h)
-{
- return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_USER_DIMENSIONS,
- w, h);
-}
-
-/*
- * native_window_set_buffers_format(..., int format)
- * All buffers dequeued after this call will have the format specified.
- *
- * If the specified format is 0, the default buffer format will be used.
- */
-static inline int native_window_set_buffers_format(
- struct ANativeWindow* window,
- int format)
-{
- return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_FORMAT, format);
-}
-
-/*
- * native_window_set_buffers_data_space(..., int dataSpace)
- * All buffers queued after this call will be associated with the dataSpace
- * parameter specified.
- *
- * dataSpace specifies additional information about the buffer that's dependent
- * on the buffer format and the endpoints. For example, it can be used to convey
- * the color space of the image data in the buffer, or it can be used to
- * indicate that the buffers contain depth measurement data instead of color
- * images. The default dataSpace is 0, HAL_DATASPACE_UNKNOWN, unless it has been
- * overridden by the consumer.
- */
-static inline int native_window_set_buffers_data_space(
- struct ANativeWindow* window,
- android_dataspace_t dataSpace)
-{
- return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_DATASPACE,
- dataSpace);
-}
-
-/*
- * native_window_set_buffers_transform(..., int transform)
- * All buffers queued after this call will be displayed transformed according
- * to the transform parameter specified.
- */
-static inline int native_window_set_buffers_transform(
- struct ANativeWindow* window,
- int transform)
-{
- return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_TRANSFORM,
- transform);
-}
-
-/*
- * native_window_set_buffers_sticky_transform(..., int transform)
- * All buffers queued after this call will be displayed transformed according
- * to the transform parameter specified applied on top of the regular buffer
- * transform. Setting this transform will disable the transform hint.
- *
- * Temporary - This is only intended to be used by the LEGACY camera mode, do
- * not use this for anything else.
- */
-static inline int native_window_set_buffers_sticky_transform(
- struct ANativeWindow* window,
- int transform)
-{
- return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_STICKY_TRANSFORM,
- transform);
-}
-
-/*
- * native_window_set_buffers_timestamp(..., int64_t timestamp)
- * All buffers queued after this call will be associated with the timestamp
- * parameter specified. If the timestamp is set to NATIVE_WINDOW_TIMESTAMP_AUTO
- * (the default), timestamps will be generated automatically when queueBuffer is
- * called. The timestamp is measured in nanoseconds, and is normally monotonically
- * increasing. The timestamp should be unaffected by time-of-day adjustments,
- * and for a camera should be strictly monotonic but for a media player may be
- * reset when the position is set.
- */
-static inline int native_window_set_buffers_timestamp(
- struct ANativeWindow* window,
- int64_t timestamp)
-{
- return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP,
- timestamp);
-}
-
-/*
- * native_window_set_scaling_mode(..., int mode)
- * All buffers queued after this call will be associated with the scaling mode
- * specified.
- */
-static inline int native_window_set_scaling_mode(
- struct ANativeWindow* window,
- int mode)
-{
- return window->perform(window, NATIVE_WINDOW_SET_SCALING_MODE,
- mode);
-}
-
-/*
- * native_window_api_connect(..., int api)
- * connects an API to this window. only one API can be connected at a time.
- * Returns -EINVAL if for some reason the window cannot be connected, which
- * can happen if it's connected to some other API.
- */
-static inline int native_window_api_connect(
- struct ANativeWindow* window, int api)
-{
- return window->perform(window, NATIVE_WINDOW_API_CONNECT, api);
-}
-
-/*
- * native_window_api_disconnect(..., int api)
- * disconnect the API from this window.
- * An error is returned if for instance the window wasn't connected in the
- * first place.
- */
-static inline int native_window_api_disconnect(
- struct ANativeWindow* window, int api)
-{
- return window->perform(window, NATIVE_WINDOW_API_DISCONNECT, api);
-}
-
-/*
- * native_window_dequeue_buffer_and_wait(...)
- * Dequeue a buffer and wait on the fence associated with that buffer. The
- * buffer may safely be accessed immediately upon this function returning. An
- * error is returned if either of the dequeue or the wait operations fail.
- */
-static inline int native_window_dequeue_buffer_and_wait(ANativeWindow *anw,
- struct ANativeWindowBuffer** anb) {
- return anw->dequeueBuffer_DEPRECATED(anw, anb);
-}
-
-/*
- * native_window_set_sideband_stream(..., native_handle_t*)
- * Attach a sideband buffer stream to a native window.
- */
-static inline int native_window_set_sideband_stream(
- struct ANativeWindow* window,
- native_handle_t* sidebandHandle)
-{
- return window->perform(window, NATIVE_WINDOW_SET_SIDEBAND_STREAM,
- sidebandHandle);
-}
-
-/*
- * native_window_set_surface_damage(..., android_native_rect_t* rects, int numRects)
- * Set the surface damage (i.e., the region of the surface that has changed
- * since the previous frame). The damage set by this call will be reset (to the
- * default of full-surface damage) after calling queue, so this must be called
- * prior to every frame with damage that does not cover the whole surface if the
- * caller desires downstream consumers to use this optimization.
- *
- * The damage region is specified as an array of rectangles, with the important
- * caveat that the origin of the surface is considered to be the bottom-left
- * corner, as in OpenGL ES.
- *
- * If numRects is set to 0, rects may be NULL, and the surface damage will be
- * set to the full surface (the same as if this function had not been called for
- * this frame).
- */
-static inline int native_window_set_surface_damage(
- struct ANativeWindow* window,
- const android_native_rect_t* rects, size_t numRects)
-{
- return window->perform(window, NATIVE_WINDOW_SET_SURFACE_DAMAGE,
- rects, numRects);
-}
-
-/*
- * native_window_set_shared_buffer_mode(..., bool sharedBufferMode)
- * Enable/disable shared buffer mode
- */
-static inline int native_window_set_shared_buffer_mode(
- struct ANativeWindow* window,
- bool sharedBufferMode)
-{
- return window->perform(window, NATIVE_WINDOW_SET_SHARED_BUFFER_MODE,
- sharedBufferMode);
-}
-
-/*
- * native_window_set_auto_refresh(..., autoRefresh)
- * Enable/disable auto refresh when in shared buffer mode
- */
-static inline int native_window_set_auto_refresh(
- struct ANativeWindow* window,
- bool autoRefresh)
-{
- return window->perform(window, NATIVE_WINDOW_SET_AUTO_REFRESH, autoRefresh);
-}
-
-static inline int native_window_get_frame_timestamps(
- struct ANativeWindow* window, uint32_t framesAgo,
- int64_t* outPostedTime, int64_t* outAcquireTime,
- int64_t* outRefreshStartTime, int64_t* outGlCompositionDoneTime,
- int64_t* outDisplayRetireTime, int64_t* outReleaseTime)
-{
- return window->perform(window, NATIVE_WINDOW_GET_FRAME_TIMESTAMPS,
- framesAgo, outPostedTime, outAcquireTime, outRefreshStartTime,
- outGlCompositionDoneTime, outDisplayRetireTime, outReleaseTime);
-}
-
-
-__END_DECLS
+#include <system/window-deprecated.h>
#endif /* SYSTEM_CORE_INCLUDE_ANDROID_WINDOW_H */
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 94f0f8e..b971a9e 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -30,19 +30,11 @@
enabled: false,
},
},
-
- arch: {
- mips: {
- enabled: false,
- },
- mips64: {
- enabled: false,
- },
- },
}
cc_library {
name: "libunwindstack",
+ vendor_available: true,
defaults: ["libunwindstack_flags"],
export_include_dirs: ["include"],
@@ -115,6 +107,7 @@
"tests/MemoryRangeTest.cpp",
"tests/MemoryRemoteTest.cpp",
"tests/MemoryTest.cpp",
+ "tests/RegsStepIfSignalHandlerTest.cpp",
"tests/RegsTest.cpp",
"tests/SymbolsTest.cpp",
"tests/UnwindTest.cpp",
diff --git a/libunwindstack/DwarfEhFrame.cpp b/libunwindstack/DwarfEhFrame.cpp
index d0b35c3..db8f558 100644
--- a/libunwindstack/DwarfEhFrame.cpp
+++ b/libunwindstack/DwarfEhFrame.cpp
@@ -100,7 +100,7 @@
fde_info_.erase(index);
return nullptr;
}
- info->pc = value;
+ info->pc = value + 4;
return info;
}
@@ -175,7 +175,7 @@
last_error_ = DWARF_ERROR_MEMORY_INVALID;
return false;
}
- info->pc = value;
+ info->pc = value + 4;
if (pc < info->pc) {
if (prev_info == nullptr) {
diff --git a/libunwindstack/DwarfMemory.cpp b/libunwindstack/DwarfMemory.cpp
index b6e0412..901f492 100644
--- a/libunwindstack/DwarfMemory.cpp
+++ b/libunwindstack/DwarfMemory.cpp
@@ -235,7 +235,7 @@
return false;
}
- return AdjustEncodedValue(encoding & 0xf0, value);
+ return AdjustEncodedValue(encoding & 0x70, value);
}
// Instantiate all of the needed template functions.
diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp
index a800c31..4fc7c67 100644
--- a/libunwindstack/Elf.cpp
+++ b/libunwindstack/Elf.cpp
@@ -96,7 +96,8 @@
}
bool Elf::Step(uint64_t rel_pc, Regs* regs, Memory* process_memory) {
- return valid_ && (interface_->Step(rel_pc, regs, process_memory) ||
+ return valid_ && (regs->StepIfSignalHandler(rel_pc, this, process_memory) ||
+ interface_->Step(rel_pc, regs, process_memory) ||
(gnu_debugdata_interface_ &&
gnu_debugdata_interface_->Step(rel_pc, regs, process_memory)));
}
@@ -147,21 +148,22 @@
machine_type_ = e_machine;
if (e_machine == EM_ARM) {
interface.reset(new ElfInterfaceArm(memory));
- } else {
+ } else if (e_machine == EM_386) {
interface.reset(new ElfInterface32(memory));
+ } else {
+ ALOGI("32 bit elf that is neither arm nor x86: e_machine = %d\n", e_machine);
+ return nullptr;
}
} else if (class_type_ == ELFCLASS64) {
Elf64_Half e_machine;
if (!memory->Read(EI_NIDENT + sizeof(Elf64_Half), &e_machine, sizeof(e_machine))) {
return nullptr;
}
-
if (e_machine != EM_AARCH64 && e_machine != EM_X86_64) {
// Unsupported.
ALOGI("64 bit elf that is neither aarch64 nor x86_64: e_machine = %d\n", e_machine);
return nullptr;
}
-
machine_type_ = e_machine;
interface.reset(new ElfInterface64(memory));
}
diff --git a/libunwindstack/Regs.cpp b/libunwindstack/Regs.cpp
index aa59e31..dea7b87 100644
--- a/libunwindstack/Regs.cpp
+++ b/libunwindstack/Regs.cpp
@@ -258,49 +258,51 @@
return regs;
}
+void RegsX86::SetFromUcontext(x86_ucontext_t* ucontext) {
+ // Put the registers in the expected order.
+ regs_[X86_REG_EDI] = ucontext->uc_mcontext.edi;
+ regs_[X86_REG_ESI] = ucontext->uc_mcontext.esi;
+ regs_[X86_REG_EBP] = ucontext->uc_mcontext.ebp;
+ regs_[X86_REG_ESP] = ucontext->uc_mcontext.esp;
+ regs_[X86_REG_EBX] = ucontext->uc_mcontext.ebx;
+ regs_[X86_REG_EDX] = ucontext->uc_mcontext.edx;
+ regs_[X86_REG_ECX] = ucontext->uc_mcontext.ecx;
+ regs_[X86_REG_EAX] = ucontext->uc_mcontext.eax;
+ regs_[X86_REG_EIP] = ucontext->uc_mcontext.eip;
+ SetFromRaw();
+}
+
static Regs* CreateFromX86Ucontext(void* ucontext) {
x86_ucontext_t* x86_ucontext = reinterpret_cast<x86_ucontext_t*>(ucontext);
RegsX86* regs = new RegsX86();
- // Put the registers in the expected order.
- (*regs)[X86_REG_GS] = x86_ucontext->uc_mcontext.gs;
- (*regs)[X86_REG_FS] = x86_ucontext->uc_mcontext.fs;
- (*regs)[X86_REG_ES] = x86_ucontext->uc_mcontext.es;
- (*regs)[X86_REG_DS] = x86_ucontext->uc_mcontext.ds;
- (*regs)[X86_REG_EDI] = x86_ucontext->uc_mcontext.edi;
- (*regs)[X86_REG_ESI] = x86_ucontext->uc_mcontext.esi;
- (*regs)[X86_REG_EBP] = x86_ucontext->uc_mcontext.ebp;
- (*regs)[X86_REG_ESP] = x86_ucontext->uc_mcontext.esp;
- (*regs)[X86_REG_EBX] = x86_ucontext->uc_mcontext.ebx;
- (*regs)[X86_REG_EDX] = x86_ucontext->uc_mcontext.edx;
- (*regs)[X86_REG_ECX] = x86_ucontext->uc_mcontext.ecx;
- (*regs)[X86_REG_EAX] = x86_ucontext->uc_mcontext.eax;
- (*regs)[X86_REG_EIP] = x86_ucontext->uc_mcontext.eip;
- regs->SetFromRaw();
+ regs->SetFromUcontext(x86_ucontext);
return regs;
}
+void RegsX86_64::SetFromUcontext(x86_64_ucontext_t* ucontext) {
+ // R8-R15
+ memcpy(®s_[X86_64_REG_R8], &ucontext->uc_mcontext.r8, 8 * sizeof(uint64_t));
+
+ // Rest of the registers.
+ regs_[X86_64_REG_RDI] = ucontext->uc_mcontext.rdi;
+ regs_[X86_64_REG_RSI] = ucontext->uc_mcontext.rsi;
+ regs_[X86_64_REG_RBP] = ucontext->uc_mcontext.rbp;
+ regs_[X86_64_REG_RBX] = ucontext->uc_mcontext.rbx;
+ regs_[X86_64_REG_RDX] = ucontext->uc_mcontext.rdx;
+ regs_[X86_64_REG_RAX] = ucontext->uc_mcontext.rax;
+ regs_[X86_64_REG_RCX] = ucontext->uc_mcontext.rcx;
+ regs_[X86_64_REG_RSP] = ucontext->uc_mcontext.rsp;
+ regs_[X86_64_REG_RIP] = ucontext->uc_mcontext.rip;
+
+ SetFromRaw();
+}
+
static Regs* CreateFromX86_64Ucontext(void* ucontext) {
x86_64_ucontext_t* x86_64_ucontext = reinterpret_cast<x86_64_ucontext_t*>(ucontext);
RegsX86_64* regs = new RegsX86_64();
- // Put the registers in the expected order.
-
- // R8-R15
- memcpy(&(*regs)[X86_64_REG_R8], &x86_64_ucontext->uc_mcontext.r8, 8 * sizeof(uint64_t));
-
- // Rest of the registers.
- (*regs)[X86_64_REG_RDI] = x86_64_ucontext->uc_mcontext.rdi;
- (*regs)[X86_64_REG_RSI] = x86_64_ucontext->uc_mcontext.rsi;
- (*regs)[X86_64_REG_RBP] = x86_64_ucontext->uc_mcontext.rbp;
- (*regs)[X86_64_REG_RBX] = x86_64_ucontext->uc_mcontext.rbx;
- (*regs)[X86_64_REG_RDX] = x86_64_ucontext->uc_mcontext.rdx;
- (*regs)[X86_64_REG_RAX] = x86_64_ucontext->uc_mcontext.rax;
- (*regs)[X86_64_REG_RCX] = x86_64_ucontext->uc_mcontext.rcx;
- (*regs)[X86_64_REG_RSP] = x86_64_ucontext->uc_mcontext.rsp;
- (*regs)[X86_64_REG_RIP] = x86_64_ucontext->uc_mcontext.rip;
-
- regs->SetFromRaw();
+ regs->SetFromUcontext(x86_64_ucontext);
return regs;
}
@@ -348,4 +350,191 @@
return regs;
}
+bool RegsArm::StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) {
+ uint32_t data;
+ Memory* elf_memory = elf->memory();
+ // Read from elf memory since it is usually more expensive to read from
+ // process memory.
+ if (!elf_memory->Read(rel_pc, &data, sizeof(data))) {
+ return false;
+ }
+
+ uint64_t offset = 0;
+ if (data == 0xe3a07077 || data == 0xef900077 || data == 0xdf002777) {
+ // non-RT sigreturn call.
+ // __restore:
+ //
+ // Form 1 (arm):
+ // 0x77 0x70 mov r7, #0x77
+ // 0xa0 0xe3 svc 0x00000000
+ //
+ // Form 2 (arm):
+ // 0x77 0x00 0x90 0xef svc 0x00900077
+ //
+ // Form 3 (thumb):
+ // 0x77 0x27 movs r7, #77
+ // 0x00 0xdf svc 0
+ if (!process_memory->Read(sp(), &data, sizeof(data))) {
+ return false;
+ }
+ if (data == 0x5ac3c35a) {
+ // SP + uc_mcontext offset + r0 offset.
+ offset = sp() + 0x14 + 0xc;
+ } else {
+ // SP + r0 offset
+ offset = sp() + 0xc;
+ }
+ } else if (data == 0xe3a070ad || data == 0xef9000ad || data == 0xdf0027ad) {
+ // RT sigreturn call.
+ // __restore_rt:
+ //
+ // Form 1 (arm):
+ // 0xad 0x70 mov r7, #0xad
+ // 0xa0 0xe3 svc 0x00000000
+ //
+ // Form 2 (arm):
+ // 0xad 0x00 0x90 0xef svc 0x009000ad
+ //
+ // Form 3 (thumb):
+ // 0xad 0x27 movs r7, #ad
+ // 0x00 0xdf svc 0
+ if (!process_memory->Read(sp(), &data, sizeof(data))) {
+ return false;
+ }
+ if (data == sp() + 8) {
+ // SP + 8 + sizeof(siginfo_t) + uc_mcontext_offset + r0 offset
+ offset = sp() + 8 + 0x80 + 0x14 + 0xc;
+ } else {
+ // SP + sizeof(siginfo_t) + uc_mcontext_offset + r0 offset
+ offset = sp() + 0x80 + 0x14 + 0xc;
+ }
+ }
+ if (offset == 0) {
+ return false;
+ }
+
+ if (!process_memory->Read(offset, regs_.data(), sizeof(uint32_t) * ARM_REG_LAST)) {
+ return false;
+ }
+ SetFromRaw();
+ return true;
+}
+
+bool RegsArm64::StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) {
+ uint64_t data;
+ Memory* elf_memory = elf->memory();
+ // Read from elf memory since it is usually more expensive to read from
+ // process memory.
+ if (!elf_memory->Read(rel_pc, &data, sizeof(data))) {
+ return false;
+ }
+
+ // Look for the kernel sigreturn function.
+ // __kernel_rt_sigreturn:
+ // 0xd2801168 mov x8, #0x8b
+ // 0xd4000001 svc #0x0
+ if (data != 0xd4000001d2801168ULL) {
+ return false;
+ }
+
+ // SP + sizeof(siginfo_t) + uc_mcontext offset + X0 offset.
+ if (!process_memory->Read(sp() + 0x80 + 0xb0 + 0x08, regs_.data(),
+ sizeof(uint64_t) * ARM64_REG_LAST)) {
+ return false;
+ }
+
+ SetFromRaw();
+ return true;
+}
+
+bool RegsX86::StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) {
+ uint64_t data;
+ Memory* elf_memory = elf->memory();
+ // Read from elf memory since it is usually more expensive to read from
+ // process memory.
+ if (!elf_memory->Read(rel_pc, &data, sizeof(data))) {
+ return false;
+ }
+
+ if (data == 0x80cd00000077b858ULL) {
+ // Without SA_SIGINFO set, the return sequence is:
+ //
+ // __restore:
+ // 0x58 pop %eax
+ // 0xb8 0x77 0x00 0x00 0x00 movl 0x77,%eax
+ // 0xcd 0x80 int 0x80
+ //
+ // SP points at arguments:
+ // int signum
+ // struct sigcontext (same format as mcontext)
+ struct x86_mcontext_t context;
+ if (!process_memory->Read(sp() + 4, &context, sizeof(context))) {
+ return false;
+ }
+ regs_[X86_REG_EBP] = context.ebp;
+ regs_[X86_REG_ESP] = context.esp;
+ regs_[X86_REG_EBX] = context.ebx;
+ regs_[X86_REG_EDX] = context.edx;
+ regs_[X86_REG_ECX] = context.ecx;
+ regs_[X86_REG_EAX] = context.eax;
+ regs_[X86_REG_EIP] = context.eip;
+ SetFromRaw();
+ return true;
+ } else if ((data & 0x00ffffffffffffffULL) == 0x0080cd000000adb8ULL) {
+ // With SA_SIGINFO set, the return sequence is:
+ //
+ // __restore_rt:
+ // 0xb8 0xad 0x00 0x00 0x00 movl 0xad,%eax
+ // 0xcd 0x80 int 0x80
+ //
+ // SP points at arguments:
+ // int signum
+ // siginfo*
+ // ucontext*
+
+ // Get the location of the sigcontext data.
+ uint32_t ptr;
+ if (!process_memory->Read(sp() + 8, &ptr, sizeof(ptr))) {
+ return false;
+ }
+ // Only read the portion of the data structure we care about.
+ x86_ucontext_t x86_ucontext;
+ if (!process_memory->Read(ptr + 0x14, &x86_ucontext.uc_mcontext, sizeof(x86_mcontext_t))) {
+ return false;
+ }
+ SetFromUcontext(&x86_ucontext);
+ return true;
+ }
+ return false;
+}
+
+bool RegsX86_64::StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) {
+ uint64_t data;
+ Memory* elf_memory = elf->memory();
+ // Read from elf memory since it is usually more expensive to read from
+ // process memory.
+ if (!elf_memory->Read(rel_pc, &data, sizeof(data)) || data != 0x0f0000000fc0c748) {
+ return false;
+ }
+
+ uint16_t data2;
+ if (!elf_memory->Read(rel_pc + 8, &data2, sizeof(data2)) || data2 != 0x0f05) {
+ return false;
+ }
+
+ // __restore_rt:
+ // 0x48 0xc7 0xc0 0x0f 0x00 0x00 0x00 mov $0xf,%rax
+ // 0x0f 0x05 syscall
+ // 0x0f nopl 0x0($rax)
+
+ // Read the mcontext data from the stack.
+ // sp points to the ucontext data structure, read only the mcontext part.
+ x86_64_ucontext_t x86_64_ucontext;
+ if (!process_memory->Read(sp() + 0x28, &x86_64_ucontext.uc_mcontext, sizeof(x86_64_mcontext_t))) {
+ return false;
+ }
+ SetFromUcontext(&x86_64_ucontext);
+ return true;
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/Ucontext.h b/libunwindstack/Ucontext.h
index 410af26..22f6a89 100644
--- a/libunwindstack/Ucontext.h
+++ b/libunwindstack/Ucontext.h
@@ -170,13 +170,13 @@
// Only care about the registers, skip everything else.
};
-typedef struct x86_64_ucontext {
+struct x86_64_ucontext_t {
uint64_t uc_flags; // unsigned long
uint64_t uc_link; // struct ucontext*
x86_64_stack_t uc_stack;
x86_64_mcontext_t uc_mcontext;
// Nothing else is used, so don't define it.
-} x86_64_ucontext_t;
+};
//-------------------------------------------------------------------
} // namespace unwindstack
diff --git a/libunwindstack/include/unwindstack/DwarfSection.h b/libunwindstack/include/unwindstack/DwarfSection.h
index a97ca2b..26485ae 100644
--- a/libunwindstack/include/unwindstack/DwarfSection.h
+++ b/libunwindstack/include/unwindstack/DwarfSection.h
@@ -106,7 +106,7 @@
DwarfMemory memory_;
DwarfError last_error_;
- uint64_t fde_count_;
+ uint64_t fde_count_ = 0;
std::unordered_map<uint64_t, DwarfFde> fde_entries_;
std::unordered_map<uint64_t, DwarfCie> cie_entries_;
std::unordered_map<uint64_t, dwarf_loc_regs_t> cie_loc_regs_;
diff --git a/libunwindstack/include/unwindstack/MapInfo.h b/libunwindstack/include/unwindstack/MapInfo.h
index 2a97dde..1854767 100644
--- a/libunwindstack/include/unwindstack/MapInfo.h
+++ b/libunwindstack/include/unwindstack/MapInfo.h
@@ -41,6 +41,7 @@
uint64_t elf_offset;
Memory* CreateMemory(pid_t pid);
+ // This function guarantees it will never return nullptr.
Elf* GetElf(pid_t pid, bool init_gnu_debugdata = false);
};
diff --git a/libunwindstack/include/unwindstack/Regs.h b/libunwindstack/include/unwindstack/Regs.h
index ab98f32..78e2c0d 100644
--- a/libunwindstack/include/unwindstack/Regs.h
+++ b/libunwindstack/include/unwindstack/Regs.h
@@ -27,6 +27,8 @@
class Elf;
struct MapInfo;
class Memory;
+struct x86_ucontext_t;
+struct x86_64_ucontext_t;
class Regs {
public:
@@ -55,6 +57,8 @@
virtual uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) = 0;
+ virtual bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) = 0;
+
virtual void SetFromRaw() = 0;
uint16_t sp_reg() { return sp_reg_; }
@@ -104,6 +108,8 @@
uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
void SetFromRaw() override;
+
+ bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) override;
};
class RegsArm64 : public RegsImpl<uint64_t> {
@@ -114,6 +120,8 @@
uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
void SetFromRaw() override;
+
+ bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) override;
};
class RegsX86 : public RegsImpl<uint32_t> {
@@ -124,6 +132,10 @@
uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
void SetFromRaw() override;
+
+ bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) override;
+
+ void SetFromUcontext(x86_ucontext_t* ucontext);
};
class RegsX86_64 : public RegsImpl<uint64_t> {
@@ -134,6 +146,10 @@
uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
void SetFromRaw() override;
+
+ bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) override;
+
+ void SetFromUcontext(x86_64_ucontext_t* ucontext);
};
} // namespace unwindstack
diff --git a/libunwindstack/include/unwindstack/RegsGetLocal.h b/libunwindstack/include/unwindstack/RegsGetLocal.h
index ffec213..d1461d8 100644
--- a/libunwindstack/include/unwindstack/RegsGetLocal.h
+++ b/libunwindstack/include/unwindstack/RegsGetLocal.h
@@ -97,6 +97,11 @@
regs->SetFromRaw();
}
+#elif defined(__mips__)
+
+// Stub to allow mips to build.
+void RegsGetLocal(Regs*) {}
+
#endif
} // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfEhFrameTest.cpp b/libunwindstack/tests/DwarfEhFrameTest.cpp
index e9501e3..07159b0 100644
--- a/libunwindstack/tests/DwarfEhFrameTest.cpp
+++ b/libunwindstack/tests/DwarfEhFrameTest.cpp
@@ -124,7 +124,7 @@
auto info = this->eh_frame_->GetFdeInfoFromIndex(2);
ASSERT_TRUE(info != nullptr);
- EXPECT_EQ(0x1380U, info->pc);
+ EXPECT_EQ(0x1384U, info->pc);
EXPECT_EQ(0x1540U, info->offset);
}
@@ -139,7 +139,7 @@
auto info = this->eh_frame_->GetFdeInfoFromIndex(2);
ASSERT_TRUE(info != nullptr);
- EXPECT_EQ(0x3340U, info->pc);
+ EXPECT_EQ(0x3344U, info->pc);
EXPECT_EQ(0x3500U, info->offset);
}
@@ -153,7 +153,7 @@
auto info = this->eh_frame_->GetFdeInfoFromIndex(2);
ASSERT_TRUE(info != nullptr);
- EXPECT_EQ(0x340U, info->pc);
+ EXPECT_EQ(0x344U, info->pc);
EXPECT_EQ(0x500U, info->offset);
// Clear the memory so that this will fail if it doesn't read cached data.
@@ -161,7 +161,7 @@
info = this->eh_frame_->GetFdeInfoFromIndex(2);
ASSERT_TRUE(info != nullptr);
- EXPECT_EQ(0x340U, info->pc);
+ EXPECT_EQ(0x344U, info->pc);
EXPECT_EQ(0x500U, info->offset);
}
@@ -220,18 +220,18 @@
// Verify that if entries is zero, that it fails.
uint64_t fde_offset;
- ASSERT_FALSE(this->eh_frame_->GetFdeOffsetSequential(0x340, &fde_offset));
+ ASSERT_FALSE(this->eh_frame_->GetFdeOffsetSequential(0x344, &fde_offset));
this->eh_frame_->TestSetCurEntriesOffset(0x1040);
- ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x340, &fde_offset));
+ ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x344, &fde_offset));
EXPECT_EQ(0x500U, fde_offset);
- ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x440, &fde_offset));
+ ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x444, &fde_offset));
EXPECT_EQ(0x600U, fde_offset);
// Expect that the data is cached so no more memory reads will occur.
this->memory_.Clear();
- ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x440, &fde_offset));
+ ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x444, &fde_offset));
EXPECT_EQ(0x600U, fde_offset);
}
diff --git a/libunwindstack/tests/DwarfMemoryTest.cpp b/libunwindstack/tests/DwarfMemoryTest.cpp
index 08fe7cf..f12d2fe 100644
--- a/libunwindstack/tests/DwarfMemoryTest.cpp
+++ b/libunwindstack/tests/DwarfMemoryTest.cpp
@@ -52,6 +52,8 @@
void ReadEncodedValue_non_zero_adjust();
template <typename AddressType>
void ReadEncodedValue_overflow();
+ template <typename AddressType>
+ void ReadEncodedValue_high_bit_set();
MemoryFake memory_;
std::unique_ptr<DwarfMemory> dwarf_mem_;
@@ -435,6 +437,26 @@
ReadEncodedValue_overflow<uint64_t>();
}
+template <typename AddressType>
+void DwarfMemoryTest::ReadEncodedValue_high_bit_set() {
+ uint64_t value;
+ memory_.SetData32(0, 0x15234);
+ ASSERT_FALSE(dwarf_mem_->ReadEncodedValue<AddressType>(0xc3, &value));
+
+ dwarf_mem_->set_func_offset(0x60000);
+ dwarf_mem_->set_cur_offset(0);
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0xc3, &value));
+ ASSERT_EQ(0x75234U, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_high_bit_set_uint32_t) {
+ ReadEncodedValue_high_bit_set<uint32_t>();
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_high_bit_set_uint64_t) {
+ ReadEncodedValue_high_bit_set<uint64_t>();
+}
+
TEST_F(DwarfMemoryTest, AdjustEncodedValue_absptr) {
uint64_t value = 0x1234;
ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x00, &value));
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index 72ceb85..ed1be3b 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -26,6 +26,7 @@
#include <unwindstack/MapInfo.h>
#include "ElfTestUtils.h"
+#include "LogFake.h"
#include "MemoryFake.h"
#if !defined(PT_ARM_EXIDX)
@@ -131,6 +132,32 @@
ASSERT_FALSE(elf.Step(0, nullptr, nullptr));
}
+TEST_F(ElfTest, elf32_invalid_machine) {
+ Elf elf(memory_);
+
+ InitElf32(EM_PPC);
+
+ ResetLogs();
+ ASSERT_FALSE(elf.Init());
+
+ ASSERT_EQ("", GetFakeLogBuf());
+ ASSERT_EQ("4 unwind 32 bit elf that is neither arm nor x86: e_machine = 20\n\n",
+ GetFakeLogPrint());
+}
+
+TEST_F(ElfTest, elf64_invalid_machine) {
+ Elf elf(memory_);
+
+ InitElf64(EM_PPC64);
+
+ ResetLogs();
+ ASSERT_FALSE(elf.Init());
+
+ ASSERT_EQ("", GetFakeLogBuf());
+ ASSERT_EQ("4 unwind 64 bit elf that is neither aarch64 nor x86_64: e_machine = 21\n\n",
+ GetFakeLogPrint());
+}
+
TEST_F(ElfTest, elf_arm) {
Elf elf(memory_);
diff --git a/libunwindstack/tests/RegsFake.h b/libunwindstack/tests/RegsFake.h
index e796c9b..6669d7d 100644
--- a/libunwindstack/tests/RegsFake.h
+++ b/libunwindstack/tests/RegsFake.h
@@ -33,6 +33,7 @@
uint64_t GetAdjustedPc(uint64_t, Elf*) override { return 0; }
void SetFromRaw() override {}
+ bool StepIfSignalHandler(uint64_t, Elf*, Memory*) override { return false; }
bool GetReturnAddressFromDefault(Memory*, uint64_t*) { return false; }
};
diff --git a/libunwindstack/tests/RegsStepIfSignalHandlerTest.cpp b/libunwindstack/tests/RegsStepIfSignalHandlerTest.cpp
new file mode 100644
index 0000000..85192d5
--- /dev/null
+++ b/libunwindstack/tests/RegsStepIfSignalHandlerTest.cpp
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/Regs.h>
+
+#include "Machine.h"
+
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+class RegsStepIfSignalHandlerTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ elf_memory_ = new MemoryFake;
+ elf_.reset(new Elf(elf_memory_));
+ }
+
+ void ArmStepIfSignalHandlerNonRt(uint32_t pc_data);
+ void ArmStepIfSignalHandlerRt(uint32_t pc_data);
+
+ MemoryFake* elf_memory_;
+ MemoryFake process_memory_;
+ std::unique_ptr<Elf> elf_;
+};
+
+void RegsStepIfSignalHandlerTest::ArmStepIfSignalHandlerNonRt(uint32_t pc_data) {
+ uint64_t addr = 0x1000;
+ RegsArm regs;
+ regs[ARM_REG_PC] = 0x5000;
+ regs[ARM_REG_SP] = addr;
+ regs.SetFromRaw();
+
+ elf_memory_->SetData32(0x5000, pc_data);
+
+ for (uint64_t index = 0; index <= 30; index++) {
+ process_memory_.SetData32(addr + index * 4, index * 0x10);
+ }
+
+ ASSERT_TRUE(regs.StepIfSignalHandler(0x5000, elf_.get(), &process_memory_));
+ EXPECT_EQ(0x100U, regs[ARM_REG_SP]);
+ EXPECT_EQ(0x120U, regs[ARM_REG_PC]);
+ EXPECT_EQ(0x100U, regs.sp());
+ EXPECT_EQ(0x120U, regs.pc());
+}
+
+TEST_F(RegsStepIfSignalHandlerTest, arm_step_if_signal_handler_non_rt) {
+ // Form 1
+ ArmStepIfSignalHandlerNonRt(0xe3a07077);
+
+ // Form 2
+ ArmStepIfSignalHandlerNonRt(0xef900077);
+
+ // Form 3
+ ArmStepIfSignalHandlerNonRt(0xdf002777);
+}
+
+void RegsStepIfSignalHandlerTest::ArmStepIfSignalHandlerRt(uint32_t pc_data) {
+ uint64_t addr = 0x1000;
+ RegsArm regs;
+ regs[ARM_REG_PC] = 0x5000;
+ regs[ARM_REG_SP] = addr;
+ regs.SetFromRaw();
+
+ elf_memory_->SetData32(0x5000, pc_data);
+
+ for (uint64_t index = 0; index <= 100; index++) {
+ process_memory_.SetData32(addr + index * 4, index * 0x10);
+ }
+
+ ASSERT_TRUE(regs.StepIfSignalHandler(0x5000, elf_.get(), &process_memory_));
+ EXPECT_EQ(0x350U, regs[ARM_REG_SP]);
+ EXPECT_EQ(0x370U, regs[ARM_REG_PC]);
+ EXPECT_EQ(0x350U, regs.sp());
+ EXPECT_EQ(0x370U, regs.pc());
+}
+
+TEST_F(RegsStepIfSignalHandlerTest, arm_step_if_signal_handler_rt) {
+ // Form 1
+ ArmStepIfSignalHandlerRt(0xe3a070ad);
+
+ // Form 2
+ ArmStepIfSignalHandlerRt(0xef9000ad);
+
+ // Form 3
+ ArmStepIfSignalHandlerRt(0xdf0027ad);
+}
+
+TEST_F(RegsStepIfSignalHandlerTest, arm64_step_if_signal_handler) {
+ uint64_t addr = 0x1000;
+ RegsArm64 regs;
+ regs[ARM64_REG_PC] = 0x8000;
+ regs[ARM64_REG_SP] = addr;
+ regs.SetFromRaw();
+
+ elf_memory_->SetData64(0x8000, 0xd4000001d2801168ULL);
+
+ for (uint64_t index = 0; index <= 100; index++) {
+ process_memory_.SetData64(addr + index * 8, index * 0x10);
+ }
+
+ ASSERT_TRUE(regs.StepIfSignalHandler(0x8000, elf_.get(), &process_memory_));
+ EXPECT_EQ(0x460U, regs[ARM64_REG_SP]);
+ EXPECT_EQ(0x470U, regs[ARM64_REG_PC]);
+ EXPECT_EQ(0x460U, regs.sp());
+ EXPECT_EQ(0x470U, regs.pc());
+}
+
+TEST_F(RegsStepIfSignalHandlerTest, x86_step_if_signal_handler_no_siginfo) {
+ uint64_t addr = 0xa00;
+ RegsX86 regs;
+ regs[X86_REG_EIP] = 0x4100;
+ regs[X86_REG_ESP] = addr;
+ regs.SetFromRaw();
+
+ elf_memory_->SetData64(0x4100, 0x80cd00000077b858ULL);
+ for (uint64_t index = 0; index <= 25; index++) {
+ process_memory_.SetData32(addr + index * 4, index * 0x10);
+ }
+
+ ASSERT_TRUE(regs.StepIfSignalHandler(0x4100, elf_.get(), &process_memory_));
+ EXPECT_EQ(0x70U, regs[X86_REG_EBP]);
+ EXPECT_EQ(0x80U, regs[X86_REG_ESP]);
+ EXPECT_EQ(0x90U, regs[X86_REG_EBX]);
+ EXPECT_EQ(0xa0U, regs[X86_REG_EDX]);
+ EXPECT_EQ(0xb0U, regs[X86_REG_ECX]);
+ EXPECT_EQ(0xc0U, regs[X86_REG_EAX]);
+ EXPECT_EQ(0xf0U, regs[X86_REG_EIP]);
+ EXPECT_EQ(0x80U, regs.sp());
+ EXPECT_EQ(0xf0U, regs.pc());
+}
+
+TEST_F(RegsStepIfSignalHandlerTest, x86_step_if_signal_handler_siginfo) {
+ uint64_t addr = 0xa00;
+ RegsX86 regs;
+ regs[X86_REG_EIP] = 0x4100;
+ regs[X86_REG_ESP] = addr;
+ regs.SetFromRaw();
+
+ elf_memory_->SetData64(0x4100, 0x0080cd000000adb8ULL);
+ addr += 8;
+ // Pointer to ucontext data.
+ process_memory_.SetData32(addr, 0x8100);
+
+ addr = 0x8100;
+ for (uint64_t index = 0; index <= 30; index++) {
+ process_memory_.SetData32(addr + index * 4, index * 0x10);
+ }
+
+ ASSERT_TRUE(regs.StepIfSignalHandler(0x4100, elf_.get(), &process_memory_));
+ EXPECT_EQ(0xb0U, regs[X86_REG_EBP]);
+ EXPECT_EQ(0xc0U, regs[X86_REG_ESP]);
+ EXPECT_EQ(0xd0U, regs[X86_REG_EBX]);
+ EXPECT_EQ(0xe0U, regs[X86_REG_EDX]);
+ EXPECT_EQ(0xf0U, regs[X86_REG_ECX]);
+ EXPECT_EQ(0x100U, regs[X86_REG_EAX]);
+ EXPECT_EQ(0x130U, regs[X86_REG_EIP]);
+ EXPECT_EQ(0xc0U, regs.sp());
+ EXPECT_EQ(0x130U, regs.pc());
+}
+
+TEST_F(RegsStepIfSignalHandlerTest, x86_64_step_if_signal_handler) {
+ uint64_t addr = 0x500;
+ RegsX86_64 regs;
+ regs[X86_64_REG_RIP] = 0x7000;
+ regs[X86_64_REG_RSP] = addr;
+ regs.SetFromRaw();
+
+ elf_memory_->SetData64(0x7000, 0x0f0000000fc0c748);
+ elf_memory_->SetData16(0x7008, 0x0f05);
+
+ for (uint64_t index = 0; index <= 30; index++) {
+ process_memory_.SetData64(addr + index * 8, index * 0x10);
+ }
+
+ ASSERT_TRUE(regs.StepIfSignalHandler(0x7000, elf_.get(), &process_memory_));
+ EXPECT_EQ(0x140U, regs[X86_64_REG_RSP]);
+ EXPECT_EQ(0x150U, regs[X86_64_REG_RIP]);
+ EXPECT_EQ(0x140U, regs.sp());
+ EXPECT_EQ(0x150U, regs.pc());
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/RegsTest.cpp b/libunwindstack/tests/RegsTest.cpp
index 3613689..e6de56a 100644
--- a/libunwindstack/tests/RegsTest.cpp
+++ b/libunwindstack/tests/RegsTest.cpp
@@ -60,6 +60,7 @@
uint64_t GetAdjustedPc(uint64_t, Elf*) override { return 0; }
void SetFromRaw() override {}
+ bool StepIfSignalHandler(uint64_t, Elf*, Memory*) override { return false; }
};
class RegsTest : public ::testing::Test {
@@ -72,7 +73,7 @@
}
template <typename AddressType>
- void regs_return_address_register();
+ void RegsReturnAddressRegister();
ElfInterfaceFake* elf_interface_;
MemoryFake* memory_;
@@ -126,7 +127,7 @@
}
template <typename AddressType>
-void RegsTest::regs_return_address_register() {
+void RegsTest::RegsReturnAddressRegister() {
RegsTestImpl<AddressType> regs(20, 10, Regs::Location(Regs::LOCATION_REGISTER, 5));
regs[5] = 0x12345;
@@ -136,11 +137,11 @@
}
TEST_F(RegsTest, regs32_return_address_register) {
- regs_return_address_register<uint32_t>();
+ RegsReturnAddressRegister<uint32_t>();
}
TEST_F(RegsTest, regs64_return_address_register) {
- regs_return_address_register<uint64_t>();
+ RegsReturnAddressRegister<uint64_t>();
}
TEST_F(RegsTest, regs32_return_address_sp_offset) {
diff --git a/libunwindstack/tests/UnwindTest.cpp b/libunwindstack/tests/UnwindTest.cpp
index 72065c9..3c69e2a 100644
--- a/libunwindstack/tests/UnwindTest.cpp
+++ b/libunwindstack/tests/UnwindTest.cpp
@@ -30,6 +30,7 @@
#include <sstream>
#include <string>
#include <thread>
+#include <vector>
#include <unwindstack/Elf.h>
#include <unwindstack/MapInfo.h>
@@ -42,16 +43,41 @@
static std::atomic_bool g_ready(false);
static volatile bool g_ready_for_remote = false;
+static volatile bool g_signal_ready_for_remote = false;
static std::atomic_bool g_finish(false);
static std::atomic_uintptr_t g_ucontext;
-static void Signal(int, siginfo_t*, void* sigcontext) {
+static std::vector<const char*> kFunctionOrder{"InnerFunction", "MiddleFunction", "OuterFunction"};
+
+static std::vector<const char*> kFunctionSignalOrder{"SignalInnerFunction", "SignalMiddleFunction",
+ "SignalOuterFunction", "InnerFunction",
+ "MiddleFunction", "OuterFunction"};
+
+static void SignalHandler(int, siginfo_t*, void* sigcontext) {
g_ucontext = reinterpret_cast<uintptr_t>(sigcontext);
while (!g_finish.load()) {
}
}
-static std::string ErrorMsg(const char** function_names, size_t index,
+extern "C" void SignalInnerFunction() {
+ g_signal_ready_for_remote = true;
+ while (!g_finish.load()) {
+ }
+}
+
+extern "C" void SignalMiddleFunction() {
+ SignalInnerFunction();
+}
+
+extern "C" void SignalOuterFunction() {
+ SignalMiddleFunction();
+}
+
+static void SignalCallerHandler(int, siginfo_t*, void*) {
+ SignalOuterFunction();
+}
+
+static std::string ErrorMsg(const std::vector<const char*>& function_names, size_t index,
std::stringstream& unwind_stream) {
return std::string(
"Unwind completed without finding all frames\n"
@@ -59,10 +85,8 @@
function_names[index] + "\n" + "Unwind data:\n" + unwind_stream.str();
}
-static void VerifyUnwind(pid_t pid, Memory* memory, Maps* maps, Regs* regs) {
- const char* function_names[] = {
- "InnerFunction", "MiddleFunction", "OuterFunction",
- };
+static void VerifyUnwind(pid_t pid, Memory* memory, Maps* maps, Regs* regs,
+ std::vector<const char*>& function_names) {
size_t function_name_index = 0;
std::stringstream unwind_stream;
@@ -91,8 +115,7 @@
uint64_t func_offset;
if (elf->GetFunctionName(adjusted_rel_pc, &name, &func_offset)) {
if (name == function_names[function_name_index]) {
- function_name_index++;
- if (function_name_index == sizeof(function_names) / sizeof(const char*)) {
+ if (++function_name_index == function_names.size()) {
return;
}
}
@@ -116,7 +139,7 @@
RegsGetLocal(regs.get());
MemoryLocal memory;
- VerifyUnwind(getpid(), &memory, &maps, regs.get());
+ VerifyUnwind(getpid(), &memory, &maps, regs.get(), kFunctionOrder);
} else {
g_ready_for_remote = true;
g_ready = true;
@@ -137,6 +160,37 @@
OuterFunction(true);
}
+void WaitForRemote(pid_t pid, uint64_t addr, bool leave_attached, bool* completed) {
+ *completed = false;
+ // Need to sleep before attempting first ptrace. Without this, on the
+ // host it becomes impossible to attach and ptrace set errno to EPERM.
+ usleep(1000);
+ for (size_t i = 0; i < 100; i++) {
+ ASSERT_EQ(0, ptrace(PTRACE_ATTACH, pid, 0, 0));
+ for (size_t j = 0; j < 100; j++) {
+ siginfo_t si;
+ if (ptrace(PTRACE_GETSIGINFO, pid, 0, &si) == 0) {
+ MemoryRemote memory(pid);
+ // Read the remote value to see if we are ready.
+ bool value;
+ if (memory.Read(addr, &value, sizeof(value)) && value) {
+ *completed = true;
+ break;
+ }
+ }
+ usleep(1000);
+ }
+ if (leave_attached && *completed) {
+ break;
+ }
+ ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0));
+ if (*completed) {
+ break;
+ }
+ usleep(1000);
+ }
+}
+
TEST(UnwindTest, remote) {
pid_t pid;
if ((pid = fork()) == 0) {
@@ -145,31 +199,9 @@
}
ASSERT_NE(-1, pid);
- bool ready = false;
- uint64_t addr = reinterpret_cast<uint64_t>(&g_ready_for_remote);
- for (size_t i = 0; i < 100; i++) {
- ASSERT_EQ(0, ptrace(PTRACE_ATTACH, pid, 0, 0));
- for (size_t j = 0; j < 100; j++) {
- siginfo_t si;
- if (ptrace(PTRACE_GETSIGINFO, pid, 0, &si) == 0) {
- // Check to see if process is ready to be unwound.
- MemoryRemote memory(pid);
- // Read the remote value to see if we are ready.
- bool value;
- if (memory.Read(addr, &value, sizeof(value)) && value) {
- ready = true;
- break;
- }
- }
- usleep(1000);
- }
- if (ready) {
- break;
- }
- ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0));
- usleep(1000);
- }
- ASSERT_TRUE(read) << "Timed out waiting for remote process to be ready.";
+ bool completed;
+ WaitForRemote(pid, reinterpret_cast<uint64_t>(&g_ready_for_remote), true, &completed);
+ ASSERT_TRUE(completed) << "Timed out waiting for remote process to be ready.";
RemoteMaps maps(pid);
ASSERT_TRUE(maps.Parse());
@@ -178,7 +210,7 @@
std::unique_ptr<Regs> regs(Regs::RemoteGet(pid, &machine_type));
ASSERT_TRUE(regs.get() != nullptr);
- VerifyUnwind(pid, &memory, &maps, regs.get());
+ VerifyUnwind(pid, &memory, &maps, regs.get(), kFunctionOrder);
ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0));
@@ -195,7 +227,7 @@
struct sigaction act, oldact;
memset(&act, 0, sizeof(act));
- act.sa_sigaction = Signal;
+ act.sa_sigaction = SignalHandler;
act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
ASSERT_EQ(0, sigaction(SIGUSR1, &act, &oldact));
// Wait for the tid to get set.
@@ -207,8 +239,7 @@
}
ASSERT_NE(0, tid.load());
// Portable tgkill method.
- ASSERT_EQ(0, syscall(__NR_tgkill, getpid(), tid.load(), SIGUSR1)) << "Failed because "
- << strerror(errno);
+ ASSERT_EQ(0, syscall(__NR_tgkill, getpid(), tid.load(), SIGUSR1)) << "Error: " << strerror(errno);
// Wait for context data.
void* ucontext;
@@ -226,7 +257,7 @@
std::unique_ptr<Regs> regs(Regs::CreateFromUcontext(Regs::GetMachineType(), ucontext));
MemoryLocal memory;
- VerifyUnwind(tid.load(), &memory, &maps, regs.get());
+ VerifyUnwind(tid.load(), &memory, &maps, regs.get(), kFunctionOrder);
ASSERT_EQ(0, sigaction(SIGUSR1, &oldact, nullptr));
@@ -234,4 +265,52 @@
thread.join();
}
+static void RemoteThroughSignal(unsigned int sa_flags) {
+ g_ready = false;
+ g_signal_ready_for_remote = false;
+ g_finish = false;
+
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ struct sigaction act, oldact;
+ memset(&act, 0, sizeof(act));
+ act.sa_sigaction = SignalCallerHandler;
+ act.sa_flags = SA_RESTART | SA_ONSTACK | sa_flags;
+ ASSERT_EQ(0, sigaction(SIGUSR1, &act, &oldact));
+
+ OuterFunction(false);
+ exit(0);
+ }
+ ASSERT_NE(-1, pid);
+
+ bool completed;
+ WaitForRemote(pid, reinterpret_cast<uint64_t>(&g_ready_for_remote), false, &completed);
+ ASSERT_TRUE(completed) << "Timed out waiting for remote process to be ready.";
+ ASSERT_EQ(0, kill(pid, SIGUSR1));
+ WaitForRemote(pid, reinterpret_cast<uint64_t>(&g_signal_ready_for_remote), true, &completed);
+ ASSERT_TRUE(completed) << "Timed out waiting for remote process to be in signal handler.";
+
+ RemoteMaps maps(pid);
+ ASSERT_TRUE(maps.Parse());
+ MemoryRemote memory(pid);
+ uint32_t machine_type;
+ std::unique_ptr<Regs> regs(Regs::RemoteGet(pid, &machine_type));
+ ASSERT_TRUE(regs.get() != nullptr);
+
+ VerifyUnwind(pid, &memory, &maps, regs.get(), kFunctionSignalOrder);
+
+ ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0));
+
+ kill(pid, SIGKILL);
+ ASSERT_EQ(pid, wait(nullptr));
+}
+
+TEST(UnwindTest, remote_through_signal) {
+ RemoteThroughSignal(0);
+}
+
+TEST(UnwindTest, remote_through_signal_sa_siginfo) {
+ RemoteThroughSignal(SA_SIGINFO);
+}
+
} // namespace unwindstack
diff --git a/libusbhost/include/usbhost/usbhost.h b/libusbhost/include/usbhost/usbhost.h
index 84594c8..a8dd673 100644
--- a/libusbhost/include/usbhost/usbhost.h
+++ b/libusbhost/include/usbhost/usbhost.h
@@ -144,17 +144,17 @@
* usb_device_get_product_name and usb_device_get_serial.
* Call free() to free the result when you are done with it.
*/
-char* usb_device_get_string(struct usb_device *device, int id);
+char* usb_device_get_string(struct usb_device *device, int id, int timeout);
/* Returns the manufacturer name for the USB device.
* Call free() to free the result when you are done with it.
*/
-char* usb_device_get_manufacturer_name(struct usb_device *device);
+char* usb_device_get_manufacturer_name(struct usb_device *device, int timeout);
/* Returns the product name for the USB device.
* Call free() to free the result when you are done with it.
*/
-char* usb_device_get_product_name(struct usb_device *device);
+char* usb_device_get_product_name(struct usb_device *device, int timeout);
/* Returns the version number for the USB device.
*/
@@ -163,7 +163,7 @@
/* Returns the USB serial number for the USB device.
* Call free() to free the result when you are done with it.
*/
-char* usb_device_get_serial(struct usb_device *device);
+char* usb_device_get_serial(struct usb_device *device, int timeout);
/* Returns true if we have write access to the USB device,
* and false if we only have access to the USB device configuration.
@@ -232,10 +232,11 @@
/* Submits a read or write request on the specified device */
int usb_request_queue(struct usb_request *req);
- /* Waits for the results of a previous usb_request_queue operation.
+ /* Waits for the results of a previous usb_request_queue operation. timeoutMillis == -1 requests
+ * to wait forever.
* Returns a usb_request, or NULL for error.
*/
-struct usb_request *usb_request_wait(struct usb_device *dev);
+struct usb_request *usb_request_wait(struct usb_device *dev, int timeoutMillis);
/* Cancels a pending usb_request_queue() operation. */
int usb_request_cancel(struct usb_request *req);
diff --git a/libusbhost/usbhost.c b/libusbhost/usbhost.c
index 050fc2f..44b878d 100644
--- a/libusbhost/usbhost.c
+++ b/libusbhost/usbhost.c
@@ -14,6 +14,10 @@
* limitations under the License.
*/
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
// #define DEBUG 1
#if DEBUG
@@ -43,6 +47,7 @@
#include <fcntl.h>
#include <errno.h>
#include <ctype.h>
+#include <poll.h>
#include <pthread.h>
#include <linux/usbdevice_fs.h>
@@ -449,7 +454,7 @@
return (struct usb_device_descriptor*)device->desc;
}
-char* usb_device_get_string(struct usb_device *device, int id)
+char* usb_device_get_string(struct usb_device *device, int id, int timeout)
{
char string[256];
__u16 buffer[MAX_STRING_DESCRIPTOR_LENGTH / sizeof(__u16)];
@@ -465,7 +470,8 @@
// read list of supported languages
result = usb_device_control_transfer(device,
USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE, USB_REQ_GET_DESCRIPTOR,
- (USB_DT_STRING << 8) | 0, 0, languages, sizeof(languages), 0);
+ (USB_DT_STRING << 8) | 0, 0, languages, sizeof(languages),
+ timeout);
if (result > 0)
languageCount = (result - 2) / 2;
@@ -474,7 +480,8 @@
result = usb_device_control_transfer(device,
USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE, USB_REQ_GET_DESCRIPTOR,
- (USB_DT_STRING << 8) | id, languages[i], buffer, sizeof(buffer), 0);
+ (USB_DT_STRING << 8) | id, languages[i], buffer, sizeof(buffer),
+ timeout);
if (result > 0) {
int i;
// skip first word, and copy the rest to the string, changing shorts to bytes.
@@ -489,16 +496,16 @@
return NULL;
}
-char* usb_device_get_manufacturer_name(struct usb_device *device)
+char* usb_device_get_manufacturer_name(struct usb_device *device, int timeout)
{
struct usb_device_descriptor *desc = (struct usb_device_descriptor *)device->desc;
- return usb_device_get_string(device, desc->iManufacturer);
+ return usb_device_get_string(device, desc->iManufacturer, timeout);
}
-char* usb_device_get_product_name(struct usb_device *device)
+char* usb_device_get_product_name(struct usb_device *device, int timeout)
{
struct usb_device_descriptor *desc = (struct usb_device_descriptor *)device->desc;
- return usb_device_get_string(device, desc->iProduct);
+ return usb_device_get_string(device, desc->iProduct, timeout);
}
int usb_device_get_version(struct usb_device *device)
@@ -507,10 +514,10 @@
return desc->bcdUSB;
}
-char* usb_device_get_serial(struct usb_device *device)
+char* usb_device_get_serial(struct usb_device *device, int timeout)
{
struct usb_device_descriptor *desc = (struct usb_device_descriptor *)device->desc;
- return usb_device_get_string(device, desc->iSerialNumber);
+ return usb_device_get_string(device, desc->iSerialNumber, timeout);
}
int usb_device_is_writeable(struct usb_device *device)
@@ -681,29 +688,38 @@
return res;
}
-struct usb_request *usb_request_wait(struct usb_device *dev)
+struct usb_request *usb_request_wait(struct usb_device *dev, int timeoutMillis)
{
- struct usbdevfs_urb *urb = NULL;
- struct usb_request *req = NULL;
+ // Poll until a request becomes available if there is a timeout
+ if (timeoutMillis > 0) {
+ struct pollfd p = {.fd = dev->fd, .events = POLLOUT, .revents = 0};
- while (1) {
- int res = ioctl(dev->fd, USBDEVFS_REAPURB, &urb);
- D("USBDEVFS_REAPURB returned %d\n", res);
- if (res < 0) {
- if(errno == EINTR) {
- continue;
- }
- D("[ reap urb - error ]\n");
+ int res = poll(&p, 1, timeoutMillis);
+
+ if (res != 1 || p.revents != POLLOUT) {
+ D("[ poll - event %d, error %d]\n", p.revents, errno);
return NULL;
- } else {
- D("[ urb @%p status = %d, actual = %d ]\n",
- urb, urb->status, urb->actual_length);
- req = (struct usb_request*)urb->usercontext;
- req->actual_length = urb->actual_length;
}
- break;
}
- return req;
+
+ // Read the request. This should usually succeed as we polled before, but it can fail e.g. when
+ // two threads are reading usb requests at the same time and only a single request is available.
+ struct usbdevfs_urb *urb = NULL;
+ int res = TEMP_FAILURE_RETRY(ioctl(dev->fd, timeoutMillis == -1 ? USBDEVFS_REAPURB :
+ USBDEVFS_REAPURBNDELAY, &urb));
+ D("%s returned %d\n", timeoutMillis == -1 ? "USBDEVFS_REAPURB" : "USBDEVFS_REAPURBNDELAY", res);
+
+ if (res < 0) {
+ D("[ reap urb - error %d]\n", errno);
+ return NULL;
+ } else {
+ D("[ urb @%p status = %d, actual = %d ]\n", urb, urb->status, urb->actual_length);
+
+ struct usb_request *req = (struct usb_request*)urb->usercontext;
+ req->actual_length = urb->actual_length;
+
+ return req;
+ }
}
int usb_request_cancel(struct usb_request *req)
@@ -711,4 +727,3 @@
struct usbdevfs_urb *urb = ((struct usbdevfs_urb*)req->private_data);
return ioctl(req->dev->fd, USBDEVFS_DISCARDURB, urb);
}
-
diff --git a/libutils/Android.bp b/libutils/Android.bp
index fbfb7c2..adcde81 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -18,12 +18,10 @@
host_supported: true,
header_libs: [
- "liblog_headers",
"libsystem_headers",
"libcutils_headers"
],
export_header_lib_headers: [
- "liblog_headers",
"libsystem_headers",
"libcutils_headers"
],
@@ -46,13 +44,16 @@
cc_library {
name: "libutils",
vendor_available: true,
+ vndk: {
+ enabled: true,
+ support_system_process: true,
+ },
host_supported: true,
srcs: [
"CallStack.cpp",
"FileMap.cpp",
"JenkinsHash.cpp",
- "LinearTransform.cpp",
"NativeHandle.cpp",
"Printer.cpp",
"PropertyMap.cpp",
@@ -74,11 +75,11 @@
cflags: ["-Werror"],
include_dirs: ["external/safe-iop/include"],
- header_libs: ["libutils_headers"],
- export_header_lib_headers: ["libutils_headers"],
-
- shared_libs: [
- "liblog",
+ header_libs: [
+ "libutils_headers",
+ ],
+ export_header_lib_headers: [
+ "libutils_headers",
],
arch: {
@@ -90,7 +91,6 @@
target: {
android: {
srcs: [
- "BlobCache.cpp",
"Looper.cpp",
"ProcessCallStack.cpp",
"Trace.cpp",
@@ -102,6 +102,8 @@
"libbacktrace",
"libcutils",
"libdl",
+ "liblog",
+ "libvndksupport",
],
sanitize: {
@@ -142,8 +144,6 @@
enabled: true,
},
},
-
- clang: true,
}
// Include subdirectory makefiles
diff --git a/libutils/BlobCache.cpp b/libutils/BlobCache.cpp
deleted file mode 100644
index 126995b..0000000
--- a/libutils/BlobCache.cpp
+++ /dev/null
@@ -1,379 +0,0 @@
-/*
- ** Copyright 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.
- */
-
-#define LOG_TAG "BlobCache"
-//#define LOG_NDEBUG 0
-
-#include <inttypes.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <utils/BlobCache.h>
-#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 = 3;
-
-// BlobCache::Header::mDeviceVersion value
-static const uint32_t blobCacheDeviceVersion = 1;
-
-BlobCache::BlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize):
- mMaxKeySize(maxKeySize),
- mMaxValueSize(maxValueSize),
- mMaxTotalSize(maxTotalSize),
- mTotalSize(0) {
- nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
-#ifdef _WIN32
- srand(now);
-#else
- mRandState[0] = (now >> 0) & 0xFFFF;
- mRandState[1] = (now >> 16) & 0xFFFF;
- mRandState[2] = (now >> 32) & 0xFFFF;
-#endif
- ALOGV("initializing random seed using %lld", (unsigned long long)now);
-}
-
-void BlobCache::set(const void* key, size_t keySize, const void* value,
- size_t valueSize) {
- if (mMaxKeySize < keySize) {
- ALOGV("set: not caching because the key is too large: %zu (limit: %zu)",
- keySize, mMaxKeySize);
- return;
- }
- if (mMaxValueSize < valueSize) {
- ALOGV("set: not caching because the value is too large: %zu (limit: %zu)",
- valueSize, mMaxValueSize);
- return;
- }
- if (mMaxTotalSize < keySize + valueSize) {
- ALOGV("set: not caching because the combined key/value size is too "
- "large: %zu (limit: %zu)", keySize + valueSize, mMaxTotalSize);
- return;
- }
- if (keySize == 0) {
- ALOGW("set: not caching because keySize is 0");
- return;
- }
- if (valueSize <= 0) {
- ALOGW("set: not caching because valueSize is 0");
- return;
- }
-
- sp<Blob> dummyKey(new Blob(key, keySize, false));
- CacheEntry dummyEntry(dummyKey, NULL);
-
- while (true) {
- ssize_t index = mCacheEntries.indexOf(dummyEntry);
- if (index < 0) {
- // Create a new cache entry.
- sp<Blob> keyBlob(new Blob(key, keySize, true));
- sp<Blob> valueBlob(new Blob(value, valueSize, true));
- size_t newTotalSize = mTotalSize + keySize + valueSize;
- if (mMaxTotalSize < newTotalSize) {
- if (isCleanable()) {
- // Clean the cache and try again.
- clean();
- continue;
- } else {
- ALOGV("set: not caching new key/value pair because the "
- "total cache size limit would be exceeded: %zu "
- "(limit: %zu)",
- keySize + valueSize, mMaxTotalSize);
- break;
- }
- }
- mCacheEntries.add(CacheEntry(keyBlob, valueBlob));
- mTotalSize = newTotalSize;
- ALOGV("set: created new cache entry with %zu byte key and %zu byte value",
- keySize, valueSize);
- } else {
- // Update the existing cache entry.
- sp<Blob> valueBlob(new Blob(value, valueSize, true));
- sp<Blob> oldValueBlob(mCacheEntries[index].getValue());
- size_t newTotalSize = mTotalSize + valueSize - oldValueBlob->getSize();
- if (mMaxTotalSize < newTotalSize) {
- if (isCleanable()) {
- // Clean the cache and try again.
- clean();
- continue;
- } else {
- ALOGV("set: not caching new value because the total cache "
- "size limit would be exceeded: %zu (limit: %zu)",
- keySize + valueSize, mMaxTotalSize);
- break;
- }
- }
- mCacheEntries.editItemAt(index).setValue(valueBlob);
- mTotalSize = newTotalSize;
- ALOGV("set: updated existing cache entry with %zu byte key and %zu byte "
- "value", keySize, valueSize);
- }
- break;
- }
-}
-
-size_t BlobCache::get(const void* key, size_t keySize, void* value,
- size_t valueSize) {
- if (mMaxKeySize < keySize) {
- ALOGV("get: not searching because the key is too large: %zu (limit %zu)",
- keySize, mMaxKeySize);
- return 0;
- }
- sp<Blob> dummyKey(new Blob(key, keySize, false));
- CacheEntry dummyEntry(dummyKey, NULL);
- ssize_t index = mCacheEntries.indexOf(dummyEntry);
- if (index < 0) {
- ALOGV("get: no cache entry found for key of size %zu", keySize);
- return 0;
- }
-
- // The key was found. Return the value if the caller's buffer is large
- // enough.
- sp<Blob> valueBlob(mCacheEntries[index].getValue());
- size_t valueBlobSize = valueBlob->getSize();
- if (valueBlobSize <= valueSize) {
- ALOGV("get: copying %zu bytes to caller's buffer", valueBlobSize);
- memcpy(value, valueBlob->getData(), valueBlobSize);
- } else {
- ALOGV("get: caller's buffer is too small for value: %zu (needs %zu)",
- valueSize, valueBlobSize);
- }
- return valueBlobSize;
-}
-
-static inline size_t align4(size_t size) {
- return (size + 3) & ~3;
-}
-
-size_t BlobCache::getFlattenedSize() const {
- 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();
- sp<Blob> valueBlob = e.getValue();
- size += align4(sizeof(EntryHeader) + keyBlob->getSize() +
- valueBlob->getSize());
- }
- return size;
-}
-
-status_t BlobCache::flatten(void* buffer, size_t size) const {
- // Write the cache header
- if (size < sizeof(Header)) {
- ALOGE("flatten: not enough room for cache header");
- return BAD_VALUE;
- }
- Header* header = reinterpret_cast<Header*>(buffer);
- header->mMagicNumber = blobCacheMagic;
- 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) + header->mBuildIdLength);
- for (size_t i = 0; i < mCacheEntries.size(); i++) {
- const CacheEntry& e(mCacheEntries[i]);
- sp<Blob> keyBlob = e.getKey();
- sp<Blob> valueBlob = e.getValue();
- size_t keySize = keyBlob->getSize();
- size_t valueSize = valueBlob->getSize();
-
- size_t entrySize = sizeof(EntryHeader) + keySize + valueSize;
- size_t totalSize = align4(entrySize);
- if (byteOffset + totalSize > size) {
- ALOGE("flatten: not enough room for cache entries");
- return BAD_VALUE;
- }
-
- EntryHeader* eheader = reinterpret_cast<EntryHeader*>(
- &byteBuffer[byteOffset]);
- eheader->mKeySize = keySize;
- eheader->mValueSize = valueSize;
-
- memcpy(eheader->mData, keyBlob->getData(), keySize);
- memcpy(eheader->mData + keySize, valueBlob->getData(), valueSize);
-
- if (totalSize > entrySize) {
- // We have padding bytes. Those will get written to storage, and contribute to the CRC,
- // so make sure we zero-them to have reproducible results.
- memset(eheader->mData + keySize + valueSize, 0, totalSize - entrySize);
- }
-
- byteOffset += totalSize;
- }
-
- return OK;
-}
-
-status_t BlobCache::unflatten(void const* buffer, size_t size) {
- // All errors should result in the BlobCache being in an empty state.
- mCacheEntries.clear();
-
- // Read the cache header
- if (size < sizeof(Header)) {
- ALOGE("unflatten: not enough room for cache header");
- return BAD_VALUE;
- }
- const Header* header = reinterpret_cast<const Header*>(buffer);
- if (header->mMagicNumber != blobCacheMagic) {
- 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 ||
- 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) + header->mBuildIdLength);
- size_t numEntries = header->mNumEntries;
- for (size_t i = 0; i < numEntries; i++) {
- if (byteOffset + sizeof(EntryHeader) > size) {
- mCacheEntries.clear();
- ALOGE("unflatten: not enough room for cache entry headers");
- return BAD_VALUE;
- }
-
- const EntryHeader* eheader = reinterpret_cast<const EntryHeader*>(
- &byteBuffer[byteOffset]);
- size_t keySize = eheader->mKeySize;
- size_t valueSize = eheader->mValueSize;
- size_t entrySize = sizeof(EntryHeader) + keySize + valueSize;
-
- size_t totalSize = align4(entrySize);
- if (byteOffset + totalSize > size) {
- mCacheEntries.clear();
- ALOGE("unflatten: not enough room for cache entry headers");
- return BAD_VALUE;
- }
-
- const uint8_t* data = eheader->mData;
- set(data, keySize, data + keySize, valueSize);
-
- byteOffset += totalSize;
- }
-
- return OK;
-}
-
-long int BlobCache::blob_random() {
-#ifdef _WIN32
- return rand();
-#else
- return nrand48(mRandState);
-#endif
-}
-
-void BlobCache::clean() {
- // Remove a random cache entry until the total cache size gets below half
- // the maximum total cache size.
- while (mTotalSize > mMaxTotalSize / 2) {
- size_t i = size_t(blob_random() % (mCacheEntries.size()));
- const CacheEntry& entry(mCacheEntries[i]);
- mTotalSize -= entry.getKey()->getSize() + entry.getValue()->getSize();
- mCacheEntries.removeAt(i);
- }
-}
-
-bool BlobCache::isCleanable() const {
- return mTotalSize > mMaxTotalSize / 2;
-}
-
-BlobCache::Blob::Blob(const void* data, size_t size, bool copyData):
- mData(copyData ? malloc(size) : data),
- mSize(size),
- mOwnsData(copyData) {
- if (data != NULL && copyData) {
- memcpy(const_cast<void*>(mData), data, size);
- }
-}
-
-BlobCache::Blob::~Blob() {
- if (mOwnsData) {
- free(const_cast<void*>(mData));
- }
-}
-
-bool BlobCache::Blob::operator<(const Blob& rhs) const {
- if (mSize == rhs.mSize) {
- return memcmp(mData, rhs.mData, mSize) < 0;
- } else {
- return mSize < rhs.mSize;
- }
-}
-
-const void* BlobCache::Blob::getData() const {
- return mData;
-}
-
-size_t BlobCache::Blob::getSize() const {
- return mSize;
-}
-
-BlobCache::CacheEntry::CacheEntry() {
-}
-
-BlobCache::CacheEntry::CacheEntry(const sp<Blob>& key, const sp<Blob>& value):
- mKey(key),
- mValue(value) {
-}
-
-BlobCache::CacheEntry::CacheEntry(const CacheEntry& ce):
- mKey(ce.mKey),
- mValue(ce.mValue) {
-}
-
-bool BlobCache::CacheEntry::operator<(const CacheEntry& rhs) const {
- return *mKey < *rhs.mKey;
-}
-
-const BlobCache::CacheEntry& BlobCache::CacheEntry::operator=(const CacheEntry& rhs) {
- mKey = rhs.mKey;
- mValue = rhs.mValue;
- return *this;
-}
-
-sp<BlobCache::Blob> BlobCache::CacheEntry::getKey() const {
- return mKey;
-}
-
-sp<BlobCache::Blob> BlobCache::CacheEntry::getValue() const {
- return mValue;
-}
-
-void BlobCache::CacheEntry::setValue(const sp<Blob>& value) {
- mValue = value;
-}
-
-} // namespace android
diff --git a/libutils/CallStack.cpp b/libutils/CallStack.cpp
index 699da74..bd6015e 100644
--- a/libutils/CallStack.cpp
+++ b/libutils/CallStack.cpp
@@ -16,9 +16,10 @@
#define LOG_TAG "CallStack"
+#include <utils/CallStack.h>
+
#include <memory>
-#include <utils/CallStack.h>
#include <utils/Printer.h>
#include <utils/Errors.h>
#include <utils/Log.h>
diff --git a/libutils/LinearTransform.cpp b/libutils/LinearTransform.cpp
deleted file mode 100644
index 138ce8b..0000000
--- a/libutils/LinearTransform.cpp
+++ /dev/null
@@ -1,282 +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.
- */
-
-#define __STDC_LIMIT_MACROS
-
-#include <assert.h>
-#include <stdint.h>
-
-#include <utils/LinearTransform.h>
-
-// disable sanitize as these functions may intentionally overflow (see comments below).
-// the ifdef can be removed when host builds use clang.
-#if defined(__clang__)
-#define ATTRIBUTE_NO_SANITIZE_INTEGER __attribute__((no_sanitize("integer")))
-#else
-#define ATTRIBUTE_NO_SANITIZE_INTEGER
-#endif
-
-namespace android {
-
-// sanitize failure with T = int32_t and x = 0x80000000
-template<class T>
-ATTRIBUTE_NO_SANITIZE_INTEGER
-static inline T ABS(T x) { return (x < 0) ? -x : x; }
-
-// Static math methods involving linear transformations
-// remote sanitize failure on overflow case.
-ATTRIBUTE_NO_SANITIZE_INTEGER
-static bool scale_u64_to_u64(
- uint64_t val,
- uint32_t N,
- uint32_t D,
- uint64_t* res,
- bool round_up_not_down) {
- uint64_t tmp1, tmp2;
- uint32_t r;
-
- assert(res);
- assert(D);
-
- // Let U32(X) denote a uint32_t containing the upper 32 bits of a 64 bit
- // integer X.
- // Let L32(X) denote a uint32_t containing the lower 32 bits of a 64 bit
- // integer X.
- // Let X[A, B] with A <= B denote bits A through B of the integer X.
- // Let (A | B) denote the concatination of two 32 bit ints, A and B.
- // IOW X = (A | B) => U32(X) == A && L32(X) == B
- //
- // compute M = val * N (a 96 bit int)
- // ---------------------------------
- // tmp2 = U32(val) * N (a 64 bit int)
- // tmp1 = L32(val) * N (a 64 bit int)
- // which means
- // M = val * N = (tmp2 << 32) + tmp1
- tmp2 = (val >> 32) * N;
- tmp1 = (val & UINT32_MAX) * N;
-
- // compute M[32, 95]
- // tmp2 = tmp2 + U32(tmp1)
- // = (U32(val) * N) + U32(L32(val) * N)
- // = M[32, 95]
- tmp2 += tmp1 >> 32;
-
- // if M[64, 95] >= D, then M/D has bits > 63 set and we have
- // an overflow.
- if ((tmp2 >> 32) >= D) {
- *res = UINT64_MAX;
- return false;
- }
-
- // Divide. Going in we know
- // tmp2 = M[32, 95]
- // U32(tmp2) < D
- r = tmp2 % D;
- tmp2 /= D;
-
- // At this point
- // tmp1 = L32(val) * N
- // tmp2 = M[32, 95] / D
- // = (M / D)[32, 95]
- // r = M[32, 95] % D
- // U32(tmp2) = 0
- //
- // compute tmp1 = (r | M[0, 31])
- tmp1 = (tmp1 & UINT32_MAX) | ((uint64_t)r << 32);
-
- // Divide again. Keep the remainder around in order to round properly.
- r = tmp1 % D;
- tmp1 /= D;
-
- // At this point
- // tmp2 = (M / D)[32, 95]
- // tmp1 = (M / D)[ 0, 31]
- // r = M % D
- // U32(tmp1) = 0
- // U32(tmp2) = 0
-
- // Pack the result and deal with the round-up case (As well as the
- // remote possiblility over overflow in such a case).
- *res = (tmp2 << 32) | tmp1;
- if (r && round_up_not_down) {
- ++(*res);
- if (!(*res)) {
- *res = UINT64_MAX;
- return false;
- }
- }
-
- return true;
-}
-
-// at least one known sanitize failure (see comment below)
-ATTRIBUTE_NO_SANITIZE_INTEGER
-static bool linear_transform_s64_to_s64(
- int64_t val,
- int64_t basis1,
- int32_t N,
- uint32_t D,
- bool invert_frac,
- int64_t basis2,
- int64_t* out) {
- uint64_t scaled, res;
- uint64_t abs_val;
- bool is_neg;
-
- if (!out)
- return false;
-
- // Compute abs(val - basis_64). Keep track of whether or not this delta
- // will be negative after the scale opertaion.
- if (val < basis1) {
- is_neg = true;
- abs_val = basis1 - val;
- } else {
- is_neg = false;
- abs_val = val - basis1;
- }
-
- if (N < 0)
- is_neg = !is_neg;
-
- if (!scale_u64_to_u64(abs_val,
- invert_frac ? D : ABS(N),
- invert_frac ? ABS(N) : D,
- &scaled,
- is_neg))
- return false; // overflow/undeflow
-
- // if scaled is >= 0x8000<etc>, then we are going to overflow or
- // underflow unless ABS(basis2) is large enough to pull us back into the
- // non-overflow/underflow region.
- if (scaled & INT64_MIN) {
- if (is_neg && (basis2 < 0))
- return false; // certain underflow
-
- if (!is_neg && (basis2 >= 0))
- return false; // certain overflow
-
- if (ABS(basis2) <= static_cast<int64_t>(scaled & INT64_MAX))
- return false; // not enough
-
- // Looks like we are OK
- *out = (is_neg ? (-scaled) : scaled) + basis2;
- } else {
- // Scaled fits within signed bounds, so we just need to check for
- // over/underflow for two signed integers. Basically, if both scaled
- // and basis2 have the same sign bit, and the result has a different
- // sign bit, then we have under/overflow. An easy way to compute this
- // is
- // (scaled_signbit XNOR basis_signbit) &&
- // (scaled_signbit XOR res_signbit)
- // ==
- // (scaled_signbit XOR basis_signbit XOR 1) &&
- // (scaled_signbit XOR res_signbit)
-
- if (is_neg)
- scaled = -scaled; // known sanitize failure
- res = scaled + basis2;
-
- if ((scaled ^ basis2 ^ INT64_MIN) & (scaled ^ res) & INT64_MIN)
- return false;
-
- *out = res;
- }
-
- return true;
-}
-
-bool LinearTransform::doForwardTransform(int64_t a_in, int64_t* b_out) const {
- if (0 == a_to_b_denom)
- return false;
-
- return linear_transform_s64_to_s64(a_in,
- a_zero,
- a_to_b_numer,
- a_to_b_denom,
- false,
- b_zero,
- b_out);
-}
-
-bool LinearTransform::doReverseTransform(int64_t b_in, int64_t* a_out) const {
- if (0 == a_to_b_numer)
- return false;
-
- return linear_transform_s64_to_s64(b_in,
- b_zero,
- a_to_b_numer,
- a_to_b_denom,
- true,
- a_zero,
- a_out);
-}
-
-template <class T> void LinearTransform::reduce(T* N, T* D) {
- T a, b;
- if (!N || !D || !(*D)) {
- assert(false);
- return;
- }
-
- a = *N;
- b = *D;
-
- if (a == 0) {
- *D = 1;
- return;
- }
-
- // This implements Euclid's method to find GCD.
- if (a < b) {
- T tmp = a;
- a = b;
- b = tmp;
- }
-
- while (1) {
- // a is now the greater of the two.
- const T remainder = a % b;
- if (remainder == 0) {
- *N /= b;
- *D /= b;
- return;
- }
- // by swapping remainder and b, we are guaranteeing that a is
- // still the greater of the two upon entrance to the loop.
- a = b;
- b = remainder;
- }
-};
-
-template void LinearTransform::reduce<uint64_t>(uint64_t* N, uint64_t* D);
-template void LinearTransform::reduce<uint32_t>(uint32_t* N, uint32_t* D);
-
-// sanitize failure if *N = 0x80000000
-ATTRIBUTE_NO_SANITIZE_INTEGER
-void LinearTransform::reduce(int32_t* N, uint32_t* D) {
- if (N && D && *D) {
- if (*N < 0) {
- *N = -(*N);
- reduce(reinterpret_cast<uint32_t*>(N), D);
- *N = -(*N);
- } else {
- reduce(reinterpret_cast<uint32_t*>(N), D);
- }
- }
-}
-
-} // namespace android
diff --git a/libutils/Looper.cpp b/libutils/Looper.cpp
index 77e69e4..6c57b2e 100644
--- a/libutils/Looper.cpp
+++ b/libutils/Looper.cpp
@@ -13,17 +13,8 @@
// Debugs callback registration and invocation.
#define DEBUG_CALLBACKS 0
-#include <errno.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <limits.h>
-#include <string.h>
-#include <sys/eventfd.h>
-#include <unistd.h>
-
-#include <log/log.h>
#include <utils/Looper.h>
-#include <utils/Timers.h>
+#include <sys/eventfd.h>
namespace android {
@@ -83,6 +74,7 @@
Looper::~Looper() {
close(mWakeEventFd);
+ mWakeEventFd = -1;
if (mEpollFd >= 0) {
close(mEpollFd);
}
@@ -412,7 +404,8 @@
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));
+ LOG_ALWAYS_FATAL("Could not write wake signal to fd %d: %s",
+ mWakeEventFd, strerror(errno));
}
}
}
diff --git a/libutils/NativeHandle.cpp b/libutils/NativeHandle.cpp
index e4daca7..97d06b8 100644
--- a/libutils/NativeHandle.cpp
+++ b/libutils/NativeHandle.cpp
@@ -19,14 +19,14 @@
namespace android {
-sp<NativeHandle> NativeHandle::create(
- native_handle_t* handle, bool ownsHandle) {
+sp<NativeHandle> NativeHandle::create(native_handle_t* handle, bool ownsHandle) {
return handle ? new NativeHandle(handle, ownsHandle) : NULL;
}
NativeHandle::NativeHandle(native_handle_t* handle, bool ownsHandle)
-: mHandle(handle), mOwnsHandle(ownsHandle)
-{}
+ : mHandle(handle), mOwnsHandle(ownsHandle) {
+
+}
NativeHandle::~NativeHandle() {
if (mOwnsHandle) {
diff --git a/libutils/Printer.cpp b/libutils/Printer.cpp
index 84af293..cbf042e 100644
--- a/libutils/Printer.cpp
+++ b/libutils/Printer.cpp
@@ -21,8 +21,6 @@
#include <utils/String8.h>
#include <utils/Log.h>
-#include <string.h>
-#include <stdio.h>
#include <stdlib.h>
namespace android {
diff --git a/libutils/ProcessCallStack.cpp b/libutils/ProcessCallStack.cpp
index 983847c..b8fb6dc 100644
--- a/libutils/ProcessCallStack.cpp
+++ b/libutils/ProcessCallStack.cpp
@@ -17,20 +17,15 @@
#define LOG_TAG "ProcessCallStack"
// #define LOG_NDEBUG 0
+#include <utils/ProcessCallStack.h>
+
#include <dirent.h>
-#include <errno.h>
-#include <stdio.h>
-#include <string.h>
#include <unistd.h>
+
#include <memory>
-#include <utils/Log.h>
-#include <utils/Errors.h>
-#include <utils/ProcessCallStack.h>
#include <utils/Printer.h>
-#include <limits.h>
-
namespace android {
enum {
diff --git a/libutils/PropertyMap.cpp b/libutils/PropertyMap.cpp
index 5520702..4bcdd0f 100644
--- a/libutils/PropertyMap.cpp
+++ b/libutils/PropertyMap.cpp
@@ -16,11 +16,7 @@
#define LOG_TAG "PropertyMap"
-#include <stdlib.h>
-#include <string.h>
-
#include <utils/PropertyMap.h>
-#include <utils/Log.h>
// Enables debug output for the parser.
#define DEBUG_PARSER 0
diff --git a/libutils/RefBase.cpp b/libutils/RefBase.cpp
index 4252ba6..24737b9 100644
--- a/libutils/RefBase.cpp
+++ b/libutils/RefBase.cpp
@@ -17,18 +17,9 @@
#define LOG_TAG "RefBase"
// #define LOG_NDEBUG 0
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
#include <utils/RefBase.h>
#include <utils/CallStack.h>
-#include <utils/Log.h>
-#include <utils/threads.h>
#ifndef __unused
#define __unused __attribute__((__unused__))
@@ -769,6 +760,4 @@
ref->mRefs->renameWeakRefId(old_id, new_id);
}
-VirtualLightRefBase::~VirtualLightRefBase() {}
-
}; // namespace android
diff --git a/libutils/SharedBuffer.cpp b/libutils/SharedBuffer.cpp
index 957aedb..bad98b2 100644
--- a/libutils/SharedBuffer.cpp
+++ b/libutils/SharedBuffer.cpp
@@ -16,13 +16,13 @@
#define LOG_TAG "sharedbuffer"
+#include "SharedBuffer.h"
+
#include <stdlib.h>
#include <string.h>
#include <log/log.h>
-#include "SharedBuffer.h"
-
// ---------------------------------------------------------------------------
namespace android {
@@ -113,16 +113,26 @@
int32_t SharedBuffer::release(uint32_t flags) const
{
- int32_t prev = 1;
- if (onlyOwner()
- || (((prev = mRefs.fetch_sub(1, std::memory_order_release)) == 1)
- && (atomic_thread_fence(std::memory_order_acquire), true))) {
+ const bool useDealloc = ((flags & eKeepStorage) == 0);
+ if (onlyOwner()) {
+ // Since we're the only owner, our reference count goes to zero.
mRefs.store(0, std::memory_order_relaxed);
- if ((flags & eKeepStorage) == 0) {
- free(const_cast<SharedBuffer*>(this));
+ if (useDealloc) {
+ dealloc(this);
+ }
+ // As the only owner, our previous reference count was 1.
+ return 1;
+ }
+ // There's multiple owners, we need to use an atomic decrement.
+ int32_t prevRefCount = mRefs.fetch_sub(1, std::memory_order_release);
+ if (prevRefCount == 1) {
+ // We're the last reference, we need the acquire fence.
+ atomic_thread_fence(std::memory_order_acquire);
+ if (useDealloc) {
+ dealloc(this);
}
}
- return prev;
+ return prevRefCount;
}
diff --git a/libutils/StopWatch.cpp b/libutils/StopWatch.cpp
index 8c7b596..219c13c 100644
--- a/libutils/StopWatch.cpp
+++ b/libutils/StopWatch.cpp
@@ -16,9 +16,7 @@
#define LOG_TAG "StopWatch"
-#include <string.h>
-#include <stdlib.h>
-#include <stdio.h>
+#include <utils/StopWatch.h>
/* for PRId64 */
#ifndef __STDC_FORMAT_MACROS
@@ -27,8 +25,6 @@
#include <inttypes.h>
#include <utils/Log.h>
-#include <utils/Errors.h>
-#include <utils/StopWatch.h>
/*****************************************************************************/
diff --git a/libutils/String16.cpp b/libutils/String16.cpp
index 9f5cfea..ad335c3 100644
--- a/libutils/String16.cpp
+++ b/libutils/String16.cpp
@@ -17,11 +17,7 @@
#include <utils/String16.h>
#include <utils/Log.h>
-#include <utils/Unicode.h>
-#include <utils/threads.h>
-#include <memory.h>
-#include <stdio.h>
#include <ctype.h>
#include "SharedBuffer.h"
diff --git a/libutils/String8.cpp b/libutils/String8.cpp
index cacaf91..ad0e72e 100644
--- a/libutils/String8.cpp
+++ b/libutils/String8.cpp
@@ -21,9 +21,7 @@
#include <utils/Compat.h>
#include <utils/Log.h>
-#include <utils/Unicode.h>
#include <utils/String16.h>
-#include <utils/threads.h>
#include <ctype.h>
diff --git a/libutils/SystemClock.cpp b/libutils/SystemClock.cpp
index 965e32c..28fc351 100644
--- a/libutils/SystemClock.cpp
+++ b/libutils/SystemClock.cpp
@@ -19,17 +19,17 @@
* System clock functions.
*/
+#define LOG_TAG "SystemClock"
+
+#include <utils/SystemClock.h>
+
#include <sys/time.h>
-#include <limits.h>
-#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <cutils/compiler.h>
-#include <utils/SystemClock.h>
-#include <utils/Timers.h>
-#define LOG_TAG "SystemClock"
+#include <utils/Timers.h>
#include <utils/Log.h>
namespace android {
diff --git a/libutils/Threads.cpp b/libutils/Threads.cpp
index def739f..6317c32 100644
--- a/libutils/Threads.cpp
+++ b/libutils/Threads.cpp
@@ -18,16 +18,10 @@
#define LOG_TAG "libutils.threads"
#include <assert.h>
-#include <errno.h>
-#include <memory.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
+#include <utils/Thread.h>
+#include <utils/AndroidThreads.h>
#if !defined(_WIN32)
-# include <pthread.h>
-# include <sched.h>
# include <sys/resource.h>
#else
# include <windows.h>
@@ -40,7 +34,6 @@
#include <sys/prctl.h>
#endif
-#include <utils/threads.h>
#include <utils/Log.h>
#include <cutils/sched_policy.h>
diff --git a/libutils/Timers.cpp b/libutils/Timers.cpp
index 201bc41..b2df9a5 100644
--- a/libutils/Timers.cpp
+++ b/libutils/Timers.cpp
@@ -20,7 +20,6 @@
#include <utils/Timers.h>
#include <limits.h>
-#include <sys/time.h>
#include <time.h>
#if defined(__ANDROID__)
@@ -53,7 +52,7 @@
int toMillisecondTimeoutDelay(nsecs_t referenceTime, nsecs_t timeoutTime)
{
- int timeoutDelayMillis;
+ nsecs_t timeoutDelayMillis;
if (timeoutTime > referenceTime) {
uint64_t timeoutDelay = uint64_t(timeoutTime - referenceTime);
if (timeoutDelay > uint64_t((INT_MAX - 1) * 1000000LL)) {
@@ -64,5 +63,5 @@
} else {
timeoutDelayMillis = 0;
}
- return timeoutDelayMillis;
+ return (int)timeoutDelayMillis;
}
diff --git a/libutils/Tokenizer.cpp b/libutils/Tokenizer.cpp
index 2d0e83d..b68a2cf 100644
--- a/libutils/Tokenizer.cpp
+++ b/libutils/Tokenizer.cpp
@@ -16,14 +16,10 @@
#define LOG_TAG "Tokenizer"
-#include <stdlib.h>
-#include <unistd.h>
+#include <utils/Tokenizer.h>
#include <fcntl.h>
-#include <errno.h>
-#include <sys/types.h>
#include <sys/stat.h>
#include <utils/Log.h>
-#include <utils/Tokenizer.h>
// Enables debug output for the tokenizer.
#define DEBUG_TOKENIZER 0
diff --git a/libutils/Trace.cpp b/libutils/Trace.cpp
index 36fd802..8530fdc 100644
--- a/libutils/Trace.cpp
+++ b/libutils/Trace.cpp
@@ -14,12 +14,11 @@
* limitations under the License.
*/
-#include <utils/misc.h>
#include <utils/Trace.h>
+#include <utils/misc.h>
static void traceInit() __attribute__((constructor));
-static void traceInit()
-{
+static void traceInit() {
::android::add_sysprop_change_callback(atrace_update_tags, 0);
}
diff --git a/libutils/Unicode.cpp b/libutils/Unicode.cpp
index f1a41b9..5fd9155 100644
--- a/libutils/Unicode.cpp
+++ b/libutils/Unicode.cpp
@@ -16,11 +16,10 @@
#define LOG_TAG "unicode"
+#include <utils/Unicode.h>
#include <limits.h>
-#include <stddef.h>
#include <log/log.h>
-#include <utils/Unicode.h>
#if defined(_WIN32)
# undef nhtol
diff --git a/libutils/VectorImpl.cpp b/libutils/VectorImpl.cpp
index f7ca8f4..ef3277f 100644
--- a/libutils/VectorImpl.cpp
+++ b/libutils/VectorImpl.cpp
@@ -16,13 +16,13 @@
#define LOG_TAG "Vector"
+#include <utils/VectorImpl.h>
+
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <log/log.h>
-#include <utils/Errors.h>
-#include <utils/VectorImpl.h>
#include <safe_iop.h>
diff --git a/libutils/include/utils/BlobCache.h b/libutils/include/utils/BlobCache.h
deleted file mode 100644
index 65dca9f..0000000
--- a/libutils/include/utils/BlobCache.h
+++ /dev/null
@@ -1,249 +0,0 @@
-/*
- ** Copyright 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_BLOB_CACHE_H
-#define ANDROID_BLOB_CACHE_H
-
-#include <stddef.h>
-
-#include <utils/Flattenable.h>
-#include <utils/RefBase.h>
-#include <utils/SortedVector.h>
-#include <utils/threads.h>
-
-namespace android {
-
-// A BlobCache is an in-memory cache for binary key/value pairs. A BlobCache
-// does NOT provide any thread-safety guarantees.
-//
-// The cache contents can be serialized to an in-memory buffer or mmap'd file
-// and then reloaded in a subsequent execution of the program. This
-// serialization is non-portable and the data should only be used by the device
-// that generated it.
-class BlobCache : public RefBase {
-
-public:
-
- // Create an empty blob cache. The blob cache will cache key/value pairs
- // with key and value sizes less than or equal to maxKeySize and
- // maxValueSize, respectively. The total combined size of ALL cache entries
- // (key sizes plus value sizes) will not exceed maxTotalSize.
- BlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize);
-
- // set inserts a new binary value into the cache and associates it with the
- // given binary key. If the key or value are too large for the cache then
- // the cache remains unchanged. This includes the case where a different
- // value was previously associated with the given key - the old value will
- // remain in the cache. If the given key and value are small enough to be
- // put in the cache (based on the maxKeySize, maxValueSize, and maxTotalSize
- // values specified to the BlobCache constructor), then the key/value pair
- // will be in the cache after set returns. Note, however, that a subsequent
- // call to set may evict old key/value pairs from the cache.
- //
- // Preconditions:
- // key != NULL
- // 0 < keySize
- // value != NULL
- // 0 < valueSize
- void set(const void* key, size_t keySize, const void* value,
- size_t valueSize);
-
- // get retrieves from the cache the binary value associated with a given
- // binary key. If the key is present in the cache then the length of the
- // binary value associated with that key is returned. If the value argument
- // is non-NULL and the size of the cached value is less than valueSize bytes
- // then the cached value is copied into the buffer pointed to by the value
- // argument. If the key is not present in the cache then 0 is returned and
- // the buffer pointed to by the value argument is not modified.
- //
- // Note that when calling get multiple times with the same key, the later
- // calls may fail, returning 0, even if earlier calls succeeded. The return
- // value must be checked for each call.
- //
- // Preconditions:
- // key != NULL
- // 0 < keySize
- // 0 <= valueSize
- size_t get(const void* key, size_t keySize, void* value, size_t valueSize);
-
-
- // getFlattenedSize returns the number of bytes needed to store the entire
- // serialized cache.
- size_t getFlattenedSize() const;
-
- // flatten serializes the current contents of the cache into the memory
- // pointed to by 'buffer'. The serialized cache contents can later be
- // loaded into a BlobCache object using the unflatten method. The contents
- // of the BlobCache object will not be modified.
- //
- // Preconditions:
- // size >= this.getFlattenedSize()
- status_t flatten(void* buffer, size_t size) const;
-
- // unflatten replaces the contents of the cache with the serialized cache
- // contents in the memory pointed to by 'buffer'. The previous contents of
- // the BlobCache will be evicted from the cache. If an error occurs while
- // unflattening the serialized cache contents then the BlobCache will be
- // left in an empty state.
- //
- status_t unflatten(void const* buffer, size_t size);
-
-private:
- // Copying is disallowed.
- BlobCache(const BlobCache&);
- void operator=(const BlobCache&);
-
- // A random function helper to get around MinGW not having nrand48()
- long int blob_random();
-
- // clean evicts a randomly chosen set of entries from the cache such that
- // the total size of all remaining entries is less than mMaxTotalSize/2.
- void clean();
-
- // isCleanable returns true if the cache is full enough for the clean method
- // to have some effect, and false otherwise.
- bool isCleanable() const;
-
- // A Blob is an immutable sized unstructured data blob.
- class Blob : public RefBase {
- public:
- Blob(const void* data, size_t size, bool copyData);
- ~Blob();
-
- bool operator<(const Blob& rhs) const;
-
- const void* getData() const;
- size_t getSize() const;
-
- private:
- // Copying is not allowed.
- Blob(const Blob&);
- void operator=(const Blob&);
-
- // mData points to the buffer containing the blob data.
- const void* mData;
-
- // mSize is the size of the blob data in bytes.
- size_t mSize;
-
- // mOwnsData indicates whether or not this Blob object should free the
- // memory pointed to by mData when the Blob gets destructed.
- bool mOwnsData;
- };
-
- // A CacheEntry is a single key/value pair in the cache.
- class CacheEntry {
- public:
- CacheEntry();
- CacheEntry(const sp<Blob>& key, const sp<Blob>& value);
- CacheEntry(const CacheEntry& ce);
-
- bool operator<(const CacheEntry& rhs) const;
- const CacheEntry& operator=(const CacheEntry&);
-
- sp<Blob> getKey() const;
- sp<Blob> getValue() const;
-
- void setValue(const sp<Blob>& value);
-
- private:
-
- // mKey is the key that identifies the cache entry.
- sp<Blob> mKey;
-
- // mValue is the cached data associated with the key.
- sp<Blob> mValue;
- };
-
- // A Header is the header for the entire BlobCache serialization format. No
- // need to make this portable, so we simply write the struct out.
- struct Header {
- // mMagicNumber is the magic number that identifies the data as
- // serialized BlobCache contents. It must always contain 'Blb$'.
- uint32_t mMagicNumber;
-
- // mBlobCacheVersion is the serialization format version.
- uint32_t mBlobCacheVersion;
-
- // mDeviceVersion is the device-specific version of the cache. This can
- // be used to invalidate the cache.
- uint32_t mDeviceVersion;
-
- // 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
- // make this portable, so we simply write the struct out. Each EntryHeader
- // is followed imediately by the key data and then the value data.
- //
- // The beginning of each serialized EntryHeader is 4-byte aligned, so the
- // number of bytes that a serialized cache entry will occupy is:
- //
- // ((sizeof(EntryHeader) + keySize + valueSize) + 3) & ~3
- //
- struct EntryHeader {
- // mKeySize is the size of the entry key in bytes.
- size_t mKeySize;
-
- // mValueSize is the size of the entry value in bytes.
- size_t mValueSize;
-
- // mData contains both the key and value data for the cache entry. The
- // key comes first followed immediately by the value.
- uint8_t mData[];
- };
-
- // mMaxKeySize is the maximum key size that will be cached. Calls to
- // BlobCache::set with a keySize parameter larger than mMaxKeySize will
- // simply not add the key/value pair to the cache.
- const size_t mMaxKeySize;
-
- // mMaxValueSize is the maximum value size that will be cached. Calls to
- // BlobCache::set with a valueSize parameter larger than mMaxValueSize will
- // simply not add the key/value pair to the cache.
- const size_t mMaxValueSize;
-
- // mMaxTotalSize is the maximum size that all cache entries can occupy. This
- // includes space for both keys and values. When a call to BlobCache::set
- // would otherwise cause this limit to be exceeded, either the key/value
- // pair passed to BlobCache::set will not be cached or other cache entries
- // will be evicted from the cache to make room for the new entry.
- const size_t mMaxTotalSize;
-
- // mTotalSize is the total combined size of all keys and values currently in
- // the cache.
- size_t mTotalSize;
-
- // mRandState is the pseudo-random number generator state. It is passed to
- // nrand48 to generate random numbers when needed.
- unsigned short mRandState[3];
-
- // mCacheEntries stores all the cache entries that are resident in memory.
- // Cache entries are added to it by the 'set' method.
- SortedVector<CacheEntry> mCacheEntries;
-};
-
-}
-
-#endif // ANDROID_BLOB_CACHE_H
diff --git a/libutils/include/utils/Flattenable.h b/libutils/include/utils/Flattenable.h
index 22b811a..070c710 100644
--- a/libutils/include/utils/Flattenable.h
+++ b/libutils/include/utils/Flattenable.h
@@ -189,11 +189,11 @@
}
inline status_t flatten(void* buffer, size_t size) const {
if (size < sizeof(T)) return NO_MEMORY;
- *reinterpret_cast<T*>(buffer) = *static_cast<T const*>(this);
+ memcpy(buffer, static_cast<T const*>(this), sizeof(T));
return NO_ERROR;
}
inline status_t unflatten(void const* buffer, size_t) {
- *static_cast<T*>(this) = *reinterpret_cast<T const*>(buffer);
+ memcpy(static_cast<T*>(this), buffer, sizeof(T));
return NO_ERROR;
}
};
diff --git a/libutils/include/utils/LightRefBase.h b/libutils/include/utils/LightRefBase.h
new file mode 100644
index 0000000..65257ed
--- /dev/null
+++ b/libutils/include/utils/LightRefBase.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#pragma once
+
+/*
+ * See documentation in RefBase.h
+ */
+
+#include <atomic>
+
+#include <sys/types.h>
+
+namespace android {
+
+class ReferenceRenamer;
+
+template <class T>
+class LightRefBase
+{
+public:
+ inline LightRefBase() : mCount(0) { }
+ inline void incStrong(__attribute__((unused)) const void* id) const {
+ mCount.fetch_add(1, std::memory_order_relaxed);
+ }
+ inline void decStrong(__attribute__((unused)) const void* id) const {
+ if (mCount.fetch_sub(1, std::memory_order_release) == 1) {
+ std::atomic_thread_fence(std::memory_order_acquire);
+ delete static_cast<const T*>(this);
+ }
+ }
+ //! DEBUGGING ONLY: Get current strong ref count.
+ inline int32_t getStrongCount() const {
+ return mCount.load(std::memory_order_relaxed);
+ }
+
+ typedef LightRefBase<T> basetype;
+
+protected:
+ inline ~LightRefBase() { }
+
+private:
+ friend class ReferenceMover;
+ inline static void renameRefs(size_t /*n*/, const ReferenceRenamer& /*renamer*/) { }
+ inline static void renameRefId(T* /*ref*/, const void* /*old_id*/ , const void* /*new_id*/) { }
+
+private:
+ mutable std::atomic<int32_t> mCount;
+};
+
+
+// This is a wrapper around LightRefBase that simply enforces a virtual
+// destructor to eliminate the template requirement of LightRefBase
+class VirtualLightRefBase : public LightRefBase<VirtualLightRefBase> {
+public:
+ virtual ~VirtualLightRefBase() = default;
+};
+
+}; // namespace android
diff --git a/libutils/include/utils/LinearTransform.h b/libutils/include/utils/LinearTransform.h
deleted file mode 100644
index 04cb355..0000000
--- a/libutils/include/utils/LinearTransform.h
+++ /dev/null
@@ -1,64 +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 _LIBS_UTILS_LINEAR_TRANSFORM_H
-#define _LIBS_UTILS_LINEAR_TRANSFORM_H
-
-#include <stdint.h>
-
-namespace android {
-
-// LinearTransform defines a structure which hold the definition of a
-// transformation from single dimensional coordinate system A into coordinate
-// system B (and back again). Values in A and in B are 64 bit, the linear
-// scale factor is expressed as a rational number using two 32 bit values.
-//
-// Specifically, let
-// f(a) = b
-// F(b) = f^-1(b) = a
-// then
-//
-// f(a) = (((a - a_zero) * a_to_b_numer) / a_to_b_denom) + b_zero;
-//
-// and
-//
-// F(b) = (((b - b_zero) * a_to_b_denom) / a_to_b_numer) + a_zero;
-//
-struct LinearTransform {
- int64_t a_zero;
- int64_t b_zero;
- int32_t a_to_b_numer;
- uint32_t a_to_b_denom;
-
- // Transform from A->B
- // Returns true on success, or false in the case of a singularity or an
- // overflow.
- bool doForwardTransform(int64_t a_in, int64_t* b_out) const;
-
- // Transform from B->A
- // Returns true on success, or false in the case of a singularity or an
- // overflow.
- bool doReverseTransform(int64_t b_in, int64_t* a_out) const;
-
- // Helpers which will reduce the fraction N/D using Euclid's method.
- template <class T> static void reduce(T* N, T* D);
- static void reduce(int32_t* N, uint32_t* D);
-};
-
-
-}
-
-#endif // _LIBS_UTILS_LINEAR_TRANSFORM_H
diff --git a/libutils/include/utils/Mutex.h b/libutils/include/utils/Mutex.h
index d106185..af6076c 100644
--- a/libutils/include/utils/Mutex.h
+++ b/libutils/include/utils/Mutex.h
@@ -28,6 +28,53 @@
#include <utils/Errors.h>
#include <utils/Timers.h>
+// Enable thread safety attributes only with clang.
+// The attributes can be safely erased when compiling with other compilers.
+#if defined(__clang__) && (!defined(SWIG))
+#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x))
+#else
+#define THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op
+#endif
+
+#define CAPABILITY(x) THREAD_ANNOTATION_ATTRIBUTE__(capability(x))
+
+#define SCOPED_CAPABILITY THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)
+
+#define GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x))
+
+#define PT_GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x))
+
+#define ACQUIRED_BEFORE(...) THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__))
+
+#define ACQUIRED_AFTER(...) THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__))
+
+#define REQUIRES(...) THREAD_ANNOTATION_ATTRIBUTE__(requires_capability(__VA_ARGS__))
+
+#define REQUIRES_SHARED(...) THREAD_ANNOTATION_ATTRIBUTE__(requires_shared_capability(__VA_ARGS__))
+
+#define ACQUIRE(...) THREAD_ANNOTATION_ATTRIBUTE__(acquire_capability(__VA_ARGS__))
+
+#define ACQUIRE_SHARED(...) THREAD_ANNOTATION_ATTRIBUTE__(acquire_shared_capability(__VA_ARGS__))
+
+#define RELEASE(...) THREAD_ANNOTATION_ATTRIBUTE__(release_capability(__VA_ARGS__))
+
+#define RELEASE_SHARED(...) THREAD_ANNOTATION_ATTRIBUTE__(release_shared_capability(__VA_ARGS__))
+
+#define TRY_ACQUIRE(...) THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_capability(__VA_ARGS__))
+
+#define TRY_ACQUIRE_SHARED(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_shared_capability(__VA_ARGS__))
+
+#define EXCLUDES(...) THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__))
+
+#define ASSERT_CAPABILITY(x) THREAD_ANNOTATION_ATTRIBUTE__(assert_capability(x))
+
+#define ASSERT_SHARED_CAPABILITY(x) THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_capability(x))
+
+#define RETURN_CAPABILITY(x) THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x))
+
+#define NO_THREAD_SAFETY_ANALYSIS THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
+
// ---------------------------------------------------------------------------
namespace android {
// ---------------------------------------------------------------------------
@@ -44,24 +91,24 @@
* The mutex must be unlocked by the thread that locked it. They are not
* recursive, i.e. the same thread can't lock it multiple times.
*/
-class Mutex {
-public:
+class CAPABILITY("mutex") Mutex {
+ public:
enum {
PRIVATE = 0,
SHARED = 1
};
- Mutex();
- explicit Mutex(const char* name);
- explicit Mutex(int type, const char* name = NULL);
- ~Mutex();
+ Mutex();
+ explicit Mutex(const char* name);
+ explicit Mutex(int type, const char* name = NULL);
+ ~Mutex();
// lock or unlock the mutex
- status_t lock();
- void unlock();
+ status_t lock() ACQUIRE();
+ void unlock() RELEASE();
// lock if possible; returns 0 on success, error otherwise
- status_t tryLock();
+ status_t tryLock() TRY_ACQUIRE(true);
#if defined(__ANDROID__)
// Lock the mutex, but don't wait longer than timeoutNs (relative time).
@@ -75,32 +122,36 @@
// which is subject to NTP adjustments, and includes time during suspend,
// so a timeout may occur even though no processes could run.
// Not holding a partial wakelock may lead to a system suspend.
- status_t timedLock(nsecs_t timeoutNs);
+ status_t timedLock(nsecs_t timeoutNs) TRY_ACQUIRE(true);
#endif
// Manages the mutex automatically. It'll be locked when Autolock is
// constructed and released when Autolock goes out of scope.
- class Autolock {
- public:
- inline explicit Autolock(Mutex& mutex) : mLock(mutex) { mLock.lock(); }
- inline explicit Autolock(Mutex* mutex) : mLock(*mutex) { mLock.lock(); }
- inline ~Autolock() { mLock.unlock(); }
- private:
+ class SCOPED_CAPABILITY Autolock {
+ public:
+ inline explicit Autolock(Mutex& mutex) ACQUIRE(mutex) : mLock(mutex) { mLock.lock(); }
+ inline explicit Autolock(Mutex* mutex) ACQUIRE(mutex) : mLock(*mutex) { mLock.lock(); }
+ inline ~Autolock() RELEASE() { mLock.unlock(); }
+
+ private:
Mutex& mLock;
+ // Cannot be copied or moved - declarations only
+ Autolock(const Autolock&);
+ Autolock& operator=(const Autolock&);
};
-private:
+ private:
friend class Condition;
// A mutex cannot be copied
- Mutex(const Mutex&);
- Mutex& operator = (const Mutex&);
+ Mutex(const Mutex&);
+ Mutex& operator=(const Mutex&);
#if !defined(_WIN32)
pthread_mutex_t mMutex;
#else
- void _init();
- void* mState;
+ void _init();
+ void* mState;
#endif
};
diff --git a/libutils/include/utils/NativeHandle.h b/libutils/include/utils/NativeHandle.h
index b825168..73fe804 100644
--- a/libutils/include/utils/NativeHandle.h
+++ b/libutils/include/utils/NativeHandle.h
@@ -24,7 +24,7 @@
namespace android {
-class NativeHandle: public LightRefBase<NativeHandle> {
+class NativeHandle : public LightRefBase<NativeHandle> {
public:
// Create a refcounted wrapper around a native_handle_t, and declare
// whether the wrapper owns the handle (so that it should clean up the
@@ -41,7 +41,7 @@
friend class LightRefBase<NativeHandle>;
NativeHandle(native_handle_t* handle, bool ownsHandle);
- virtual ~NativeHandle();
+ ~NativeHandle();
native_handle_t* mHandle;
bool mOwnsHandle;
diff --git a/libutils/include/utils/RefBase.h b/libutils/include/utils/RefBase.h
index 36016cd..223b666 100644
--- a/libutils/include/utils/RefBase.h
+++ b/libutils/include/utils/RefBase.h
@@ -177,6 +177,9 @@
#include <stdlib.h>
#include <string.h>
+// LightRefBase used to be declared in this header, so we have to include it
+#include <utils/LightRefBase.h>
+
#include <utils/StrongPointer.h>
#include <utils/TypeHelpers.h>
@@ -216,7 +219,7 @@
class ReferenceRenamer {
protected:
- // destructor is purposedly not virtual so we avoid code overhead from
+ // destructor is purposely not virtual so we avoid code overhead from
// subclasses; we have to make it protected to guarantee that it
// cannot be called from this base class (and to make strict compilers
// happy).
@@ -246,13 +249,13 @@
{
public:
RefBase* refBase() const;
-
+
void incWeak(const void* id);
void decWeak(const void* id);
-
+
// acquires a strong reference if there is already one.
bool attemptIncStrong(const void* id);
-
+
// acquires a weak reference if there is already one.
// This is not always safe. see ProcessState.cpp and BpBinder.cpp
// for proper use.
@@ -268,12 +271,12 @@
// enable -- enable/disable tracking
// retain -- when tracking is enable, if true, then we save a stack trace
// for each reference and dereference; when retain == false, we
- // match up references and dereferences and keep only the
+ // match up references and dereferences and keep only the
// outstanding ones.
-
+
void trackMe(bool enable, bool retain);
};
-
+
weakref_type* createWeak(const void* id) const;
weakref_type* getWeakRefs() const;
@@ -345,56 +348,12 @@
// ---------------------------------------------------------------------------
-template <class T>
-class LightRefBase
-{
-public:
- inline LightRefBase() : mCount(0) { }
- inline void incStrong(__attribute__((unused)) const void* id) const {
- mCount.fetch_add(1, std::memory_order_relaxed);
- }
- inline void decStrong(__attribute__((unused)) const void* id) const {
- if (mCount.fetch_sub(1, std::memory_order_release) == 1) {
- std::atomic_thread_fence(std::memory_order_acquire);
- delete static_cast<const T*>(this);
- }
- }
- //! DEBUGGING ONLY: Get current strong ref count.
- inline int32_t getStrongCount() const {
- return mCount.load(std::memory_order_relaxed);
- }
-
- typedef LightRefBase<T> basetype;
-
-protected:
- inline ~LightRefBase() { }
-
-private:
- friend class ReferenceMover;
- inline static void renameRefs(size_t /*n*/,
- const ReferenceRenamer& /*renamer*/) { }
- inline static void renameRefId(T* /*ref*/,
- const void* /*old_id*/ , const void* /*new_id*/) { }
-
-private:
- mutable std::atomic<int32_t> mCount;
-};
-
-// This is a wrapper around LightRefBase that simply enforces a virtual
-// destructor to eliminate the template requirement of LightRefBase
-class VirtualLightRefBase : public LightRefBase<VirtualLightRefBase> {
-public:
- virtual ~VirtualLightRefBase();
-};
-
-// ---------------------------------------------------------------------------
-
template <typename T>
class wp
{
public:
typedef typename RefBase::weakref_type weakref_type;
-
+
inline wp() : m_ptr(0) { }
wp(T* other); // NOLINT(implicit)
@@ -405,31 +364,31 @@
template<typename U> wp(const wp<U>& other); // NOLINT(implicit)
~wp();
-
+
// Assignment
wp& operator = (T* other);
wp& operator = (const wp<T>& other);
wp& operator = (const sp<T>& other);
-
+
template<typename U> wp& operator = (U* other);
template<typename U> wp& operator = (const wp<U>& other);
template<typename U> wp& operator = (const sp<U>& other);
-
+
void set_object_and_refs(T* other, weakref_type* refs);
// promotion to sp
-
+
sp<T> promote() const;
// Reset
-
+
void clear();
// Accessors
-
+
inline weakref_type* get_refs() const { return m_refs; }
-
+
inline T* unsafe_get() const { return m_ptr; }
// Operators
diff --git a/libutils/include/utils/Singleton.h b/libutils/include/utils/Singleton.h
index a989a47..9afedd4 100644
--- a/libutils/include/utils/Singleton.h
+++ b/libutils/include/utils/Singleton.h
@@ -18,9 +18,12 @@
#define ANDROID_UTILS_SINGLETON_H
#include <stdint.h>
+
+// some vendor code assumes they have atoi() after including this file.
+#include <stdlib.h>
+
#include <sys/types.h>
#include <utils/Mutex.h>
-#include <utils/threads.h>
#include <cutils/compiler.h>
namespace android {
@@ -82,7 +85,7 @@
#define ANDROID_SINGLETON_STATIC_INSTANCE(TYPE) \
template<> ::android::Mutex \
(::android::Singleton< TYPE >::sLock)(::android::Mutex::PRIVATE); \
- template<> TYPE* ::android::Singleton< TYPE >::sInstance(0); \
+ template<> TYPE* ::android::Singleton< TYPE >::sInstance(0); /* NOLINT */ \
template class ::android::Singleton< TYPE >;
diff --git a/libutils/include/utils/SortedVector.h b/libutils/include/utils/SortedVector.h
index 86f3496..5b2a232 100644
--- a/libutils/include/utils/SortedVector.h
+++ b/libutils/include/utils/SortedVector.h
@@ -37,18 +37,18 @@
public:
typedef TYPE value_type;
-
- /*!
+
+ /*!
* Constructors and destructors
*/
-
+
SortedVector();
SortedVector(const SortedVector<TYPE>& rhs);
virtual ~SortedVector();
/*! copy operator */
- const SortedVector<TYPE>& operator = (const SortedVector<TYPE>& rhs) const;
- SortedVector<TYPE>& operator = (const SortedVector<TYPE>& rhs);
+ const SortedVector<TYPE>& operator = (const SortedVector<TYPE>& rhs) const;
+ SortedVector<TYPE>& operator = (const SortedVector<TYPE>& rhs);
/*
* empty the vector
@@ -56,7 +56,7 @@
inline void clear() { VectorImpl::clear(); }
- /*!
+ /*!
* vector stats
*/
@@ -69,11 +69,11 @@
//! sets the capacity. capacity can never be reduced less than size()
inline ssize_t setCapacity(size_t size) { return VectorImpl::setCapacity(size); }
- /*!
+ /*!
* C-style array access
*/
-
- //! read-only C-style access
+
+ //! read-only C-style access
inline const TYPE* array() const;
//! read-write C-style access. BE VERY CAREFUL when modifying the array
@@ -82,12 +82,12 @@
//! finds the index of an item
ssize_t indexOf(const TYPE& item) const;
-
+
//! finds where this item should be inserted
size_t orderOf(const TYPE& item) const;
-
-
- /*!
+
+
+ /*!
* accessors
*/
@@ -104,7 +104,7 @@
//! add an item in the right place (and replace the one that is there)
ssize_t add(const TYPE& item);
-
+
//! editItemAt() MUST NOT change the order of this item
TYPE& editItemAt(size_t index) {
return *( static_cast<TYPE *>(VectorImpl::editItemLocation(index)) );
@@ -113,7 +113,7 @@
//! merges a vector into this one
ssize_t merge(const Vector<TYPE>& vector);
ssize_t merge(const SortedVector<TYPE>& vector);
-
+
//! removes an item
ssize_t remove(const TYPE&);
@@ -121,7 +121,24 @@
inline ssize_t removeItemsAt(size_t index, size_t count = 1);
//! remove one item
inline ssize_t removeAt(size_t index) { return removeItemsAt(index); }
-
+
+ /*
+ * these inlines add some level of compatibility with STL.
+ */
+ typedef TYPE* iterator;
+ typedef TYPE const* const_iterator;
+
+ inline iterator begin() { return editArray(); }
+ inline iterator end() { return editArray() + size(); }
+ inline const_iterator begin() const { return array(); }
+ inline const_iterator end() const { return array() + size(); }
+ inline void reserve(size_t n) { setCapacity(n); }
+ inline bool empty() const{ return isEmpty(); }
+ inline iterator erase(iterator pos) {
+ ssize_t index = removeItemsAt(pos-array());
+ return begin() + index;
+ }
+
protected:
virtual void do_construct(void* storage, size_t num) const;
virtual void do_destroy(void* storage, size_t num) const;
@@ -159,13 +176,13 @@
template<class TYPE> inline
SortedVector<TYPE>& SortedVector<TYPE>::operator = (const SortedVector<TYPE>& rhs) {
SortedVectorImpl::operator = (rhs);
- return *this;
+ return *this;
}
template<class TYPE> inline
const SortedVector<TYPE>& SortedVector<TYPE>::operator = (const SortedVector<TYPE>& rhs) const {
SortedVectorImpl::operator = (rhs);
- return *this;
+ return *this;
}
template<class TYPE> inline
@@ -235,7 +252,7 @@
// ---------------------------------------------------------------------------
template<class TYPE>
-void SortedVector<TYPE>::do_construct(void* storage, size_t num) const {
+UTILS_VECTOR_NO_CFI void SortedVector<TYPE>::do_construct(void* storage, size_t num) const {
construct_type( reinterpret_cast<TYPE*>(storage), num );
}
@@ -245,22 +262,22 @@
}
template<class TYPE>
-void SortedVector<TYPE>::do_copy(void* dest, const void* from, size_t num) const {
+UTILS_VECTOR_NO_CFI void SortedVector<TYPE>::do_copy(void* dest, const void* from, size_t num) const {
copy_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
}
template<class TYPE>
-void SortedVector<TYPE>::do_splat(void* dest, const void* item, size_t num) const {
+UTILS_VECTOR_NO_CFI void SortedVector<TYPE>::do_splat(void* dest, const void* item, size_t num) const {
splat_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(item), num );
}
template<class TYPE>
-void SortedVector<TYPE>::do_move_forward(void* dest, const void* from, size_t num) const {
+UTILS_VECTOR_NO_CFI void SortedVector<TYPE>::do_move_forward(void* dest, const void* from, size_t num) const {
move_forward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
}
template<class TYPE>
-void SortedVector<TYPE>::do_move_backward(void* dest, const void* from, size_t num) const {
+UTILS_VECTOR_NO_CFI void SortedVector<TYPE>::do_move_backward(void* dest, const void* from, size_t num) const {
move_backward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
}
diff --git a/libutils/include/utils/String16.h b/libutils/include/utils/String16.h
index f6433a8..cb3d338 100644
--- a/libutils/include/utils/String16.h
+++ b/libutils/include/utils/String16.h
@@ -67,7 +67,6 @@
inline const char16_t* string() const;
-//TODO(b/35363681): remove
private:
static inline std::string std_string(const String16& str);
public:
diff --git a/libutils/include/utils/String8.h b/libutils/include/utils/String8.h
index f5f9219..1f3e5d8 100644
--- a/libutils/include/utils/String8.h
+++ b/libutils/include/utils/String8.h
@@ -67,7 +67,6 @@
inline const char* c_str() const;
inline const char* string() const;
-// TODO(b/35363681): remove
private:
static inline std::string std_string(const String8& str);
public:
diff --git a/libutils/include/utils/StrongPointer.h b/libutils/include/utils/StrongPointer.h
index c2f7722..0c20607 100644
--- a/libutils/include/utils/StrongPointer.h
+++ b/libutils/include/utils/StrongPointer.h
@@ -17,12 +17,6 @@
#ifndef ANDROID_STRONG_POINTER_H
#define ANDROID_STRONG_POINTER_H
-#include <cutils/atomic.h>
-
-#include <stdint.h>
-#include <sys/types.h>
-#include <stdlib.h>
-
// ---------------------------------------------------------------------------
namespace android {
diff --git a/libutils/include/utils/Trace.h b/libutils/include/utils/Trace.h
index eeba40d..5e9229c 100644
--- a/libutils/include/utils/Trace.h
+++ b/libutils/include/utils/Trace.h
@@ -19,16 +19,8 @@
#if defined(__ANDROID__)
-#include <fcntl.h>
#include <stdint.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <cutils/compiler.h>
-#include <utils/threads.h>
#include <cutils/trace.h>
// See <cutils/trace.h> for more ATRACE_* macros.
@@ -37,6 +29,7 @@
#define _PASTE(x, y) x ## y
#define PASTE(x, y) _PASTE(x,y)
#define ATRACE_NAME(name) android::ScopedTrace PASTE(___tracer, __LINE__) (ATRACE_TAG, name)
+
// ATRACE_CALL is an ATRACE_NAME that uses the current function name.
#define ATRACE_CALL() ATRACE_NAME(__FUNCTION__)
@@ -44,14 +37,13 @@
class ScopedTrace {
public:
-inline ScopedTrace(uint64_t tag, const char* name)
- : mTag(tag) {
- atrace_begin(mTag,name);
-}
+ inline ScopedTrace(uint64_t tag, const char* name) : mTag(tag) {
+ atrace_begin(mTag, name);
+ }
-inline ~ScopedTrace() {
- atrace_end(mTag);
-}
+ inline ~ScopedTrace() {
+ atrace_end(mTag);
+ }
private:
uint64_t mTag;
diff --git a/libutils/include/utils/TypeHelpers.h b/libutils/include/utils/TypeHelpers.h
index 2a25227..28fbca5 100644
--- a/libutils/include/utils/TypeHelpers.h
+++ b/libutils/include/utils/TypeHelpers.h
@@ -36,7 +36,7 @@
template <typename T> struct trait_trivial_dtor { enum { value = false }; };
template <typename T> struct trait_trivial_copy { enum { value = false }; };
template <typename T> struct trait_trivial_move { enum { value = false }; };
-template <typename T> struct trait_pointer { enum { value = false }; };
+template <typename T> struct trait_pointer { enum { value = false }; };
template <typename T> struct trait_pointer<T*> { enum { value = true }; };
template <typename TYPE>
@@ -59,13 +59,13 @@
struct aggregate_traits {
enum {
is_pointer = false,
- has_trivial_ctor =
+ has_trivial_ctor =
traits<T>::has_trivial_ctor && traits<U>::has_trivial_ctor,
- has_trivial_dtor =
+ has_trivial_dtor =
traits<T>::has_trivial_dtor && traits<U>::has_trivial_dtor,
- has_trivial_copy =
+ has_trivial_copy =
traits<T>::has_trivial_copy && traits<U>::has_trivial_copy,
- has_trivial_move =
+ has_trivial_move =
traits<T>::has_trivial_move && traits<U>::has_trivial_move
};
};
diff --git a/libutils/include/utils/Vector.h b/libutils/include/utils/Vector.h
index 9a643f9..7e00123 100644
--- a/libutils/include/utils/Vector.h
+++ b/libutils/include/utils/Vector.h
@@ -24,6 +24,20 @@
#include <utils/TypeHelpers.h>
#include <utils/VectorImpl.h>
+/*
+ * Used to blacklist some functions from CFI.
+ *
+ */
+#ifndef __has_attribute
+#define __has_attribute(x) 0
+#endif
+
+#if __has_attribute(no_sanitize)
+#define UTILS_VECTOR_NO_CFI __attribute__((no_sanitize("cfi")))
+#else
+#define UTILS_VECTOR_NO_CFI
+#endif
+
// ---------------------------------------------------------------------------
namespace android {
@@ -42,11 +56,11 @@
{
public:
typedef TYPE value_type;
-
- /*!
+
+ /*!
* Constructors and destructors
*/
-
+
Vector();
Vector(const Vector<TYPE>& rhs);
explicit Vector(const SortedVector<TYPE>& rhs);
@@ -54,7 +68,7 @@
/*! copy operator */
const Vector<TYPE>& operator = (const Vector<TYPE>& rhs) const;
- Vector<TYPE>& operator = (const Vector<TYPE>& rhs);
+ Vector<TYPE>& operator = (const Vector<TYPE>& rhs);
const Vector<TYPE>& operator = (const SortedVector<TYPE>& rhs) const;
Vector<TYPE>& operator = (const SortedVector<TYPE>& rhs);
@@ -65,7 +79,7 @@
inline void clear() { VectorImpl::clear(); }
- /*!
+ /*!
* vector stats
*/
@@ -87,13 +101,13 @@
/*!
* C-style array access
*/
-
- //! read-only C-style access
+
+ //! read-only C-style access
inline const TYPE* array() const;
//! read-write C-style access
TYPE* editArray();
-
- /*!
+
+ /*!
* accessors
*/
@@ -113,10 +127,10 @@
//! grants right access to the top of the stack (last element)
TYPE& editTop();
- /*!
+ /*!
* append/insert another vector
*/
-
+
//! insert another vector at a given index
ssize_t insertVectorAt(const Vector<TYPE>& vector, size_t index);
@@ -130,10 +144,10 @@
//! append an array at the end of this vector
ssize_t appendArray(const TYPE* array, size_t length);
- /*!
+ /*!
* add/insert/replace items
*/
-
+
//! insert one or several items initialized with their default constructor
inline ssize_t insertAt(size_t index, size_t numItems = 1);
//! insert one or several items initialized from a prototype item
@@ -147,7 +161,7 @@
//! same as push() but returns the index the item was added at (or an error)
inline ssize_t add();
//! same as push() but returns the index the item was added at (or an error)
- ssize_t add(const TYPE& item);
+ ssize_t add(const TYPE& item);
//! replace an item with a new one initialized with its default constructor
inline ssize_t replaceAt(size_t index);
//! replace an item with a new one
@@ -165,10 +179,10 @@
/*!
* sort (stable) the array
*/
-
+
typedef int (*compar_t)(const TYPE* lhs, const TYPE* rhs);
typedef int (*compar_r_t)(const TYPE* lhs, const TYPE* rhs, void* state);
-
+
inline status_t sort(compar_t cmp);
inline status_t sort(compar_r_t cmp, void* state);
@@ -237,7 +251,7 @@
template<class TYPE> inline
Vector<TYPE>& Vector<TYPE>::operator = (const Vector<TYPE>& rhs) {
VectorImpl::operator = (rhs);
- return *this;
+ return *this;
}
template<class TYPE> inline
@@ -255,7 +269,7 @@
template<class TYPE> inline
const Vector<TYPE>& Vector<TYPE>::operator = (const SortedVector<TYPE>& rhs) const {
VectorImpl::operator = (rhs);
- return *this;
+ return *this;
}
template<class TYPE> inline
@@ -380,7 +394,7 @@
// ---------------------------------------------------------------------------
template<class TYPE>
-void Vector<TYPE>::do_construct(void* storage, size_t num) const {
+UTILS_VECTOR_NO_CFI void Vector<TYPE>::do_construct(void* storage, size_t num) const {
construct_type( reinterpret_cast<TYPE*>(storage), num );
}
@@ -390,22 +404,22 @@
}
template<class TYPE>
-void Vector<TYPE>::do_copy(void* dest, const void* from, size_t num) const {
+UTILS_VECTOR_NO_CFI void Vector<TYPE>::do_copy(void* dest, const void* from, size_t num) const {
copy_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
}
template<class TYPE>
-void Vector<TYPE>::do_splat(void* dest, const void* item, size_t num) const {
+UTILS_VECTOR_NO_CFI void Vector<TYPE>::do_splat(void* dest, const void* item, size_t num) const {
splat_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(item), num );
}
template<class TYPE>
-void Vector<TYPE>::do_move_forward(void* dest, const void* from, size_t num) const {
+UTILS_VECTOR_NO_CFI void Vector<TYPE>::do_move_forward(void* dest, const void* from, size_t num) const {
move_forward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
}
template<class TYPE>
-void Vector<TYPE>::do_move_backward(void* dest, const void* from, size_t num) const {
+UTILS_VECTOR_NO_CFI void Vector<TYPE>::do_move_backward(void* dest, const void* from, size_t num) const {
move_backward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
}
diff --git a/libutils/misc.cpp b/libutils/misc.cpp
index 216dc14..d95fd05 100644
--- a/libutils/misc.cpp
+++ b/libutils/misc.cpp
@@ -16,21 +16,19 @@
#define LOG_TAG "misc"
-//
-// Miscellaneous utility functions.
-//
#include <utils/misc.h>
+
+#include <pthread.h>
+
#include <utils/Log.h>
+#include <utils/Vector.h>
-#include <sys/stat.h>
-#include <string.h>
-#include <stdio.h>
-
-#if !defined(_WIN32)
-# include <pthread.h>
+#if defined(__ANDROID__)
+#include <dlfcn.h>
+#include <vndksupport/linker.h>
#endif
-#include <utils/Vector.h>
+extern "C" void do_report_sysprop_change();
using namespace android;
@@ -70,7 +68,36 @@
#endif
}
+#if defined(__ANDROID__)
+void (*get_report_sysprop_change_func())() {
+ void (*func)() = nullptr;
+ void* handle = android_load_sphal_library("libutils.so", RTLD_NOW);
+ if (handle != nullptr) {
+ func = reinterpret_cast<decltype(func)>(dlsym(handle, "do_report_sysprop_change"));
+ }
+
+ return func;
+}
+#endif
+
void report_sysprop_change() {
+ do_report_sysprop_change();
+
+#if defined(__ANDROID__)
+ // libutils.so is double loaded; from the default namespace and from the
+ // 'sphal' namespace. Redirect the sysprop change event to the other instance
+ // of libutils.so loaded in the 'sphal' namespace so that listeners attached
+ // to that instance is also notified with this event.
+ static auto func = get_report_sysprop_change_func();
+ if (func != nullptr) {
+ (*func)();
+ }
+#endif
+}
+
+}; // namespace android
+
+void do_report_sysprop_change() {
#if !defined(_WIN32)
pthread_mutex_lock(&gSyspropMutex);
Vector<sysprop_change_callback_info> listeners;
@@ -85,5 +112,3 @@
}
#endif
}
-
-}; // namespace android
diff --git a/libutils/tests/Android.bp b/libutils/tests/Android.bp
index ea606a1..0869175 100644
--- a/libutils/tests/Android.bp
+++ b/libutils/tests/Android.bp
@@ -23,6 +23,7 @@
srcs: [
"BitSet_test.cpp",
"LruCache_test.cpp",
+ "Mutex_test.cpp",
"Singleton_test.cpp",
"String8_test.cpp",
"StrongPointer_test.cpp",
@@ -33,7 +34,6 @@
target: {
android: {
srcs: [
- "BlobCache_test.cpp",
"Looper_test.cpp",
"RefBase_test.cpp",
"SystemClock_test.cpp",
@@ -72,6 +72,7 @@
"-Wall",
"-Wextra",
"-Werror",
+ "-Wthread-safety",
],
}
diff --git a/libutils/tests/BlobCache_test.cpp b/libutils/tests/BlobCache_test.cpp
deleted file mode 100644
index 1e2ff98..0000000
--- a/libutils/tests/BlobCache_test.cpp
+++ /dev/null
@@ -1,425 +0,0 @@
-/*
- ** Copyright 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.
- */
-
-#include <fcntl.h>
-#include <stdio.h>
-
-#include <gtest/gtest.h>
-
-#include <utils/BlobCache.h>
-#include <utils/Errors.h>
-
-namespace android {
-
-class BlobCacheTest : public ::testing::Test {
-protected:
- enum {
- MAX_KEY_SIZE = 6,
- MAX_VALUE_SIZE = 8,
- MAX_TOTAL_SIZE = 13,
- };
-
- virtual void SetUp() {
- mBC = new BlobCache(MAX_KEY_SIZE, MAX_VALUE_SIZE, MAX_TOTAL_SIZE);
- }
-
- virtual void TearDown() {
- mBC.clear();
- }
-
- sp<BlobCache> mBC;
-};
-
-TEST_F(BlobCacheTest, CacheSingleValueSucceeds) {
- unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
- mBC->set("abcd", 4, "efgh", 4);
- ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4));
- ASSERT_EQ('e', buf[0]);
- ASSERT_EQ('f', buf[1]);
- ASSERT_EQ('g', buf[2]);
- ASSERT_EQ('h', buf[3]);
-}
-
-TEST_F(BlobCacheTest, CacheTwoValuesSucceeds) {
- unsigned char buf[2] = { 0xee, 0xee };
- mBC->set("ab", 2, "cd", 2);
- mBC->set("ef", 2, "gh", 2);
- ASSERT_EQ(size_t(2), mBC->get("ab", 2, buf, 2));
- ASSERT_EQ('c', buf[0]);
- ASSERT_EQ('d', buf[1]);
- ASSERT_EQ(size_t(2), mBC->get("ef", 2, buf, 2));
- ASSERT_EQ('g', buf[0]);
- ASSERT_EQ('h', buf[1]);
-}
-
-TEST_F(BlobCacheTest, GetOnlyWritesInsideBounds) {
- unsigned char buf[6] = { 0xee, 0xee, 0xee, 0xee, 0xee, 0xee };
- mBC->set("abcd", 4, "efgh", 4);
- ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf+1, 4));
- ASSERT_EQ(0xee, buf[0]);
- ASSERT_EQ('e', buf[1]);
- ASSERT_EQ('f', buf[2]);
- ASSERT_EQ('g', buf[3]);
- ASSERT_EQ('h', buf[4]);
- ASSERT_EQ(0xee, buf[5]);
-}
-
-TEST_F(BlobCacheTest, GetOnlyWritesIfBufferIsLargeEnough) {
- unsigned char buf[3] = { 0xee, 0xee, 0xee };
- mBC->set("abcd", 4, "efgh", 4);
- ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 3));
- ASSERT_EQ(0xee, buf[0]);
- ASSERT_EQ(0xee, buf[1]);
- ASSERT_EQ(0xee, buf[2]);
-}
-
-TEST_F(BlobCacheTest, GetDoesntAccessNullBuffer) {
- mBC->set("abcd", 4, "efgh", 4);
- ASSERT_EQ(size_t(4), mBC->get("abcd", 4, NULL, 0));
-}
-
-TEST_F(BlobCacheTest, MultipleSetsCacheLatestValue) {
- unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
- mBC->set("abcd", 4, "efgh", 4);
- mBC->set("abcd", 4, "ijkl", 4);
- ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4));
- ASSERT_EQ('i', buf[0]);
- ASSERT_EQ('j', buf[1]);
- ASSERT_EQ('k', buf[2]);
- ASSERT_EQ('l', buf[3]);
-}
-
-TEST_F(BlobCacheTest, SecondSetKeepsFirstValueIfTooLarge) {
- unsigned char buf[MAX_VALUE_SIZE+1] = { 0xee, 0xee, 0xee, 0xee };
- mBC->set("abcd", 4, "efgh", 4);
- mBC->set("abcd", 4, buf, MAX_VALUE_SIZE+1);
- ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4));
- ASSERT_EQ('e', buf[0]);
- ASSERT_EQ('f', buf[1]);
- ASSERT_EQ('g', buf[2]);
- ASSERT_EQ('h', buf[3]);
-}
-
-TEST_F(BlobCacheTest, DoesntCacheIfKeyIsTooBig) {
- char key[MAX_KEY_SIZE+1];
- unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
- for (int i = 0; i < MAX_KEY_SIZE+1; i++) {
- key[i] = 'a';
- }
- mBC->set(key, MAX_KEY_SIZE+1, "bbbb", 4);
- ASSERT_EQ(size_t(0), mBC->get(key, MAX_KEY_SIZE+1, buf, 4));
- ASSERT_EQ(0xee, buf[0]);
- ASSERT_EQ(0xee, buf[1]);
- ASSERT_EQ(0xee, buf[2]);
- ASSERT_EQ(0xee, buf[3]);
-}
-
-TEST_F(BlobCacheTest, DoesntCacheIfValueIsTooBig) {
- char buf[MAX_VALUE_SIZE+1];
- for (int i = 0; i < MAX_VALUE_SIZE+1; i++) {
- buf[i] = 'b';
- }
- mBC->set("abcd", 4, buf, MAX_VALUE_SIZE+1);
- for (int i = 0; i < MAX_VALUE_SIZE+1; i++) {
- buf[i] = 0xee;
- }
- ASSERT_EQ(size_t(0), mBC->get("abcd", 4, buf, MAX_VALUE_SIZE+1));
- for (int i = 0; i < MAX_VALUE_SIZE+1; i++) {
- SCOPED_TRACE(i);
- ASSERT_EQ(0xee, buf[i]);
- }
-}
-
-TEST_F(BlobCacheTest, DoesntCacheIfKeyValuePairIsTooBig) {
- // Check a testing assumptions
- ASSERT_TRUE(MAX_TOTAL_SIZE < MAX_KEY_SIZE + MAX_VALUE_SIZE);
- ASSERT_TRUE(MAX_KEY_SIZE < MAX_TOTAL_SIZE);
-
- enum { bufSize = MAX_TOTAL_SIZE - MAX_KEY_SIZE + 1 };
-
- char key[MAX_KEY_SIZE];
- char buf[bufSize];
- for (int i = 0; i < MAX_KEY_SIZE; i++) {
- key[i] = 'a';
- }
- for (int i = 0; i < bufSize; i++) {
- buf[i] = 'b';
- }
-
- mBC->set(key, MAX_KEY_SIZE, buf, MAX_VALUE_SIZE);
- ASSERT_EQ(size_t(0), mBC->get(key, MAX_KEY_SIZE, NULL, 0));
-}
-
-TEST_F(BlobCacheTest, CacheMaxKeySizeSucceeds) {
- char key[MAX_KEY_SIZE];
- unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
- for (int i = 0; i < MAX_KEY_SIZE; i++) {
- key[i] = 'a';
- }
- mBC->set(key, MAX_KEY_SIZE, "wxyz", 4);
- ASSERT_EQ(size_t(4), mBC->get(key, MAX_KEY_SIZE, buf, 4));
- ASSERT_EQ('w', buf[0]);
- ASSERT_EQ('x', buf[1]);
- ASSERT_EQ('y', buf[2]);
- ASSERT_EQ('z', buf[3]);
-}
-
-TEST_F(BlobCacheTest, CacheMaxValueSizeSucceeds) {
- char buf[MAX_VALUE_SIZE];
- for (int i = 0; i < MAX_VALUE_SIZE; i++) {
- buf[i] = 'b';
- }
- mBC->set("abcd", 4, buf, MAX_VALUE_SIZE);
- for (int i = 0; i < MAX_VALUE_SIZE; i++) {
- buf[i] = 0xee;
- }
- ASSERT_EQ(size_t(MAX_VALUE_SIZE), mBC->get("abcd", 4, buf,
- MAX_VALUE_SIZE));
- for (int i = 0; i < MAX_VALUE_SIZE; i++) {
- SCOPED_TRACE(i);
- ASSERT_EQ('b', buf[i]);
- }
-}
-
-TEST_F(BlobCacheTest, CacheMaxKeyValuePairSizeSucceeds) {
- // Check a testing assumption
- ASSERT_TRUE(MAX_KEY_SIZE < MAX_TOTAL_SIZE);
-
- enum { bufSize = MAX_TOTAL_SIZE - MAX_KEY_SIZE };
-
- char key[MAX_KEY_SIZE];
- char buf[bufSize];
- for (int i = 0; i < MAX_KEY_SIZE; i++) {
- key[i] = 'a';
- }
- for (int i = 0; i < bufSize; i++) {
- buf[i] = 'b';
- }
-
- mBC->set(key, MAX_KEY_SIZE, buf, bufSize);
- ASSERT_EQ(size_t(bufSize), mBC->get(key, MAX_KEY_SIZE, NULL, 0));
-}
-
-TEST_F(BlobCacheTest, CacheMinKeyAndValueSizeSucceeds) {
- unsigned char buf[1] = { 0xee };
- mBC->set("x", 1, "y", 1);
- ASSERT_EQ(size_t(1), mBC->get("x", 1, buf, 1));
- ASSERT_EQ('y', buf[0]);
-}
-
-TEST_F(BlobCacheTest, CacheSizeDoesntExceedTotalLimit) {
- for (int i = 0; i < 256; i++) {
- uint8_t k = i;
- mBC->set(&k, 1, "x", 1);
- }
- int numCached = 0;
- for (int i = 0; i < 256; i++) {
- uint8_t k = i;
- if (mBC->get(&k, 1, NULL, 0) == 1) {
- numCached++;
- }
- }
- ASSERT_GE(MAX_TOTAL_SIZE / 2, numCached);
-}
-
-TEST_F(BlobCacheTest, ExceedingTotalLimitHalvesCacheSize) {
- // Fill up the entire cache with 1 char key/value pairs.
- const int maxEntries = MAX_TOTAL_SIZE / 2;
- for (int i = 0; i < maxEntries; i++) {
- uint8_t k = i;
- mBC->set(&k, 1, "x", 1);
- }
- // Insert one more entry, causing a cache overflow.
- {
- uint8_t k = maxEntries;
- mBC->set(&k, 1, "x", 1);
- }
- // Count the number of entries in the cache.
- int numCached = 0;
- for (int i = 0; i < maxEntries+1; i++) {
- uint8_t k = i;
- if (mBC->get(&k, 1, NULL, 0) == 1) {
- numCached++;
- }
- }
- ASSERT_EQ(maxEntries/2 + 1, numCached);
-}
-
-class BlobCacheFlattenTest : public BlobCacheTest {
-protected:
- virtual void SetUp() {
- BlobCacheTest::SetUp();
- mBC2 = new BlobCache(MAX_KEY_SIZE, MAX_VALUE_SIZE, MAX_TOTAL_SIZE);
- }
-
- virtual void TearDown() {
- mBC2.clear();
- BlobCacheTest::TearDown();
- }
-
- void roundTrip() {
- size_t size = mBC->getFlattenedSize();
- uint8_t* flat = new uint8_t[size];
- ASSERT_EQ(OK, mBC->flatten(flat, size));
- ASSERT_EQ(OK, mBC2->unflatten(flat, size));
- delete[] flat;
- }
-
- sp<BlobCache> mBC2;
-};
-
-TEST_F(BlobCacheFlattenTest, FlattenOneValue) {
- unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
- mBC->set("abcd", 4, "efgh", 4);
- roundTrip();
- ASSERT_EQ(size_t(4), mBC2->get("abcd", 4, buf, 4));
- ASSERT_EQ('e', buf[0]);
- ASSERT_EQ('f', buf[1]);
- ASSERT_EQ('g', buf[2]);
- ASSERT_EQ('h', buf[3]);
-}
-
-TEST_F(BlobCacheFlattenTest, FlattenFullCache) {
- // Fill up the entire cache with 1 char key/value pairs.
- const int maxEntries = MAX_TOTAL_SIZE / 2;
- for (int i = 0; i < maxEntries; i++) {
- uint8_t k = i;
- mBC->set(&k, 1, &k, 1);
- }
-
- roundTrip();
-
- // Verify the deserialized cache
- for (int i = 0; i < maxEntries; i++) {
- uint8_t k = i;
- uint8_t v = 0xee;
- ASSERT_EQ(size_t(1), mBC2->get(&k, 1, &v, 1));
- ASSERT_EQ(k, v);
- }
-}
-
-TEST_F(BlobCacheFlattenTest, FlattenDoesntChangeCache) {
- // Fill up the entire cache with 1 char key/value pairs.
- const int maxEntries = MAX_TOTAL_SIZE / 2;
- for (int i = 0; i < maxEntries; i++) {
- uint8_t k = i;
- mBC->set(&k, 1, &k, 1);
- }
-
- size_t size = mBC->getFlattenedSize();
- uint8_t* flat = new uint8_t[size];
- ASSERT_EQ(OK, mBC->flatten(flat, size));
- delete[] flat;
-
- // Verify the cache that we just serialized
- for (int i = 0; i < maxEntries; i++) {
- uint8_t k = i;
- uint8_t v = 0xee;
- ASSERT_EQ(size_t(1), mBC->get(&k, 1, &v, 1));
- ASSERT_EQ(k, v);
- }
-}
-
-TEST_F(BlobCacheFlattenTest, FlattenCatchesBufferTooSmall) {
- // Fill up the entire cache with 1 char key/value pairs.
- const int maxEntries = MAX_TOTAL_SIZE / 2;
- for (int i = 0; i < maxEntries; i++) {
- uint8_t k = i;
- mBC->set(&k, 1, &k, 1);
- }
-
- size_t size = mBC->getFlattenedSize() - 1;
- uint8_t* flat = new uint8_t[size];
- // ASSERT_EQ(BAD_VALUE, mBC->flatten(flat, size));
- // TODO: The above fails. I expect this is so because getFlattenedSize()
- // overstimates the size by using PROPERTY_VALUE_MAX.
- delete[] flat;
-}
-
-TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadMagic) {
- unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
- mBC->set("abcd", 4, "efgh", 4);
-
- size_t size = mBC->getFlattenedSize();
- uint8_t* flat = new uint8_t[size];
- ASSERT_EQ(OK, mBC->flatten(flat, size));
- flat[1] = ~flat[1];
-
- // Bad magic should cause an error.
- ASSERT_EQ(BAD_VALUE, mBC2->unflatten(flat, size));
- delete[] flat;
-
- // The error should cause the unflatten to result in an empty cache
- ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4));
-}
-
-TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadBlobCacheVersion) {
- unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
- mBC->set("abcd", 4, "efgh", 4);
-
- size_t size = mBC->getFlattenedSize();
- uint8_t* flat = new uint8_t[size];
- ASSERT_EQ(OK, mBC->flatten(flat, size));
- flat[5] = ~flat[5];
-
- // Version mismatches shouldn't cause errors, but should not use the
- // serialized entries
- ASSERT_EQ(OK, mBC2->unflatten(flat, size));
- delete[] flat;
-
- // The version mismatch should cause the unflatten to result in an empty
- // cache
- ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4));
-}
-
-TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadBlobCacheDeviceVersion) {
- unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
- mBC->set("abcd", 4, "efgh", 4);
-
- size_t size = mBC->getFlattenedSize();
- uint8_t* flat = new uint8_t[size];
- ASSERT_EQ(OK, mBC->flatten(flat, size));
- flat[10] = ~flat[10];
-
- // Version mismatches shouldn't cause errors, but should not use the
- // serialized entries
- ASSERT_EQ(OK, mBC2->unflatten(flat, size));
- delete[] flat;
-
- // The version mismatch should cause the unflatten to result in an empty
- // cache
- ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4));
-}
-
-TEST_F(BlobCacheFlattenTest, UnflattenCatchesBufferTooSmall) {
- unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
- mBC->set("abcd", 4, "efgh", 4);
-
- size_t size = mBC->getFlattenedSize();
- uint8_t* flat = new uint8_t[size];
- ASSERT_EQ(OK, mBC->flatten(flat, size));
-
- // A buffer truncation shouldt cause an error
- // ASSERT_EQ(BAD_VALUE, mBC2->unflatten(flat, size-1));
- // TODO: The above appears to fail because getFlattenedSize() is
- // conservative.
- delete[] flat;
-
- // The error should cause the unflatten to result in an empty cache
- ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4));
-}
-
-} // namespace android
diff --git a/healthd/healthd_board_default.cpp b/libutils/tests/Mutex_test.cpp
similarity index 63%
copy from healthd/healthd_board_default.cpp
copy to libutils/tests/Mutex_test.cpp
index eb55773..8a1805f 100644
--- a/healthd/healthd_board_default.cpp
+++ b/libutils/tests/Mutex_test.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2016 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.
@@ -14,16 +14,19 @@
* limitations under the License.
*/
-#include <healthd/healthd.h>
+#include <utils/Mutex.h>
-void healthd_board_init(struct healthd_config*)
-{
- // use defaults
+#include <gtest/gtest.h>
+
+static android::Mutex mLock;
+static int i GUARDED_BY(mLock);
+
+void modifyLockedVariable() REQUIRES(mLock) {
+ i = 1;
}
-
-int healthd_board_battery_update(struct android::BatteryProperties*)
-{
- // return 0 to log periodic polled battery status to kernel log
- return 0;
-}
+TEST(Mutex, compile) {
+ android::Mutex::Autolock _l(mLock);
+ i = 0;
+ modifyLockedVariable();
+}
\ No newline at end of file
diff --git a/libvndksupport/Android.bp b/libvndksupport/Android.bp
new file mode 100644
index 0000000..b624223
--- /dev/null
+++ b/libvndksupport/Android.bp
@@ -0,0 +1,15 @@
+subdirs = ["tests"]
+
+cc_library {
+ name: "libvndksupport",
+ srcs: ["linker.c"],
+ local_include_dirs: ["include/vndksupport"],
+ export_include_dirs: ["include"],
+ shared_libs: ["liblog"],
+}
+
+llndk_library {
+ name: "libvndksupport",
+ symbol_file: "libvndksupport.map.txt",
+ export_include_dirs: ["include"],
+}
diff --git a/healthd/healthd_board_default.cpp b/libvndksupport/include/vndksupport/linker.h
similarity index 63%
rename from healthd/healthd_board_default.cpp
rename to libvndksupport/include/vndksupport/linker.h
index eb55773..f509564 100644
--- a/healthd/healthd_board_default.cpp
+++ b/libvndksupport/include/vndksupport/linker.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2017 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.
@@ -13,17 +13,19 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#ifndef VNDKSUPPORT_LINKER_H_
+#define VNDKSUPPORT_LINKER_H_
-#include <healthd/healthd.h>
+#ifdef __cplusplus
+extern "C" {
+#endif
-void healthd_board_init(struct healthd_config*)
-{
- // use defaults
+void* android_load_sphal_library(const char* name, int flag);
+
+int android_unload_sphal_library(void* handle);
+
+#ifdef __cplusplus
}
+#endif
-
-int healthd_board_battery_update(struct android::BatteryProperties*)
-{
- // return 0 to log periodic polled battery status to kernel log
- return 0;
-}
+#endif // VNDKSUPPORT_LINKER_H_
diff --git a/libvndksupport/libvndksupport.map.txt b/libvndksupport/libvndksupport.map.txt
new file mode 100644
index 0000000..16e38da
--- /dev/null
+++ b/libvndksupport/libvndksupport.map.txt
@@ -0,0 +1,7 @@
+LIBVNDKSUPPORT {
+ global:
+ android_load_sphal_library; # vndk
+ android_unload_sphal_library; # vndk
+ local:
+ *;
+};
diff --git a/libvndksupport/linker.c b/libvndksupport/linker.c
new file mode 100644
index 0000000..289f153
--- /dev/null
+++ b/libvndksupport/linker.c
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2017 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 "linker.h"
+
+#include <android/dlext.h>
+#include <dlfcn.h>
+
+#define LOG_TAG "vndksupport"
+#include <log/log.h>
+
+extern struct android_namespace_t* android_get_exported_namespace(const char*);
+
+void* android_load_sphal_library(const char* name, int flag) {
+ struct android_namespace_t* sphal_namespace = android_get_exported_namespace("sphal");
+ if (sphal_namespace != NULL) {
+ const android_dlextinfo dlextinfo = {
+ .flags = ANDROID_DLEXT_USE_NAMESPACE, .library_namespace = sphal_namespace,
+ };
+ void* handle = android_dlopen_ext(name, flag, &dlextinfo);
+ if (!handle) {
+ ALOGE("Could not load %s from sphal namespace: %s.", name, dlerror());
+ }
+ return handle;
+ } else {
+ ALOGI(
+ "sphal namespace is not configured for this process. "
+ "Loading %s from the current namespace instead.",
+ name);
+ return dlopen(name, flag);
+ }
+}
+
+int android_unload_sphal_library(void* handle) { return dlclose(handle); }
diff --git a/libvndksupport/tests/Android.bp b/libvndksupport/tests/Android.bp
new file mode 100644
index 0000000..3587cf8
--- /dev/null
+++ b/libvndksupport/tests/Android.bp
@@ -0,0 +1,26 @@
+// Copyright (C) 2017 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.
+
+cc_test {
+ name: "libvndksupport-tests",
+ srcs: [
+ "linker_test.cpp",
+ ],
+
+ host_supported: false,
+ shared_libs: [
+ "libvndksupport",
+ "libbase",
+ ]
+}
diff --git a/libvndksupport/tests/linker_test.cpp b/libvndksupport/tests/linker_test.cpp
new file mode 100644
index 0000000..7ce27d4
--- /dev/null
+++ b/libvndksupport/tests/linker_test.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2017 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 <gtest/gtest.h>
+
+#include <android-base/strings.h>
+#include <dirent.h>
+#include <dlfcn.h>
+#include <vndksupport/linker.h>
+#include <string>
+
+// Since the test executable will be in /data and ld.config.txt does not
+// configure sphal namespace for executables in /data, the call to
+// android_load_sphal_library will always fallback to the plain dlopen from the
+// default namespace.
+
+// Let's use libEGL_<chipset>.so as a SP-HAL in test
+static std::string find_sphal_lib() {
+ const char* path =
+#if defined(__LP64__)
+ "/vendor/lib64/egl";
+#else
+ "/vendor/lib/egl";
+#endif
+ std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);
+
+ dirent* dp;
+ while ((dp = readdir(dir.get())) != nullptr) {
+ std::string name = dp->d_name;
+ if (android::base::StartsWith(name, "libEGL_")) {
+ return std::string(path) + "/" + name;
+ }
+ }
+ return "";
+}
+
+TEST(linker, load_existing_lib) {
+ std::string name = find_sphal_lib();
+ ASSERT_NE("", name);
+ void* handle = android_load_sphal_library(name.c_str(), RTLD_NOW | RTLD_LOCAL);
+ ASSERT_NE(nullptr, handle);
+ android_unload_sphal_library(handle);
+}
+
+TEST(linker, load_nonexisting_lib) {
+ void* handle = android_load_sphal_library("libNeverUseThisName.so", RTLD_NOW | RTLD_LOCAL);
+ ASSERT_EQ(nullptr, handle);
+}
diff --git a/libziparchive/include/ziparchive/zip_archive_stream_entry.h b/libziparchive/include/ziparchive/zip_archive_stream_entry.h
index a40b799..b4766f8 100644
--- a/libziparchive/include/ziparchive/zip_archive_stream_entry.h
+++ b/libziparchive/include/ziparchive/zip_archive_stream_entry.h
@@ -40,7 +40,8 @@
ZipArchiveHandle handle_;
- uint32_t crc32_;
+ off64_t offset_ = 0;
+ uint32_t crc32_ = 0u;
};
#endif // LIBZIPARCHIVE_ZIPARCHIVESTREAMENTRY_H_
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index 17c268b..ad40d42 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -435,13 +435,20 @@
static int32_t ValidateDataDescriptor(MappedZipFile& mapped_zip, ZipEntry* entry) {
uint8_t ddBuf[sizeof(DataDescriptor) + sizeof(DataDescriptor::kOptSignature)];
- if (!mapped_zip.ReadData(ddBuf, sizeof(ddBuf))) {
+ off64_t offset = entry->offset;
+ if (entry->method != kCompressStored) {
+ offset += entry->compressed_length;
+ } else {
+ offset += entry->uncompressed_length;
+ }
+
+ if (!mapped_zip.ReadAtOffset(ddBuf, sizeof(ddBuf), offset)) {
return kIoError;
}
const uint32_t ddSignature = *(reinterpret_cast<const uint32_t*>(ddBuf));
- const uint16_t offset = (ddSignature == DataDescriptor::kOptSignature) ? 4 : 0;
- const DataDescriptor* descriptor = reinterpret_cast<const DataDescriptor*>(ddBuf + offset);
+ const uint16_t ddOffset = (ddSignature == DataDescriptor::kOptSignature) ? 4 : 0;
+ const DataDescriptor* descriptor = reinterpret_cast<const DataDescriptor*>(ddBuf + ddOffset);
// Validate that the values in the data descriptor match those in the central
// directory.
@@ -899,7 +906,9 @@
/* read as much as we can */
if (zstream.avail_in == 0) {
const size_t getSize = (compressed_length > kBufSize) ? kBufSize : compressed_length;
- if (!mapped_zip.ReadData(read_buf.data(), getSize)) {
+ off64_t offset = entry->offset + (entry->compressed_length - compressed_length);
+ // Make sure to read at offset to ensure concurrent access to the fd.
+ if (!mapped_zip.ReadAtOffset(read_buf.data(), getSize, offset)) {
ALOGW("Zip: inflate read failed, getSize = %zu: %s", getSize, strerror(errno));
return kIoError;
}
@@ -962,12 +971,15 @@
uint64_t crc = 0;
while (count < length) {
uint32_t remaining = length - count;
+ off64_t offset = entry->offset + count;
- // Safe conversion because kBufSize is narrow enough for a 32 bit signed
- // value.
+ // Safe conversion because kBufSize is narrow enough for a 32 bit signed value.
const size_t block_size = (remaining > kBufSize) ? kBufSize : remaining;
- if (!mapped_zip.ReadData(buf.data(), block_size)) {
- ALOGW("CopyFileToFile: copy read failed, block_size = %zu: %s", block_size, strerror(errno));
+
+ // Make sure to read at offset to ensure concurrent access to the fd.
+ if (!mapped_zip.ReadAtOffset(buf.data(), block_size, offset)) {
+ ALOGW("CopyFileToFile: copy read failed, block_size = %zu, offset = %" PRId64 ": %s",
+ block_size, static_cast<int64_t>(offset), strerror(errno));
return kIoError;
}
@@ -986,12 +998,6 @@
int32_t ExtractToWriter(ZipArchiveHandle handle, ZipEntry* entry, Writer* writer) {
ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
const uint16_t method = entry->method;
- off64_t data_offset = entry->offset;
-
- if (!archive->mapped_zip.SeekToOffset(data_offset)) {
- ALOGW("Zip: lseek to data at %" PRId64 " failed", static_cast<int64_t>(data_offset));
- return kIoError;
- }
// this should default to kUnknownCompressionMethod.
int32_t return_value = -1;
@@ -1111,52 +1117,21 @@
}
}
-bool MappedZipFile::SeekToOffset(off64_t offset) {
- if (has_fd_) {
- if (lseek64(fd_, offset, SEEK_SET) != offset) {
- ALOGE("Zip: lseek to %" PRId64 " failed: %s\n", offset, strerror(errno));
- return false;
- }
- return true;
- } else {
- if (offset < 0 || offset > static_cast<off64_t>(data_length_)) {
- ALOGE("Zip: invalid offset: %" PRId64 ", data length: %" PRId64 "\n", offset, data_length_);
- return false;
- }
-
- read_pos_ = offset;
- return true;
- }
-}
-
-bool MappedZipFile::ReadData(uint8_t* buffer, size_t read_amount) {
- if (has_fd_) {
- if (!android::base::ReadFully(fd_, buffer, read_amount)) {
- ALOGE("Zip: read from %d failed\n", fd_);
- return false;
- }
- } else {
- memcpy(buffer, static_cast<uint8_t*>(base_ptr_) + read_pos_, read_amount);
- read_pos_ += read_amount;
- }
- return true;
-}
-
// Attempts to read |len| bytes into |buf| at offset |off|.
bool MappedZipFile::ReadAtOffset(uint8_t* buf, size_t len, off64_t off) {
-#if !defined(_WIN32)
if (has_fd_) {
- if (static_cast<size_t>(TEMP_FAILURE_RETRY(pread64(fd_, buf, len, off))) != len) {
+ if (!android::base::ReadFullyAtOffset(fd_, buf, len, off)) {
ALOGE("Zip: failed to read at offset %" PRId64 "\n", off);
return false;
}
- return true;
+ } else {
+ if (off < 0 || off > static_cast<off64_t>(data_length_)) {
+ ALOGE("Zip: invalid offset: %" PRId64 ", data length: %" PRId64 "\n", off, data_length_);
+ return false;
+ }
+ memcpy(buf, static_cast<uint8_t*>(base_ptr_) + off, len);
}
-#endif
- if (!SeekToOffset(off)) {
- return false;
- }
- return ReadData(buf, len);
+ return true;
}
void CentralDirectory::Initialize(void* map_base_ptr, off64_t cd_start_offset, size_t cd_size) {
diff --git a/libziparchive/zip_archive_private.h b/libziparchive/zip_archive_private.h
index 840f1af..174aa3f 100644
--- a/libziparchive/zip_archive_private.h
+++ b/libziparchive/zip_archive_private.h
@@ -93,14 +93,10 @@
class MappedZipFile {
public:
explicit MappedZipFile(const int fd)
- : has_fd_(true), fd_(fd), base_ptr_(nullptr), data_length_(0), read_pos_(0) {}
+ : has_fd_(true), fd_(fd), base_ptr_(nullptr), data_length_(0) {}
explicit MappedZipFile(void* address, size_t length)
- : has_fd_(false),
- fd_(-1),
- base_ptr_(address),
- data_length_(static_cast<off64_t>(length)),
- read_pos_(0) {}
+ : has_fd_(false), fd_(-1), base_ptr_(address), data_length_(static_cast<off64_t>(length)) {}
bool HasFd() const { return has_fd_; }
@@ -110,10 +106,6 @@
off64_t GetFileLength() const;
- bool SeekToOffset(off64_t offset);
-
- bool ReadData(uint8_t* buffer, size_t read_amount);
-
bool ReadAtOffset(uint8_t* buf, size_t len, off64_t off);
private:
@@ -127,8 +119,6 @@
void* const base_ptr_;
const off64_t data_length_;
- // read_pos_ is the offset to the base_ptr_ where we read data from.
- size_t read_pos_;
};
class CentralDirectory {
diff --git a/libziparchive/zip_archive_stream_entry.cc b/libziparchive/zip_archive_stream_entry.cc
index 50352ef..9ec89b1 100644
--- a/libziparchive/zip_archive_stream_entry.cc
+++ b/libziparchive/zip_archive_stream_entry.cc
@@ -38,13 +38,8 @@
static constexpr size_t kBufSize = 65535;
bool ZipArchiveStreamEntry::Init(const ZipEntry& entry) {
- ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle_);
- off64_t data_offset = entry.offset;
- if (!archive->mapped_zip.SeekToOffset(data_offset)) {
- ALOGW("lseek to data at %" PRId64 " failed: %s", data_offset, strerror(errno));
- return false;
- }
crc32_ = entry.crc32;
+ offset_ = entry.offset;
return true;
}
@@ -61,11 +56,11 @@
protected:
bool Init(const ZipEntry& entry) override;
- uint32_t length_;
+ uint32_t length_ = 0u;
private:
std::vector<uint8_t> data_;
- uint32_t computed_crc32_;
+ uint32_t computed_crc32_ = 0u;
};
bool ZipArchiveStreamEntryUncompressed::Init(const ZipEntry& entry) {
@@ -89,7 +84,7 @@
size_t bytes = (length_ > data_.size()) ? data_.size() : length_;
ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle_);
errno = 0;
- if (!archive->mapped_zip.ReadData(data_.data(), bytes)) {
+ if (!archive->mapped_zip.ReadAtOffset(data_.data(), bytes, offset_)) {
if (errno != 0) {
ALOGE("Error reading from archive fd: %s", strerror(errno));
} else {
@@ -104,6 +99,7 @@
}
computed_crc32_ = crc32(computed_crc32_, data_.data(), data_.size());
length_ -= bytes;
+ offset_ += bytes;
return &data_;
}
@@ -129,9 +125,9 @@
z_stream z_stream_;
std::vector<uint8_t> in_;
std::vector<uint8_t> out_;
- uint32_t uncompressed_length_;
- uint32_t compressed_length_;
- uint32_t computed_crc32_;
+ uint32_t uncompressed_length_ = 0u;
+ uint32_t compressed_length_ = 0u;
+ uint32_t computed_crc32_ = 0u;
};
// This method is using libz macros with old-style-casts
@@ -210,7 +206,7 @@
size_t bytes = (compressed_length_ > in_.size()) ? in_.size() : compressed_length_;
ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle_);
errno = 0;
- if (!archive->mapped_zip.ReadData(in_.data(), bytes)) {
+ if (!archive->mapped_zip.ReadAtOffset(in_.data(), bytes, offset_)) {
if (errno != 0) {
ALOGE("Error reading from archive fd: %s", strerror(errno));
} else {
@@ -220,6 +216,7 @@
}
compressed_length_ -= bytes;
+ offset_ += bytes;
z_stream_.next_in = in_.data();
z_stream_.avail_in = bytes;
}
diff --git a/logcat/Android.bp b/logcat/Android.bp
new file mode 100644
index 0000000..729c8ff
--- /dev/null
+++ b/logcat/Android.bp
@@ -0,0 +1,74 @@
+//
+// Copyright (C) 2006-2017 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.
+//
+
+cc_defaults {
+ name: "logcat_defaults",
+
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+ shared_libs: [
+ "libbase",
+ "libcutils",
+ "liblog",
+ "libpcrecpp",
+ ],
+ logtags: ["event.logtags"],
+}
+
+cc_library {
+ name: "liblogcat",
+
+ defaults: ["logcat_defaults"],
+ srcs: [
+ "logcat.cpp",
+ "getopt_long.cpp",
+ "logcat_system.cpp",
+ ],
+ export_include_dirs: ["include"],
+}
+
+cc_binary {
+ name: "logcat",
+
+ defaults: ["logcat_defaults"],
+ shared_libs: ["liblogcat"],
+ srcs: [
+ "logcat_main.cpp",
+ ],
+}
+
+cc_binary {
+ name: "logcatd",
+
+ defaults: ["logcat_defaults"],
+ shared_libs: ["liblogcat"],
+ srcs: [
+ "logcatd_main.cpp",
+ ],
+}
+
+cc_prebuilt_binary {
+ name: "logpersist.start",
+ srcs: ["logpersist"],
+ init_rc: ["logcatd.rc"],
+ symlinks: ["logpersist.stop", "logpersist.cat"],
+ strip: {
+ none: true,
+ }
+}
diff --git a/logcat/Android.mk b/logcat/Android.mk
index 4e11ca9..a716993 100644
--- a/logcat/Android.mk
+++ b/logcat/Android.mk
@@ -2,48 +2,4 @@
LOCAL_PATH := $(call my-dir)
-logcatLibs := liblog libbase libcutils libpcrecpp
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := logcat
-LOCAL_SRC_FILES := logcat_main.cpp event.logtags
-LOCAL_SHARED_LIBRARIES := liblogcat $(logcatLibs)
-LOCAL_CFLAGS := -Werror
-
-include $(BUILD_EXECUTABLE)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := logcatd
-LOCAL_MODULE_TAGS := debug
-LOCAL_SRC_FILES := logcatd_main.cpp event.logtags
-LOCAL_SHARED_LIBRARIES := liblogcat $(logcatLibs)
-LOCAL_CFLAGS := -Werror
-
-include $(BUILD_EXECUTABLE)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := liblogcat
-LOCAL_SRC_FILES := logcat.cpp getopt_long.cpp logcat_system.cpp
-LOCAL_SHARED_LIBRARIES := $(logcatLibs)
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_CFLAGS := -Werror
-
-include $(BUILD_SHARED_LIBRARY)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := logpersist.start
-LOCAL_MODULE_TAGS := debug
-LOCAL_MODULE_CLASS := EXECUTABLES
-LOCAL_INIT_RC := logcatd.rc
-LOCAL_MODULE_PATH := $(bin_dir)
-LOCAL_SRC_FILES := logpersist
-ALL_TOOLS := logpersist.start logpersist.stop logpersist.cat
-LOCAL_POST_INSTALL_CMD := $(hide) $(foreach t,$(filter-out $(LOCAL_MODULE),$(ALL_TOOLS)),ln -sf $(LOCAL_MODULE) $(TARGET_OUT)/bin/$(t);)
-include $(BUILD_PREBUILT)
-
include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index f64196f..3d56472 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -1019,7 +1019,6 @@
break;
case 'm': {
- char* end = nullptr;
if (!getSizeTArg(optctx.optarg, &context->maxCount)) {
logcat_panic(context, HELP_FALSE,
"-%c \"%s\" isn't an "
@@ -1182,7 +1181,6 @@
std::unique_ptr<char, void (*)(void*)> formats(
strdup(optctx.optarg), free);
char* arg = formats.get();
- unsigned idMask = 0;
char* sv = nullptr; // protect against -ENOMEM above
while (!!(arg = strtok_r(arg, delimiters, &sv))) {
err = setLogFormat(context, arg);
@@ -1256,7 +1254,7 @@
// example: "qemu_pipe,pipe:logcat"
// upon opening of /dev/qemu_pipe, the "pipe:logcat"
// string with trailing '\0' should be written to the fd
- size_t pos = devname.find(",");
+ size_t pos = devname.find(',');
if (pos != std::string::npos) {
pipePurpose = devname.substr(pos + 1);
devname = devname.substr(0, pos);
@@ -1733,7 +1731,7 @@
pthread_attr_setschedparam(&attr, ¶m);
pthread_attr_setschedpolicy(&attr, SCHED_BATCH);
if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) {
- int save_errno = errno;
+ save_errno = errno;
goto pthread_attr_exit;
}
@@ -1773,7 +1771,7 @@
context->retval = EXIT_SUCCESS;
if (pthread_create(&context->thr, &attr,
(void*(*)(void*))__logcat, context)) {
- int save_errno = errno;
+ save_errno = errno;
goto argv_exit;
}
pthread_attr_destroy(&attr);
diff --git a/logcat/tests/liblogcat_test.cpp b/logcat/tests/liblogcat_test.cpp
index 9e9a2c2..c8a00da 100644
--- a/logcat/tests/liblogcat_test.cpp
+++ b/logcat/tests/liblogcat_test.cpp
@@ -17,8 +17,8 @@
#include <log/logcat.h>
#define logcat_define(context) android_logcat_context context
-#define logcat_popen(context, command) android_logcat_popen(&context, command)
-#define logcat_pclose(context, fp) android_logcat_pclose(&context, fp)
+#define logcat_popen(context, command) android_logcat_popen(&(context), command)
+#define logcat_pclose(context, fp) android_logcat_pclose(&(context), fp)
#define logcat_system(command) android_logcat_system(command)
#define logcat liblogcat
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index 76a4aff..7498325 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -165,15 +165,17 @@
(lenl == sizeof(android_log_event_int_t)) &&
!fastcmp<memcmp>(msgl, msgr, sizeof(android_log_event_int_t) -
sizeof(int32_t)) &&
- (elem->getTag() == LIBLOG_LOG_TAG))
+ (elem->getTag() == LIBLOG_LOG_TAG)) {
return SAME_LIBLOG;
+ }
}
// audit message (except sequence number) identical?
if (last->isBinary()) {
if (fastcmp<memcmp>(msgl, msgr, sizeof(android_log_event_string_t) -
- sizeof(int32_t)))
+ sizeof(int32_t))) {
return DIFFERENT;
+ }
msgl += sizeof(android_log_event_string_t);
lenl -= sizeof(android_log_event_string_t);
msgr += sizeof(android_log_event_string_t);
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index 381c974..f20ac45 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -41,22 +41,20 @@
mTid(tid),
mRealTime(realtime),
mMsgLen(len),
- mLogId(log_id) {
+ mLogId(log_id),
+ mDropped(false) {
mMsg = new char[len];
memcpy(mMsg, msg, len);
- mTag = (isBinary() && (mMsgLen >= sizeof(uint32_t)))
- ? le32toh(reinterpret_cast<android_event_header_t*>(mMsg)->tag)
- : 0;
}
LogBufferElement::LogBufferElement(const LogBufferElement& elem)
- : mTag(elem.mTag),
- mUid(elem.mUid),
+ : mUid(elem.mUid),
mPid(elem.mPid),
mTid(elem.mTid),
mRealTime(elem.mRealTime),
mMsgLen(elem.mMsgLen),
- mLogId(elem.mLogId) {
+ mLogId(elem.mLogId),
+ mDropped(elem.mDropped) {
mMsg = new char[mMsgLen];
memcpy(mMsg, elem.mMsg, mMsgLen);
}
@@ -65,6 +63,32 @@
delete[] mMsg;
}
+uint32_t LogBufferElement::getTag() const {
+ return (isBinary() &&
+ ((mDropped && mMsg != nullptr) ||
+ (!mDropped && mMsgLen >= sizeof(android_event_header_t))))
+ ? reinterpret_cast<const android_event_header_t*>(mMsg)->tag
+ : 0;
+}
+
+unsigned short LogBufferElement::setDropped(unsigned short value) {
+ // The tag information is saved in mMsg data, if the tag is non-zero
+ // save only the information needed to get the tag.
+ if (getTag() != 0) {
+ if (mMsgLen > sizeof(android_event_header_t)) {
+ char* truncated_msg = new char[sizeof(android_event_header_t)];
+ memcpy(truncated_msg, mMsg, sizeof(android_event_header_t));
+ delete[] mMsg;
+ mMsg = truncated_msg;
+ } // mMsgLen == sizeof(android_event_header_t), already at minimum.
+ } else {
+ delete[] mMsg;
+ mMsg = nullptr;
+ }
+ mDropped = true;
+ return mDroppedCount = value;
+}
+
// caller must own and free character string
char* android::tidToName(pid_t tid) {
char* retval = NULL;
@@ -164,8 +188,8 @@
// identical to below to calculate the buffer size required
const char* type = lastSame ? "identical" : "expire";
size_t len = snprintf(NULL, 0, format_uid, mUid, name ? name : "",
- commName ? commName : "", type, mDropped,
- (mDropped > 1) ? "s" : "");
+ commName ? commName : "", type, getDropped(),
+ (getDropped() > 1) ? "s" : "");
size_t hdrLen;
if (isBinary()) {
@@ -196,8 +220,8 @@
}
snprintf(buffer + hdrLen, len + 1, format_uid, mUid, name ? name : "",
- commName ? commName : "", type, mDropped,
- (mDropped > 1) ? "s" : "");
+ commName ? commName : "", type, getDropped(),
+ (getDropped() > 1) ? "s" : "");
free(const_cast<char*>(name));
free(const_cast<char*>(commName));
@@ -225,7 +249,7 @@
char* buffer = NULL;
- if (!mMsg) {
+ if (mDropped) {
entry.len = populateDroppedMessage(buffer, parent, lastSame);
if (!entry.len) return mRealTime;
iovec[1].iov_base = buffer;
diff --git a/logd/LogBufferElement.h b/logd/LogBufferElement.h
index 814ec87..b168645 100644
--- a/logd/LogBufferElement.h
+++ b/logd/LogBufferElement.h
@@ -32,25 +32,25 @@
// chatty for the temporal expire messages
#define EXPIRE_RATELIMIT 10 // maximum rate in seconds to report expiration
-class LogBufferElement {
+class __attribute__((packed)) LogBufferElement {
friend LogBuffer;
// sized to match reality of incoming log packets
- uint32_t mTag; // only valid for isBinary()
const uint32_t mUid;
const uint32_t mPid;
const uint32_t mTid;
log_time mRealTime;
char* mMsg;
union {
- const uint16_t mMsgLen; // mMSg != NULL
- uint16_t mDropped; // mMsg == NULL
+ const uint16_t mMsgLen; // mDropped == false
+ uint16_t mDroppedCount; // mDropped == true
};
const uint8_t mLogId;
+ bool mDropped;
static atomic_int_fast64_t sequence;
- // assumption: mMsg == NULL
+ // assumption: mDropped == true
size_t populateDroppedMessage(char*& buffer, LogBuffer* parent,
bool lastSame);
@@ -58,7 +58,7 @@
LogBufferElement(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid,
pid_t tid, const char* msg, unsigned short len);
LogBufferElement(const LogBufferElement& elem);
- virtual ~LogBufferElement();
+ ~LogBufferElement();
bool isBinary(void) const {
return (mLogId == LOG_ID_EVENTS) || (mLogId == LOG_ID_SECURITY);
@@ -76,24 +76,16 @@
pid_t getTid(void) const {
return mTid;
}
- uint32_t getTag() const {
- return mTag;
- }
+ uint32_t getTag() const;
unsigned short getDropped(void) const {
- return mMsg ? 0 : mDropped;
+ return mDropped ? mDroppedCount : 0;
}
- unsigned short setDropped(unsigned short value) {
- if (mMsg) {
- delete[] mMsg;
- mMsg = NULL;
- }
- return mDropped = value;
- }
+ unsigned short setDropped(unsigned short value);
unsigned short getMsgLen() const {
- return mMsg ? mMsgLen : 0;
+ return mDropped ? 0 : mMsgLen;
}
const char* getMsg() const {
- return mMsg;
+ return mDropped ? nullptr : mMsg;
}
log_time getRealTime(void) const {
return mRealTime;
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
index 4397b14..8ee5ea1 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/tests/logd_test.cpp
@@ -646,16 +646,20 @@
recv(fd, msg_timeout.buf, sizeof(msg_timeout), 0) > 0;
}
- alarm_timeout =
- alarm((old_alarm <= 0) ? old_alarm
- : (old_alarm > (1 + 3 - alarm_wrap))
- ? old_alarm - 3 + alarm_wrap
- : 2);
+ if (old_alarm > 0) {
+ unsigned int time_spent = 3 - alarm_wrap;
+ if (old_alarm > time_spent + 1) {
+ old_alarm -= time_spent;
+ } else {
+ old_alarm = 2;
+ }
+ }
+ alarm_timeout = alarm(old_alarm);
sigaction(SIGALRM, &old_sigaction, nullptr);
close(fd);
- if (!content_wrap && !alarm_wrap && content_timeout && alarm_timeout) {
+ if (content_wrap && alarm_wrap && content_timeout && alarm_timeout) {
break;
}
}
@@ -710,8 +714,8 @@
// A few tries to get it right just in case wrap kicks in due to
// content providers being active during the test.
int i = 5;
- log_time now(android_log_clockid());
- now.tv_sec -= 30; // reach back a moderate period of time
+ log_time start(android_log_clockid());
+ start.tv_sec -= 30; // reach back a moderate period of time
while (--i) {
int fd = socket_local_client("logdr", ANDROID_SOCKET_NAMESPACE_RESERVED,
@@ -726,7 +730,7 @@
std::string ask = android::base::StringPrintf(
"dumpAndClose lids=0,1,2,3,4,5 timeout=6 start=%" PRIu32
".%09" PRIu32,
- now.tv_sec, now.tv_nsec);
+ start.tv_sec, start.tv_nsec);
struct sigaction ignore, old_sigaction;
memset(&ignore, 0, sizeof(ignore));
@@ -756,11 +760,15 @@
recv(fd, msg_timeout.buf, sizeof(msg_timeout), 0) > 0;
}
- alarm_timeout =
- alarm((old_alarm <= 0) ? old_alarm
- : (old_alarm > (1 + 3 - alarm_wrap))
- ? old_alarm - 3 + alarm_wrap
- : 2);
+ if (old_alarm > 0) {
+ unsigned int time_spent = 3 - alarm_wrap;
+ if (old_alarm > time_spent + 1) {
+ old_alarm -= time_spent;
+ } else {
+ old_alarm = 2;
+ }
+ }
+ alarm_timeout = alarm(old_alarm);
sigaction(SIGALRM, &old_sigaction, nullptr);
close(fd);
@@ -773,23 +781,23 @@
// active _or_ inactive during the test.
if (content_timeout) {
log_time msg(msg_timeout.entry.sec, msg_timeout.entry.nsec);
- if (msg < now) {
+ if (msg < start) {
fprintf(stderr, "%u.%09u < %u.%09u\n", msg_timeout.entry.sec,
- msg_timeout.entry.nsec, (unsigned)now.tv_sec,
- (unsigned)now.tv_nsec);
+ msg_timeout.entry.nsec, (unsigned)start.tv_sec,
+ (unsigned)start.tv_nsec);
_exit(-1);
}
- if (msg > now) {
- now = msg;
- now.tv_sec += 30;
- msg = log_time(android_log_clockid());
- if (now > msg) {
- now = msg;
- --now.tv_sec;
+ if (msg > start) {
+ start = msg;
+ start.tv_sec += 30;
+ log_time now = log_time(android_log_clockid());
+ if (start > now) {
+ start = now;
+ --start.tv_sec;
}
}
} else {
- now.tv_sec -= 120; // inactive, reach further back!
+ start.tv_sec -= 120; // inactive, reach further back!
}
}
@@ -802,8 +810,8 @@
}
if (content_wrap || !content_timeout) {
- fprintf(stderr, "now=%" PRIu32 ".%09" PRIu32 "\n", now.tv_sec,
- now.tv_nsec);
+ fprintf(stderr, "start=%" PRIu32 ".%09" PRIu32 "\n", start.tv_sec,
+ start.tv_nsec);
}
EXPECT_TRUE(written);
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 86fec6a..48a46c6 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -173,4 +173,18 @@
bcp_md5 :=
bcp_dep :=
+
#######################################
+# ld.config.txt
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := ld.config.txt
+ifeq ($(PRODUCT_FULL_TREBLE),true)
+LOCAL_SRC_FILES := etc/ld.config.txt
+else
+LOCAL_SRC_FILES := etc/ld.config.legacy.txt
+endif
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
+LOCAL_MODULE_STEM := $(LOCAL_MODULE)
+include $(BUILD_PREBUILT)
diff --git a/rootdir/asan.options b/rootdir/asan.options
index d728f12..a264d2d 100644
--- a/rootdir/asan.options
+++ b/rootdir/asan.options
@@ -5,3 +5,4 @@
detect_container_overflow=0
abort_on_error=1
include_if_exists=/system/asan.options.%b
+include_if_exists=/data/asan/system/asan.options.%b
diff --git a/rootdir/etc/ld.config.legacy.txt b/rootdir/etc/ld.config.legacy.txt
new file mode 100644
index 0000000..c22edfe
--- /dev/null
+++ b/rootdir/etc/ld.config.legacy.txt
@@ -0,0 +1,15 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Bionic loader config file.
+# This gives the exactly the same namespace setup in pre-O.
+#
+
+# All binaries gets the same configuration 'legacy'
+dir.legacy = /system
+dir.legacy = /vendor
+dir.legacy = /sbin
+
+[legacy]
+namespace.default.isolated = false
+namespace.default.search.paths = /system/${LIB}:/vendor/${LIB}
+namespace.default.asan.search.paths = /data/asan/system/${LIB}:/system/${LIB}:/data/asan/vendor/${LIB}:/vendor/${LIB}
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index e3468ca..436589e 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -3,45 +3,111 @@
# Bionic loader config file.
#
-#dir.vendor=/vendor/bin/
-#dir.system=/system/bin/
-
-[vendor]
-
-# When this flag is set to true linker will
-# set target_sdk_version for this binary to
-# the version specified in <dirname>/.version
-# file, where <dirname> = dirname(executable_path)
-#
-# default value is false
-enable.target.sdk.version = true
-
-# There is always the default namespace no
-# need to mention it in this list
-additional.namespaces=system
-
-# Is the namespace isolated
-namespace.default.isolated = true
-namespace.default.search.paths = /vendor/${LIB}
-
-# TODO: property for asan search path?
-namespace.default.permitted.paths = /vendor/${LIB}
-namespace.default.asan.permitted.paths = /data/vendor/${LIB}
-namespace.default.links = system
-
-# Todo complete this list
-namespace.default.link.system.shared_libs = libc.so:libm.so:libdl.so:libstdc++.so
-
-namespace.system.isolated = true
-namespace.system.search.paths = /system/${LIB}:/system/${LIB}/framework
-namespace.system.permitted.paths = /system/${LIB}
+# Don't change the order here.
+dir.system = /system/bin/
+dir.system = /system/xbin/
+dir.vendor = /vendor/bin/
[system]
-namespace.default.isolated = true
-namespace.default.search.paths = /system/${LIB}
-namespace.default.permitted.paths = /system/${LIB}
+additional.namespaces = sphal,vndk,rs
-# app_process will setup additional vendor namespace manually.
-# Note that zygote will need vendor namespace to setup list of public
-# libraries provided by vendors to apps.
+###############################################################################
+# "default" namespace
+#
+# Framework-side code runs in this namespace. Anything from /vendor partition
+# can't be loaded in this namespace.
+###############################################################################
+namespace.default.isolated = false
+namespace.default.search.paths = /system/${LIB}:/vendor/${LIB}
+namespace.default.permitted.paths = /system/${LIB}:/vendor/${LIB}
+namespace.default.asan.search.paths = /data/asan/system/${LIB}:/system/${LIB}:/data/asan/vendor/${LIB}:/vendor/${LIB}
+namespace.default.asan.permitted.paths = /data/asan/system/${LIB}:/system/${LIB}:/data/asan/vendor/${LIB}:/vendor/${LIB}
+
+# TODO(b/37013858): remove all dependencies to /vendor/lib from system processes
+# When this is done, comment out following three lines and remove the three
+# lines above
+#namespace.default.isolated = true
+#namespace.default.search.paths = /system/${LIB}
+#namespace.default.permitted.paths = /system/${LIB}
+#
+#namespace.default.asan.search.paths = /data/asan/system/${LIB}:/system/${LIB}
+#namespace.default.asan.permitted.paths = /data/asan/system/${LIB}:/system/${LIB}
+
+###############################################################################
+# "sphal" namespace
+#
+# SP-HAL(Sameprocess-HAL)s are the only vendor libraries that are allowed to be
+# loaded inside system processes. libEGL_<chipset>.so, libGLESv2_<chipset>.so,
+# android.hardware.graphics.mapper@2.0-impl.so, etc are SP-HALs.
+#
+# This namespace is exclusivly for SP-HALs. When the framework tries to dynami-
+# cally load SP-HALs, android_dlopen_ext() is used to explicitly specifying
+# that they should be searched and loaded from this namespace.
+#
+# Note that there is no link from the default namespace to this namespace.
+###############################################################################
+namespace.sphal.isolated = true
+namespace.sphal.visible = true
+namespace.sphal.search.paths = /vendor/${LIB}/egl:/vendor/${LIB}/hw:/vendor/${LIB}
+namespace.sphal.permitted.paths = /vendor/${LIB}
+
+namespace.sphal.asan.search.paths = /data/asan/vendor/${LIB}/egl:/vendor/${LIB}/egl:/data/asan/vendor/${LIB}/hw:/vendor/${LIB}/hw:/data/asan/vendor/${LIB}:/vendor/${LIB}
+namespace.sphal.asan.permitted.paths = /data/asan/vendor/${LIB}:/vendor/${LIB}
+
+# Once in this namespace, access to libraries in /system/lib is restricted. Only
+# libs listed here can be used.
+namespace.sphal.links = default,vndk,rs
+
+# WARNING: only NDK libs can be listed here.
+namespace.sphal.link.default.shared_libs = libc.so:libz.so:libm.so:libdl.so:libstdc++.so:liblog.so:libnativewindow.so:libEGL.so:libsync.so:libGLESv1_CM.so:libGLESv2.so:libvndksupport.so
+
+# WARNING: only VNDK-SP libs can be listed here. DO NOT EDIT this line.
+namespace.sphal.link.vndk.shared_libs = android.hardware.renderscript@1.0.so:android.hardware.graphics.allocator@2.0.so:android.hardware.graphics.mapper@2.0.so:android.hardware.graphics.common@1.0.so:android.hidl.memory@1.0.so:libhwbinder.so:libbase.so:libcutils.so:libhardware.so:libhidlbase.so:libhidlmemory.so:libhidltransport.so:libion.so:libutils.so:libc++.so
+
+# Renderscript gets separate namespace
+namespace.sphal.link.rs.shared_libs = libRS_internal.so
+
+###############################################################################
+# "rs" namespace
+#
+# This namespace is exclusively for Renderscript internal libraries.
+# This namespace has slightly looser restriction than the vndk namespace because
+# of the genuine characteristics of Renderscript; /data is in the permitted path
+# to load the compiled *.so file and libmediandk.so can be used here.
+###############################################################################
+namespace.rs.isolated = true
+namespace.rs.search.paths = /vendor/${LIB}/vndk-sp:/system/${LIB}/vndk-sp:/vendor/${LIB}
+namespace.rs.permitted.paths = /vendor/${LIB}:/data
+
+namespace.rs.asan.search.paths = /data/asan/vendor/${LIB}/vndk-sp:/vendor/${LIB}/vndk-sp:/data/asan/system/${LIB}/vndk-sp:/system/${LIB}/vndk-sp:/data/asan/vendor/${LIB}:/vendor/${LIB}
+namespace.rs.asan.permitted.paths = /data/asan/vendor/${LIB}:/vendor/${LIB}:/data
+
+namespace.rs.links = default,vndk
+namespace.rs.link.default.shared_libs = libc.so:libz.so:libm.so:libdl.so:libstdc++.so:liblog.so:libnativewindow.so:libEGL.so:libsync.so:libGLESv1_CM.so:libGLESv2.so:libmediandk.so:libui.so:libvndksupport.so
+namespace.rs.link.vndk.shared_libs = android.hardware.renderscript@1.0.so:android.hardware.graphics.allocator@2.0.so:android.hardware.graphics.mapper@2.0.so:android.hardware.graphics.common@1.0.so:android.hidl.memory@1.0.so:libhwbinder.so:libbase.so:libcutils.so:libhardware.so:libhidlbase.so:libhidlmemory.so:libhidltransport.so:libion.so:libutils.so:libc++.so
+
+###############################################################################
+# "vndk" namespace
+#
+# This namespace is exclusively for vndk-sp libs.
+###############################################################################
+namespace.vndk.isolated = true
+namespace.vndk.search.paths = /vendor/${LIB}/vndk-sp:/system/${LIB}/vndk-sp:/vendor/${LIB}
+namespace.vndk.permitted.paths = /vendor/${LIB}/hw:/vendor/${LIB}/egl
+
+namespace.vndk.asan.search.paths = /data/asan/vendor/${LIB}/vndk-sp:/vendor/${LIB}/vndk-sp:/data/asan/system/${LIB}/vndk-sp:/system/${LIB}/vndk-sp:/data/asan/vendor/${LIB}:/vendor/${LIB}
+namespace.vndk.asan.permitted.paths = /data/asan/vendor/${LIB}/hw:/vendor/${LIB}/hw:/data/asan/vendor/${LIB}/egl:/vendor/${LIB}/egl
+
+# When these NDK libs are required inside this namespace, then it is redirected
+# to the default namespace. This is possible since their ABI is stable across
+# Android releases.
+namespace.vndk.links = default
+namespace.vndk.link.default.shared_libs = android.hidl.memory@1.0-impl.so:libc.so:libz.so:libm.so:libdl.so:libstdc++.so:liblog.so:libnativewindow.so:libEGL.so:libsync.so:libvndksupport.so
+
+
+[vendor]
+namespace.default.isolated = false
+namespace.default.search.paths = /vendor/${LIB}:/vendor/${LIB}/vndk-sp:/system/${LIB}/vndk-sp:/system/${LIB}
+
+namespace.default.asan.search.paths = /data/asan/vendor/${LIB}:/vendor/${LIB}:/data/asan/vendor/${LIB}/vndk-sp:/vendor/${LIB}/vndk-sp:/data/asan/system/${LIB}/vndk-sp:/system/${LIB}/vndk-sp:/data/asan/system/${LIB}:/system/${LIB}
diff --git a/rootdir/etc/public.libraries.android.txt b/rootdir/etc/public.libraries.android.txt
index e6c94ff..c5e149c 100644
--- a/rootdir/etc/public.libraries.android.txt
+++ b/rootdir/etc/public.libraries.android.txt
@@ -1,4 +1,6 @@
+# See https://android.googlesource.com/platform/ndk/+/master/docs/PlatformApis.md
libandroid.so
+libaaudio.so
libc.so
libcamera2ndk.so
libdl.so
@@ -12,10 +14,12 @@
liblog.so
libmediandk.so
libm.so
+libnativewindow.so
libOpenMAXAL.so
libOpenSLES.so
libRS.so
libstdc++.so
+libsync.so
libvulkan.so
libwebviewchromium_plat_support.so
libz.so
diff --git a/rootdir/etc/public.libraries.wear.txt b/rootdir/etc/public.libraries.wear.txt
index 292730a..a4ca683 100644
--- a/rootdir/etc/public.libraries.wear.txt
+++ b/rootdir/etc/public.libraries.wear.txt
@@ -1,4 +1,6 @@
+# See https://android.googlesource.com/platform/ndk/+/master/docs/PlatformApis.md
libandroid.so
+libaaudio.so
libc.so
libcamera2ndk.so
libdl.so
@@ -12,9 +14,11 @@
liblog.so
libmediandk.so
libm.so
+libnativewindow.so
libOpenMAXAL.so
libOpenSLES.so
libRS.so
libstdc++.so
+libsync.so
libvulkan.so
libz.so
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 9301743..1199543 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -302,19 +302,21 @@
load_system_props
# start essential services
start logd
+ start servicemanager
start hwservicemanager
+ start vndservicemanager
# once everything is setup, no need to modify /
mount rootfs rootfs / ro remount
# 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
+ mount none /mnt/runtime/default /storage bind rec
+ mount none none /storage slave rec
# Make sure /sys/kernel/debug (if present) is labeled properly
# Note that tracefs may be mounted under debug, so we need to cross filesystems
restorecon --recursive --cross-filesystems /sys/kernel/debug
- chmod 0755 /sys/kernel/debug/tracing
# We chown/chmod /cache again so because mount is run as root + defaults
chown system cache /cache
@@ -352,6 +354,10 @@
mkdir /cache/lost+found 0770 root root
on late-fs
+ # Ensure that tracefs has the correct permissions.
+ # This does not work correctly if it is called in post-fs.
+ chmod 0755 /sys/kernel/debug/tracing
+
# HALs required before storage encryption can get unlocked (FBE/FDE)
class_start early_hal
@@ -377,12 +383,12 @@
# create basic filesystem structure
mkdir /data/misc 01771 system misc
mkdir /data/misc/recovery 0770 system log
- copy /data/misc/recovery/default.prop /data/misc/recovery/default.prop.1
- chmod 0440 /data/misc/recovery/default.prop.1
- chown system log /data/misc/recovery/default.prop.1
- copy /default.prop /data/misc/recovery/default.prop
- chmod 0440 /data/misc/recovery/default.prop
- chown system log /data/misc/recovery/default.prop
+ copy /data/misc/recovery/ro.build.fingerprint /data/misc/recovery/ro.build.fingerprint.1
+ chmod 0440 /data/misc/recovery/ro.build.fingerprint.1
+ chown system log /data/misc/recovery/ro.build.fingerprint.1
+ write /data/misc/recovery/ro.build.fingerprint ${ro.build.fingerprint}
+ chmod 0440 /data/misc/recovery/ro.build.fingerprint
+ chown system log /data/misc/recovery/ro.build.fingerprint
mkdir /data/misc/recovery/proc 0770 system log
copy /data/misc/recovery/proc/version /data/misc/recovery/proc/version.1
chmod 0440 /data/misc/recovery/proc/version.1
@@ -424,7 +430,6 @@
mkdir /data/misc/boottrace 0771 system shell
mkdir /data/misc/update_engine 0700 root root
mkdir /data/misc/trace 0700 root root
- mkdir /data/misc/reboot 0700 system system
# profile file layout
mkdir /data/misc/profiles 0771 system system
mkdir /data/misc/profiles/cur 0771 system system
@@ -673,12 +678,6 @@
on property:sys.boot_completed=1
bootchart stop
-on property:sys.boot_completed=1 && property:ro.build.type=user
- write /proc/sys/kernel/modules_disabled 1
-
-on property:sys.boot_completed=1 && property:ro.build.type=userdebug
- write /proc/sys/kernel/modules_disabled 1
-
# system server cannot write to /proc/sys files,
# and chown/chmod does not work for /proc/sys/ entries.
# So proxy writes through init.
@@ -707,10 +706,9 @@
seclabel u:r:ueventd:s0
shutdown critical
-service healthd /sbin/healthd
+service healthd /system/bin/healthd
class core
critical
- seclabel u:r:healthd:s0
group root system wakelock
service console /system/bin/sh
diff --git a/rootdir/init.zygote64_32.rc b/rootdir/init.zygote64_32.rc
index 36bb443..09db7b0 100644
--- a/rootdir/init.zygote64_32.rc
+++ b/rootdir/init.zygote64_32.rc
@@ -13,7 +13,7 @@
onrestart restart wificond
writepid /dev/cpuset/foreground/tasks
-service zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary
+service zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary --enable-lazy-preload
class main
priority -20
user root
diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc
index efd0da5..eadf219 100644
--- a/rootdir/ueventd.rc
+++ b/rootdir/ueventd.rc
@@ -52,6 +52,7 @@
/dev/ashmem 0666 root root
/dev/binder 0666 root root
/dev/hwbinder 0666 root root
+/dev/vndbinder 0666 root root
/dev/pmsg0 0222 root log
diff --git a/sdcard/Android.mk b/sdcard/Android.mk
index 0c58574..5b4dc58 100644
--- a/sdcard/Android.mk
+++ b/sdcard/Android.mk
@@ -8,6 +8,5 @@
LOCAL_SHARED_LIBRARIES := libbase libcutils libminijail libpackagelistparser
LOCAL_SANITIZE := integer
-LOCAL_CLANG := true
include $(BUILD_EXECUTABLE)
diff --git a/sdcard/fuse.cpp b/sdcard/fuse.cpp
index 3f0f95f..95559d7 100644
--- a/sdcard/fuse.cpp
+++ b/sdcard/fuse.cpp
@@ -997,7 +997,7 @@
{
struct node* node;
char path[PATH_MAX];
- struct fuse_open_out out;
+ struct fuse_open_out out = {};
struct handle *h;
pthread_mutex_lock(&fuse->global->lock);
@@ -1026,13 +1026,6 @@
}
out.fh = ptr_to_id(h);
out.open_flags = 0;
-
-#ifdef FUSE_SHORTCIRCUIT
- out.lower_fd = h->fd;
-#else
- out.padding = 0;
-#endif
-
fuse_reply(fuse, hdr->unique, &out, sizeof(out));
return NO_STATUS;
}
@@ -1169,7 +1162,7 @@
{
struct node* node;
char path[PATH_MAX];
- struct fuse_open_out out;
+ struct fuse_open_out out = {};
struct dirhandle *h;
pthread_mutex_lock(&fuse->global->lock);
@@ -1196,13 +1189,6 @@
}
out.fh = ptr_to_id(h);
out.open_flags = 0;
-
-#ifdef FUSE_SHORTCIRCUIT
- out.lower_fd = -1;
-#else
- out.padding = 0;
-#endif
-
fuse_reply(fuse, hdr->unique, &out, sizeof(out));
return NO_STATUS;
}
@@ -1282,11 +1268,6 @@
out.major = FUSE_KERNEL_VERSION;
out.max_readahead = req->max_readahead;
out.flags = FUSE_ATOMIC_O_TRUNC | FUSE_BIG_WRITES;
-
-#ifdef FUSE_SHORTCIRCUIT
- out.flags |= FUSE_SHORTCIRCUIT;
-#endif
-
out.max_background = 32;
out.congestion_threshold = 32;
out.max_write = MAX_WRITE;
diff --git a/sdcard/sdcard.cpp b/sdcard/sdcard.cpp
index df3ce85..c342cf8 100644
--- a/sdcard/sdcard.cpp
+++ b/sdcard/sdcard.cpp
@@ -420,7 +420,7 @@
}
// Fall back to device opinion about state
- if (property_get_bool(PROP_SDCARDFS_DEVICE, false)) {
+ if (property_get_bool(PROP_SDCARDFS_DEVICE, true)) {
LOG(WARNING) << "Device explicitly enabled sdcardfs";
return supports_sdcardfs();
} else {
diff --git a/storaged/Android.mk b/storaged/Android.mk
new file mode 100644
index 0000000..5e6a3c0
--- /dev/null
+++ b/storaged/Android.mk
@@ -0,0 +1,45 @@
+# Copyright 2016 The Android Open Source Project
+
+LOCAL_PATH := $(call my-dir)
+
+LIBSTORAGED_SHARED_LIBRARIES := \
+ libbinder \
+ libbase \
+ libutils \
+ libcutils \
+ liblog \
+ libsysutils \
+ libpackagelistparser \
+ libbatteryservice \
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ storaged.cpp \
+ storaged_info.cpp \
+ storaged_service.cpp \
+ storaged_utils.cpp \
+ storaged_uid_monitor.cpp \
+ EventLogTags.logtags
+
+LOCAL_MODULE := libstoraged
+LOCAL_CFLAGS := -Werror
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include external/googletest/googletest/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_SHARED_LIBRARIES := $(LIBSTORAGED_SHARED_LIBRARIES)
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := storaged
+LOCAL_INIT_RC := storaged.rc
+LOCAL_SRC_FILES := main.cpp
+# libstoraged is an internal static library, only main.cpp and storaged_test.cpp should be using it
+LOCAL_STATIC_LIBRARIES := libstoraged
+LOCAL_SHARED_LIBRARIES := $(LIBSTORAGED_SHARED_LIBRARIES)
+LOCAL_CFLAGS := -Wall -Werror -Wno-unused-parameter
+LOCAL_C_INCLUDES := external/googletest/googletest/include
+
+include $(BUILD_EXECUTABLE)
+
+include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/storaged/EventLogTags.logtags b/storaged/EventLogTags.logtags
new file mode 100644
index 0000000..2e25d4a
--- /dev/null
+++ b/storaged/EventLogTags.logtags
@@ -0,0 +1,39 @@
+# The entries in this file map a sparse set of log tag numbers to tag names.
+# This is installed on the device, in /system/etc, and parsed by logcat.
+#
+# Tag numbers are decimal integers, from 0 to 2^31. (Let's leave the
+# negative values alone for now.)
+#
+# Tag names are one or more ASCII letters and numbers or underscores, i.e.
+# "[A-Z][a-z][0-9]_". Do not include spaces or punctuation (the former
+# impacts log readability, the latter makes regex searches more annoying).
+#
+# Tag numbers and names are separated by whitespace. Blank lines and lines
+# starting with '#' are ignored.
+#
+# Optionally, after the tag names can be put a description for the value(s)
+# of the tag. Description are in the format
+# (<name>|data type[|data unit])
+# Multiple values are separated by commas.
+#
+# The data type is a number from the following values:
+# 1: int
+# 2: long
+# 3: string
+# 4: list
+# 5: float
+#
+# The data unit is a number taken from the following list:
+# 1: Number of objects
+# 2: Number of bytes
+# 3: Number of milliseconds
+# 4: Number of allocations
+# 5: Id
+# 6: Percent
+# Default value for data of type int/long is 2 (bytes).
+#
+# TODO: generate ".java" and ".h" files with integer constants from this file.
+
+2732 storaged_disk_stats (type|3),(start_time|2|3),(end_time|2|3),(read_ios|2|1),(read_merges|2|1),(read_sectors|2|1),(read_ticks|2|3),(write_ios|2|1),(write_merges|2|1),(write_sectors|2|1),(write_ticks|2|3),(o_in_flight|2|1),(io_ticks|2|3),(io_in_queue|2|1)
+
+2733 storaged_emmc_info (mmc_ver|3),(eol|1),(lifetime_a|1),(lifetime_b|1)
\ No newline at end of file
diff --git a/storaged/README.properties b/storaged/README.properties
new file mode 100644
index 0000000..2d8397f
--- /dev/null
+++ b/storaged/README.properties
@@ -0,0 +1,5 @@
+ro.storaged.event.interval # interval storaged scans for IO stats, in seconds
+ro.storaged.event.perf_check # check for time spent in event loop, in microseconds
+ro.storaged.disk_stats_pub # interval storaged publish disk stats, in seconds
+ro.storaged.uid_io.interval # interval storaged checks Per UID IO usage, in seconds
+ro.storaged.uid_io.threshold # Per UID IO usage limit, in bytes
diff --git a/storaged/include/storaged.h b/storaged/include/storaged.h
new file mode 100644
index 0000000..514798b
--- /dev/null
+++ b/storaged/include/storaged.h
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 2016 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 _STORAGED_H_
+#define _STORAGED_H_
+
+#include <semaphore.h>
+#include <stdint.h>
+#include <time.h>
+
+#include <queue>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <batteryservice/IBatteryPropertiesListener.h>
+#include <batteryservice/IBatteryPropertiesRegistrar.h>
+
+#include "storaged_info.h"
+#include "storaged_uid_monitor.h"
+
+using namespace android;
+
+#define FRIEND_TEST(test_case_name, test_name) \
+friend class test_case_name##_##test_name##_Test
+
+/* For debug */
+#ifdef DEBUG
+#define debuginfo(fmt, ...) \
+ do {printf("%s():\t" fmt "\t[%s:%d]\n", __FUNCTION__, ##__VA_ARGS__, __FILE__, __LINE__);} \
+ while(0)
+#else
+#define debuginfo(...)
+#endif
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+#define SECTOR_SIZE ( 512 )
+#define SEC_TO_MSEC ( 1000 )
+#define MSEC_TO_USEC ( 1000 )
+#define USEC_TO_NSEC ( 1000 )
+#define SEC_TO_USEC ( 1000000 )
+#define HOUR_TO_SEC ( 3600 )
+#define DAY_TO_SEC ( 3600 * 24 )
+
+// number of attributes diskstats has
+#define DISK_STATS_SIZE ( 11 )
+// maximum size limit of a stats file
+#define DISK_STATS_FILE_MAX_SIZE ( 256 )
+#define DISK_STATS_IO_IN_FLIGHT_IDX ( 8 )
+struct disk_stats {
+ /* It will be extremely unlikely for any of the following entries to overflow.
+ * For read_bytes(which will be greater than any of the following entries), it
+ * will take 27 years to overflow uint64_t at the reading rate of 20GB/s, which
+ * is the peak memory transfer rate for current memory.
+ * The diskstats entries (first 11) need to be at top in this structure _after_
+ * compiler's optimization.
+ */
+ uint64_t read_ios; // number of read I/Os processed
+ uint64_t read_merges; // number of read I/Os merged with in-queue I/Os
+ uint64_t read_sectors; // number of sectors read
+ uint64_t read_ticks; // total wait time for read requests
+ uint64_t write_ios; // number of write I/Os processed
+ uint64_t write_merges; // number of write I/Os merged with in-queue I/Os
+ uint64_t write_sectors; // number of sectors written
+ uint64_t write_ticks; // total wait time for write requests
+ uint64_t io_in_flight; // number of I/Os currently in flight
+ uint64_t io_ticks; // total time this block device has been active
+ uint64_t io_in_queue; // total wait time for all requests
+
+ uint64_t start_time; // monotonic time accounting starts
+ uint64_t end_time; // monotonic time accounting ends
+ uint32_t counter; // private counter for accumulate calculations
+ double io_avg; // average io_in_flight for accumulate calculations
+};
+
+
+
+struct disk_perf {
+ uint32_t read_perf; // read speed (kbytes/s)
+ uint32_t read_ios; // read I/Os per second
+ uint32_t write_perf; // write speed (kbytes/s)
+ uint32_t write_ios; // write I/Os per second
+ uint32_t queue; // I/Os in queue
+};
+
+#define CMD_MAX_LEN ( 64 )
+struct task_info {
+ uint32_t pid; // task id
+ uint64_t rchar; // characters read
+ uint64_t wchar; // characters written
+ uint64_t syscr; // read syscalls
+ uint64_t syscw; // write syscalls
+ uint64_t read_bytes; // bytes read (from storage layer)
+ uint64_t write_bytes; // bytes written (to storage layer)
+ uint64_t cancelled_write_bytes; // cancelled write byte by truncate
+
+ uint64_t starttime; // start time of task
+
+ char cmd[CMD_MAX_LEN]; // filename of the executable
+};
+
+class lock_t {
+ sem_t* mSem;
+public:
+ lock_t(sem_t* sem) {
+ mSem = sem;
+ sem_wait(mSem);
+ }
+ ~lock_t() {
+ sem_post(mSem);
+ }
+};
+
+class stream_stats {
+private:
+ double mSum;
+ double mSquareSum;
+ uint32_t mCnt;
+public:
+ stream_stats() : mSum(0), mSquareSum(0), mCnt(0) {};
+ ~stream_stats() {};
+ double get_mean() {
+ return mSum / mCnt;
+ }
+ double get_std() {
+ return sqrt(mSquareSum / mCnt - mSum * mSum / (mCnt * mCnt));
+ }
+ void add(uint32_t num) {
+ mSum += (double)num;
+ mSquareSum += (double)num * (double)num;
+ mCnt++;
+ }
+ void evict(uint32_t num) {
+ if (mSum < num || mSquareSum < (double)num * (double)num) return;
+ mSum -= (double)num;
+ mSquareSum -= (double)num * (double)num;
+ mCnt--;
+ }
+};
+
+#define MMC_DISK_STATS_PATH "/sys/block/mmcblk0/stat"
+#define SDA_DISK_STATS_PATH "/sys/block/sda/stat"
+#define EMMC_ECSD_PATH "/d/mmc0/mmc0:0001/ext_csd"
+#define UID_IO_STATS_PATH "/proc/uid_io/stats"
+
+class disk_stats_monitor {
+private:
+ FRIEND_TEST(storaged_test, disk_stats_monitor);
+ const char* DISK_STATS_PATH;
+ struct disk_stats mPrevious;
+ struct disk_stats mAccumulate;
+ bool mStall;
+ std::queue<struct disk_perf> mBuffer;
+ struct {
+ stream_stats read_perf; // read speed (bytes/s)
+ stream_stats read_ios; // read I/Os per second
+ stream_stats write_perf; // write speed (bytes/s)
+ stream_stats write_ios; // write I/O per second
+ stream_stats queue; // I/Os in queue
+ } mStats;
+ bool mValid;
+ const uint32_t mWindow;
+ const double mSigma;
+ struct disk_perf mMean;
+ struct disk_perf mStd;
+
+ void update_mean();
+ void update_std();
+ void add(struct disk_perf* perf);
+ void evict(struct disk_perf* perf);
+ bool detect(struct disk_perf* perf);
+
+ void update(struct disk_stats* stats);
+
+public:
+ disk_stats_monitor(uint32_t window_size = 5, double sigma = 1.0) :
+ mStall(false),
+ mValid(false),
+ mWindow(window_size),
+ mSigma(sigma) {
+ memset(&mPrevious, 0, sizeof(mPrevious));
+ memset(&mMean, 0, sizeof(mMean));
+ memset(&mStd, 0, sizeof(mStd));
+
+ if (access(MMC_DISK_STATS_PATH, R_OK) >= 0) {
+ DISK_STATS_PATH = MMC_DISK_STATS_PATH;
+ } else {
+ DISK_STATS_PATH = SDA_DISK_STATS_PATH;
+ }
+ }
+ void update(void);
+};
+
+class disk_stats_publisher {
+private:
+ FRIEND_TEST(storaged_test, disk_stats_publisher);
+ const char* DISK_STATS_PATH;
+ struct disk_stats mAccumulate;
+ struct disk_stats mPrevious;
+public:
+ disk_stats_publisher(void) {
+ memset(&mAccumulate, 0, sizeof(struct disk_stats));
+ memset(&mPrevious, 0, sizeof(struct disk_stats));
+
+ if (access(MMC_DISK_STATS_PATH, R_OK) >= 0) {
+ DISK_STATS_PATH = MMC_DISK_STATS_PATH;
+ } else {
+ DISK_STATS_PATH = SDA_DISK_STATS_PATH;
+ }
+ }
+
+ ~disk_stats_publisher(void) {}
+ void publish(void);
+ void update(void);
+};
+
+// Periodic chores intervals in seconds
+#define DEFAULT_PERIODIC_CHORES_INTERVAL_UNIT ( 60 )
+#define DEFAULT_PERIODIC_CHORES_INTERVAL_DISK_STATS_PUBLISH ( 3600 )
+#define DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO ( 3600 )
+#define DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO_LIMIT (300)
+
+// UID IO threshold in bytes
+#define DEFAULT_PERIODIC_CHORES_UID_IO_THRESHOLD ( 1024 * 1024 * 1024ULL )
+
+struct storaged_config {
+ int periodic_chores_interval_unit;
+ int periodic_chores_interval_disk_stats_publish;
+ int periodic_chores_interval_uid_io;
+ bool proc_uid_io_available; // whether uid_io is accessible
+ bool diskstats_available; // whether diskstats is accessible
+ int event_time_check_usec; // check how much cputime spent in event loop
+};
+
+class storaged_t : public BnBatteryPropertiesListener,
+ public IBinder::DeathRecipient {
+private:
+ time_t mTimer;
+ storaged_config mConfig;
+ disk_stats_publisher mDiskStats;
+ disk_stats_monitor mDsm;
+ uid_monitor mUidm;
+ time_t mStarttime;
+ sp<IBatteryPropertiesRegistrar> battery_properties;
+public:
+ storaged_t(void);
+ ~storaged_t() {}
+ void event(void);
+ void event_checked(void);
+ void pause(void) {
+ sleep(mConfig.periodic_chores_interval_unit);
+ }
+
+ time_t get_starttime(void) {
+ return mStarttime;
+ }
+
+ std::unordered_map<uint32_t, struct uid_info> get_uids(void) {
+ return mUidm.get_uid_io_stats();
+ }
+ std::map<uint64_t, struct uid_records> get_uid_records(
+ double hours, uint64_t threshold, bool force_report) {
+ return mUidm.dump(hours, threshold, force_report);
+ }
+ void update_uid_io_interval(int interval) {
+ if (interval >= DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO_LIMIT) {
+ mConfig.periodic_chores_interval_uid_io = interval;
+ }
+ }
+
+ void init_battery_service();
+ virtual void batteryPropertiesChanged(struct BatteryProperties props);
+ void binderDied(const wp<IBinder>& who);
+};
+
+// Eventlog tag
+// The content must match the definition in EventLogTags.logtags
+#define EVENTLOGTAG_DISKSTATS ( 2732 )
+#define EVENTLOGTAG_EMMCINFO ( 2733 )
+#define EVENTLOGTAG_UID_IO_ALERT ( 2734 )
+
+#endif /* _STORAGED_H_ */
diff --git a/storaged/include/storaged_info.h b/storaged/include/storaged_info.h
new file mode 100644
index 0000000..913c814
--- /dev/null
+++ b/storaged/include/storaged_info.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2017 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 _STORAGED_INFO_H_
+#define _STORAGED_INFO_H_
+
+#include <string.h>
+
+#define FRIEND_TEST(test_case_name, test_name) \
+friend class test_case_name##_##test_name##_Test
+
+using namespace std;
+
+class storage_info_t {
+protected:
+ FRIEND_TEST(storaged_test, storage_info_t);
+ uint16_t eol; // pre-eol (end of life) information
+ uint16_t lifetime_a; // device life time estimation (type A)
+ uint16_t lifetime_b; // device life time estimation (type B)
+ string version; // version string
+ void publish();
+public:
+ storage_info_t() : eol(0), lifetime_a(0), lifetime_b(0) {}
+ virtual ~storage_info_t() {}
+ virtual bool report() = 0;
+};
+
+class emmc_info_t : public storage_info_t {
+private:
+ const string emmc_sysfs = "/sys/bus/mmc/devices/mmc0:0001/";
+ const string emmc_debugfs = "/d/mmc0/mmc0:0001/ext_csd";
+ const char* emmc_ver_str[9] = {
+ "4.0", "4.1", "4.2", "4.3", "Obsolete", "4.41", "4.5", "5.0", "5.1"
+ };
+public:
+ virtual ~emmc_info_t() {}
+ bool report();
+ bool report_sysfs();
+ bool report_debugfs();
+};
+
+class ufs_info_t : public storage_info_t {
+private:
+ const string health_file = "/sys/devices/soc/624000.ufshc/health";
+public:
+ virtual ~ufs_info_t() {}
+ bool report();
+};
+
+void report_storage_health();
+
+#endif /* _STORAGED_INFO_H_ */
diff --git a/storaged/include/storaged_service.h b/storaged/include/storaged_service.h
new file mode 100644
index 0000000..a8ddf4c
--- /dev/null
+++ b/storaged/include/storaged_service.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2016 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 _STORAGED_SERVICE_H_
+#define _STORAGED_SERVICE_H_
+
+#include <vector>
+
+#include <binder/IInterface.h>
+#include <binder/IBinder.h>
+
+#include "storaged.h"
+
+using namespace android;
+
+// Interface
+class IStoraged : public IInterface {
+public:
+ enum {
+ DUMPUIDS = IBinder::FIRST_CALL_TRANSACTION,
+ };
+ // Request the service to run the test function
+ virtual std::vector<struct uid_info> dump_uids(const char* option) = 0;
+
+ DECLARE_META_INTERFACE(Storaged);
+};
+
+// Client
+class BpStoraged : public BpInterface<IStoraged> {
+public:
+ BpStoraged(const sp<IBinder>& impl) : BpInterface<IStoraged>(impl){};
+ virtual std::vector<struct uid_info> dump_uids(const char* option);
+};
+
+// Server
+class BnStoraged : public BnInterface<IStoraged> {
+ virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0);
+};
+
+class Storaged : public BnStoraged {
+ virtual std::vector<struct uid_info> dump_uids(const char* option);
+ virtual status_t dump(int fd, const Vector<String16>& args);
+};
+
+sp<IStoraged> get_storaged_service();
+
+#endif /* _STORAGED_SERVICE_H_ */
\ No newline at end of file
diff --git a/storaged/include/storaged_uid_monitor.h b/storaged/include/storaged_uid_monitor.h
new file mode 100644
index 0000000..901a872
--- /dev/null
+++ b/storaged/include/storaged_uid_monitor.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2016 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 _STORAGED_UID_MONITOR_H_
+#define _STORAGED_UID_MONITOR_H_
+
+#include <stdint.h>
+
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+enum uid_stat_t {
+ FOREGROUND = 0,
+ BACKGROUND = 1,
+ UID_STATS = 2
+};
+
+enum charger_stat_t {
+ CHARGER_OFF = 0,
+ CHARGER_ON = 1,
+ CHARGER_STATS = 2
+};
+
+enum io_type_t {
+ READ = 0,
+ WRITE = 1,
+ IO_TYPES = 2
+};
+
+struct uid_io_stats {
+ uint64_t rchar; // characters read
+ uint64_t wchar; // characters written
+ uint64_t read_bytes; // bytes read (from storage layer)
+ uint64_t write_bytes; // bytes written (to storage layer)
+ uint64_t fsync; // number of fsync syscalls
+};
+
+struct uid_info {
+ uint32_t uid; // user id
+ std::string name; // package name
+ struct uid_io_stats io[UID_STATS]; // [0]:foreground [1]:background
+};
+
+struct uid_io_usage {
+ uint64_t bytes[IO_TYPES][UID_STATS][CHARGER_STATS];
+};
+
+struct uid_record {
+ std::string name;
+ struct uid_io_usage ios;
+};
+
+struct uid_records {
+ uint64_t start_ts;
+ std::vector<struct uid_record> entries;
+};
+
+class uid_monitor {
+private:
+ // last dump from /proc/uid_io/stats, uid -> uid_info
+ std::unordered_map<uint32_t, struct uid_info> last_uid_io_stats;
+ // current io usage for next report, app name -> uid_io_usage
+ std::unordered_map<std::string, struct uid_io_usage> curr_io_stats;
+ // io usage records, end timestamp -> {start timestamp, vector of records}
+ std::map<uint64_t, struct uid_records> records;
+ // charger ON/OFF
+ charger_stat_t charger_stat;
+ // protects curr_io_stats, last_uid_io_stats, records and charger_stat
+ sem_t um_lock;
+ // start time for IO records
+ uint64_t start_ts;
+
+ // reads from /proc/uid_io/stats
+ std::unordered_map<uint32_t, struct uid_info> get_uid_io_stats_locked();
+ // flushes curr_io_stats to records
+ void add_records_locked(uint64_t curr_ts);
+ // updates curr_io_stats and set last_uid_io_stats
+ void update_curr_io_stats_locked();
+
+public:
+ uid_monitor();
+ ~uid_monitor();
+ // called by storaged main thread
+ void init(charger_stat_t stat);
+ // called by storaged -u
+ std::unordered_map<uint32_t, struct uid_info> get_uid_io_stats();
+ // called by dumpsys
+ std::map<uint64_t, struct uid_records> dump(
+ double hours, uint64_t threshold, bool force_report);
+ // called by battery properties listener
+ void set_charger_state(charger_stat_t stat);
+ // called by storaged periodic_chore or dump with force_report
+ void report();
+};
+
+#endif /* _STORAGED_UID_MONITOR_H_ */
diff --git a/storaged/include/storaged_utils.h b/storaged/include/storaged_utils.h
new file mode 100644
index 0000000..2161c40
--- /dev/null
+++ b/storaged/include/storaged_utils.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2016 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 _STORAGED_UTILS_H_
+#define _STORAGED_UTILS_H_
+
+#include <stdint.h>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include "storaged.h"
+
+// Diskstats
+bool parse_disk_stats(const char* disk_stats_path, struct disk_stats* stats);
+struct disk_perf get_disk_perf(struct disk_stats* stats);
+struct disk_stats get_inc_disk_stats(struct disk_stats* prev, struct disk_stats* curr);
+void add_disk_stats(struct disk_stats* src, struct disk_stats* dst);
+bool parse_emmc_ecsd(int ext_csd_fd, struct emmc_info* info);
+
+// UID I/O
+void sort_running_uids_info(std::vector<struct uid_info> &uids);
+
+// Logging
+void log_console_running_uids_info(std::vector<struct uid_info> uids);
+
+void log_debug_disk_perf(struct disk_perf* perf, const char* type);
+
+void log_event_disk_stats(struct disk_stats* stats, const char* type);
+void log_event_emmc_info(struct emmc_info* info_);
+#endif /* _STORAGED_UTILS_H_ */
diff --git a/storaged/main.cpp b/storaged/main.cpp
new file mode 100644
index 0000000..4d1e430
--- /dev/null
+++ b/storaged/main.cpp
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2016 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 "storaged"
+#define KLOG_LEVEL 6
+
+#include <fcntl.h>
+#include <getopt.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <sys/capability.h>
+#include <sys/prctl.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <vector>
+
+#include <android-base/macros.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <binder/ProcessState.h>
+#include <binder/IServiceManager.h>
+#include <binder/IPCThreadState.h>
+#include <cutils/android_get_control_file.h>
+#include <cutils/sched_policy.h>
+#include <private/android_filesystem_config.h>
+
+#include <storaged.h>
+#include <storaged_service.h>
+#include <storaged_utils.h>
+
+sp<storaged_t> storaged;
+
+// Function of storaged's main thread
+void* storaged_main(void* /* unused */) {
+ storaged = new storaged_t();
+
+ storaged->init_battery_service();
+
+ LOG_TO(SYSTEM, INFO) << "storaged: Start";
+
+ for (;;) {
+ storaged->event_checked();
+ storaged->pause();
+ }
+ return NULL;
+}
+
+static void help_message(void) {
+ printf("usage: storaged [OPTION]\n");
+ printf(" -u --uid Dump uid I/O usage to stdout\n");
+ printf(" -s --start Start storaged (default)\n");
+ fflush(stdout);
+}
+
+int main(int argc, char** argv) {
+ int flag_main_service = 0;
+ int flag_dump_uid = 0;
+ int opt;
+
+ for (;;) {
+ int opt_idx = 0;
+ static struct option long_options[] = {
+ {"start", no_argument, 0, 's'},
+ {"kill", no_argument, 0, 'k'},
+ {"uid", no_argument, 0, 'u'},
+ {"help", no_argument, 0, 'h'}
+ };
+ opt = getopt_long(argc, argv, ":skdhu0", long_options, &opt_idx);
+ if (opt == -1) {
+ break;
+ }
+
+ switch (opt) {
+ case 's':
+ flag_main_service = 1;
+ break;
+ case 'u':
+ flag_dump_uid = 1;
+ break;
+ case 'h':
+ help_message();
+ return 0;
+ case '?':
+ default:
+ fprintf(stderr, "no supported option\n");
+ help_message();
+ return -1;
+ }
+ }
+
+ if (argc == 1) {
+ flag_main_service = 1;
+ }
+
+ if (flag_main_service && flag_dump_uid) {
+ fprintf(stderr, "Invalid arguments. Option \"start\" and \"dump\" cannot be used together.\n");
+ help_message();
+ return -1;
+ }
+
+ if (flag_main_service) { // start main thread
+ report_storage_health();
+ // Start the main thread of storaged
+ pthread_t storaged_main_thread;
+ errno = pthread_create(&storaged_main_thread, NULL, storaged_main, NULL);
+ if (errno != 0) {
+ PLOG_TO(SYSTEM, ERROR) << "Failed to create main thread";
+ return -1;
+ }
+
+ defaultServiceManager()->addService(String16("storaged"), new Storaged());
+ android::ProcessState::self()->startThreadPool();
+ IPCThreadState::self()->joinThreadPool();
+ pthread_join(storaged_main_thread, NULL);
+
+ return 0;
+ }
+
+ if (flag_dump_uid) {
+ sp<IStoraged> storaged_service = get_storaged_service();
+ if (storaged_service == NULL) {
+ fprintf(stderr, "Cannot find storaged service.\nMaybe run storaged --start first?\n");
+ return -1;
+ }
+ std::vector<struct uid_info> res = storaged_service->dump_uids(NULL);
+
+ if (res.size() == 0) {
+ fprintf(stderr, "UID I/O is not readable in this version of kernel.\n");
+ return 0;
+ }
+
+ sort_running_uids_info(res);
+ log_console_running_uids_info(res);
+
+ return 0;
+ }
+
+ return 0;
+}
diff --git a/storaged/storaged.cpp b/storaged/storaged.cpp
new file mode 100644
index 0000000..54d429c
--- /dev/null
+++ b/storaged/storaged.cpp
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2016 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 "storaged"
+
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <android-base/logging.h>
+#include <batteryservice/BatteryServiceConstants.h>
+#include <batteryservice/IBatteryPropertiesRegistrar.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <cutils/properties.h>
+#include <log/log.h>
+
+#include <storaged.h>
+#include <storaged_utils.h>
+
+/* disk_stats_publisher */
+void disk_stats_publisher::publish(void) {
+ // Logging
+ struct disk_perf perf = get_disk_perf(&mAccumulate);
+ log_debug_disk_perf(&perf, "regular");
+ log_event_disk_stats(&mAccumulate, "regular");
+ // Reset global structures
+ memset(&mAccumulate, 0, sizeof(struct disk_stats));
+}
+
+void disk_stats_publisher::update(void) {
+ struct disk_stats curr;
+ if (parse_disk_stats(DISK_STATS_PATH, &curr)) {
+ struct disk_stats inc = get_inc_disk_stats(&mPrevious, &curr);
+ add_disk_stats(&inc, &mAccumulate);
+#ifdef DEBUG
+// log_kernel_disk_stats(&mPrevious, "prev stats");
+// log_kernel_disk_stats(&curr, "curr stats");
+// log_kernel_disk_stats(&inc, "inc stats");
+// log_kernel_disk_stats(&mAccumulate, "accumulated stats");
+#endif
+ mPrevious = curr;
+ }
+}
+
+/* disk_stats_monitor */
+void disk_stats_monitor::update_mean() {
+ CHECK(mValid);
+ mMean.read_perf = (uint32_t)mStats.read_perf.get_mean();
+ mMean.read_ios = (uint32_t)mStats.read_ios.get_mean();
+ mMean.write_perf = (uint32_t)mStats.write_perf.get_mean();
+ mMean.write_ios = (uint32_t)mStats.write_ios.get_mean();
+ mMean.queue = (uint32_t)mStats.queue.get_mean();
+}
+
+void disk_stats_monitor::update_std() {
+ CHECK(mValid);
+ mStd.read_perf = (uint32_t)mStats.read_perf.get_std();
+ mStd.read_ios = (uint32_t)mStats.read_ios.get_std();
+ mStd.write_perf = (uint32_t)mStats.write_perf.get_std();
+ mStd.write_ios = (uint32_t)mStats.write_ios.get_std();
+ mStd.queue = (uint32_t)mStats.queue.get_std();
+}
+
+void disk_stats_monitor::add(struct disk_perf* perf) {
+ mStats.read_perf.add(perf->read_perf);
+ mStats.read_ios.add(perf->read_ios);
+ mStats.write_perf.add(perf->write_perf);
+ mStats.write_ios.add(perf->write_ios);
+ mStats.queue.add(perf->queue);
+}
+
+void disk_stats_monitor::evict(struct disk_perf* perf) {
+ mStats.read_perf.evict(perf->read_perf);
+ mStats.read_ios.evict(perf->read_ios);
+ mStats.write_perf.evict(perf->write_perf);
+ mStats.write_ios.evict(perf->write_ios);
+ mStats.queue.evict(perf->queue);
+}
+
+bool disk_stats_monitor::detect(struct disk_perf* perf) {
+ return ((double)perf->queue >= (double)mMean.queue + mSigma * (double)mStd.queue) &&
+ ((double)perf->read_perf < (double)mMean.read_perf - mSigma * (double)mStd.read_perf) &&
+ ((double)perf->write_perf < (double)mMean.write_perf - mSigma * (double)mStd.write_perf);
+}
+
+void disk_stats_monitor::update(struct disk_stats* stats) {
+ struct disk_stats inc = get_inc_disk_stats(&mPrevious, stats);
+ struct disk_perf perf = get_disk_perf(&inc);
+ // Update internal data structures
+ if (LIKELY(mValid)) {
+ CHECK_EQ(mBuffer.size(), mWindow);
+
+ if (UNLIKELY(detect(&perf))) {
+ mStall = true;
+ add_disk_stats(&inc, &mAccumulate);
+ log_debug_disk_perf(&mMean, "stalled_mean");
+ log_debug_disk_perf(&mStd, "stalled_std");
+ } else {
+ if (mStall) {
+ struct disk_perf acc_perf = get_disk_perf(&mAccumulate);
+ log_debug_disk_perf(&acc_perf, "stalled");
+ log_event_disk_stats(&mAccumulate, "stalled");
+ mStall = false;
+ memset(&mAccumulate, 0, sizeof(mAccumulate));
+ }
+ }
+
+ evict(&mBuffer.front());
+ mBuffer.pop();
+ add(&perf);
+ mBuffer.push(perf);
+
+ update_mean();
+ update_std();
+
+ } else { /* mValid == false */
+ CHECK_LT(mBuffer.size(), mWindow);
+ add(&perf);
+ mBuffer.push(perf);
+ if (mBuffer.size() == mWindow) {
+ mValid = true;
+ update_mean();
+ update_std();
+ }
+ }
+
+ mPrevious = *stats;
+}
+
+void disk_stats_monitor::update(void) {
+ struct disk_stats curr;
+ if (LIKELY(parse_disk_stats(DISK_STATS_PATH, &curr))) {
+ update(&curr);
+ }
+}
+
+static sp<IBatteryPropertiesRegistrar> get_battery_properties_service() {
+ sp<IServiceManager> sm = defaultServiceManager();
+ if (sm == NULL) return NULL;
+
+ sp<IBinder> binder = sm->getService(String16("batteryproperties"));
+ if (binder == NULL) return NULL;
+
+ sp<IBatteryPropertiesRegistrar> battery_properties =
+ interface_cast<IBatteryPropertiesRegistrar>(binder);
+
+ return battery_properties;
+}
+
+static inline charger_stat_t is_charger_on(int64_t prop) {
+ return (prop == BATTERY_STATUS_CHARGING || prop == BATTERY_STATUS_FULL) ?
+ CHARGER_ON : CHARGER_OFF;
+}
+
+void storaged_t::batteryPropertiesChanged(struct BatteryProperties props) {
+ mUidm.set_charger_state(is_charger_on(props.batteryStatus));
+}
+
+void storaged_t::init_battery_service() {
+ if (!mConfig.proc_uid_io_available)
+ return;
+
+ battery_properties = get_battery_properties_service();
+ if (battery_properties == NULL) {
+ LOG_TO(SYSTEM, WARNING) << "failed to find batteryproperties service";
+ return;
+ }
+
+ struct BatteryProperty val;
+ battery_properties->getProperty(BATTERY_PROP_BATTERY_STATUS, &val);
+ mUidm.init(is_charger_on(val.valueInt64));
+
+ // register listener after init uid_monitor
+ battery_properties->registerListener(this);
+ IInterface::asBinder(battery_properties)->linkToDeath(this);
+}
+
+void storaged_t::binderDied(const wp<IBinder>& who) {
+ if (battery_properties != NULL &&
+ IInterface::asBinder(battery_properties) == who) {
+ LOG_TO(SYSTEM, ERROR) << "batteryproperties service died, exiting";
+ IPCThreadState::self()->stopProcess();
+ exit(1);
+ } else {
+ LOG_TO(SYSTEM, ERROR) << "unknown service died";
+ }
+}
+
+/* storaged_t */
+storaged_t::storaged_t(void) {
+ if (access(MMC_DISK_STATS_PATH, R_OK) < 0 && access(SDA_DISK_STATS_PATH, R_OK) < 0) {
+ mConfig.diskstats_available = false;
+ } else {
+ mConfig.diskstats_available = true;
+ }
+
+ mConfig.proc_uid_io_available = (access(UID_IO_STATS_PATH, R_OK) == 0);
+
+ mConfig.periodic_chores_interval_unit =
+ property_get_int32("ro.storaged.event.interval", DEFAULT_PERIODIC_CHORES_INTERVAL_UNIT);
+
+ mConfig.event_time_check_usec =
+ property_get_int32("ro.storaged.event.perf_check", 0);
+
+ mConfig.periodic_chores_interval_disk_stats_publish =
+ property_get_int32("ro.storaged.disk_stats_pub", DEFAULT_PERIODIC_CHORES_INTERVAL_DISK_STATS_PUBLISH);
+
+ mConfig.periodic_chores_interval_uid_io =
+ property_get_int32("ro.storaged.uid_io.interval", DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO);
+
+ mStarttime = time(NULL);
+}
+
+void storaged_t::event(void) {
+ if (mConfig.diskstats_available) {
+ mDiskStats.update();
+ mDsm.update();
+ if (mTimer && (mTimer % mConfig.periodic_chores_interval_disk_stats_publish) == 0) {
+ mDiskStats.publish();
+ }
+ }
+
+ if (mConfig.proc_uid_io_available && mTimer &&
+ (mTimer % mConfig.periodic_chores_interval_uid_io) == 0) {
+ mUidm.report();
+ }
+
+ mTimer += mConfig.periodic_chores_interval_unit;
+}
+
+void storaged_t::event_checked(void) {
+ struct timespec start_ts, end_ts;
+ bool check_time = true;
+
+ if (mConfig.event_time_check_usec &&
+ clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &start_ts) < 0) {
+ check_time = false;
+ static time_t state_a;
+ IF_ALOG_RATELIMIT_LOCAL(300, &state_a) {
+ PLOG_TO(SYSTEM, ERROR) << "clock_gettime() failed";
+ }
+ }
+
+ event();
+
+ if (mConfig.event_time_check_usec && check_time) {
+ if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &end_ts) < 0) {
+ static time_t state_b;
+ IF_ALOG_RATELIMIT_LOCAL(300, &state_b) {
+ PLOG_TO(SYSTEM, ERROR) << "clock_gettime() failed";
+ }
+ return;
+ }
+ int64_t cost = (end_ts.tv_sec - start_ts.tv_sec) * SEC_TO_USEC +
+ (end_ts.tv_nsec - start_ts.tv_nsec) / USEC_TO_NSEC;
+ if (cost > mConfig.event_time_check_usec) {
+ LOG_TO(SYSTEM, ERROR)
+ << "event loop spent " << cost << " usec, threshold "
+ << mConfig.event_time_check_usec << " usec";
+ }
+ }
+}
diff --git a/storaged/storaged.rc b/storaged/storaged.rc
new file mode 100644
index 0000000..a24c7fb
--- /dev/null
+++ b/storaged/storaged.rc
@@ -0,0 +1,7 @@
+service storaged /system/bin/storaged
+ class main
+ priority 10
+ file /d/mmc0/mmc0:0001/ext_csd r
+ writepid /dev/cpuset/system-background/tasks
+ user root
+ group package_info
\ No newline at end of file
diff --git a/storaged/storaged_info.cpp b/storaged/storaged_info.cpp
new file mode 100644
index 0000000..434bd74
--- /dev/null
+++ b/storaged/storaged_info.cpp
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2016 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 "storaged"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <android-base/file.h>
+#include <android-base/parseint.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <log/log_event_list.h>
+
+#include "storaged.h"
+
+using namespace std;
+using namespace android::base;
+
+void report_storage_health()
+{
+ emmc_info_t mmc;
+ ufs_info_t ufs;
+
+ mmc.report();
+ ufs.report();
+}
+
+void storage_info_t::publish()
+{
+ android_log_event_list(EVENTLOGTAG_EMMCINFO)
+ << version << eol << lifetime_a << lifetime_b
+ << LOG_ID_EVENTS;
+}
+
+bool emmc_info_t::report()
+{
+ if (!report_sysfs() && !report_debugfs())
+ return false;
+
+ publish();
+ return true;
+}
+
+bool emmc_info_t::report_sysfs()
+{
+ string buffer;
+ uint16_t rev = 0;
+
+ if (!ReadFileToString(emmc_sysfs + "rev", &buffer)) {
+ return false;
+ }
+
+ if (sscanf(buffer.c_str(), "0x%hx", &rev) < 1 ||
+ rev < 7 || rev > ARRAY_SIZE(emmc_ver_str)) {
+ return false;
+ }
+
+ version = "emmc ";
+ version += emmc_ver_str[rev];
+
+ if (!ReadFileToString(emmc_sysfs + "pre_eol_info", &buffer)) {
+ return false;
+ }
+
+ if (sscanf(buffer.c_str(), "%hx", &eol) < 1 || eol == 0) {
+ return false;
+ }
+
+ if (!ReadFileToString(emmc_sysfs + "life_time", &buffer)) {
+ return false;
+ }
+
+ if (sscanf(buffer.c_str(), "0x%hx 0x%hx", &lifetime_a, &lifetime_b) < 2 ||
+ (lifetime_a == 0 && lifetime_b == 0)) {
+ return false;
+ }
+
+ return true;
+}
+
+const size_t EXT_CSD_FILE_MIN_SIZE = 1024;
+/* 2 characters in string for each byte */
+const size_t EXT_CSD_REV_IDX = 192 * 2;
+const size_t EXT_PRE_EOL_INFO_IDX = 267 * 2;
+const size_t EXT_DEVICE_LIFE_TIME_EST_A_IDX = 268 * 2;
+const size_t EXT_DEVICE_LIFE_TIME_EST_B_IDX = 269 * 2;
+
+bool emmc_info_t::report_debugfs()
+{
+ string buffer;
+ uint16_t rev = 0;
+
+ if (!ReadFileToString(emmc_debugfs, &buffer) ||
+ buffer.length() < (size_t)EXT_CSD_FILE_MIN_SIZE) {
+ return false;
+ }
+
+ string str = buffer.substr(EXT_CSD_REV_IDX, 2);
+ if (!ParseUint(str, &rev) ||
+ rev < 7 || rev > ARRAY_SIZE(emmc_ver_str)) {
+ return false;
+ }
+
+ version = "emmc ";
+ version += emmc_ver_str[rev];
+
+ str = buffer.substr(EXT_PRE_EOL_INFO_IDX, 2);
+ if (!ParseUint(str, &eol)) {
+ return false;
+ }
+
+ str = buffer.substr(EXT_DEVICE_LIFE_TIME_EST_A_IDX, 2);
+ if (!ParseUint(str, &lifetime_a)) {
+ return false;
+ }
+
+ str = buffer.substr(EXT_DEVICE_LIFE_TIME_EST_B_IDX, 2);
+ if (!ParseUint(str, &lifetime_b)) {
+ return false;
+ }
+
+ return true;
+}
+
+bool ufs_info_t::report()
+{
+ string buffer;
+ if (!ReadFileToString(health_file, &buffer)) {
+ return false;
+ }
+
+ vector<string> lines = Split(buffer, "\n");
+ if (lines.empty()) {
+ return false;
+ }
+
+ char rev[8];
+ if (sscanf(lines[0].c_str(), "ufs version: 0x%7s\n", rev) < 1) {
+ return false;
+ }
+
+ version = "ufs " + string(rev);
+
+ for (size_t i = 1; i < lines.size(); i++) {
+ char token[32];
+ uint16_t val;
+ int ret;
+ if ((ret = sscanf(lines[i].c_str(),
+ "Health Descriptor[Byte offset 0x%*d]: %31s = 0x%hx",
+ token, &val)) < 2) {
+ continue;
+ }
+
+ if (string(token) == "bPreEOLInfo") {
+ eol = val;
+ } else if (string(token) == "bDeviceLifeTimeEstA") {
+ lifetime_a = val;
+ } else if (string(token) == "bDeviceLifeTimeEstB") {
+ lifetime_b = val;
+ }
+ }
+
+ if (eol == 0 || (lifetime_a == 0 && lifetime_b == 0)) {
+ return false;
+ }
+
+ publish();
+ return true;
+}
+
diff --git a/storaged/storaged_service.cpp b/storaged/storaged_service.cpp
new file mode 100644
index 0000000..b1d3bfd
--- /dev/null
+++ b/storaged/storaged_service.cpp
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <vector>
+
+#include <android-base/parseint.h>
+#include <android-base/parsedouble.h>
+#include <binder/IBinder.h>
+#include <binder/IInterface.h>
+
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/PermissionCache.h>
+#include <private/android_filesystem_config.h>
+
+#include <storaged.h>
+#include <storaged_service.h>
+
+using namespace android::base;
+
+extern sp<storaged_t> storaged;
+
+std::vector<struct uid_info> BpStoraged::dump_uids(const char* /*option*/) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IStoraged::getInterfaceDescriptor());
+
+ remote()->transact(DUMPUIDS, data, &reply);
+
+ uint32_t res_size = reply.readInt32();
+ std::vector<struct uid_info> res(res_size);
+ for (auto&& uid : res) {
+ uid.uid = reply.readInt32();
+ uid.name = reply.readCString();
+ reply.read(&uid.io, sizeof(uid.io));
+ }
+ return res;
+}
+IMPLEMENT_META_INTERFACE(Storaged, "Storaged");
+
+status_t BnStoraged::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
+ switch(code) {
+ case DUMPUIDS: {
+ if (!data.checkInterface(this))
+ return BAD_TYPE;
+ std::vector<struct uid_info> res = dump_uids(NULL);
+ reply->writeInt32(res.size());
+ for (auto uid : res) {
+ reply->writeInt32(uid.uid);
+ reply->writeCString(uid.name.c_str());
+ reply->write(&uid.io, sizeof(uid.io));
+ }
+ return NO_ERROR;
+ }
+ break;
+ default:
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+}
+
+std::vector<struct uid_info> Storaged::dump_uids(const char* /* option */) {
+ std::vector<struct uid_info> uids_v;
+ std::unordered_map<uint32_t, struct uid_info> uids_m = storaged->get_uids();
+
+ for (const auto& it : uids_m) {
+ uids_v.push_back(it.second);
+ }
+ return uids_v;
+}
+
+status_t Storaged::dump(int fd, const Vector<String16>& args) {
+ IPCThreadState* self = IPCThreadState::self();
+ const int pid = self->getCallingPid();
+ const int uid = self->getCallingUid();
+ if ((uid != AID_SHELL) &&
+ !PermissionCache::checkPermission(
+ String16("android.permission.DUMP"), pid, uid)) {
+ return PERMISSION_DENIED;
+ }
+
+ double hours = 0;
+ int time_window = 0;
+ uint64_t threshold = 0;
+ bool force_report = false;
+ for (size_t i = 0; i < args.size(); i++) {
+ const auto& arg = args[i];
+ if (arg == String16("--hours")) {
+ if (++i >= args.size())
+ break;
+ if(!ParseDouble(String8(args[i]).c_str(), &hours))
+ return BAD_VALUE;
+ continue;
+ }
+ if (arg == String16("--time_window")) {
+ if (++i >= args.size())
+ break;
+ if(!ParseInt(String8(args[i]).c_str(), &time_window))
+ return BAD_VALUE;
+ continue;
+ }
+ if (arg == String16("--threshold")) {
+ if (++i >= args.size())
+ break;
+ if(!ParseUint(String8(args[i]).c_str(), &threshold))
+ return BAD_VALUE;
+ continue;
+ }
+ if (arg == String16("--force")) {
+ force_report = true;
+ continue;
+ }
+ }
+
+ uint64_t last_ts = 0;
+ const std::map<uint64_t, struct uid_records>& records =
+ storaged->get_uid_records(hours, threshold, force_report);
+ for (const auto& it : records) {
+ if (last_ts != it.second.start_ts) {
+ dprintf(fd, "%llu", (unsigned long long)it.second.start_ts);
+ }
+ dprintf(fd, ",%llu\n", (unsigned long long)it.first);
+ last_ts = it.first;
+
+ for (const auto& record : it.second.entries) {
+ dprintf(fd, "%s %ju %ju %ju %ju %ju %ju %ju %ju\n",
+ record.name.c_str(),
+ record.ios.bytes[READ][FOREGROUND][CHARGER_OFF],
+ record.ios.bytes[WRITE][FOREGROUND][CHARGER_OFF],
+ record.ios.bytes[READ][BACKGROUND][CHARGER_OFF],
+ record.ios.bytes[WRITE][BACKGROUND][CHARGER_OFF],
+ record.ios.bytes[READ][FOREGROUND][CHARGER_ON],
+ record.ios.bytes[WRITE][FOREGROUND][CHARGER_ON],
+ record.ios.bytes[READ][BACKGROUND][CHARGER_ON],
+ record.ios.bytes[WRITE][BACKGROUND][CHARGER_ON]);
+ }
+ }
+
+ if (time_window) {
+ storaged->update_uid_io_interval(time_window);
+ }
+
+ return NO_ERROR;
+}
+
+sp<IStoraged> get_storaged_service() {
+ sp<IServiceManager> sm = defaultServiceManager();
+ if (sm == NULL) return NULL;
+
+ sp<IBinder> binder = sm->getService(String16("storaged"));
+ if (binder == NULL) return NULL;
+
+ sp<IStoraged> storaged = interface_cast<IStoraged>(binder);
+
+ return storaged;
+}
diff --git a/storaged/storaged_uid_monitor.cpp b/storaged/storaged_uid_monitor.cpp
new file mode 100644
index 0000000..5bb98e1
--- /dev/null
+++ b/storaged/storaged_uid_monitor.cpp
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2016 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 "storaged"
+
+#include <stdint.h>
+#include <time.h>
+
+#include <string>
+#include <unordered_map>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
+#include <android-base/stringprintf.h>
+#include <log/log_event_list.h>
+#include <packagelistparser/packagelistparser.h>
+
+#include "storaged.h"
+#include "storaged_uid_monitor.h"
+
+using namespace android;
+using namespace android::base;
+
+static bool packagelist_parse_cb(pkg_info* info, void* userdata)
+{
+ std::unordered_map<uint32_t, struct uid_info>* uids =
+ reinterpret_cast<std::unordered_map<uint32_t, struct uid_info>*>(userdata);
+
+ if (uids->find(info->uid) != uids->end()) {
+ (*uids)[info->uid].name = info->name;
+ }
+
+ packagelist_free(info);
+ return true;
+}
+
+std::unordered_map<uint32_t, struct uid_info> uid_monitor::get_uid_io_stats()
+{
+ std::unique_ptr<lock_t> lock(new lock_t(&um_lock));
+ return get_uid_io_stats_locked();
+};
+
+std::unordered_map<uint32_t, struct uid_info> uid_monitor::get_uid_io_stats_locked()
+{
+ std::unordered_map<uint32_t, struct uid_info> uid_io_stats;
+ std::string buffer;
+ if (!ReadFileToString(UID_IO_STATS_PATH, &buffer)) {
+ PLOG_TO(SYSTEM, ERROR) << UID_IO_STATS_PATH << ": ReadFileToString failed";
+ return uid_io_stats;
+ }
+
+ std::vector<std::string> io_stats = Split(buffer, "\n");
+ struct uid_info u;
+ bool refresh_uid = false;
+
+ for (uint32_t i = 0; i < io_stats.size(); i++) {
+ if (io_stats[i].empty()) {
+ continue;
+ }
+ std::vector<std::string> fields = Split(io_stats[i], " ");
+ if (fields.size() < 11 ||
+ !ParseUint(fields[0], &u.uid) ||
+ !ParseUint(fields[1], &u.io[FOREGROUND].rchar) ||
+ !ParseUint(fields[2], &u.io[FOREGROUND].wchar) ||
+ !ParseUint(fields[3], &u.io[FOREGROUND].read_bytes) ||
+ !ParseUint(fields[4], &u.io[FOREGROUND].write_bytes) ||
+ !ParseUint(fields[5], &u.io[BACKGROUND].rchar) ||
+ !ParseUint(fields[6], &u.io[BACKGROUND].wchar) ||
+ !ParseUint(fields[7], &u.io[BACKGROUND].read_bytes) ||
+ !ParseUint(fields[8], &u.io[BACKGROUND].write_bytes) ||
+ !ParseUint(fields[9], &u.io[FOREGROUND].fsync) ||
+ !ParseUint(fields[10], &u.io[BACKGROUND].fsync)) {
+ LOG_TO(SYSTEM, WARNING) << "Invalid I/O stats: \""
+ << io_stats[i] << "\"";
+ continue;
+ }
+
+ if (last_uid_io_stats.find(u.uid) == last_uid_io_stats.end()) {
+ refresh_uid = true;
+ u.name = std::to_string(u.uid);
+ } else {
+ u.name = last_uid_io_stats[u.uid].name;
+ }
+ uid_io_stats[u.uid] = u;
+ }
+
+ if (refresh_uid) {
+ packagelist_parse(packagelist_parse_cb, &uid_io_stats);
+ }
+
+ return uid_io_stats;
+}
+
+static const int MAX_UID_RECORDS_SIZE = 1000 * 48; // 1000 uids in 48 hours
+
+static inline int records_size(
+ const std::map<uint64_t, struct uid_records>& curr_records)
+{
+ int count = 0;
+ for (auto const& it : curr_records) {
+ count += it.second.entries.size();
+ }
+ return count;
+}
+
+static struct uid_io_usage zero_io_usage;
+
+void uid_monitor::add_records_locked(uint64_t curr_ts)
+{
+ // remove records more than 5 days old
+ if (curr_ts > 5 * DAY_TO_SEC) {
+ auto it = records.lower_bound(curr_ts - 5 * DAY_TO_SEC);
+ records.erase(records.begin(), it);
+ }
+
+ struct uid_records new_records;
+ for (const auto& p : curr_io_stats) {
+ struct uid_record record = {};
+ record.name = p.first;
+ record.ios = p.second;
+ if (memcmp(&record.ios, &zero_io_usage, sizeof(struct uid_io_usage))) {
+ new_records.entries.push_back(record);
+ }
+ }
+
+ curr_io_stats.clear();
+ new_records.start_ts = start_ts;
+ start_ts = curr_ts;
+
+ if (new_records.entries.empty())
+ return;
+
+ // make some room for new records
+ int overflow = records_size(records) +
+ new_records.entries.size() - MAX_UID_RECORDS_SIZE;
+ while (overflow > 0 && records.size() > 0) {
+ auto del_it = records.begin();
+ overflow -= del_it->second.entries.size();
+ records.erase(records.begin());
+ }
+
+ records[curr_ts] = new_records;
+}
+
+std::map<uint64_t, struct uid_records> uid_monitor::dump(
+ double hours, uint64_t threshold, bool force_report)
+{
+ if (force_report) {
+ report();
+ }
+
+ std::unique_ptr<lock_t> lock(new lock_t(&um_lock));
+
+ std::map<uint64_t, struct uid_records> dump_records;
+ uint64_t first_ts = 0;
+
+ if (hours != 0) {
+ first_ts = time(NULL) - hours * HOUR_TO_SEC;
+ }
+
+ for (auto it = records.lower_bound(first_ts); it != records.end(); ++it) {
+ const std::vector<struct uid_record>& recs = it->second.entries;
+ struct uid_records filtered;
+
+ for (const auto& rec : recs) {
+ if (rec.ios.bytes[READ][FOREGROUND][CHARGER_ON] +
+ rec.ios.bytes[READ][FOREGROUND][CHARGER_OFF] +
+ rec.ios.bytes[READ][BACKGROUND][CHARGER_ON] +
+ rec.ios.bytes[READ][BACKGROUND][CHARGER_OFF] +
+ rec.ios.bytes[WRITE][FOREGROUND][CHARGER_ON] +
+ rec.ios.bytes[WRITE][FOREGROUND][CHARGER_OFF] +
+ rec.ios.bytes[WRITE][BACKGROUND][CHARGER_ON] +
+ rec.ios.bytes[WRITE][BACKGROUND][CHARGER_OFF] > threshold) {
+ filtered.entries.push_back(rec);
+ }
+ }
+
+ if (filtered.entries.empty())
+ continue;
+
+ filtered.start_ts = it->second.start_ts;
+ dump_records.insert(
+ std::pair<uint64_t, struct uid_records>(it->first, filtered));
+ }
+
+ return dump_records;
+}
+
+void uid_monitor::update_curr_io_stats_locked()
+{
+ std::unordered_map<uint32_t, struct uid_info> uid_io_stats =
+ get_uid_io_stats_locked();
+ if (uid_io_stats.empty()) {
+ return;
+ }
+
+ for (const auto& it : uid_io_stats) {
+ const struct uid_info& uid = it.second;
+
+ if (curr_io_stats.find(uid.name) == curr_io_stats.end()) {
+ curr_io_stats[uid.name] = {};
+ }
+
+ struct uid_io_usage& usage = curr_io_stats[uid.name];
+ int64_t fg_rd_delta = uid.io[FOREGROUND].read_bytes -
+ last_uid_io_stats[uid.uid].io[FOREGROUND].read_bytes;
+ int64_t bg_rd_delta = uid.io[BACKGROUND].read_bytes -
+ last_uid_io_stats[uid.uid].io[BACKGROUND].read_bytes;
+ int64_t fg_wr_delta = uid.io[FOREGROUND].write_bytes -
+ last_uid_io_stats[uid.uid].io[FOREGROUND].write_bytes;
+ int64_t bg_wr_delta = uid.io[BACKGROUND].write_bytes -
+ last_uid_io_stats[uid.uid].io[BACKGROUND].write_bytes;
+
+ usage.bytes[READ][FOREGROUND][charger_stat] +=
+ (fg_rd_delta < 0) ? uid.io[FOREGROUND].read_bytes : fg_rd_delta;
+ usage.bytes[READ][BACKGROUND][charger_stat] +=
+ (bg_rd_delta < 0) ? uid.io[BACKGROUND].read_bytes : bg_rd_delta;
+ usage.bytes[WRITE][FOREGROUND][charger_stat] +=
+ (fg_wr_delta < 0) ? uid.io[FOREGROUND].write_bytes : fg_wr_delta;
+ usage.bytes[WRITE][BACKGROUND][charger_stat] +=
+ (bg_wr_delta < 0) ? uid.io[BACKGROUND].write_bytes : bg_wr_delta;
+ }
+
+ last_uid_io_stats = uid_io_stats;
+}
+
+void uid_monitor::report()
+{
+ std::unique_ptr<lock_t> lock(new lock_t(&um_lock));
+
+ update_curr_io_stats_locked();
+ add_records_locked(time(NULL));
+}
+
+void uid_monitor::set_charger_state(charger_stat_t stat)
+{
+ std::unique_ptr<lock_t> lock(new lock_t(&um_lock));
+
+ if (charger_stat == stat) {
+ return;
+ }
+
+ update_curr_io_stats_locked();
+ charger_stat = stat;
+}
+
+void uid_monitor::init(charger_stat_t stat)
+{
+ charger_stat = stat;
+ start_ts = time(NULL);
+ last_uid_io_stats = get_uid_io_stats();
+}
+
+uid_monitor::uid_monitor()
+{
+ sem_init(&um_lock, 0, 1);
+}
+
+uid_monitor::~uid_monitor()
+{
+ sem_destroy(&um_lock);
+}
diff --git a/storaged/storaged_utils.cpp b/storaged/storaged_utils.cpp
new file mode 100644
index 0000000..74b7436
--- /dev/null
+++ b/storaged/storaged_utils.cpp
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2016 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 "storaged"
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <linux/time.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <iomanip>
+#include <sstream>
+#include <string>
+#include <unordered_map>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <log/log_event_list.h>
+
+#include <storaged.h>
+#include <storaged_utils.h>
+
+bool parse_disk_stats(const char* disk_stats_path, struct disk_stats* stats) {
+ // Get time
+ struct timespec ts;
+ // Use monotonic to exclude suspend time so that we measure IO bytes/sec
+ // when system is running.
+ int ret = clock_gettime(CLOCK_MONOTONIC, &ts);
+ if (ret < 0) {
+ PLOG_TO(SYSTEM, ERROR) << "clock_gettime() failed";
+ return false;
+ }
+
+ std::string buffer;
+ if (!android::base::ReadFileToString(disk_stats_path, &buffer)) {
+ PLOG_TO(SYSTEM, ERROR) << disk_stats_path << ": ReadFileToString failed.";
+ return false;
+ }
+
+ // Regular diskstats entries
+ std::stringstream ss(buffer);
+ for (uint i = 0; i < DISK_STATS_SIZE; ++i) {
+ ss >> *((uint64_t*)stats + i);
+ }
+ // Other entries
+ stats->start_time = 0;
+ stats->end_time = (uint64_t)ts.tv_sec * SEC_TO_MSEC +
+ ts.tv_nsec / (MSEC_TO_USEC * USEC_TO_NSEC);
+ stats->counter = 1;
+ stats->io_avg = (double)stats->io_in_flight;
+ return true;
+}
+
+struct disk_perf get_disk_perf(struct disk_stats* stats) {
+ struct disk_perf perf;
+ memset(&perf, 0, sizeof(struct disk_perf)); // initialize
+
+ if (stats->io_ticks) {
+ if (stats->read_ticks) {
+ unsigned long long divisor = stats->read_ticks * stats->io_ticks;
+ perf.read_perf = ((unsigned long long)SECTOR_SIZE *
+ stats->read_sectors *
+ stats->io_in_queue +
+ (divisor >> 1)) /
+ divisor;
+ perf.read_ios = ((unsigned long long)SEC_TO_MSEC *
+ stats->read_ios *
+ stats->io_in_queue +
+ (divisor >> 1)) /
+ divisor;
+ }
+ if (stats->write_ticks) {
+ unsigned long long divisor = stats->write_ticks * stats->io_ticks;
+ perf.write_perf = ((unsigned long long)SECTOR_SIZE *
+ stats->write_sectors *
+ stats->io_in_queue +
+ (divisor >> 1)) /
+ divisor;
+ perf.write_ios = ((unsigned long long)SEC_TO_MSEC *
+ stats->write_ios *
+ stats->io_in_queue +
+ (divisor >> 1)) /
+ divisor;
+ }
+ perf.queue = (stats->io_in_queue + (stats->io_ticks >> 1)) /
+ stats->io_ticks;
+ }
+ return perf;
+}
+
+struct disk_stats get_inc_disk_stats(struct disk_stats* prev, struct disk_stats* curr) {
+ struct disk_stats inc;
+ for (uint i = 0; i < DISK_STATS_SIZE; ++i) {
+ if (i == DISK_STATS_IO_IN_FLIGHT_IDX) {
+ continue;
+ }
+
+ *((uint64_t*)&inc + i) =
+ *((uint64_t*)curr + i) - *((uint64_t*)prev + i);
+ }
+ // io_in_flight is exception
+ inc.io_in_flight = curr->io_in_flight;
+
+ inc.start_time = prev->end_time;
+ inc.end_time = curr->end_time;
+ inc.io_avg = curr->io_avg;
+ inc.counter = 1;
+
+ return inc;
+}
+
+// Add src to dst
+void add_disk_stats(struct disk_stats* src, struct disk_stats* dst) {
+ if (dst->end_time != 0 && dst->end_time != src->start_time) {
+ LOG_TO(SYSTEM, WARNING) << "Two dis-continuous periods of diskstats"
+ << " are added. dst end with " << dst->end_time
+ << ", src start with " << src->start_time;
+ }
+
+ for (uint i = 0; i < DISK_STATS_SIZE; ++i) {
+ if (i == DISK_STATS_IO_IN_FLIGHT_IDX) {
+ continue;
+ }
+
+ *((uint64_t*)dst + i) += *((uint64_t*)src + i);
+ }
+
+ dst->io_in_flight = src->io_in_flight;
+ if (dst->counter + src->counter) {
+ dst->io_avg = ((dst->io_avg * dst->counter) + (src->io_avg * src->counter)) /
+ (dst->counter + src->counter);
+ }
+ dst->counter += src->counter;
+ dst->end_time = src->end_time;
+ if (dst->start_time == 0) {
+ dst->start_time = src->start_time;
+ }
+}
+
+static bool cmp_uid_info(struct uid_info l, struct uid_info r) {
+ // Compare background I/O first.
+ for (int i = UID_STATS - 1; i >= 0; i--) {
+ uint64_t l_bytes = l.io[i].read_bytes + l.io[i].write_bytes;
+ uint64_t r_bytes = r.io[i].read_bytes + r.io[i].write_bytes;
+ uint64_t l_chars = l.io[i].rchar + l.io[i].wchar;
+ uint64_t r_chars = r.io[i].rchar + r.io[i].wchar;
+
+ if (l_bytes != r_bytes) {
+ return l_bytes > r_bytes;
+ }
+ if (l_chars != r_chars) {
+ return l_chars > r_chars;
+ }
+ }
+
+ return l.name < r.name;
+}
+
+void sort_running_uids_info(std::vector<struct uid_info> &uids) {
+ std::sort(uids.begin(), uids.end(), cmp_uid_info);
+}
+
+// Logging functions
+void log_console_running_uids_info(std::vector<struct uid_info> uids) {
+ printf("name/uid fg_rchar fg_wchar fg_rbytes fg_wbytes "
+ "bg_rchar bg_wchar bg_rbytes bg_wbytes fg_fsync bg_fsync\n");
+
+ for (const auto& uid : uids) {
+ printf("%s %ju %ju %ju %ju %ju %ju %ju %ju %ju %ju\n", uid.name.c_str(),
+ uid.io[0].rchar, uid.io[0].wchar, uid.io[0].read_bytes, uid.io[0].write_bytes,
+ uid.io[1].rchar, uid.io[1].wchar, uid.io[1].read_bytes, uid.io[1].write_bytes,
+ uid.io[0].fsync, uid.io[1].fsync);
+ }
+ fflush(stdout);
+}
+
+#if DEBUG
+void log_debug_disk_perf(struct disk_perf* perf, const char* type) {
+ // skip if the input structure are all zeros
+ if (perf == NULL) return;
+ struct disk_perf zero_cmp;
+ memset(&zero_cmp, 0, sizeof(zero_cmp));
+ if (memcmp(&zero_cmp, perf, sizeof(struct disk_perf)) == 0) return;
+
+ LOG_TO(SYSTEM, INFO) << "perf(ios) " << type
+ << " rd:" << perf->read_perf << "KB/s(" << perf->read_ios << "/s)"
+ << " wr:" << perf->write_perf << "KB/s(" << perf->write_ios << "/s)"
+ << " q:" << perf->queue;
+}
+#else
+void log_debug_disk_perf(struct disk_perf* /* perf */, const char* /* type */) {}
+#endif
+
+void log_event_disk_stats(struct disk_stats* stats, const char* type) {
+ // skip if the input structure are all zeros
+ if (stats == NULL) return;
+ struct disk_stats zero_cmp;
+ memset(&zero_cmp, 0, sizeof(zero_cmp));
+ // skip event logging diskstats when it is zero increment (all first 11 entries are zero)
+ if (memcmp(&zero_cmp, stats, sizeof(uint64_t) * DISK_STATS_SIZE) == 0) return;
+
+ android_log_event_list(EVENTLOGTAG_DISKSTATS)
+ << type << stats->start_time << stats->end_time
+ << stats->read_ios << stats->read_merges
+ << stats->read_sectors << stats->read_ticks
+ << stats->write_ios << stats->write_merges
+ << stats->write_sectors << stats->write_ticks
+ << (uint64_t)stats->io_avg << stats->io_ticks << stats->io_in_queue
+ << LOG_ID_EVENTS;
+}
+
diff --git a/storaged/tests/Android.mk b/storaged/tests/Android.mk
new file mode 100644
index 0000000..26d04b1
--- /dev/null
+++ b/storaged/tests/Android.mk
@@ -0,0 +1,45 @@
+#
+# 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+test_module_prefix := storaged-
+test_tags := tests
+
+# -----------------------------------------------------------------------------
+# Unit tests.
+# -----------------------------------------------------------------------------
+
+test_c_flags := \
+ -fstack-protector-all \
+ -g \
+ -Wall -Wextra \
+ -Werror \
+ -fno-builtin \
+
+test_src_files := \
+ storaged_test.cpp \
+
+# Build tests for the logger. Run with:
+# adb shell /data/nativetest/storaged-unit-tests/storaged-unit-tests
+include $(CLEAR_VARS)
+LOCAL_MODULE := $(test_module_prefix)unit-tests
+LOCAL_MODULE_TAGS := $(test_tags)
+LOCAL_CFLAGS += $(test_c_flags)
+LOCAL_STATIC_LIBRARIES := libstoraged
+LOCAL_SHARED_LIBRARIES := libbase libcutils liblog libpackagelistparser
+LOCAL_SRC_FILES := $(test_src_files)
+include $(BUILD_NATIVE_TEST)
diff --git a/storaged/tests/storaged_test.cpp b/storaged/tests/storaged_test.cpp
new file mode 100644
index 0000000..b103ac1
--- /dev/null
+++ b/storaged/tests/storaged_test.cpp
@@ -0,0 +1,351 @@
+/*
+ * Copyright (C) 2016 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 <deque>
+#include <fcntl.h>
+#include <random>
+#include <string.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <gtest/gtest.h>
+
+#include <storaged.h> // data structures
+#include <storaged_utils.h> // functions to test
+
+#define MMC_DISK_STATS_PATH "/sys/block/mmcblk0/stat"
+#define SDA_DISK_STATS_PATH "/sys/block/sda/stat"
+
+static void pause(uint32_t sec) {
+ const char* path = "/cache/test";
+ int fd = open(path, O_WRONLY | O_CREAT, 0600);
+ ASSERT_LT(-1, fd);
+ char buffer[2048];
+ memset(buffer, 1, sizeof(buffer));
+ int loop_size = 100;
+ for (int i = 0; i < loop_size; ++i) {
+ ASSERT_EQ(2048, write(fd, buffer, sizeof(buffer)));
+ }
+ fsync(fd);
+ close(fd);
+
+ fd = open(path, O_RDONLY);
+ ASSERT_LT(-1, fd);
+ for (int i = 0; i < loop_size; ++i) {
+ ASSERT_EQ(2048, read(fd, buffer, sizeof(buffer)));
+ }
+ close(fd);
+
+ sleep(sec);
+}
+
+// the return values of the tested functions should be the expected ones
+const char* DISK_STATS_PATH;
+TEST(storaged_test, retvals) {
+ struct disk_stats stats;
+ memset(&stats, 0, sizeof(struct disk_stats));
+
+ if (access(MMC_DISK_STATS_PATH, R_OK) >= 0) {
+ DISK_STATS_PATH = MMC_DISK_STATS_PATH;
+ } else if (access(SDA_DISK_STATS_PATH, R_OK) >= 0) {
+ DISK_STATS_PATH = SDA_DISK_STATS_PATH;
+ } else {
+ return;
+ }
+
+ EXPECT_TRUE(parse_disk_stats(DISK_STATS_PATH, &stats));
+
+ struct disk_stats old_stats;
+ memset(&old_stats, 0, sizeof(struct disk_stats));
+ old_stats = stats;
+
+ const char wrong_path[] = "/this/is/wrong";
+ EXPECT_FALSE(parse_disk_stats(wrong_path, &stats));
+
+ // reading a wrong path should not damage the output structure
+ EXPECT_EQ(0, memcmp(&stats, &old_stats, sizeof(disk_stats)));
+}
+
+TEST(storaged_test, disk_stats) {
+ struct disk_stats stats;
+ memset(&stats, 0, sizeof(struct disk_stats));
+
+ ASSERT_TRUE(parse_disk_stats(DISK_STATS_PATH, &stats));
+
+ // every entry of stats (except io_in_flight) should all be greater than 0
+ for (uint i = 0; i < DISK_STATS_SIZE; ++i) {
+ if (i == 8) continue; // skip io_in_flight which can be 0
+ EXPECT_LT((uint64_t)0, *((uint64_t*)&stats + i));
+ }
+
+ // accumulation of the increments should be the same with the overall increment
+ struct disk_stats base, tmp, curr, acc, inc[5];
+ memset(&base, 0, sizeof(struct disk_stats));
+ memset(&tmp, 0, sizeof(struct disk_stats));
+ memset(&acc, 0, sizeof(struct disk_stats));
+
+ for (uint i = 0; i < 5; ++i) {
+ ASSERT_TRUE(parse_disk_stats(DISK_STATS_PATH, &curr));
+ if (i == 0) {
+ base = curr;
+ tmp = curr;
+ sleep(5);
+ continue;
+ }
+ inc[i] = get_inc_disk_stats(&tmp, &curr);
+ add_disk_stats(&inc[i], &acc);
+ tmp = curr;
+ pause(5);
+ }
+ struct disk_stats overall_inc;
+ memset(&overall_inc, 0, sizeof(disk_stats));
+ overall_inc= get_inc_disk_stats(&base, &curr);
+
+ for (uint i = 0; i < DISK_STATS_SIZE; ++i) {
+ if (i == 8) continue; // skip io_in_flight which can be 0
+ EXPECT_EQ(*((uint64_t*)&overall_inc + i), *((uint64_t*)&acc + i));
+ }
+}
+
+static double mean(std::deque<uint32_t> nums) {
+ double sum = 0.0;
+ for (uint32_t i : nums) {
+ sum += i;
+ }
+ return sum / nums.size();
+}
+
+static double standard_deviation(std::deque<uint32_t> nums) {
+ double sum = 0.0;
+ double avg = mean(nums);
+ for (uint32_t i : nums) {
+ sum += ((double)i - avg) * ((double)i - avg);
+ }
+ return sqrt(sum / nums.size());
+}
+
+TEST(storaged_test, stream_stats) {
+ // 100 random numbers
+ std::vector<uint32_t> data = {8147,9058,1270,9134,6324,975,2785,5469,9575,9649,1576,9706,9572,4854,8003,1419,4218,9157,7922,9595,6557,357,8491,9340,6787,7577,7431,3922,6555,1712,7060,318,2769,462,971,8235,6948,3171,9502,344,4387,3816,7655,7952,1869,4898,4456,6463,7094,7547,2760,6797,6551,1626,1190,4984,9597,3404,5853,2238,7513,2551,5060,6991,8909,9593,5472,1386,1493,2575,8407,2543,8143,2435,9293,3500,1966,2511,6160,4733,3517,8308,5853,5497,9172,2858,7572,7537,3804,5678,759,540,5308,7792,9340,1299,5688,4694,119,3371};
+ std::deque<uint32_t> test_data;
+ stream_stats sstats;
+ for (uint32_t i : data) {
+ test_data.push_back(i);
+ sstats.add(i);
+
+ EXPECT_EQ((int)standard_deviation(test_data), (int)sstats.get_std());
+ EXPECT_EQ((int)mean(test_data), (int)sstats.get_mean());
+ }
+
+ for (uint32_t i : data) {
+ test_data.pop_front();
+ sstats.evict(i);
+
+ EXPECT_EQ((int)standard_deviation(test_data), (int)sstats.get_std());
+ EXPECT_EQ((int)mean(test_data), (int)sstats.get_mean());
+ }
+
+ // some real data
+ std::vector<uint32_t> another_data = {113875,81620,103145,28327,86855,207414,96526,52567,28553,250311};
+ test_data.clear();
+ uint32_t window_size = 2;
+ uint32_t idx;
+ stream_stats sstats1;
+ for (idx = 0; idx < window_size; ++idx) {
+ test_data.push_back(another_data[idx]);
+ sstats1.add(another_data[idx]);
+ }
+ EXPECT_EQ((int)standard_deviation(test_data), (int)sstats1.get_std());
+ EXPECT_EQ((int)mean(test_data), (int)sstats1.get_mean());
+ for (;idx < another_data.size(); ++idx) {
+ test_data.pop_front();
+ sstats1.evict(another_data[idx - window_size]);
+ test_data.push_back(another_data[idx]);
+ sstats1.add(another_data[idx]);
+ EXPECT_EQ((int)standard_deviation(test_data), (int)sstats1.get_std());
+ EXPECT_EQ((int)mean(test_data), (int)sstats1.get_mean());
+ }
+}
+
+static struct disk_perf disk_perf_multiply(struct disk_perf perf, double mul) {
+ struct disk_perf retval;
+ retval.read_perf = (double)perf.read_perf * mul;
+ retval.read_ios = (double)perf.read_ios * mul;
+ retval.write_perf = (double)perf.write_perf * mul;
+ retval.write_ios = (double)perf.write_ios * mul;
+ retval.queue = (double)perf.queue * mul;
+
+ return retval;
+}
+
+static struct disk_stats disk_stats_add(struct disk_stats stats1, struct disk_stats stats2) {
+ struct disk_stats retval;
+ retval.read_ios = stats1.read_ios + stats2.read_ios;
+ retval.read_merges = stats1.read_merges + stats2.read_merges;
+ retval.read_sectors = stats1.read_sectors + stats2.read_sectors;
+ retval.read_ticks = stats1.read_ticks + stats2.read_ticks;
+ retval.write_ios = stats1.write_ios + stats2.write_ios;
+ retval.write_merges = stats1.write_merges + stats2.write_merges;
+ retval.write_sectors = stats1.write_sectors + stats2.write_sectors;
+ retval.write_ticks = stats1.write_ticks + stats2.write_ticks;
+ retval.io_in_flight = stats1.io_in_flight + stats2.io_in_flight;
+ retval.io_ticks = stats1.io_ticks + stats2.io_ticks;
+ retval.io_in_queue = stats1.io_in_queue + stats2.io_in_queue;
+ retval.end_time = stats1.end_time + stats2.end_time;
+
+ return retval;
+}
+
+TEST(storaged_test, disk_stats_monitor) {
+ // asserting that there is one file for diskstats
+ ASSERT_TRUE(access(MMC_DISK_STATS_PATH, R_OK) >= 0 || access(SDA_DISK_STATS_PATH, R_OK) >= 0);
+ // testing if detect() will return the right value
+ disk_stats_monitor dsm_detect;
+ // feed monitor with constant perf data for io perf baseline
+ // using constant perf is reasonable since the functionality of stream_stats
+ // has already been tested
+ struct disk_perf norm_perf = {
+ .read_perf = 10 * 1024,
+ .read_ios = 50,
+ .write_perf = 5 * 1024,
+ .write_ios = 25,
+ .queue = 5
+ };
+
+ std::random_device rd;
+ std::mt19937 gen(rd());
+ std::uniform_real_distribution<> rand(0.8, 1.2);
+
+ for (uint i = 0; i < dsm_detect.mWindow; ++i) {
+ struct disk_perf perf = disk_perf_multiply(norm_perf, rand(gen));
+
+ dsm_detect.add(&perf);
+ dsm_detect.mBuffer.push(perf);
+ EXPECT_EQ(dsm_detect.mBuffer.size(), (uint64_t)i + 1);
+ }
+
+ dsm_detect.mValid = true;
+ dsm_detect.update_mean();
+ dsm_detect.update_std();
+
+ for (double i = 0; i < 2 * dsm_detect.mSigma; i += 0.5) {
+ struct disk_perf test_perf;
+ struct disk_perf test_mean = dsm_detect.mMean;
+ struct disk_perf test_std = dsm_detect.mStd;
+
+ test_perf.read_perf = (double)test_mean.read_perf - i * test_std.read_perf;
+ test_perf.read_ios = (double)test_mean.read_ios - i * test_std.read_ios;
+ test_perf.write_perf = (double)test_mean.write_perf - i * test_std.write_perf;
+ test_perf.write_ios = (double)test_mean.write_ios - i * test_std.write_ios;
+ test_perf.queue = (double)test_mean.queue + i * test_std.queue;
+
+ EXPECT_EQ((i > dsm_detect.mSigma), dsm_detect.detect(&test_perf));
+ }
+
+ // testing if stalled disk_stats can be correctly accumulated in the monitor
+ disk_stats_monitor dsm_acc;
+ struct disk_stats norm_inc = {
+ .read_ios = 200,
+ .read_merges = 0,
+ .read_sectors = 200,
+ .read_ticks = 200,
+ .write_ios = 100,
+ .write_merges = 0,
+ .write_sectors = 100,
+ .write_ticks = 100,
+ .io_in_flight = 0,
+ .io_ticks = 600,
+ .io_in_queue = 300,
+ .start_time = 0,
+ .end_time = 100,
+ .counter = 0,
+ .io_avg = 0
+ };
+
+ struct disk_stats stall_inc = {
+ .read_ios = 200,
+ .read_merges = 0,
+ .read_sectors = 20,
+ .read_ticks = 200,
+ .write_ios = 100,
+ .write_merges = 0,
+ .write_sectors = 10,
+ .write_ticks = 100,
+ .io_in_flight = 0,
+ .io_ticks = 600,
+ .io_in_queue = 1200,
+ .start_time = 0,
+ .end_time = 100,
+ .counter = 0,
+ .io_avg = 0
+ };
+
+ struct disk_stats stats_base;
+ memset(&stats_base, 0, sizeof(stats_base));
+
+ int loop_size = 100;
+ for (int i = 0; i < loop_size; ++i) {
+ stats_base = disk_stats_add(stats_base, norm_inc);
+ dsm_acc.update(&stats_base);
+ EXPECT_EQ(dsm_acc.mValid, (uint32_t)(i + 1) >= dsm_acc.mWindow);
+ EXPECT_FALSE(dsm_acc.mStall);
+ }
+
+ stats_base = disk_stats_add(stats_base, stall_inc);
+ dsm_acc.update(&stats_base);
+ EXPECT_TRUE(dsm_acc.mValid);
+ EXPECT_TRUE(dsm_acc.mStall);
+
+ for (int i = 0; i < 10; ++i) {
+ stats_base = disk_stats_add(stats_base, norm_inc);
+ dsm_acc.update(&stats_base);
+ EXPECT_TRUE(dsm_acc.mValid);
+ EXPECT_FALSE(dsm_acc.mStall);
+ }
+}
+
+static void expect_increasing(struct disk_stats stats1, struct disk_stats stats2) {
+ EXPECT_LE(stats1.read_ios, stats2.read_ios);
+ EXPECT_LE(stats1.read_merges, stats2.read_merges);
+ EXPECT_LE(stats1.read_sectors, stats2.read_sectors);
+ EXPECT_LE(stats1.read_ticks, stats2.read_ticks);
+
+ EXPECT_LE(stats1.write_ios, stats2.write_ios);
+ EXPECT_LE(stats1.write_merges, stats2.write_merges);
+ EXPECT_LE(stats1.write_sectors, stats2.write_sectors);
+ EXPECT_LE(stats1.write_ticks, stats2.write_ticks);
+
+ EXPECT_LE(stats1.io_ticks, stats2.io_ticks);
+ EXPECT_LE(stats1.io_in_queue, stats2.io_in_queue);
+}
+
+#define TEST_LOOPS 20
+TEST(storaged_test, disk_stats_publisher) {
+ // asserting that there is one file for diskstats
+ ASSERT_TRUE(access(MMC_DISK_STATS_PATH, R_OK) >= 0 || access(SDA_DISK_STATS_PATH, R_OK) >= 0);
+ disk_stats_publisher dsp;
+ struct disk_stats prev;
+ memset(&prev, 0, sizeof(prev));
+
+ for (int i = 0; i < TEST_LOOPS; ++i) {
+ dsp.update();
+ expect_increasing(prev, dsp.mPrevious);
+ prev = dsp.mPrevious;
+ pause(10);
+ }
+}
+
diff --git a/trusty/keymaster/keymaster_ipc.h b/trusty/keymaster/keymaster_ipc.h
index b38eb05..d63757b 100644
--- a/trusty/keymaster/keymaster_ipc.h
+++ b/trusty/keymaster/keymaster_ipc.h
@@ -24,7 +24,8 @@
// Commands
enum keymaster_command : uint32_t {
KEYMASTER_RESP_BIT = 1,
- KEYMASTER_REQ_SHIFT = 1,
+ KEYMASTER_STOP_BIT = 2,
+ KEYMASTER_REQ_SHIFT = 2,
KM_GENERATE_KEY = (0 << KEYMASTER_REQ_SHIFT),
KM_BEGIN_OPERATION = (1 << KEYMASTER_REQ_SHIFT),
diff --git a/trusty/keymaster/trusty_keymaster_device.cpp b/trusty/keymaster/trusty_keymaster_device.cpp
index 5f16fd0..55a03bd 100644
--- a/trusty/keymaster/trusty_keymaster_device.cpp
+++ b/trusty/keymaster/trusty_keymaster_device.cpp
@@ -36,7 +36,8 @@
#include "trusty_keymaster_device.h"
#include "trusty_keymaster_ipc.h"
-const uint32_t RECV_BUF_SIZE = PAGE_SIZE;
+// Maximum size of message from Trusty is 8K (for RSA attestation key and chain)
+const uint32_t RECV_BUF_SIZE = 2*PAGE_SIZE;
const uint32_t SEND_BUF_SIZE = (PAGE_SIZE - sizeof(struct keymaster_message) - 16 /* tipc header */);
const size_t kMaximumAttestationChallengeLength = 128;
@@ -176,14 +177,14 @@
}
AuthorizationSet params_copy(*params);
- ConfigureRequest request;
+ ConfigureRequest request(message_version_);
if (!params_copy.GetTagValue(TAG_OS_VERSION, &request.os_version) ||
!params_copy.GetTagValue(TAG_OS_PATCHLEVEL, &request.os_patchlevel)) {
ALOGD("Configuration parameters must contain OS version and patch level");
return KM_ERROR_INVALID_ARGUMENT;
}
- ConfigureResponse response;
+ ConfigureResponse response(message_version_);
keymaster_error_t err = Send(KM_CONFIGURE, request, &response);
if (err != KM_ERROR_OK) {
return err;
@@ -199,9 +200,9 @@
return error_;
}
- AddEntropyRequest request;
+ AddEntropyRequest request(message_version_);
request.random_data.Reinitialize(data, data_length);
- AddEntropyResponse response;
+ AddEntropyResponse response(message_version_);
return Send(KM_ADD_RNG_ENTROPY, request, &response);
}
@@ -260,11 +261,11 @@
return KM_ERROR_OUTPUT_PARAMETER_NULL;
}
- GetKeyCharacteristicsRequest request;
+ GetKeyCharacteristicsRequest request(message_version_);
request.SetKeyMaterial(*key_blob);
AddClientAndAppData(client_id, app_data, &request);
- GetKeyCharacteristicsResponse response;
+ GetKeyCharacteristicsResponse response(message_version_);
keymaster_error_t err = Send(KM_GET_KEY_CHARACTERISTICS, request, &response);
if (err != KM_ERROR_OK) {
return err;
@@ -378,7 +379,7 @@
cert_chain->entry_count = 0;
cert_chain->entries = nullptr;
- AttestKeyRequest request;
+ AttestKeyRequest request(message_version_);
request.SetKeyMaterial(*key_to_attest);
request.attest_params.Reinitialize(*attest_params);
@@ -390,7 +391,7 @@
return KM_ERROR_INVALID_INPUT_LENGTH;
}
- AttestKeyResponse response;
+ AttestKeyResponse response(message_version_);
keymaster_error_t err = Send(KM_ATTEST_KEY, request, &response);
if (err != KM_ERROR_OK) {
return err;
@@ -438,11 +439,11 @@
return KM_ERROR_OUTPUT_PARAMETER_NULL;
}
- UpgradeKeyRequest request;
+ UpgradeKeyRequest request(message_version_);
request.SetKeyMaterial(*key_to_upgrade);
request.upgrade_params.Reinitialize(*upgrade_params);
- UpgradeKeyResponse response;
+ UpgradeKeyResponse response(message_version_);
keymaster_error_t err = Send(KM_UPGRADE_KEY, request, &response);
if (err != KM_ERROR_OK) {
return err;
@@ -479,12 +480,12 @@
*out_params = {};
}
- BeginOperationRequest request;
+ BeginOperationRequest request(message_version_);
request.purpose = purpose;
request.SetKeyMaterial(*key);
request.additional_params.Reinitialize(*in_params);
- BeginOperationResponse response;
+ BeginOperationResponse response(message_version_);
keymaster_error_t err = Send(KM_BEGIN_OPERATION, request, &response);
if (err != KM_ERROR_OK) {
return err;
@@ -527,7 +528,7 @@
*output = {};
}
- UpdateOperationRequest request;
+ UpdateOperationRequest request(message_version_);
request.op_handle = operation_handle;
if (in_params) {
request.additional_params.Reinitialize(*in_params);
@@ -537,7 +538,7 @@
request.input.Reinitialize(input->data, std::min(input->data_length, max_input_size));
}
- UpdateOperationResponse response;
+ UpdateOperationResponse response(message_version_);
keymaster_error_t err = Send(KM_UPDATE_OPERATION, request, &response);
if (err != KM_ERROR_OK) {
return err;
@@ -576,7 +577,9 @@
return error_;
}
if (input && input->data_length > kMaximumFinishInputLength) {
- return KM_ERROR_INVALID_ARGUMENT;
+ ALOGE("%zu-byte input to finish; only %zu bytes allowed",
+ input->data_length, kMaximumFinishInputLength);
+ return KM_ERROR_INVALID_INPUT_LENGTH;
}
if (out_params) {
@@ -586,7 +589,7 @@
*output = {};
}
- FinishOperationRequest request;
+ FinishOperationRequest request(message_version_);
request.op_handle = operation_handle;
if (signature && signature->data && signature->data_length > 0) {
request.signature.Reinitialize(signature->data, signature->data_length);
@@ -598,7 +601,7 @@
request.additional_params.Reinitialize(*in_params);
}
- FinishOperationResponse response;
+ FinishOperationResponse response(message_version_);
keymaster_error_t err = Send(KM_FINISH_OPERATION, request, &response);
if (err != KM_ERROR_OK) {
return err;
@@ -631,9 +634,9 @@
return error_;
}
- AbortOperationRequest request;
+ AbortOperationRequest request(message_version_);
request.op_handle = operation_handle;
- AbortOperationResponse response;
+ AbortOperationResponse response(message_version_);
return Send(KM_ABORT_OPERATION, request, &response);
}
@@ -768,6 +771,9 @@
ALOGV("Sending %d byte request\n", (int)req.SerializedSize());
int rc = trusty_keymaster_call(command, send_buf, req_size, recv_buf, &rsp_size);
if (rc < 0) {
+ // Reset the connection on tipc error
+ trusty_keymaster_disconnect();
+ trusty_keymaster_connect();
ALOGE("tipc error: %d\n", rc);
// TODO(swillden): Distinguish permanent from transient errors and set error_ appropriately.
return translate_error(rc);
@@ -775,8 +781,7 @@
ALOGV("Received %d byte response\n", rsp_size);
}
- const keymaster_message* msg = (keymaster_message*)recv_buf;
- const uint8_t* p = msg->payload;
+ const uint8_t* p = recv_buf;
if (!rsp->Deserialize(&p, p + rsp_size)) {
ALOGE("Error deserializing response of size %d\n", (int)rsp_size);
return KM_ERROR_UNKNOWN_ERROR;
diff --git a/trusty/keymaster/trusty_keymaster_device_test.cpp b/trusty/keymaster/trusty_keymaster_device_test.cpp
index 3bb5430..e8f5c0b 100644
--- a/trusty/keymaster/trusty_keymaster_device_test.cpp
+++ b/trusty/keymaster/trusty_keymaster_device_test.cpp
@@ -16,8 +16,8 @@
#include <algorithm>
#include <fstream>
-#include <UniquePtr.h>
#include <gtest/gtest.h>
+#include <nativehelper/UniquePtr.h>
#include <openssl/engine.h>
#include <hardware/keymaster0.h>
diff --git a/trusty/keymaster/trusty_keymaster_ipc.cpp b/trusty/keymaster/trusty_keymaster_ipc.cpp
index cdc2778..54b251e 100644
--- a/trusty/keymaster/trusty_keymaster_ipc.cpp
+++ b/trusty/keymaster/trusty_keymaster_ipc.cpp
@@ -23,6 +23,8 @@
#include <string.h>
#include <unistd.h>
+#include <algorithm>
+
#include <log/log.h>
#include <trusty/tipc.h>
@@ -31,7 +33,7 @@
#define TRUSTY_DEVICE_NAME "/dev/trusty-ipc-dev0"
-static int handle_ = 0;
+static int handle_ = -1;
int trusty_keymaster_connect() {
int rc = tipc_connect(TRUSTY_DEVICE_NAME, KEYMASTER_PORT);
@@ -45,7 +47,7 @@
int trusty_keymaster_call(uint32_t cmd, void* in, uint32_t in_size, uint8_t* out,
uint32_t* out_size) {
- if (handle_ == 0) {
+ if (handle_ < 0) {
ALOGE("not connected\n");
return -EINVAL;
}
@@ -62,32 +64,43 @@
ALOGE("failed to send cmd (%d) to %s: %s\n", cmd, KEYMASTER_PORT, strerror(errno));
return -errno;
}
+ size_t out_max_size = *out_size;
+ *out_size = 0;
+ struct iovec iov[2];
+ struct keymaster_message header;
+ iov[0] = {.iov_base = &header, .iov_len = sizeof(struct keymaster_message)};
+ while (true) {
+ iov[1] = {
+ .iov_base = out + *out_size,
+ .iov_len = std::min<uint32_t>(KEYMASTER_MAX_BUFFER_LENGTH, out_max_size - *out_size)};
+ rc = readv(handle_, iov, 2);
+ if (rc < 0) {
+ ALOGE("failed to retrieve response for cmd (%d) to %s: %s\n", cmd, KEYMASTER_PORT,
+ strerror(errno));
+ return -errno;
+ }
- rc = read(handle_, out, *out_size);
- if (rc < 0) {
- ALOGE("failed to retrieve response for cmd (%d) to %s: %s\n", cmd, KEYMASTER_PORT,
- strerror(errno));
- return -errno;
+ if ((size_t)rc < sizeof(struct keymaster_message)) {
+ ALOGE("invalid response size (%d)\n", (int)rc);
+ return -EINVAL;
+ }
+
+ if ((cmd | KEYMASTER_RESP_BIT) != (header.cmd & ~(KEYMASTER_STOP_BIT))) {
+ ALOGE("invalid command (%d)", header.cmd);
+ return -EINVAL;
+ }
+ *out_size += ((size_t)rc - sizeof(struct keymaster_message));
+ if (header.cmd & KEYMASTER_STOP_BIT) {
+ break;
+ }
}
- if ((size_t)rc < sizeof(struct keymaster_message)) {
- ALOGE("invalid response size (%d)\n", (int)rc);
- return -EINVAL;
- }
-
- msg = (struct keymaster_message*)out;
-
- if ((cmd | KEYMASTER_RESP_BIT) != msg->cmd) {
- ALOGE("invalid command (%d)", msg->cmd);
- return -EINVAL;
- }
-
- *out_size = ((size_t)rc) - sizeof(struct keymaster_message);
return rc;
}
void trusty_keymaster_disconnect() {
- if (handle_ != 0) {
+ if (handle_ >= 0) {
tipc_close(handle_);
}
+ handle_ = -1;
}
diff --git a/trusty/storage/tests/Android.bp b/trusty/storage/tests/Android.bp
index 3eff3f2..1e4fced 100644
--- a/trusty/storage/tests/Android.bp
+++ b/trusty/storage/tests/Android.bp
@@ -21,7 +21,6 @@
"-g",
"-Wall",
"-Werror",
- "-std=gnu++11",
"-Wno-missing-field-initializers",
],