Merge "Credential FRP: keep gatekeeperd credentials after reset"
diff --git a/adb/Android.bp b/adb/Android.bp
new file mode 100644
index 0000000..41f7b89
--- /dev/null
+++ b/adb/Android.bp
@@ -0,0 +1,51 @@
+// 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.
+
+python_binary_host {
+ name: "adb_integration_test_adb",
+ main: "test_adb.py",
+ srcs: [
+ "test_adb.py",
+ ],
+ libs: [
+ "adb_py",
+ ],
+ version: {
+ py2: {
+ enabled: true,
+ },
+ py3: {
+ enabled: false,
+ },
+ },
+}
+
+python_binary_host {
+ name: "adb_integration_test_device",
+ main: "test_device.py",
+ srcs: [
+ "test_device.py",
+ ],
+ libs: [
+ "adb_py",
+ ],
+ version: {
+ py2: {
+ enabled: true,
+ },
+ py3: {
+ enabled: false,
+ },
+ },
+}
diff --git a/adb/Android.mk b/adb/Android.mk
index a05bb55..6ed01fa 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -5,23 +5,27 @@
LOCAL_PATH:= $(call my-dir)
+include $(LOCAL_PATH)/../platform_tools_tool_version.mk
+
adb_host_sanitize :=
adb_target_sanitize :=
-adb_version := $(shell git -C $(LOCAL_PATH) rev-parse --short=12 HEAD 2>/dev/null)-android
-
ADB_COMMON_CFLAGS := \
-Wall -Wextra -Werror \
-Wno-unused-parameter \
-Wno-missing-field-initializers \
-Wvla \
- -DADB_REVISION='"$(adb_version)"' \
+ -DADB_VERSION="\"$(tool_version)\"" \
+
+ADB_COMMON_posix_CFLAGS := \
+ -Wexit-time-destructors \
+ -Wthread-safety \
ADB_COMMON_linux_CFLAGS := \
- -Wexit-time-destructors \
+ $(ADB_COMMON_posix_CFLAGS) \
ADB_COMMON_darwin_CFLAGS := \
- -Wexit-time-destructors \
+ $(ADB_COMMON_posix_CFLAGS) \
# Define windows.h and tchar.h Unicode preprocessor symbols so that
# CreateFile(), _tfopen(), etc. map to versions that take wchar_t*, breaking the
@@ -81,12 +85,14 @@
LIBADB_darwin_SRC_FILES := \
sysdeps_unix.cpp \
+ sysdeps/posix/network.cpp \
client/usb_dispatch.cpp \
client/usb_libusb.cpp \
client/usb_osx.cpp \
LIBADB_linux_SRC_FILES := \
sysdeps_unix.cpp \
+ sysdeps/posix/network.cpp \
client/usb_dispatch.cpp \
client/usb_libusb.cpp \
client/usb_linux.cpp \
@@ -123,6 +129,7 @@
$(LIBADB_SRC_FILES) \
adbd_auth.cpp \
jdwp_service.cpp \
+ sysdeps/posix/network.cpp \
LOCAL_SANITIZE := $(adb_target_sanitize)
@@ -217,15 +224,15 @@
LOCAL_SRC_FILES_darwin := $(LIBADB_TEST_darwin_SRCS)
LOCAL_SRC_FILES_windows := $(LIBADB_TEST_windows_SRCS)
LOCAL_SANITIZE := $(adb_host_sanitize)
-LOCAL_SHARED_LIBRARIES := libbase
LOCAL_STATIC_LIBRARIES := \
libadb \
+ libbase \
libcrypto_utils \
libcrypto \
libcutils \
libdiagnose_usb \
libmdnssd \
- libgmock_host
+ libgmock_host \
LOCAL_STATIC_LIBRARIES_linux := libusb
LOCAL_STATIC_LIBRARIES_darwin := libusb
@@ -293,7 +300,7 @@
libcrypto \
libdiagnose_usb \
liblog \
- libmdnssd
+ libmdnssd \
# Don't use libcutils on Windows.
LOCAL_STATIC_LIBRARIES_darwin := libcutils
@@ -360,6 +367,7 @@
LOCAL_STRIP_MODULE := keep_symbols
LOCAL_STATIC_LIBRARIES := \
libadbd \
+ libavb_user \
libbase \
libqemu_pipe \
libbootloader_message \
@@ -380,4 +388,9 @@
include $(BUILD_EXECUTABLE)
+# adb integration test
+# =========================================================
+$(call dist-for-goals,sdk,$(ALL_MODULES.adb_integration_test_adb.BUILT))
+$(call dist-for-goals,sdk,$(ALL_MODULES.adb_integration_test_device.BUILT))
+
include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/adb/adb.cpp b/adb/adb.cpp
index cf6b359..a7706a0 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -31,6 +31,8 @@
#include <time.h>
#include <chrono>
+#include <condition_variable>
+#include <mutex>
#include <string>
#include <thread>
#include <vector>
@@ -48,6 +50,7 @@
#include "adb_io.h"
#include "adb_listeners.h"
#include "adb_utils.h"
+#include "sysdeps/chrono.h"
#include "transport.h"
#if !ADB_HOST
@@ -61,9 +64,9 @@
// Don't change the format of this --- it's parsed by ddmlib.
return android::base::StringPrintf(
"Android Debug Bridge version %d.%d.%d\n"
- "Revision %s\n"
+ "Version %s\n"
"Installed as %s\n",
- ADB_VERSION_MAJOR, ADB_VERSION_MINOR, ADB_SERVER_VERSION, ADB_REVISION,
+ ADB_VERSION_MAJOR, ADB_VERSION_MINOR, ADB_SERVER_VERSION, ADB_VERSION,
android::base::GetExecutablePath().c_str());
}
@@ -253,6 +256,19 @@
send_packet(cp, t);
}
+#if ADB_HOST
+
+void SendConnectOnHost(atransport* t) {
+ // Send an empty message before A_CNXN message. This is because the data toggle of the ep_out on
+ // host and ep_in on device may not be the same.
+ apacket* p = get_apacket();
+ CHECK(p);
+ send_packet(p, t);
+ send_connect(t);
+}
+
+#endif
+
// qual_overwrite is used to overwrite a qualifier string. dst is a
// pointer to a char pointer. It is assumed that if *dst is non-NULL, it
// was malloc'ed and needs to freed. *dst will be set to a dup of src.
@@ -299,29 +315,25 @@
const std::string& type = pieces[0];
if (type == "bootloader") {
D("setting connection_state to kCsBootloader");
- t->connection_state = kCsBootloader;
- update_transports();
+ t->SetConnectionState(kCsBootloader);
} else if (type == "device") {
D("setting connection_state to kCsDevice");
- t->connection_state = kCsDevice;
- update_transports();
+ t->SetConnectionState(kCsDevice);
} else if (type == "recovery") {
D("setting connection_state to kCsRecovery");
- t->connection_state = kCsRecovery;
- update_transports();
+ t->SetConnectionState(kCsRecovery);
} else if (type == "sideload") {
D("setting connection_state to kCsSideload");
- t->connection_state = kCsSideload;
- update_transports();
+ t->SetConnectionState(kCsSideload);
} else {
D("setting connection_state to kCsHost");
- t->connection_state = kCsHost;
+ t->SetConnectionState(kCsHost);
}
}
static void handle_new_connection(atransport* t, apacket* p) {
- if (t->connection_state != kCsOffline) {
- t->connection_state = kCsOffline;
+ if (t->GetConnectionState() != kCsOffline) {
+ t->SetConnectionState(kCsOffline);
handle_offline(t);
}
@@ -340,6 +352,8 @@
send_auth_request(t);
}
#endif
+
+ update_transports();
}
void handle_packet(apacket *p, atransport *t)
@@ -355,10 +369,10 @@
if (p->msg.arg0){
send_packet(p, t);
#if ADB_HOST
- send_connect(t);
+ SendConnectOnHost(t);
#endif
} else {
- t->connection_state = kCsOffline;
+ t->SetConnectionState(kCsOffline);
handle_offline(t);
send_packet(p, t);
}
@@ -372,7 +386,9 @@
switch (p->msg.arg0) {
#if ADB_HOST
case ADB_AUTH_TOKEN:
- t->connection_state = kCsUnauthorized;
+ if (t->GetConnectionState() == kCsOffline) {
+ t->SetConnectionState(kCsUnauthorized);
+ }
send_auth_response(p->data, p->msg.data_length, t);
break;
#else
@@ -391,7 +407,7 @@
break;
#endif
default:
- t->connection_state = kCsOffline;
+ t->SetConnectionState(kCsOffline);
handle_offline(t);
break;
}
@@ -507,8 +523,8 @@
if (!_try_make_handle_noninheritable(h)) {
// Show the handle value to give us a clue in case we have problems
// with pseudo-handle values.
- fprintf(stderr, "Cannot make handle 0x%p non-inheritable: %s\n",
- h, android::base::SystemErrorCodeToString(GetLastError()).c_str());
+ fprintf(stderr, "adb: cannot make handle 0x%p non-inheritable: %s\n", h,
+ android::base::SystemErrorCodeToString(GetLastError()).c_str());
return false;
}
@@ -523,7 +539,7 @@
HANDLE pipe_read_raw = NULL;
HANDLE pipe_write_raw = NULL;
if (!CreatePipe(&pipe_read_raw, &pipe_write_raw, sa, 0)) {
- fprintf(stderr, "Cannot create pipe: %s\n",
+ fprintf(stderr, "adb: CreatePipe failed: %s\n",
android::base::SystemErrorCodeToString(GetLastError()).c_str());
return false;
}
@@ -554,7 +570,8 @@
std::unique_ptr<FILE, decltype(&fclose)> stream(nullptr, fclose);
if (original_fd == -1) {
- fprintf(stderr, "Failed to get file descriptor for %s: %s\n", output_name, strerror(errno));
+ fprintf(stderr, "adb: failed to get file descriptor for %s: %s\n", output_name,
+ strerror(errno));
return EXIT_FAILURE;
}
@@ -566,7 +583,7 @@
// call this function if subprocesses may be started concurrently.
const int fd = dup(original_fd);
if (fd == -1) {
- fprintf(stderr, "Failed to duplicate file descriptor for %s: %s\n", output_name,
+ fprintf(stderr, "adb: failed to duplicate file descriptor for %s: %s\n", output_name,
strerror(errno));
return EXIT_FAILURE;
}
@@ -574,7 +591,7 @@
// Note that although we call fdopen() below with a binary flag, it may not adhere to that
// flag, so we have to set the mode manually.
if (_setmode(fd, _O_BINARY) == -1) {
- fprintf(stderr, "Failed to set binary mode for duplicate of %s: %s\n", output_name,
+ fprintf(stderr, "adb: failed to set binary mode for duplicate of %s: %s\n", output_name,
strerror(errno));
unix_close(fd);
return EXIT_FAILURE;
@@ -582,7 +599,7 @@
stream.reset(fdopen(fd, "wb"));
if (stream.get() == nullptr) {
- fprintf(stderr, "Failed to open duplicate stream for %s: %s\n", output_name,
+ fprintf(stderr, "adb: failed to open duplicate stream for %s: %s\n", output_name,
strerror(errno));
unix_close(fd);
return EXIT_FAILURE;
@@ -591,7 +608,7 @@
// Unbuffer the stream because it will be buffered by default and we want subprocess output
// to be shown immediately.
if (setvbuf(stream.get(), NULL, _IONBF, 0) == -1) {
- fprintf(stderr, "Failed to unbuffer %s: %s\n", output_name, strerror(errno));
+ fprintf(stderr, "adb: failed to unbuffer %s: %s\n", output_name, strerror(errno));
return EXIT_FAILURE;
}
@@ -608,7 +625,7 @@
if (err == ERROR_BROKEN_PIPE) {
return EXIT_SUCCESS;
} else {
- fprintf(stderr, "Failed to read from %s: %s\n", output_name,
+ fprintf(stderr, "adb: failed to read from %s: %s\n", output_name,
android::base::SystemErrorCodeToString(err).c_str());
return EXIT_FAILURE;
}
@@ -619,8 +636,8 @@
// fwrite() actually calls adb_fwrite() which can write UTF-8 to the console.
const size_t bytes_written = fwrite(buf, 1, bytes_read, stream.get());
if (bytes_written != bytes_read) {
- fprintf(stderr, "Only wrote %zu of %lu bytes to %s\n", bytes_written, bytes_read,
- output_name);
+ fprintf(stderr, "adb: error: only wrote %zu of %lu bytes to %s\n", bytes_written,
+ bytes_read, output_name);
return EXIT_FAILURE;
}
}
@@ -663,7 +680,7 @@
FILE_SHARE_READ | FILE_SHARE_WRITE, &sa, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL));
if (nul_read.get() == INVALID_HANDLE_VALUE) {
- fprintf(stderr, "Cannot open 'nul': %s\n",
+ fprintf(stderr, "adb: CreateFileW 'nul' failed: %s\n",
android::base::SystemErrorCodeToString(GetLastError()).c_str());
return -1;
}
@@ -725,8 +742,7 @@
// If this fires, either handle values are larger than 32-bits or else
// there is a bug in our casting.
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa384203%28v=vs.85%29.aspx
- fprintf(stderr, "Cannot fit pipe handle value into 32-bits: 0x%p\n",
- ack_write.get());
+ fprintf(stderr, "adb: cannot fit pipe handle value into 32-bits: 0x%p\n", ack_write.get());
return -1;
}
@@ -736,7 +752,7 @@
arraysize(program_path));
if ((module_result >= arraysize(program_path)) || (module_result == 0)) {
// String truncation or some other error.
- fprintf(stderr, "Cannot get executable path: %s\n",
+ fprintf(stderr, "adb: cannot get executable path: %s\n",
android::base::SystemErrorCodeToString(GetLastError()).c_str());
return -1;
}
@@ -761,7 +777,7 @@
NULL, /* use parent's starting directory */
&startup, /* startup info, i.e. std handles */
&pinfo )) {
- fprintf(stderr, "Cannot create process: %s\n",
+ fprintf(stderr, "adb: CreateProcessW failed: %s\n",
android::base::SystemErrorCodeToString(GetLastError()).c_str());
return -1;
}
@@ -791,7 +807,7 @@
_beginthreadex(NULL, 0, _redirect_stdout_thread, stdout_read.get(),
0, NULL)));
if (stdout_thread.get() == nullptr) {
- fprintf(stderr, "Cannot create thread: %s\n", strerror(errno));
+ fprintf(stderr, "adb: cannot create thread: %s\n", strerror(errno));
return -1;
}
stdout_read.release(); // Transfer ownership to new thread
@@ -800,7 +816,7 @@
_beginthreadex(NULL, 0, _redirect_stderr_thread, stderr_read.get(),
0, NULL)));
if (stderr_thread.get() == nullptr) {
- fprintf(stderr, "Cannot create thread: %s\n", strerror(errno));
+ fprintf(stderr, "adb: cannot create thread: %s\n", strerror(errno));
return -1;
}
stderr_read.release(); // Transfer ownership to new thread
@@ -845,22 +861,20 @@
if (wait_result == WAIT_TIMEOUT) {
// Threads did not finish after waiting a little while. Perhaps the
// server didn't close pipes, or it is hung.
- fprintf(stderr, "Timed-out waiting for threads to finish reading from "
- "ADB Server\n");
+ fprintf(stderr, "adb: timed out waiting for threads to finish reading from ADB server\n");
// Process handles are signaled when the process exits, so if we wait
// on the handle for 0 seconds and it returns 'timeout', that means that
// the process is still running.
if (WaitForSingleObject(process_handle.get(), 0) == WAIT_TIMEOUT) {
// We could TerminateProcess(), but that seems somewhat presumptive.
- fprintf(stderr, "ADB Server is running: process id %lu\n",
- pinfo.dwProcessId);
+ fprintf(stderr, "adb: server is running with process id %lu\n", pinfo.dwProcessId);
}
return -1;
}
if (wait_result != WAIT_OBJECT_0) {
- fprintf(stderr, "Unexpected result waiting for threads: %lu: %s\n",
- wait_result, android::base::SystemErrorCodeToString(GetLastError()).c_str());
+ fprintf(stderr, "adb: unexpected result waiting for threads: %lu: %s\n", wait_result,
+ android::base::SystemErrorCodeToString(GetLastError()).c_str());
return -1;
}
@@ -894,7 +908,7 @@
int result = execl(path.c_str(), "adb", "-L", socket_spec.c_str(), "fork-server", "server",
"--reply-fd", reply_fd, NULL);
// this should not return
- fprintf(stderr, "OOPS! execl returned %d, errno: %d\n", result, errno);
+ fprintf(stderr, "adb: execl returned %d: %s\n", result, strerror(errno));
} else {
// parent side of the fork
@@ -1034,26 +1048,21 @@
SendProtocolString(fd, s);
return 0;
}
-#endif
int handle_host_request(const char* service, TransportType type,
const char* serial, int reply_fd, asocket* s) {
if (strcmp(service, "kill") == 0) {
fprintf(stderr, "adb server killed by remote request\n");
fflush(stdout);
+
+ // Send a reply even though we don't read it anymore, so that old versions
+ // of adb that do read it don't spew error messages.
SendOkay(reply_fd);
- // On Windows, if the process exits with open sockets that
- // shutdown(SD_SEND) has not been called on, TCP RST segments will be
- // sent to the peers which will cause their next recv() to error-out
- // with WSAECONNRESET. In the case of this code, that means the client
- // may not read the OKAY sent above.
- adb_shutdown(reply_fd);
-
+ // Rely on process exit to close the socket for us.
android::base::quick_exit(0);
}
-#if ADB_HOST
// "transport:" is used for switching transport with a specified serial number
// "transport-usb:" is used for switching transport to the only USB transport
// "transport-local:" is used for switching transport to the only local transport
@@ -1098,16 +1107,10 @@
if (!strcmp(service, "reconnect-offline")) {
std::string response;
close_usb_devices([&response](const atransport* transport) {
- switch (transport->connection_state) {
+ switch (transport->GetConnectionState()) {
case kCsOffline:
case kCsUnauthorized:
- response += "reconnecting ";
- if (transport->serial) {
- response += transport->serial;
- } else {
- response += "<unknown>";
- }
- response += "\n";
+ response += "reconnecting " + transport->serial_name() + "\n";
return true;
default:
return false;
@@ -1131,17 +1134,16 @@
return 0;
}
-#if ADB_HOST
if (!strcmp(service, "host-features")) {
FeatureSet features = supported_features();
// Abuse features to report libusb status.
if (should_use_libusb()) {
features.insert(kFeatureLibusb);
}
+ features.insert(kFeaturePushSync);
SendOkay(reply_fd, FeatureSetToString(features));
return 0;
}
-#endif
// remove TCP transport
if (!strncmp(service, "disconnect:", 11)) {
@@ -1211,15 +1213,59 @@
}
if (!strcmp(service, "reconnect")) {
- if (s->transport != nullptr) {
- kick_transport(s->transport);
+ std::string response;
+ atransport* t = acquire_one_transport(type, serial, nullptr, &response, true);
+ if (t != nullptr) {
+ kick_transport(t);
+ response =
+ "reconnecting " + t->serial_name() + " [" + t->connection_state_name() + "]\n";
}
- return SendOkay(reply_fd, "done");
+ return SendOkay(reply_fd, response);
}
-#endif // ADB_HOST
int ret = handle_forward_request(service, type, serial, reply_fd);
if (ret >= 0)
return ret - 1;
return -1;
}
+
+static auto& init_mutex = *new std::mutex();
+static auto& init_cv = *new std::condition_variable();
+static bool device_scan_complete = false;
+static bool transports_ready = false;
+
+void update_transport_status() {
+ bool result = iterate_transports([](const atransport* t) {
+ if (t->type == kTransportUsb && t->online != 1) {
+ return false;
+ }
+ return true;
+ });
+
+ bool ready;
+ {
+ std::lock_guard<std::mutex> lock(init_mutex);
+ transports_ready = result;
+ ready = transports_ready && device_scan_complete;
+ }
+
+ if (ready) {
+ init_cv.notify_all();
+ }
+}
+
+void adb_notify_device_scan_complete() {
+ {
+ std::lock_guard<std::mutex> lock(init_mutex);
+ device_scan_complete = true;
+ }
+
+ update_transport_status();
+}
+
+void adb_wait_for_device_initialization() {
+ std::unique_lock<std::mutex> lock(init_mutex);
+ init_cv.wait_for(lock, 3s, []() { return device_scan_complete && transports_ready; });
+}
+
+#endif // ADB_HOST
diff --git a/adb/adb.h b/adb/adb.h
index aea5fb8..d6b2b81 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -139,7 +139,7 @@
int get_available_local_transport_index();
#endif
int init_socket_transport(atransport *t, int s, int port, int local);
-void init_usb_transport(atransport *t, usb_handle *usb, ConnectionState state);
+void init_usb_transport(atransport* t, usb_handle* usb);
std::string getEmulatorSerialString(int console_port);
#if ADB_HOST
@@ -222,7 +222,24 @@
void handle_offline(atransport *t);
void send_connect(atransport *t);
+#if ADB_HOST
+void SendConnectOnHost(atransport* t);
+#endif
void parse_banner(const std::string&, atransport* t);
+// On startup, the adb server needs to wait until all of the connected devices are ready.
+// To do this, we need to know when the scan has identified all of the potential new transports, and
+// when each transport becomes ready.
+// TODO: Do this for mDNS as well, instead of just USB?
+
+// We've found all of the transports we potentially care about.
+void adb_notify_device_scan_complete();
+
+// One or more transports have changed status, check to see if we're ready.
+void update_transport_status();
+
+// Wait until device scan has completed and every transport is ready, or a timeout elapses.
+void adb_wait_for_device_initialization();
+
#endif
diff --git a/adb/adb_auth_host.cpp b/adb/adb_auth_host.cpp
index c3f1fe0..365bf77 100644
--- a/adb/adb_auth_host.cpp
+++ b/adb/adb_auth_host.cpp
@@ -82,16 +82,17 @@
return false;
}
- size_t base64_key_length;
- if (!EVP_EncodedLength(&base64_key_length, sizeof(binary_key_data))) {
+ size_t expected_length;
+ if (!EVP_EncodedLength(&expected_length, sizeof(binary_key_data))) {
LOG(ERROR) << "Public key too large to base64 encode";
return false;
}
std::string content;
- content.resize(base64_key_length);
- base64_key_length = EVP_EncodeBlock(reinterpret_cast<uint8_t*>(&content[0]), binary_key_data,
- sizeof(binary_key_data));
+ content.resize(expected_length);
+ size_t actual_length = EVP_EncodeBlock(reinterpret_cast<uint8_t*>(&content[0]), binary_key_data,
+ sizeof(binary_key_data));
+ content.resize(actual_length);
content += get_user_info();
diff --git a/adb/adb_client.cpp b/adb/adb_client.cpp
index ef52189..f5d0f02 100644
--- a/adb/adb_client.cpp
+++ b/adb/adb_client.cpp
@@ -28,12 +28,15 @@
#include <sys/stat.h>
#include <sys/types.h>
+#include <condition_variable>
+#include <mutex>
#include <string>
#include <thread>
#include <vector>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
+#include <android-base/thread_annotations.h>
#include <cutils/sockets.h>
#include "adb_io.h"
@@ -120,7 +123,7 @@
return false;
}
-int _adb_connect(const std::string& service, std::string* error) {
+static int _adb_connect(const std::string& service, std::string* error) {
D("_adb_connect: %s", service.c_str());
if (service.empty() || service.size() > MAX_PAYLOAD_V1) {
*error = android::base::StringPrintf("bad service name length (%zd)",
@@ -136,8 +139,7 @@
return -2;
}
- if ((memcmp(&service[0],"host",4) != 0 || service == "host:reconnect") &&
- switch_socket_transport(fd, error)) {
+ if (memcmp(&service[0], "host", 4) != 0 && switch_socket_transport(fd, error)) {
return -1;
}
@@ -147,42 +149,58 @@
return -1;
}
- if (service != "reconnect") {
- if (!adb_status(fd, error)) {
- adb_close(fd);
- return -1;
- }
+ if (!adb_status(fd, error)) {
+ adb_close(fd);
+ return -1;
}
D("_adb_connect: return fd %d", fd);
return fd;
}
+bool adb_kill_server() {
+ D("adb_kill_server");
+ std::string reason;
+ int fd = socket_spec_connect(__adb_server_socket_spec, &reason);
+ if (fd < 0) {
+ fprintf(stderr, "cannot connect to daemon at %s: %s\n", __adb_server_socket_spec,
+ reason.c_str());
+ return true;
+ }
+
+ if (!SendProtocolString(fd, "host:kill")) {
+ fprintf(stderr, "error: write failure during connection: %s\n", strerror(errno));
+ return false;
+ }
+
+ ReadOrderlyShutdown(fd);
+ return true;
+}
+
int adb_connect(const std::string& service, std::string* error) {
// first query the adb server's version
int fd = _adb_connect("host:version", error);
D("adb_connect: service %s", service.c_str());
if (fd == -2 && !is_local_socket_spec(__adb_server_socket_spec)) {
- fprintf(stderr,"** Cannot start server on remote host\n");
+ fprintf(stderr, "* cannot start server on remote host\n");
// error is the original network connection error
return fd;
} else if (fd == -2) {
- fprintf(stdout, "* daemon not running. starting it now at %s *\n", __adb_server_socket_spec);
+ fprintf(stderr, "* daemon not running; starting now at %s\n", __adb_server_socket_spec);
start_server:
if (launch_server(__adb_server_socket_spec)) {
- fprintf(stderr,"* failed to start daemon *\n");
+ fprintf(stderr, "* failed to start daemon\n");
// launch_server() has already printed detailed error info, so just
// return a generic error string about the overall adb_connect()
// that the caller requested.
*error = "cannot connect to daemon";
return -1;
} else {
- fprintf(stdout,"* daemon started successfully *\n");
+ fprintf(stderr, "* daemon started successfully\n");
}
- // Give the server some time to start properly and detect devices.
- std::this_thread::sleep_for(3s);
- // fall through to _adb_connect
+ // The server will wait until it detects all of its connected devices before acking.
+ // Fall through to _adb_connect.
} else {
// If a server is already running, check its version matches.
int version = ADB_SERVER_VERSION - 1;
@@ -213,20 +231,9 @@
}
if (version != ADB_SERVER_VERSION) {
- printf("adb server version (%d) doesn't match this client (%d); killing...\n",
- version, ADB_SERVER_VERSION);
- fd = _adb_connect("host:kill", error);
- if (fd >= 0) {
- ReadOrderlyShutdown(fd);
- adb_close(fd);
- } else {
- // If we couldn't connect to the server or had some other error,
- // report it, but still try to start the server.
- fprintf(stderr, "error: %s\n", error->c_str());
- }
-
- /* XXX can we better detect its death? */
- std::this_thread::sleep_for(2s);
+ fprintf(stderr, "adb server version (%d) doesn't match this client (%d); killing...\n",
+ version, ADB_SERVER_VERSION);
+ adb_kill_server();
goto start_server;
}
}
@@ -240,7 +247,7 @@
if (fd == -1) {
D("_adb_connect error: %s", error->c_str());
} else if(fd == -2) {
- fprintf(stderr,"** daemon still not running\n");
+ fprintf(stderr, "* daemon still not running\n");
}
D("adb_connect: return fd %d", fd);
diff --git a/adb/adb_client.h b/adb/adb_client.h
index d07c1e9..fabec00 100644
--- a/adb/adb_client.h
+++ b/adb/adb_client.h
@@ -26,7 +26,9 @@
// Connect to adb, connect to the named service, and return a valid fd for
// interacting with that service upon success or a negative number on failure.
int adb_connect(const std::string& service, std::string* _Nonnull error);
-int _adb_connect(const std::string& service, std::string* _Nonnull error);
+
+// Kill the currently running adb server, if it exists.
+bool adb_kill_server();
// Connect to adb, connect to the named service, returns true if the connection
// succeeded AND the service returned OKAY. Outputs any returned error otherwise.
diff --git a/adb/adb_listeners.cpp b/adb/adb_listeners.cpp
index 18b1492..30cb29b 100644
--- a/adb/adb_listeners.cpp
+++ b/adb/adb_listeners.cpp
@@ -19,8 +19,12 @@
#include <stdio.h>
#include <stdlib.h>
+#include <algorithm>
+#include <list>
+
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
+#include <android-base/thread_annotations.h>
#include <cutils/sockets.h>
#include "socket_spec.h"
@@ -64,8 +68,9 @@
// listener_list retains ownership of all created alistener objects. Removing an alistener from
// this list will cause it to be deleted.
+static auto& listener_list_mutex = *new std::mutex();
typedef std::list<std::unique_ptr<alistener>> ListenerList;
-static ListenerList& listener_list = *new ListenerList();
+static ListenerList& listener_list GUARDED_BY(listener_list_mutex) = *new ListenerList();
static void ss_listener_event_func(int _fd, unsigned ev, void *_l) {
if (ev & FDE_READ) {
@@ -108,7 +113,8 @@
}
// Called as a transport disconnect function. |arg| is the raw alistener*.
-static void listener_disconnect(void* arg, atransport*) {
+static void listener_disconnect(void* arg, atransport*) EXCLUDES(listener_list_mutex) {
+ std::lock_guard<std::mutex> lock(listener_list_mutex);
for (auto iter = listener_list.begin(); iter != listener_list.end(); ++iter) {
if (iter->get() == arg) {
(*iter)->transport = nullptr;
@@ -119,7 +125,8 @@
}
// Write the list of current listeners (network redirections) into a string.
-std::string format_listeners() {
+std::string format_listeners() EXCLUDES(listener_list_mutex) {
+ std::lock_guard<std::mutex> lock(listener_list_mutex);
std::string result;
for (auto& l : listener_list) {
// Ignore special listeners like those for *smartsocket*
@@ -135,7 +142,9 @@
return result;
}
-InstallStatus remove_listener(const char* local_name, atransport* transport) {
+InstallStatus remove_listener(const char* local_name, atransport* transport)
+ EXCLUDES(listener_list_mutex) {
+ std::lock_guard<std::mutex> lock(listener_list_mutex);
for (auto iter = listener_list.begin(); iter != listener_list.end(); ++iter) {
if (local_name == (*iter)->local_name) {
listener_list.erase(iter);
@@ -145,7 +154,8 @@
return INSTALL_STATUS_LISTENER_NOT_FOUND;
}
-void remove_all_listeners() {
+void remove_all_listeners() EXCLUDES(listener_list_mutex) {
+ std::lock_guard<std::mutex> lock(listener_list_mutex);
auto iter = listener_list.begin();
while (iter != listener_list.end()) {
// Never remove smart sockets.
@@ -157,9 +167,18 @@
}
}
+void close_smartsockets() EXCLUDES(listener_list_mutex) {
+ std::lock_guard<std::mutex> lock(listener_list_mutex);
+ auto pred = [](const std::unique_ptr<alistener>& listener) {
+ return listener->local_name == "*smartsocket*";
+ };
+ listener_list.erase(std::remove_if(listener_list.begin(), listener_list.end(), pred));
+}
+
InstallStatus install_listener(const std::string& local_name, const char* connect_to,
atransport* transport, int no_rebind, int* resolved_tcp_port,
- std::string* error) {
+ std::string* error) EXCLUDES(listener_list_mutex) {
+ std::lock_guard<std::mutex> lock(listener_list_mutex);
for (auto& l : listener_list) {
if (local_name == l->local_name) {
// Can't repurpose a smartsocket.
@@ -212,7 +231,7 @@
if (transport) {
listener->disconnect.opaque = listener.get();
- listener->disconnect.func = listener_disconnect;
+ listener->disconnect.func = listener_disconnect;
transport->AddDisconnect(&listener->disconnect);
}
diff --git a/adb/adb_listeners.h b/adb/adb_listeners.h
index 8eba00a..70a2ee1 100644
--- a/adb/adb_listeners.h
+++ b/adb/adb_listeners.h
@@ -41,4 +41,6 @@
InstallStatus remove_listener(const char* local_name, atransport* transport);
void remove_all_listeners(void);
+void close_smartsockets();
+
#endif /* __ADB_LISTENERS_H */
diff --git a/adb/adb_trace.cpp b/adb/adb_trace.cpp
index c369d60..eac923d 100644
--- a/adb/adb_trace.cpp
+++ b/adb/adb_trace.cpp
@@ -155,7 +155,7 @@
}
#endif
-#if !defined(_WIN32)
+#if ADB_HOST && !defined(_WIN32)
// adb historically ignored $ANDROID_LOG_TAGS but passed it through to logcat.
// If set, move it out of the way so that libbase logging doesn't try to parse it.
std::string log_tags;
@@ -168,7 +168,7 @@
android::base::InitLogging(argv, &AdbLogger);
-#if !defined(_WIN32)
+#if ADB_HOST && !defined(_WIN32)
// Put $ANDROID_LOG_TAGS back so we can pass it to logcat.
if (!log_tags.empty()) setenv("ANDROID_LOG_TAGS", log_tags.c_str(), 1);
#endif
diff --git a/adb/adb_trace.h b/adb/adb_trace.h
index aaffa29..fc6560c 100644
--- a/adb/adb_trace.h
+++ b/adb/adb_trace.h
@@ -58,6 +58,9 @@
void adb_trace_init(char**);
void adb_trace_enable(AdbTrace trace_tag);
+// Include <atomic> before stdatomic.h (introduced in cutils/trace.h) to avoid compile error.
+#include <atomic>
+
#define ATRACE_TAG ATRACE_TAG_ADB
#include <cutils/trace.h>
#include <utils/Trace.h>
diff --git a/adb/adb_utils.cpp b/adb/adb_utils.cpp
index 31d3dc6..6f2403d 100644
--- a/adb/adb_utils.cpp
+++ b/adb/adb_utils.cpp
@@ -267,8 +267,8 @@
adb_close(fd);
}
-int usage(const char* fmt, ...) {
- fprintf(stderr, "adb: ");
+int syntax_error(const char* fmt, ...) {
+ fprintf(stderr, "adb: usage: ");
va_list ap;
va_start(ap, fmt);
diff --git a/adb/adb_utils.h b/adb/adb_utils.h
index e0ad103..c1d5549 100644
--- a/adb/adb_utils.h
+++ b/adb/adb_utils.h
@@ -21,7 +21,7 @@
#include <android-base/macros.h>
-int usage(const char*, ...);
+int syntax_error(const char*, ...);
void close_stdin();
diff --git a/adb/bugreport.cpp b/adb/bugreport.cpp
index 9dc9811..372a3b4 100644
--- a/adb/bugreport.cpp
+++ b/adb/bugreport.cpp
@@ -48,7 +48,7 @@
show_progress_(show_progress),
status_(0),
line_(),
- last_progress_(0) {
+ last_progress_percentage_(0) {
SetLineMessage("generating");
}
@@ -137,7 +137,7 @@
SetSrcFile(&line[strlen(BUGZ_OK_PREFIX)]);
} else if (android::base::StartsWith(line, BUGZ_FAIL_PREFIX)) {
const char* error_message = &line[strlen(BUGZ_FAIL_PREFIX)];
- fprintf(stderr, "Device failed to take a zipped bugreport: %s\n", error_message);
+ fprintf(stderr, "adb: device failed to take a zipped bugreport: %s\n", error_message);
status_ = -1;
} else if (show_progress_ && android::base::StartsWith(line, BUGZ_PROGRESS_PREFIX)) {
// progress_line should have the following format:
@@ -147,13 +147,14 @@
size_t idx1 = line.rfind(BUGZ_PROGRESS_PREFIX) + strlen(BUGZ_PROGRESS_PREFIX);
size_t idx2 = line.rfind(BUGZ_PROGRESS_SEPARATOR);
int progress = std::stoi(line.substr(idx1, (idx2 - idx1)));
- if (progress <= last_progress_) {
+ int total = std::stoi(line.substr(idx2 + 1));
+ int progress_percentage = (progress * 100 / total);
+ if (progress_percentage != 0 && progress_percentage <= last_progress_percentage_) {
// Ignore.
return;
}
- last_progress_ = progress;
- int total = std::stoi(line.substr(idx2 + 1));
- br_->UpdateProgress(line_message_, progress, total);
+ last_progress_percentage_ = progress_percentage;
+ br_->UpdateProgress(line_message_, progress_percentage);
} else {
invalid_lines_.push_back(line);
}
@@ -189,13 +190,13 @@
// Last displayed progress.
// Since dumpstate progress can recede, only forward progress should be displayed
- int last_progress_;
+ int last_progress_percentage_;
DISALLOW_COPY_AND_ASSIGN(BugreportStandardStreamsCallback);
};
int Bugreport::DoIt(TransportType transport_type, const char* serial, int argc, const char** argv) {
- if (argc > 2) return usage("usage: adb bugreport [PATH]");
+ if (argc > 2) return syntax_error("adb bugreport [PATH]");
// Gets bugreportz version.
std::string bugz_stdout, bugz_stderr;
@@ -267,8 +268,7 @@
return SendShellCommand(transport_type, serial, bugz_command, false, &bugz_callback);
}
-void Bugreport::UpdateProgress(const std::string& message, int progress, int total) {
- int progress_percentage = (progress * 100 / total);
+void Bugreport::UpdateProgress(const std::string& message, int progress_percentage) {
line_printer_.Print(
android::base::StringPrintf("[%3d%%] %s", progress_percentage, message.c_str()),
LinePrinter::INFO);
diff --git a/adb/bugreport.h b/adb/bugreport.h
index ee99cbc..d9a4468 100644
--- a/adb/bugreport.h
+++ b/adb/bugreport.h
@@ -43,7 +43,7 @@
const char* name);
private:
- virtual void UpdateProgress(const std::string& file_name, int progress, int total);
+ virtual void UpdateProgress(const std::string& file_name, int progress_percentage);
LinePrinter line_printer_;
DISALLOW_COPY_AND_ASSIGN(Bugreport);
};
diff --git a/adb/bugreport_test.cpp b/adb/bugreport_test.cpp
index b500c49..d3787b4 100644
--- a/adb/bugreport_test.cpp
+++ b/adb/bugreport_test.cpp
@@ -123,7 +123,7 @@
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_METHOD3(UpdateProgress, void(const std::string&, int, int));
+ MOCK_METHOD2(UpdateProgress, void(const std::string&, int));
};
class BugreportTest : public ::testing::Test {
@@ -142,8 +142,8 @@
WithArg<4>(ReturnCallbackDone(0))));
}
- void ExpectProgress(int progress, int total, const std::string& file = "file.zip") {
- EXPECT_CALL(br_, UpdateProgress(StrEq("generating " + file), progress, total));
+ void ExpectProgress(int progress_percentage, const std::string& file = "file.zip") {
+ EXPECT_CALL(br_, UpdateProgress(StrEq("generating " + file), progress_percentage));
}
BugreportMock br_;
@@ -200,7 +200,7 @@
ExpectBugreportzVersion("1.1");
std::string dest_file =
android::base::StringPrintf("%s%cda_bugreport.zip", cwd_.c_str(), OS_PATH_SEPARATOR);
- ExpectProgress(50, 100, "da_bugreport.zip");
+ 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")),
@@ -247,10 +247,10 @@
// Tests 'adb bugreport file.zip' when it succeeds and displays progress.
TEST_F(BugreportTest, OkProgress) {
ExpectBugreportzVersion("1.1");
- ExpectProgress(1, 100);
- ExpectProgress(10, 100);
- ExpectProgress(50, 100);
- ExpectProgress(99, 100);
+ ExpectProgress(1);
+ ExpectProgress(10);
+ ExpectProgress(50);
+ ExpectProgress(99);
// clang-format off
EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
// NOTE: DoAll accepts at most 10 arguments, and we're almost reached that limit...
@@ -283,21 +283,46 @@
// Tests 'adb bugreport file.zip' when it succeeds and displays progress, even if progress recedes.
TEST_F(BugreportTest, OkProgressAlwaysForward) {
ExpectBugreportzVersion("1.1");
- ExpectProgress(1, 100);
- ExpectProgress(50, 100);
- ExpectProgress(75, 100);
+ ExpectProgress(1);
+ ExpectProgress(50);
+ ExpectProgress(75);
// clang-format off
EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "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")),
- WithArg<4>(WriteOnStdout("PROGRESS:50/100\n")),
- // 25 should be ignored becaused it receded.
- WithArg<4>(WriteOnStdout("PROGRESS:25/100\n")),
- WithArg<4>(WriteOnStdout("PROGRESS:75/100\n")),
- // 75 should be ignored becaused it didn't change.
- WithArg<4>(WriteOnStdout("PROGRESS:75/100\n")),
+ WithArg<4>(WriteOnStdout("PROGRESS:1/100\n")), // 1%
+ WithArg<4>(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%
+ // 75% should be ignored becaused it didn't change.
+ WithArg<4>(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())));
+ // clang-format on
+ EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
+ true, StrEq("pulling file.zip")))
+ .WillOnce(Return(true));
+
+ const char* args[] = {"bugreport", "file.zip"};
+ ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+}
+
+// Tests 'adb bugreport file.zip' when it succeeds and displays the initial progress of 0%
+TEST_F(BugreportTest, OkProgressZeroPercentIsNotIgnored) {
+ ExpectBugreportzVersion("1.1");
+ ExpectProgress(0);
+ ExpectProgress(1);
+ // clang-format off
+ EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "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())));
// clang-format on
diff --git a/adb/client/main.cpp b/adb/client/main.cpp
index 606203c..62798cd 100644
--- a/adb/client/main.cpp
+++ b/adb/client/main.cpp
@@ -92,6 +92,16 @@
}
#endif
+void adb_server_cleanup() {
+ // Upon exit, we want to clean up in the following order:
+ // 1. close_smartsockets, so that we don't get any new clients
+ // 2. kick_all_transports, to avoid writing only part of a packet to a transport.
+ // 3. usb_cleanup, to tear down the USB stack.
+ close_smartsockets();
+ kick_all_transports();
+ usb_cleanup();
+}
+
int adb_server_main(int is_daemon, const std::string& socket_spec, int ack_reply_fd) {
#if defined(_WIN32)
// adb start-server starts us up with stdout and stderr hooked up to
@@ -111,12 +121,18 @@
SetConsoleCtrlHandler(ctrlc_handler, TRUE);
#else
signal(SIGINT, [](int) {
- android::base::quick_exit(0);
+ fdevent_run_on_main_thread([]() { android::base::quick_exit(0); });
});
#endif
- init_transport_registration();
+ if (is_daemon) {
+ close_stdin();
+ setup_daemon_logging();
+ }
+ android::base::at_quick_exit(adb_server_cleanup);
+
+ init_transport_registration();
init_mdns_transport_discovery();
usb_init();
@@ -137,11 +153,6 @@
std::this_thread::sleep_for(100ms);
}
- if (is_daemon) {
- close_stdin();
- setup_daemon_logging();
- }
-
adb_auth_init();
if (is_daemon) {
@@ -156,33 +167,38 @@
}
#endif
- // Inform our parent that we are up and running.
+ // Wait for the USB scan to complete before notifying the parent that we're up.
+ // We need to perform this in a thread, because we would otherwise block the event loop.
+ std::thread notify_thread([ack_reply_fd]() {
+ adb_wait_for_device_initialization();
- // Any error output written to stderr now goes to adb.log. We could
- // keep around a copy of the stderr fd and use that to write any errors
- // encountered by the following code, but that is probably overkill.
+ // Any error output written to stderr now goes to adb.log. We could
+ // keep around a copy of the stderr fd and use that to write any errors
+ // encountered by the following code, but that is probably overkill.
#if defined(_WIN32)
- const HANDLE ack_reply_handle = cast_int_to_handle(ack_reply_fd);
- const CHAR ack[] = "OK\n";
- const DWORD bytes_to_write = arraysize(ack) - 1;
- DWORD written = 0;
- if (!WriteFile(ack_reply_handle, ack, bytes_to_write, &written, NULL)) {
- fatal("adb: cannot write ACK to handle 0x%p: %s", ack_reply_handle,
- android::base::SystemErrorCodeToString(GetLastError()).c_str());
- }
- if (written != bytes_to_write) {
- fatal("adb: cannot write %lu bytes of ACK: only wrote %lu bytes",
- bytes_to_write, written);
- }
- CloseHandle(ack_reply_handle);
+ const HANDLE ack_reply_handle = cast_int_to_handle(ack_reply_fd);
+ const CHAR ack[] = "OK\n";
+ const DWORD bytes_to_write = arraysize(ack) - 1;
+ DWORD written = 0;
+ if (!WriteFile(ack_reply_handle, ack, bytes_to_write, &written, NULL)) {
+ fatal("adb: cannot write ACK to handle 0x%p: %s", ack_reply_handle,
+ android::base::SystemErrorCodeToString(GetLastError()).c_str());
+ }
+ if (written != bytes_to_write) {
+ fatal("adb: cannot write %lu bytes of ACK: only wrote %lu bytes", bytes_to_write,
+ written);
+ }
+ CloseHandle(ack_reply_handle);
#else
- // TODO(danalbert): Can't use SendOkay because we're sending "OK\n", not
- // "OKAY".
- if (!android::base::WriteStringToFd("OK\n", ack_reply_fd)) {
- fatal_errno("error writing ACK to fd %d", ack_reply_fd);
- }
- unix_close(ack_reply_fd);
+ // TODO(danalbert): Can't use SendOkay because we're sending "OK\n", not
+ // "OKAY".
+ if (!android::base::WriteStringToFd("OK\n", ack_reply_fd)) {
+ fatal_errno("error writing ACK to fd %d", ack_reply_fd);
+ }
+ unix_close(ack_reply_fd);
#endif
+ });
+ notify_thread.detach();
}
D("Event loop starting");
diff --git a/adb/client/usb_dispatch.cpp b/adb/client/usb_dispatch.cpp
index bfc8e16..ce57731 100644
--- a/adb/client/usb_dispatch.cpp
+++ b/adb/client/usb_dispatch.cpp
@@ -27,6 +27,14 @@
}
}
+void usb_cleanup() {
+ if (should_use_libusb()) {
+ libusb::usb_cleanup();
+ } else {
+ native::usb_cleanup();
+ }
+}
+
int usb_write(usb_handle* h, const void* data, int len) {
return should_use_libusb()
? libusb::usb_write(reinterpret_cast<libusb::usb_handle*>(h), data, len)
@@ -48,3 +56,9 @@
should_use_libusb() ? libusb::usb_kick(reinterpret_cast<libusb::usb_handle*>(h))
: native::usb_kick(reinterpret_cast<native::usb_handle*>(h));
}
+
+size_t usb_get_max_packet_size(usb_handle* h) {
+ return should_use_libusb()
+ ? libusb::usb_get_max_packet_size(reinterpret_cast<libusb::usb_handle*>(h))
+ : native::usb_get_max_packet_size(reinterpret_cast<native::usb_handle*>(h));
+}
diff --git a/adb/client/usb_libusb.cpp b/adb/client/usb_libusb.cpp
index c48a251..9477c56 100644
--- a/adb/client/usb_libusb.cpp
+++ b/adb/client/usb_libusb.cpp
@@ -62,12 +62,11 @@
using unique_device_handle = std::unique_ptr<libusb_device_handle, DeviceHandleDeleter>;
struct transfer_info {
- transfer_info(const char* name, uint16_t zero_mask) :
- name(name),
- transfer(libusb_alloc_transfer(0)),
- zero_mask(zero_mask)
- {
- }
+ transfer_info(const char* name, uint16_t zero_mask, bool is_bulk_out)
+ : name(name),
+ transfer(libusb_alloc_transfer(0)),
+ is_bulk_out(is_bulk_out),
+ zero_mask(zero_mask) {}
~transfer_info() {
libusb_free_transfer(transfer);
@@ -75,6 +74,7 @@
const char* name;
libusb_transfer* transfer;
+ bool is_bulk_out;
bool transfer_complete;
std::condition_variable cv;
std::mutex mutex;
@@ -91,17 +91,17 @@
struct usb_handle : public ::usb_handle {
usb_handle(const std::string& device_address, const std::string& serial,
unique_device_handle&& device_handle, uint8_t interface, uint8_t bulk_in,
- uint8_t bulk_out, size_t zero_mask)
+ uint8_t bulk_out, size_t zero_mask, size_t max_packet_size)
: device_address(device_address),
serial(serial),
closing(false),
device_handle(device_handle.release()),
- read("read", zero_mask),
- write("write", zero_mask),
+ read("read", zero_mask, false),
+ write("write", zero_mask, true),
interface(interface),
bulk_in(bulk_in),
- bulk_out(bulk_out) {
- }
+ bulk_out(bulk_out),
+ max_packet_size(max_packet_size) {}
~usb_handle() {
Close();
@@ -144,19 +144,47 @@
uint8_t interface;
uint8_t bulk_in;
uint8_t bulk_out;
+
+ size_t max_packet_size;
};
static auto& usb_handles = *new std::unordered_map<std::string, std::unique_ptr<usb_handle>>();
static auto& usb_handles_mutex = *new std::mutex();
-static std::thread* device_poll_thread = nullptr;
-static std::atomic<bool> terminate_device_poll_thread(false);
+static libusb_hotplug_callback_handle hotplug_handle;
static std::string get_device_address(libusb_device* device) {
return StringPrintf("usb:%d:%d", libusb_get_bus_number(device),
libusb_get_device_address(device));
}
+#if defined(__linux__)
+static std::string get_device_serial_path(libusb_device* device) {
+ uint8_t ports[7];
+ int port_count = libusb_get_port_numbers(device, ports, 7);
+ if (port_count < 0) return "";
+
+ std::string path =
+ StringPrintf("/sys/bus/usb/devices/%d-%d", libusb_get_bus_number(device), ports[0]);
+ for (int port = 1; port < port_count; ++port) {
+ path += StringPrintf(".%d", ports[port]);
+ }
+ path += "/serial";
+ return path;
+}
+
+static std::string get_device_dev_path(libusb_device* device) {
+ uint8_t ports[7];
+ int port_count = libusb_get_port_numbers(device, ports, 7);
+ if (port_count < 0) return "";
+ return StringPrintf("/dev/bus/usb/%03u/%03u", libusb_get_bus_number(device), ports[0]);
+}
+
+static bool is_device_accessible(libusb_device* device) {
+ return access(get_device_dev_path(device).c_str(), R_OK | W_OK) == 0;
+}
+#endif
+
static bool endpoint_is_output(uint8_t endpoint) {
return (endpoint & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_OUT;
}
@@ -166,183 +194,249 @@
(write_length & zero_mask) == 0;
}
-static void poll_for_devices() {
- libusb_device** list;
- adb_thread_setname("device poll");
- while (!terminate_device_poll_thread) {
- const ssize_t device_count = libusb_get_device_list(nullptr, &list);
+static void process_device(libusb_device* device) {
+ std::string device_address = get_device_address(device);
+ std::string device_serial;
- LOG(VERBOSE) << "found " << device_count << " attached devices";
-
- for (ssize_t i = 0; i < device_count; ++i) {
- libusb_device* device = list[i];
- std::string device_address = get_device_address(device);
- std::string device_serial;
-
- // Figure out if we want to open the device.
- libusb_device_descriptor device_desc;
- int rc = libusb_get_device_descriptor(device, &device_desc);
- if (rc != 0) {
- LOG(WARNING) << "failed to get device descriptor for device at " << device_address
- << ": " << libusb_error_name(rc);
- }
-
- if (device_desc.bDeviceClass != LIBUSB_CLASS_PER_INTERFACE) {
- // Assume that all Android devices have the device class set to per interface.
- // TODO: Is this assumption valid?
- LOG(VERBOSE) << "skipping device with incorrect class at " << device_address;
- continue;
- }
-
- libusb_config_descriptor* config_raw;
- rc = libusb_get_active_config_descriptor(device, &config_raw);
- if (rc != 0) {
- LOG(WARNING) << "failed to get active config descriptor for device at "
- << device_address << ": " << libusb_error_name(rc);
- continue;
- }
- const unique_config_descriptor config(config_raw);
-
- // Use size_t for interface_num so <iostream>s don't mangle it.
- size_t interface_num;
- uint16_t zero_mask;
- uint8_t bulk_in = 0, bulk_out = 0;
- bool found_adb = false;
-
- for (interface_num = 0; interface_num < config->bNumInterfaces; ++interface_num) {
- const libusb_interface& interface = config->interface[interface_num];
- if (interface.num_altsetting != 1) {
- // Assume that interfaces with alternate settings aren't adb interfaces.
- // TODO: Is this assumption valid?
- LOG(VERBOSE) << "skipping interface with incorrect num_altsetting at "
- << device_address << " (interface " << interface_num << ")";
- continue;
- }
-
- const libusb_interface_descriptor& interface_desc = interface.altsetting[0];
- if (!is_adb_interface(interface_desc.bInterfaceClass,
- interface_desc.bInterfaceSubClass,
- interface_desc.bInterfaceProtocol)) {
- LOG(VERBOSE) << "skipping non-adb interface at " << device_address
- << " (interface " << interface_num << ")";
- continue;
- }
-
- LOG(VERBOSE) << "found potential adb interface at " << device_address
- << " (interface " << interface_num << ")";
-
- bool found_in = false;
- bool found_out = false;
- for (size_t endpoint_num = 0; endpoint_num < interface_desc.bNumEndpoints;
- ++endpoint_num) {
- const auto& endpoint_desc = interface_desc.endpoint[endpoint_num];
- const uint8_t endpoint_addr = endpoint_desc.bEndpointAddress;
- const uint8_t endpoint_attr = endpoint_desc.bmAttributes;
-
- const uint8_t transfer_type = endpoint_attr & LIBUSB_TRANSFER_TYPE_MASK;
-
- if (transfer_type != LIBUSB_TRANSFER_TYPE_BULK) {
- continue;
- }
-
- if (endpoint_is_output(endpoint_addr) && !found_out) {
- found_out = true;
- bulk_out = endpoint_addr;
- zero_mask = endpoint_desc.wMaxPacketSize - 1;
- } else if (!endpoint_is_output(endpoint_addr) && !found_in) {
- found_in = true;
- bulk_in = endpoint_addr;
- }
- }
-
- if (found_in && found_out) {
- found_adb = true;
- break;
- } else {
- LOG(VERBOSE) << "rejecting potential adb interface at " << device_address
- << "(interface " << interface_num << "): missing bulk endpoints "
- << "(found_in = " << found_in << ", found_out = " << found_out
- << ")";
- }
- }
-
- if (!found_adb) {
- LOG(VERBOSE) << "skipping device with no adb interfaces at " << device_address;
- continue;
- }
-
- {
- std::unique_lock<std::mutex> lock(usb_handles_mutex);
- if (usb_handles.find(device_address) != usb_handles.end()) {
- LOG(VERBOSE) << "device at " << device_address
- << " has already been registered, skipping";
- continue;
- }
- }
-
- libusb_device_handle* handle_raw;
- rc = libusb_open(list[i], &handle_raw);
- if (rc != 0) {
- LOG(WARNING) << "failed to open usb device at " << device_address << ": "
- << libusb_error_name(rc);
- continue;
- }
-
- unique_device_handle handle(handle_raw);
- LOG(DEBUG) << "successfully opened adb device at " << device_address << ", "
- << StringPrintf("bulk_in = %#x, bulk_out = %#x", bulk_in, bulk_out);
-
- device_serial.resize(255);
- rc = libusb_get_string_descriptor_ascii(
- handle_raw, device_desc.iSerialNumber,
- reinterpret_cast<unsigned char*>(&device_serial[0]), device_serial.length());
- if (rc == 0) {
- LOG(WARNING) << "received empty serial from device at " << device_address;
- continue;
- } else if (rc < 0) {
- LOG(WARNING) << "failed to get serial from device at " << device_address
- << libusb_error_name(rc);
- continue;
- }
- device_serial.resize(rc);
-
- // WARNING: this isn't released via RAII.
- rc = libusb_claim_interface(handle.get(), interface_num);
- if (rc != 0) {
- LOG(WARNING) << "failed to claim adb interface for device '" << device_serial << "'"
- << libusb_error_name(rc);
- continue;
- }
-
- for (uint8_t endpoint : {bulk_in, bulk_out}) {
- rc = libusb_clear_halt(handle.get(), endpoint);
- if (rc != 0) {
- LOG(WARNING) << "failed to clear halt on device '" << device_serial
- << "' endpoint 0x" << std::hex << endpoint << ": "
- << libusb_error_name(rc);
- libusb_release_interface(handle.get(), interface_num);
- continue;
- }
- }
-
- auto result =
- std::make_unique<usb_handle>(device_address, device_serial, std::move(handle),
- interface_num, bulk_in, bulk_out, zero_mask);
- usb_handle* usb_handle_raw = result.get();
-
- {
- std::unique_lock<std::mutex> lock(usb_handles_mutex);
- usb_handles[device_address] = std::move(result);
- }
-
- register_usb_transport(usb_handle_raw, device_serial.c_str(), device_address.c_str(), 1);
-
- LOG(INFO) << "registered new usb device '" << device_serial << "'";
- }
- libusb_free_device_list(list, 1);
-
- std::this_thread::sleep_for(500ms);
+ // Figure out if we want to open the device.
+ libusb_device_descriptor device_desc;
+ int rc = libusb_get_device_descriptor(device, &device_desc);
+ if (rc != 0) {
+ LOG(WARNING) << "failed to get device descriptor for device at " << device_address << ": "
+ << libusb_error_name(rc);
+ return;
}
+
+ if (device_desc.bDeviceClass != LIBUSB_CLASS_PER_INTERFACE) {
+ // Assume that all Android devices have the device class set to per interface.
+ // TODO: Is this assumption valid?
+ LOG(VERBOSE) << "skipping device with incorrect class at " << device_address;
+ return;
+ }
+
+ libusb_config_descriptor* config_raw;
+ rc = libusb_get_active_config_descriptor(device, &config_raw);
+ if (rc != 0) {
+ LOG(WARNING) << "failed to get active config descriptor for device at " << device_address
+ << ": " << libusb_error_name(rc);
+ return;
+ }
+ const unique_config_descriptor config(config_raw);
+
+ // Use size_t for interface_num so <iostream>s don't mangle it.
+ size_t interface_num;
+ uint16_t zero_mask;
+ uint8_t bulk_in = 0, bulk_out = 0;
+ size_t packet_size = 0;
+ bool found_adb = false;
+
+ for (interface_num = 0; interface_num < config->bNumInterfaces; ++interface_num) {
+ const libusb_interface& interface = config->interface[interface_num];
+ if (interface.num_altsetting != 1) {
+ // Assume that interfaces with alternate settings aren't adb interfaces.
+ // TODO: Is this assumption valid?
+ LOG(VERBOSE) << "skipping interface with incorrect num_altsetting at " << device_address
+ << " (interface " << interface_num << ")";
+ continue;
+ }
+
+ const libusb_interface_descriptor& interface_desc = interface.altsetting[0];
+ if (!is_adb_interface(interface_desc.bInterfaceClass, interface_desc.bInterfaceSubClass,
+ interface_desc.bInterfaceProtocol)) {
+ LOG(VERBOSE) << "skipping non-adb interface at " << device_address << " (interface "
+ << interface_num << ")";
+ continue;
+ }
+
+ LOG(VERBOSE) << "found potential adb interface at " << device_address << " (interface "
+ << interface_num << ")";
+
+ bool found_in = false;
+ bool found_out = false;
+ for (size_t endpoint_num = 0; endpoint_num < interface_desc.bNumEndpoints; ++endpoint_num) {
+ const auto& endpoint_desc = interface_desc.endpoint[endpoint_num];
+ const uint8_t endpoint_addr = endpoint_desc.bEndpointAddress;
+ const uint8_t endpoint_attr = endpoint_desc.bmAttributes;
+
+ const uint8_t transfer_type = endpoint_attr & LIBUSB_TRANSFER_TYPE_MASK;
+
+ if (transfer_type != LIBUSB_TRANSFER_TYPE_BULK) {
+ continue;
+ }
+
+ if (endpoint_is_output(endpoint_addr) && !found_out) {
+ found_out = true;
+ bulk_out = endpoint_addr;
+ zero_mask = endpoint_desc.wMaxPacketSize - 1;
+ } else if (!endpoint_is_output(endpoint_addr) && !found_in) {
+ found_in = true;
+ bulk_in = endpoint_addr;
+ }
+
+ size_t endpoint_packet_size = endpoint_desc.wMaxPacketSize;
+ CHECK(endpoint_packet_size != 0);
+ if (packet_size == 0) {
+ packet_size = endpoint_packet_size;
+ } else {
+ CHECK(packet_size == endpoint_packet_size);
+ }
+ }
+
+ if (found_in && found_out) {
+ found_adb = true;
+ break;
+ } else {
+ LOG(VERBOSE) << "rejecting potential adb interface at " << device_address
+ << "(interface " << interface_num << "): missing bulk endpoints "
+ << "(found_in = " << found_in << ", found_out = " << found_out << ")";
+ }
+ }
+
+ if (!found_adb) {
+ LOG(VERBOSE) << "skipping device with no adb interfaces at " << device_address;
+ return;
+ }
+
+ {
+ std::unique_lock<std::mutex> lock(usb_handles_mutex);
+ if (usb_handles.find(device_address) != usb_handles.end()) {
+ LOG(VERBOSE) << "device at " << device_address
+ << " has already been registered, skipping";
+ return;
+ }
+ }
+
+ bool writable = true;
+ libusb_device_handle* handle_raw = nullptr;
+ rc = libusb_open(device, &handle_raw);
+ unique_device_handle handle(handle_raw);
+ if (rc == 0) {
+ LOG(DEBUG) << "successfully opened adb device at " << device_address << ", "
+ << StringPrintf("bulk_in = %#x, bulk_out = %#x", bulk_in, bulk_out);
+
+ device_serial.resize(255);
+ rc = libusb_get_string_descriptor_ascii(handle_raw, device_desc.iSerialNumber,
+ reinterpret_cast<unsigned char*>(&device_serial[0]),
+ device_serial.length());
+ if (rc == 0) {
+ LOG(WARNING) << "received empty serial from device at " << device_address;
+ return;
+ } else if (rc < 0) {
+ LOG(WARNING) << "failed to get serial from device at " << device_address
+ << libusb_error_name(rc);
+ return;
+ }
+ device_serial.resize(rc);
+
+ // WARNING: this isn't released via RAII.
+ rc = libusb_claim_interface(handle.get(), interface_num);
+ if (rc != 0) {
+ LOG(WARNING) << "failed to claim adb interface for device '" << device_serial << "'"
+ << libusb_error_name(rc);
+ return;
+ }
+
+ for (uint8_t endpoint : {bulk_in, bulk_out}) {
+ rc = libusb_clear_halt(handle.get(), endpoint);
+ if (rc != 0) {
+ LOG(WARNING) << "failed to clear halt on device '" << device_serial
+ << "' endpoint 0x" << std::hex << endpoint << ": "
+ << libusb_error_name(rc);
+ libusb_release_interface(handle.get(), interface_num);
+ return;
+ }
+ }
+ } else {
+ LOG(WARNING) << "failed to open usb device at " << device_address << ": "
+ << libusb_error_name(rc);
+ writable = false;
+
+#if defined(__linux__)
+ // libusb doesn't think we should be messing around with devices we don't have
+ // write access to, but Linux at least lets us get the serial number anyway.
+ if (!android::base::ReadFileToString(get_device_serial_path(device), &device_serial)) {
+ // We don't actually want to treat an unknown serial as an error because
+ // devices aren't able to communicate a serial number in early bringup.
+ // http://b/20883914
+ device_serial = "unknown";
+ }
+ device_serial = android::base::Trim(device_serial);
+#else
+ // On Mac OS and Windows, we're screwed. But I don't think this situation actually
+ // happens on those OSes.
+ return;
+#endif
+ }
+
+ auto result =
+ std::make_unique<usb_handle>(device_address, device_serial, std::move(handle),
+ interface_num, bulk_in, bulk_out, zero_mask, packet_size);
+ usb_handle* usb_handle_raw = result.get();
+
+ {
+ std::unique_lock<std::mutex> lock(usb_handles_mutex);
+ usb_handles[device_address] = std::move(result);
+ }
+
+ register_usb_transport(usb_handle_raw, device_serial.c_str(), device_address.c_str(), writable);
+ LOG(INFO) << "registered new usb device '" << device_serial << "'";
+}
+
+static std::atomic<int> connecting_devices(0);
+
+static void device_connected(libusb_device* device) {
+#if defined(__linux__)
+ // Android's host linux libusb uses netlink instead of udev for device hotplug notification,
+ // which means we can get hotplug notifications before udev has updated ownership/perms on the
+ // device. Since we're not going to be able to link against the system's libudev any time soon,
+ // hack around this by checking for accessibility in a loop.
+ ++connecting_devices;
+ auto thread = std::thread([device]() {
+ std::string device_path = get_device_dev_path(device);
+ auto start = std::chrono::steady_clock::now();
+ while (std::chrono::steady_clock::now() - start < 500ms) {
+ if (is_device_accessible(device)) {
+ break;
+ }
+ std::this_thread::sleep_for(10ms);
+ }
+
+ process_device(device);
+ --connecting_devices;
+ });
+ thread.detach();
+#else
+ process_device(device);
+#endif
+}
+
+static void device_disconnected(libusb_device* device) {
+ std::string device_address = get_device_address(device);
+
+ LOG(INFO) << "device disconnected: " << device_address;
+ std::unique_lock<std::mutex> lock(usb_handles_mutex);
+ auto it = usb_handles.find(device_address);
+ if (it != usb_handles.end()) {
+ if (!it->second->device_handle) {
+ // If the handle is null, we were never able to open the device.
+ unregister_usb_transport(it->second.get());
+ }
+ usb_handles.erase(it);
+ }
+}
+
+static int hotplug_callback(libusb_context*, libusb_device* device, libusb_hotplug_event event,
+ void*) {
+ // We're called with the libusb lock taken. Call these on the main thread outside of this
+ // function so that the usb_handle mutex is always taken before the libusb mutex.
+ fdevent_run_on_main_thread([device, event]() {
+ if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED) {
+ device_connected(device);
+ } else if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) {
+ device_disconnected(device);
+ }
+ });
+ return 0;
}
void usb_init() {
@@ -352,6 +446,24 @@
LOG(FATAL) << "failed to initialize libusb: " << libusb_error_name(rc);
}
+ // Register the hotplug callback.
+ rc = libusb_hotplug_register_callback(
+ nullptr, static_cast<libusb_hotplug_event>(LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED |
+ LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT),
+ LIBUSB_HOTPLUG_ENUMERATE, LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY,
+ LIBUSB_CLASS_PER_INTERFACE, hotplug_callback, nullptr, &hotplug_handle);
+
+ if (rc != LIBUSB_SUCCESS) {
+ LOG(FATAL) << "failed to register libusb hotplug callback";
+ }
+
+ // Wait for all of the connecting devices to finish.
+ while (connecting_devices != 0) {
+ std::this_thread::sleep_for(10ms);
+ }
+
+ adb_notify_device_scan_complete();
+
// Spawn a thread for libusb_handle_events.
std::thread([]() {
adb_thread_setname("libusb");
@@ -359,19 +471,10 @@
libusb_handle_events(nullptr);
}
}).detach();
+}
- // Spawn a thread to do device enumeration.
- // TODO: Use libusb_hotplug_* instead?
- device_poll_thread = new std::thread(poll_for_devices);
- android::base::at_quick_exit([]() {
- terminate_device_poll_thread = true;
- std::unique_lock<std::mutex> lock(usb_handles_mutex);
- for (auto& it : usb_handles) {
- it.second->Close();
- }
- lock.unlock();
- device_poll_thread->join();
- });
+void usb_cleanup() {
+ libusb_hotplug_deregister_callback(nullptr, hotplug_handle);
}
// Dispatch a libusb transfer, unlock |device_lock|, and then wait for the result.
@@ -397,7 +500,8 @@
return;
}
- if (transfer->actual_length != transfer->length) {
+ // usb_read() can return when receiving some data.
+ if (info->is_bulk_out && transfer->actual_length != transfer->length) {
LOG(DEBUG) << info->name << " transfer incomplete, resubmitting";
transfer->length -= transfer->actual_length;
transfer->buffer += transfer->actual_length;
@@ -491,8 +595,12 @@
info->transfer->num_iso_packets = 0;
int rc = perform_usb_transfer(h, info, std::move(lock));
- LOG(DEBUG) << "usb_read(" << len << ") = " << rc;
- return rc;
+ LOG(DEBUG) << "usb_read(" << len << ") = " << rc << ", actual_length "
+ << info->transfer->actual_length;
+ if (rc < 0) {
+ return rc;
+ }
+ return info->transfer->actual_length;
}
int usb_close(usb_handle* h) {
@@ -508,4 +616,10 @@
void usb_kick(usb_handle* h) {
h->Close();
}
+
+size_t usb_get_max_packet_size(usb_handle* h) {
+ CHECK(h->max_packet_size != 0);
+ return h->max_packet_size;
+}
+
} // namespace libusb
diff --git a/adb/client/usb_linux.cpp b/adb/client/usb_linux.cpp
index 13b7674..a7df0ed 100644
--- a/adb/client/usb_linux.cpp
+++ b/adb/client/usb_linux.cpp
@@ -65,6 +65,7 @@
unsigned char ep_in;
unsigned char ep_out;
+ size_t max_packet_size;
unsigned zero_mask;
unsigned writeable = 1;
@@ -120,9 +121,9 @@
}
static void find_usb_device(const std::string& base,
- void (*register_device_callback)
- (const char*, const char*, unsigned char, unsigned char, int, int, unsigned))
-{
+ void (*register_device_callback)(const char*, const char*,
+ unsigned char, unsigned char, int, int,
+ unsigned, size_t)) {
std::unique_ptr<DIR, int(*)(DIR*)> bus_dir(opendir(base.c_str()), closedir);
if (!bus_dir) return;
@@ -144,6 +145,7 @@
struct usb_interface_descriptor* interface;
struct usb_endpoint_descriptor *ep1, *ep2;
unsigned zero_mask = 0;
+ size_t max_packet_size = 0;
unsigned vid, pid;
if (contains_non_digit(de->d_name)) continue;
@@ -251,7 +253,8 @@
continue;
}
/* aproto 01 needs 0 termination */
- if(interface->bInterfaceProtocol == 0x01) {
+ if (interface->bInterfaceProtocol == 0x01) {
+ max_packet_size = ep1->wMaxPacketSize;
zero_mask = ep1->wMaxPacketSize - 1;
}
@@ -281,9 +284,9 @@
}
}
- register_device_callback(dev_name.c_str(), devpath,
- local_ep_in, local_ep_out,
- interface->bInterfaceNumber, device->iSerialNumber, zero_mask);
+ register_device_callback(dev_name.c_str(), devpath, local_ep_in,
+ local_ep_out, interface->bInterfaceNumber,
+ device->iSerialNumber, zero_mask, max_packet_size);
break;
}
} else {
@@ -401,7 +404,6 @@
}
}
-
int usb_write(usb_handle *h, const void *_data, int len)
{
D("++ usb_write ++");
@@ -429,19 +431,16 @@
int n;
D("++ usb_read ++");
- while(len > 0) {
+ int orig_len = len;
+ while (len == orig_len) {
int xfer = len;
D("[ usb read %d fd = %d], path=%s", xfer, h->fd, h->path.c_str());
n = usb_bulk_read(h, data, xfer);
D("[ usb read %d ] = %d, path=%s", xfer, n, h->path.c_str());
- if(n != xfer) {
+ if (n <= 0) {
if((errno == ETIMEDOUT) && (h->fd != -1)) {
D("[ timeout ]");
- if(n > 0){
- data += n;
- len -= n;
- }
continue;
}
D("ERROR: n = %d, errno = %d (%s)",
@@ -449,12 +448,12 @@
return -1;
}
- len -= xfer;
- data += xfer;
+ len -= n;
+ data += n;
}
D("-- usb_read --");
- return 0;
+ return orig_len - len;
}
void usb_kick(usb_handle* h) {
@@ -501,10 +500,13 @@
return 0;
}
-static void register_device(const char* dev_name, const char* dev_path,
- unsigned char ep_in, unsigned char ep_out,
- int interface, int serial_index,
- unsigned zero_mask) {
+size_t usb_get_max_packet_size(usb_handle* h) {
+ return h->max_packet_size;
+}
+
+static void register_device(const char* dev_name, const char* dev_path, unsigned char ep_in,
+ unsigned char ep_out, int interface, int serial_index,
+ unsigned zero_mask, size_t max_packet_size) {
// Since Linux will not reassign the device ID (and dev_name) as long as the
// device is open, we can add to the list here once we open it and remove
// from the list when we're finally closed and everything will work out
@@ -527,6 +529,7 @@
usb->ep_in = ep_in;
usb->ep_out = ep_out;
usb->zero_mask = zero_mask;
+ usb->max_packet_size = max_packet_size;
// Initialize mark so we don't get garbage collected after the device scan.
usb->mark = true;
@@ -574,7 +577,7 @@
register_usb_transport(done_usb, serial.c_str(), dev_path, done_usb->writeable);
}
-static void device_poll_thread(void*) {
+static void device_poll_thread() {
adb_thread_setname("device poll");
D("Created device thread");
while (true) {
@@ -593,8 +596,9 @@
actions.sa_handler = [](int) {};
sigaction(SIGALRM, &actions, nullptr);
- if (!adb_thread_create(device_poll_thread, nullptr)) {
- fatal_errno("cannot create device_poll thread");
- }
+ std::thread(device_poll_thread).detach();
}
+
+void usb_cleanup() {}
+
} // namespace native
diff --git a/adb/client/usb_osx.cpp b/adb/client/usb_osx.cpp
index d4fc7c0..4e1480f 100644
--- a/adb/client/usb_osx.cpp
+++ b/adb/client/usb_osx.cpp
@@ -38,6 +38,7 @@
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
+#include <android-base/thread_annotations.h>
#include "adb.h"
#include "transport.h"
@@ -51,15 +52,21 @@
UInt8 bulkOut;
IOUSBInterfaceInterface190** interface;
unsigned int zero_mask;
+ size_t max_packet_size;
// For garbage collecting disconnected devices.
bool mark;
std::string devpath;
std::atomic<bool> dead;
- usb_handle() : bulkIn(0), bulkOut(0), interface(nullptr),
- zero_mask(0), mark(false), dead(false) {
- }
+ usb_handle()
+ : bulkIn(0),
+ bulkOut(0),
+ interface(nullptr),
+ zero_mask(0),
+ max_packet_size(0),
+ mark(false),
+ dead(false) {}
};
static std::atomic<bool> usb_inited_flag;
@@ -390,6 +397,7 @@
}
handle->zero_mask = maxPacketSize - 1;
+ handle->max_packet_size = maxPacketSize;
}
handle->interface = interface;
@@ -405,7 +413,7 @@
std::mutex& operate_device_lock = *new std::mutex();
-static void RunLoopThread(void* unused) {
+static void RunLoopThread() {
adb_thread_setname("RunLoop");
VLOG(USB) << "RunLoopThread started";
@@ -422,7 +430,7 @@
VLOG(USB) << "RunLoopThread done";
}
-static void usb_cleanup() {
+void usb_cleanup() NO_THREAD_SAFETY_ANALYSIS {
VLOG(USB) << "usb_cleanup";
// Wait until usb operations in RunLoopThread finish, and prevent further operations.
operate_device_lock.lock();
@@ -432,13 +440,9 @@
void usb_init() {
static bool initialized = false;
if (!initialized) {
- atexit(usb_cleanup);
-
usb_inited_flag = false;
- if (!adb_thread_create(RunLoopThread, nullptr)) {
- fatal_errno("cannot create RunLoop thread");
- }
+ std::thread(RunLoopThread).detach();
// Wait for initialization to finish
while (!usb_inited_flag) {
@@ -520,7 +524,7 @@
}
if (kIOReturnSuccess == result)
- return 0;
+ return numBytes;
else {
LOG(ERROR) << "usb_read failed with status: " << std::hex << result;
}
@@ -560,4 +564,9 @@
std::lock_guard<std::mutex> lock_guard(g_usb_handles_mutex);
usb_kick_locked(handle);
}
+
+size_t usb_get_max_packet_size(usb_handle* handle) {
+ return handle->max_packet_size;
+}
+
} // namespace native
diff --git a/adb/client/usb_windows.cpp b/adb/client/usb_windows.cpp
index 640e91e..1620e6e 100644
--- a/adb/client/usb_windows.cpp
+++ b/adb/client/usb_windows.cpp
@@ -65,6 +65,9 @@
/// Interface name
wchar_t* interface_name;
+ /// Maximum packet size.
+ unsigned max_packet_size;
+
/// Mask for determining when to use zero length packets
unsigned zero_mask;
};
@@ -103,7 +106,7 @@
/// Entry point for thread that polls (every second) for new usb interfaces.
/// This routine calls find_devices in infinite loop.
-static void device_poll_thread(void*);
+static void device_poll_thread();
/// Initializes this module
void usb_init();
@@ -174,7 +177,7 @@
return 1;
}
-void device_poll_thread(void*) {
+void device_poll_thread() {
adb_thread_setname("Device Poll");
D("Created device thread");
@@ -203,7 +206,7 @@
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
}
-static void _power_notification_thread(void*) {
+static void _power_notification_thread() {
// This uses a thread with its own window message pump to get power
// notifications. If adb runs from a non-interactive service account, this
// might not work (not sure). If that happens to not work, we could use
@@ -258,14 +261,12 @@
}
void usb_init() {
- if (!adb_thread_create(device_poll_thread, nullptr)) {
- fatal_errno("cannot create device poll thread");
- }
- if (!adb_thread_create(_power_notification_thread, nullptr)) {
- fatal_errno("cannot create power notification thread");
- }
+ std::thread(device_poll_thread).detach();
+ std::thread(_power_notification_thread).detach();
}
+void usb_cleanup() {}
+
usb_handle* do_usb_open(const wchar_t* interface_name) {
unsigned long name_len = 0;
@@ -419,6 +420,7 @@
unsigned long time_out = 0;
unsigned long read = 0;
int err = 0;
+ int orig_len = len;
D("usb_read %d", len);
if (NULL == handle) {
@@ -427,9 +429,8 @@
goto fail;
}
- while (len > 0) {
- if (!AdbReadEndpointSync(handle->adb_read_pipe, data, len, &read,
- time_out)) {
+ while (len == orig_len) {
+ if (!AdbReadEndpointSync(handle->adb_read_pipe, data, len, &read, time_out)) {
D("AdbReadEndpointSync failed: %s",
android::base::SystemErrorCodeToString(GetLastError()).c_str());
err = EIO;
@@ -437,11 +438,11 @@
}
D("usb_read got: %ld, expected: %d", read, len);
- data = (char *)data + read;
+ data = (char*)data + read;
len -= read;
}
- return 0;
+ return orig_len - len;
fail:
// Any failure should cause us to kick the device instead of leaving it a
@@ -526,6 +527,10 @@
return 0;
}
+size_t usb_get_max_packet_size(usb_handle* handle) {
+ return handle->max_packet_size;
+}
+
int recognized_device(usb_handle* handle) {
if (NULL == handle)
return 0;
@@ -561,6 +566,7 @@
AdbEndpointInformation endpoint_info;
// assuming zero is a valid bulk endpoint ID
if (AdbGetEndpointInformation(handle->adb_interface, 0, &endpoint_info)) {
+ handle->max_packet_size = endpoint_info.max_packet_size;
handle->zero_mask = endpoint_info.max_packet_size - 1;
D("device zero_mask: 0x%x", handle->zero_mask);
} else {
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index 4979eef..68ae4af 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -68,20 +68,17 @@
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 auto& gProductOutPath = *new std::string();
extern int gListenAll;
DefaultStandardStreamsCallback DEFAULT_STANDARD_STREAMS_CALLBACK(nullptr, nullptr);
-static std::string product_file(const char *extra) {
- if (gProductOutPath.empty()) {
- fprintf(stderr, "adb: Product directory not specified; "
- "use -p or define ANDROID_PRODUCT_OUT\n");
+static std::string product_file(const char* file) {
+ const char* ANDROID_PRODUCT_OUT = getenv("ANDROID_PRODUCT_OUT");
+ if (ANDROID_PRODUCT_OUT == nullptr) {
+ fprintf(stderr, "adb: product directory not specified; set $ANDROID_PRODUCT_OUT\n");
exit(1);
}
-
- return android::base::StringPrintf("%s%s%s",
- gProductOutPath.c_str(), OS_PATH_SEPARATOR_STR, extra);
+ return android::base::StringPrintf("%s%s%s", ANDROID_PRODUCT_OUT, OS_PATH_SEPARATOR_STR, file);
}
static void help() {
@@ -92,11 +89,7 @@
" -a listen on all network interfaces, not just localhost\n"
" -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\n"
- " use device with given serial number (overrides $ANDROID_SERIAL)\n"
- " -p PRODUCT\n"
- " name or path ('angler'/'out/target/product/angler');\n"
- " default $ANDROID_PRODUCT_OUT\n"
+ " -s SERIAL use device with given serial (overrides $ANDROID_SERIAL)\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"
@@ -133,14 +126,14 @@
" reverse --remove-all remove all reverse socket connections from device\n"
"\n"
"file transfer:\n"
- " push LOCAL... REMOTE\n"
+ " push [--sync] LOCAL... REMOTE\n"
" copy local files/directories to device\n"
+ " --sync: only push files that are newer on the host than the device\n"
" pull [-a] REMOTE... LOCAL\n"
" copy files/dirs from device\n"
" -a: preserve file timestamp and mode\n"
- " sync [DIR]\n"
- " copy all changed files to device; if DIR is \"system\", \"vendor\", \"oem\",\n"
- " or \"data\", only sync that partition (default all)\n"
+ " sync [system|vendor|oem|data|all]\n"
+ " sync a local build from $ANDROID_PRODUCT_OUT to the device (default all)\n"
" -l: list but don't copy\n"
"\n"
"shell:\n"
@@ -169,15 +162,7 @@
" '-k': keep the data and cache directories\n"
"\n"
"backup/restore:\n"
- " backup [-f FILE] [-apk|-noapk] [-obb|-noobb] [-shared|-noshared] [-all] [-system|-nosystem] [PACKAGE...]\n"
- " write an archive of the device's data to FILE [default=backup.adb]\n"
- " package list optional if -all/-shared are supplied\n"
- " -apk/-noapk: do/don't back up .apk files (default -noapk)\n"
- " -obb/-noobb: do/don't back up .obb files (default -noobb)\n"
- " -shared|-noshared: do/don't back up shared storage (default -noshared)\n"
- " -all: back up all installed applications\n"
- " -system|-nosystem: include system apps in -all (default -system)\n"
- " restore FILE restore device contents from FILE\n"
+ " to show usage run \"adb shell bu help\"\n"
"\n"
"debugging:\n"
" bugreport [PATH]\n"
@@ -220,6 +205,7 @@
" kill-server kill the server if it is running\n"
" reconnect kick connection from host side to force reconnect\n"
" reconnect device kick connection from device side to force reconnect\n"
+ " reconnect offline reset offline/unauthorized devices to force reconnect\n"
"\n"
"environment variables:\n"
" $ADB_TRACE\n"
@@ -663,13 +649,8 @@
#endif
// TODO: combine read_and_dump with stdin_read_thread to make life simpler?
- int exit_code = 1;
- if (!adb_thread_create(stdin_read_thread_loop, args)) {
- PLOG(ERROR) << "error starting stdin read thread";
- delete args;
- } else {
- exit_code = read_and_dump(fd, use_shell_protocol);
- }
+ std::thread(stdin_read_thread_loop, args).detach();
+ int exit_code = read_and_dump(fd, use_shell_protocol);
// TODO: properly exit stdin_read_thread_loop and close |fd|.
@@ -703,8 +684,7 @@
switch (opt) {
case 'e':
if (!(strlen(optarg) == 1 || strcmp(optarg, "none") == 0)) {
- fprintf(stderr, "error: -e requires a single-character argument or 'none'\n");
- return 1;
+ return syntax_error("-e requires a single-character argument or 'none'");
}
escape_char = (strcmp(optarg, "none") == 0) ? 0 : optarg[0];
break;
@@ -782,55 +762,46 @@
return RemoteShell(use_shell_protocol, shell_type_arg, escape_char, command);
}
-static int adb_download_buffer(const char* service, const char* filename) {
- std::string content;
- if (!android::base::ReadFileToString(filename, &content)) {
- fprintf(stderr, "error: couldn't read %s: %s\n", filename, strerror(errno));
- return -1;
- }
-
- const uint8_t* data = reinterpret_cast<const uint8_t*>(content.data());
- unsigned sz = content.size();
-
+static int adb_sideload_legacy(const char* filename, int in_fd, int size) {
std::string error;
- int fd = adb_connect(android::base::StringPrintf("%s:%d", service, sz), &error);
- if (fd < 0) {
- fprintf(stderr,"error: %s\n", error.c_str());
+ int out_fd = adb_connect(android::base::StringPrintf("sideload:%d", size), &error);
+ if (out_fd < 0) {
+ fprintf(stderr, "adb: pre-KitKat sideload connection failed: %s\n", error.c_str());
return -1;
}
int opt = CHUNK_SIZE;
- opt = adb_setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (const void *) &opt, sizeof(opt));
+ opt = adb_setsockopt(out_fd, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt));
- unsigned total = sz;
- const uint8_t* ptr = reinterpret_cast<const uint8_t*>(data);
-
- const char* x = strrchr(service, ':');
- if (x) service = x + 1;
-
- while (sz > 0) {
- unsigned xfer = (sz > CHUNK_SIZE) ? CHUNK_SIZE : sz;
- if (!WriteFdExactly(fd, ptr, xfer)) {
- std::string error;
- adb_status(fd, &error);
- fprintf(stderr,"* failed to write data '%s' *\n", error.c_str());
- adb_close(fd);
+ char buf[CHUNK_SIZE];
+ int total = size;
+ while (size > 0) {
+ unsigned xfer = (size > CHUNK_SIZE) ? CHUNK_SIZE : size;
+ if (!ReadFdExactly(in_fd, buf, xfer)) {
+ fprintf(stderr, "adb: failed to read data from %s: %s\n", filename, strerror(errno));
+ adb_close(out_fd);
return -1;
}
- sz -= xfer;
- ptr += xfer;
- printf("sending: '%s' %4d%% \r", filename, (int)(100LL - ((100LL * sz) / (total))));
+ if (!WriteFdExactly(out_fd, buf, xfer)) {
+ std::string error;
+ adb_status(out_fd, &error);
+ fprintf(stderr, "adb: failed to write data: %s\n", error.c_str());
+ adb_close(out_fd);
+ return -1;
+ }
+ size -= xfer;
+ printf("sending: '%s' %4d%% \r", filename, (int)(100LL - ((100LL * size) / (total))));
fflush(stdout);
}
printf("\n");
- if (!adb_status(fd, &error)) {
- fprintf(stderr,"* error response '%s' *\n", error.c_str());
- adb_close(fd);
+ if (!adb_status(out_fd, &error)) {
+ fprintf(stderr, "adb: error response: %s\n", error.c_str());
+ adb_close(out_fd);
return -1;
}
- adb_close(fd);
+ adb_close(out_fd);
return 0;
}
@@ -856,19 +827,18 @@
* we hang up.
*/
static int adb_sideload_host(const char* filename) {
- fprintf(stderr, "opening '%s'...\n", filename);
+ // TODO: use a LinePrinter instead...
struct stat sb;
if (stat(filename, &sb) == -1) {
- fprintf(stderr, "failed to stat file %s: %s\n", filename, strerror(errno));
+ fprintf(stderr, "adb: failed to stat file %s: %s\n", filename, strerror(errno));
return -1;
}
unique_fd package_fd(adb_open(filename, O_RDONLY));
if (package_fd == -1) {
- fprintf(stderr, "failed to open file %s: %s\n", filename, strerror(errno));
+ fprintf(stderr, "adb: failed to open file %s: %s\n", filename, strerror(errno));
return -1;
}
- fprintf(stderr, "connecting...\n");
std::string service = android::base::StringPrintf(
"sideload-host:%d:%d", static_cast<int>(sb.st_size), SIDELOAD_HOST_BLOCK_SIZE);
std::string error;
@@ -876,8 +846,9 @@
if (device_fd < 0) {
// Try falling back to the older (<= K) sideload method. Maybe this
// is an older device that doesn't support sideload-host.
- fprintf(stderr, "falling back to older sideload method...\n");
- return adb_download_buffer("sideload", filename);
+ fprintf(stderr, "adb: sideload connection failed: %s\n", error.c_str());
+ fprintf(stderr, "adb: trying pre-KitKat sideload method...\n");
+ return adb_sideload_legacy(filename, package_fd, static_cast<int>(sb.st_size));
}
int opt = SIDELOAD_HOST_BLOCK_SIZE;
@@ -889,7 +860,7 @@
int last_percent = -1;
while (true) {
if (!ReadFdExactly(device_fd, buf, 8)) {
- fprintf(stderr, "* failed to read command: %s\n", strerror(errno));
+ fprintf(stderr, "adb: failed to read command: %s\n", strerror(errno));
return -1;
}
buf[8] = '\0';
@@ -905,7 +876,7 @@
size_t offset = block * SIDELOAD_HOST_BLOCK_SIZE;
if (offset >= static_cast<size_t>(sb.st_size)) {
- fprintf(stderr, "* attempt to read block %d past end\n", block);
+ fprintf(stderr, "adb: failed to read block %d past end\n", block);
return -1;
}
@@ -915,17 +886,17 @@
}
if (adb_lseek(package_fd, offset, SEEK_SET) != static_cast<int>(offset)) {
- fprintf(stderr, "* failed to seek to package block: %s\n", strerror(errno));
+ fprintf(stderr, "adb: failed to seek to package block: %s\n", strerror(errno));
return -1;
}
if (!ReadFdExactly(package_fd, buf, to_write)) {
- fprintf(stderr, "* failed to read package block: %s\n", strerror(errno));
+ fprintf(stderr, "adb: failed to read package block: %s\n", strerror(errno));
return -1;
}
if (!WriteFdExactly(device_fd, buf, to_write)) {
adb_status(device_fd, &error);
- fprintf(stderr,"* failed to write data '%s' *\n", error.c_str());
+ fprintf(stderr, "adb: failed to write data '%s' *\n", error.c_str());
return -1;
}
xfer += to_write;
@@ -957,19 +928,13 @@
fprintf(stderr, "error: adb %s not implemented on Win32\n", argv[0]);
return -1;
#else
- if (argc < 2) {
- fprintf(stderr, "usage: adb %s <adb service name> [ppp opts]\n",
- argv[0]);
-
- return 1;
- }
+ if (argc < 2) return syntax_error("adb %s <adb service name> [ppp opts]", argv[0]);
const char* adb_service_name = argv[1];
std::string error;
int fd = adb_connect(adb_service_name, &error);
if (fd < 0) {
- fprintf(stderr,"Error: Could not open adb service: %s. Error: %s\n",
- adb_service_name, error.c_str());
+ fprintf(stderr, "adb: could not open adb service %s: %s\n", adb_service_name, error.c_str());
return 1;
}
@@ -1188,10 +1153,7 @@
/* find, extract, and use any -f argument */
for (int i = 1; i < argc; i++) {
if (!strcmp("-f", argv[i])) {
- if (i == argc-1) {
- fprintf(stderr, "adb: backup -f passed with no filename.\n");
- return EXIT_FAILURE;
- }
+ if (i == argc - 1) return syntax_error("backup -f passed with no filename");
filename = argv[i+1];
for (int j = i+2; j <= argc; ) {
argv[i++] = argv[j++];
@@ -1203,10 +1165,7 @@
// Bare "adb backup" or "adb backup -f filename" are not valid invocations ---
// a list of packages is required.
- if (argc < 2) {
- fprintf(stderr, "adb: backup either needs a list of packages or -all/-shared.\n");
- return EXIT_FAILURE;
- }
+ if (argc < 2) return syntax_error("backup either needs a list of packages or -all/-shared");
adb_unlink(filename);
int outFd = adb_creat(filename, 0640);
@@ -1231,7 +1190,7 @@
return EXIT_FAILURE;
}
- printf("Now unlock your device and confirm the backup operation...\n");
+ fprintf(stdout, "Now unlock your device and confirm the backup operation...\n");
fflush(stdout);
copy_to_file(fd, outFd);
@@ -1242,7 +1201,7 @@
}
static int restore(int argc, const char** argv) {
- if (argc != 2) return usage("restore requires an argument");
+ if (argc != 2) return syntax_error("restore requires an argument");
const char* filename = argv[1];
int tarFd = adb_open(filename, O_RDONLY);
@@ -1259,7 +1218,9 @@
return -1;
}
- printf("Now unlock your device and confirm the restore operation.\n");
+ fprintf(stdout, "Now unlock your device and confirm the restore operation.\n");
+ fflush(stdout);
+
copy_to_file(tarFd, fd);
// Provide an in-band EOD marker in case the archive file is malformed
@@ -1273,69 +1234,8 @@
return 0;
}
-/* <hint> may be:
- * - A simple product name
- * e.g., "sooner"
- * - A relative path from the CWD to the ANDROID_PRODUCT_OUT dir
- * e.g., "out/target/product/sooner"
- * - An absolute path to the PRODUCT_OUT dir
- * e.g., "/src/device/out/target/product/sooner"
- *
- * Given <hint>, try to construct an absolute path to the
- * ANDROID_PRODUCT_OUT dir.
- */
-static std::string find_product_out_path(const std::string& hint) {
- if (hint.empty()) {
- return "";
- }
-
- // If it's already absolute, don't bother doing any work.
- if (adb_is_absolute_host_path(hint.c_str())) {
- return hint;
- }
-
- // If any of the OS_PATH_SEPARATORS is found, assume it's a relative path;
- // make it absolute.
- // NOLINT: Do not complain if OS_PATH_SEPARATORS has only one character.
- if (hint.find_first_of(OS_PATH_SEPARATORS) != std::string::npos) { // NOLINT
- std::string cwd;
- if (!getcwd(&cwd)) {
- perror("adb: getcwd failed");
- return "";
- }
- return android::base::StringPrintf("%s%c%s", cwd.c_str(), OS_PATH_SEPARATOR, hint.c_str());
- }
-
- // It's a string without any slashes. Try to do something with it.
- //
- // Try to find the root of the build tree, and build a PRODUCT_OUT
- // path from there.
- char* top = getenv("ANDROID_BUILD_TOP");
- if (top == nullptr) {
- fprintf(stderr, "adb: ANDROID_BUILD_TOP not set!\n");
- return "";
- }
-
- std::string path = top;
- path += OS_PATH_SEPARATOR_STR;
- path += "out";
- path += OS_PATH_SEPARATOR_STR;
- path += "target";
- path += OS_PATH_SEPARATOR_STR;
- path += "product";
- path += OS_PATH_SEPARATOR_STR;
- path += hint;
- if (!directory_exists(path)) {
- fprintf(stderr, "adb: Couldn't find a product dir based on -p %s; "
- "\"%s\" doesn't exist\n", hint.c_str(), path.c_str());
- return "";
- }
- return path;
-}
-
-static void parse_push_pull_args(const char** arg, int narg,
- std::vector<const char*>* srcs,
- const char** dst, bool* copy_attrs) {
+static void parse_push_pull_args(const char** arg, int narg, std::vector<const char*>* srcs,
+ const char** dst, bool* copy_attrs, bool* sync) {
*copy_attrs = false;
srcs->clear();
@@ -1348,10 +1248,14 @@
// Silently ignore for backwards compatibility.
} else if (!strcmp(*arg, "-a")) {
*copy_attrs = true;
+ } else if (!strcmp(*arg, "--sync")) {
+ if (sync != nullptr) {
+ *sync = true;
+ }
} else if (!strcmp(*arg, "--")) {
ignore_flags = true;
} else {
- fprintf(stderr, "adb: unrecognized option '%s'\n", *arg);
+ syntax_error("unrecognized option '%s'", *arg);
exit(1);
}
}
@@ -1423,17 +1327,6 @@
signal(SIGPIPE, SIG_IGN);
#endif
- // If defined, this should be an absolute path to
- // the directory containing all of the various system images
- // for a particular product. If not defined, and the adb
- // command requires this information, then the user must
- // specify the path using "-p".
- char* ANDROID_PRODUCT_OUT = getenv("ANDROID_PRODUCT_OUT");
- if (ANDROID_PRODUCT_OUT != nullptr) {
- gProductOutPath = ANDROID_PRODUCT_OUT;
- }
- // TODO: also try TARGET_PRODUCT/TARGET_DEVICE as a hint
-
const char* server_host_str = nullptr;
const char* server_port_str = nullptr;
const char* server_socket_str = nullptr;
@@ -1450,7 +1343,7 @@
/* this is a special flag used only when the ADB client launches the ADB Server */
is_daemon = 1;
} else if (!strcmp(argv[0], "--reply-fd")) {
- if (argc < 2) return usage("--reply-fd requires an argument");
+ if (argc < 2) return syntax_error("--reply-fd requires an argument");
const char* reply_fd_str = argv[1];
argc--;
argv++;
@@ -1459,26 +1352,11 @@
fprintf(stderr, "adb: invalid reply fd \"%s\"\n", reply_fd_str);
return 1;
}
- } else if (!strncmp(argv[0], "-p", 2)) {
- const char* product = nullptr;
- if (argv[0][2] == '\0') {
- if (argc < 2) return usage("-p requires an argument");
- product = argv[1];
- argc--;
- argv++;
- } else {
- product = argv[0] + 2;
- }
- gProductOutPath = find_product_out_path(product);
- if (gProductOutPath.empty()) {
- fprintf(stderr, "adb: could not resolve \"-p %s\"\n", product);
- return 1;
- }
} else if (argv[0][0]=='-' && argv[0][1]=='s') {
if (isdigit(argv[0][2])) {
serial = argv[0] + 2;
} else {
- if (argc < 2 || argv[0][2] != '\0') return usage("-s requires an argument");
+ if (argc < 2 || argv[0][2] != '\0') return syntax_error("-s requires an argument");
serial = argv[1];
argc--;
argv++;
@@ -1491,7 +1369,7 @@
gListenAll = 1;
} else if (!strncmp(argv[0], "-H", 2)) {
if (argv[0][2] == '\0') {
- if (argc < 2) return usage("-H requires an argument");
+ if (argc < 2) return syntax_error("-H requires an argument");
server_host_str = argv[1];
argc--;
argv++;
@@ -1500,7 +1378,7 @@
}
} else if (!strncmp(argv[0], "-P", 2)) {
if (argv[0][2] == '\0') {
- if (argc < 2) return usage("-P requires an argument");
+ if (argc < 2) return syntax_error("-P requires an argument");
server_port_str = argv[1];
argc--;
argv++;
@@ -1508,7 +1386,7 @@
server_port_str = argv[0] + 2;
}
} else if (!strcmp(argv[0], "-L")) {
- if (argc < 2) return usage("-L requires an argument");
+ if (argc < 2) return syntax_error("-L requires an argument");
server_socket_str = argv[1];
argc--;
argv++;
@@ -1521,8 +1399,7 @@
}
if ((server_host_str || server_port_str) && server_socket_str) {
- fprintf(stderr, "adb: -L is incompatible with -H or -P\n");
- exit(1);
+ return syntax_error("-L is incompatible with -H or -P");
}
// If -L, -H, or -P are specified, ignore environment variables.
@@ -1617,8 +1494,7 @@
} else if (argc == 2 && !strcmp(argv[1], "-l")) {
listopt = argv[1];
} else {
- fprintf(stderr, "Usage: adb devices [-l]\n");
- return 1;
+ return syntax_error("adb devices [-l]");
}
std::string query = android::base::StringPrintf("host:%s%s", argv[0], listopt);
@@ -1626,19 +1502,13 @@
return adb_query_command(query);
}
else if (!strcmp(argv[0], "connect")) {
- if (argc != 2) {
- fprintf(stderr, "Usage: adb connect <host>[:<port>]\n");
- return 1;
- }
+ if (argc != 2) return syntax_error("adb connect <host>[:<port>]");
std::string query = android::base::StringPrintf("host:connect:%s", argv[1]);
return adb_query_command(query);
}
else if (!strcmp(argv[0], "disconnect")) {
- if (argc > 2) {
- fprintf(stderr, "Usage: adb disconnect [<host>[:<port>]]\n");
- return 1;
- }
+ if (argc > 2) return syntax_error("adb disconnect [<host>[:<port>]]");
std::string query = android::base::StringPrintf("host:disconnect:%s",
(argc == 2) ? argv[1] : "");
@@ -1653,10 +1523,7 @@
else if (!strcmp(argv[0], "exec-in") || !strcmp(argv[0], "exec-out")) {
int exec_in = !strcmp(argv[0], "exec-in");
- if (argc < 2) {
- fprintf(stderr, "Usage: adb %s command\n", argv[0]);
- return 1;
- }
+ if (argc < 2) return syntax_error("adb %s command", argv[0]);
std::string cmd = "exec:";
cmd += argv[1];
@@ -1683,28 +1550,10 @@
return 0;
}
else if (!strcmp(argv[0], "kill-server")) {
- std::string error;
- int fd = _adb_connect("host:kill", &error);
- if (fd == -2) {
- // Failed to make network connection to server. Don't output the
- // network error since that is expected.
- fprintf(stderr,"* server not running *\n");
- // Successful exit code because the server is already "killed".
- return 0;
- } else if (fd == -1) {
- // Some other error.
- fprintf(stderr, "error: %s\n", error.c_str());
- return 1;
- } else {
- // Successfully connected, kill command sent, okay status came back.
- // Server should exit() in a moment, if not already.
- ReadOrderlyShutdown(fd);
- adb_close(fd);
- return 0;
- }
+ return adb_kill_server() ? 0 : 1;
}
else if (!strcmp(argv[0], "sideload")) {
- if (argc != 2) return usage("sideload requires an argument");
+ if (argc != 2) return syntax_error("sideload requires an argument");
if (adb_sideload_host(argv[1])) {
return 1;
} else {
@@ -1738,7 +1587,7 @@
bool reverse = !strcmp(argv[0], "reverse");
++argv;
--argc;
- if (argc < 1) return usage("%s requires an argument", argv[0]);
+ if (argc < 1) return syntax_error("%s requires an argument", argv[0]);
// Determine the <host-prefix> for this command.
std::string host_prefix;
@@ -1758,24 +1607,24 @@
std::string cmd, error;
if (strcmp(argv[0], "--list") == 0) {
- if (argc != 1) return usage("--list doesn't take any arguments");
+ if (argc != 1) return syntax_error("--list doesn't take any arguments");
return adb_query_command(host_prefix + ":list-forward");
} else if (strcmp(argv[0], "--remove-all") == 0) {
- if (argc != 1) return usage("--remove-all doesn't take any arguments");
+ if (argc != 1) return syntax_error("--remove-all doesn't take any arguments");
cmd = host_prefix + ":killforward-all";
} else if (strcmp(argv[0], "--remove") == 0) {
// forward --remove <local>
- if (argc != 2) return usage("--remove requires an argument");
+ if (argc != 2) return syntax_error("--remove requires an argument");
cmd = host_prefix + ":killforward:" + argv[1];
} else if (strcmp(argv[0], "--no-rebind") == 0) {
// forward --no-rebind <local> <remote>
- if (argc != 3) return usage("--no-rebind takes two arguments");
+ if (argc != 3) return syntax_error("--no-rebind takes two arguments");
if (forward_targets_are_valid(argv[1], argv[2], &error)) {
cmd = host_prefix + ":forward:norebind:" + argv[1] + ";" + argv[2];
}
} else {
// forward <local> <remote>
- if (argc != 2) return usage("forward takes two arguments");
+ if (argc != 2) return syntax_error("forward takes two arguments");
if (forward_targets_are_valid(argv[0], argv[1], &error)) {
cmd = host_prefix + ":forward:" + argv[0] + ";" + argv[1];
}
@@ -1804,40 +1653,41 @@
}
/* do_sync_*() commands */
else if (!strcmp(argv[0], "ls")) {
- if (argc != 2) return usage("ls requires an argument");
+ if (argc != 2) return syntax_error("ls requires an argument");
return do_sync_ls(argv[1]) ? 0 : 1;
}
else if (!strcmp(argv[0], "push")) {
bool copy_attrs = false;
+ bool sync = false;
std::vector<const char*> srcs;
const char* dst = nullptr;
- parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, ©_attrs);
- if (srcs.empty() || !dst) return usage("push requires an argument");
- return do_sync_push(srcs, dst) ? 0 : 1;
+ parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, ©_attrs, &sync);
+ if (srcs.empty() || !dst) return syntax_error("push requires an argument");
+ return do_sync_push(srcs, dst, sync) ? 0 : 1;
}
else if (!strcmp(argv[0], "pull")) {
bool copy_attrs = false;
std::vector<const char*> srcs;
const char* dst = ".";
- parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, ©_attrs);
- if (srcs.empty()) return usage("pull requires an argument");
+ parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, ©_attrs, nullptr);
+ if (srcs.empty()) return syntax_error("pull requires an argument");
return do_sync_pull(srcs, dst, copy_attrs) ? 0 : 1;
}
else if (!strcmp(argv[0], "install")) {
- if (argc < 2) return usage("install requires an argument");
+ 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(transport_type, serial, argc, argv);
}
else if (!strcmp(argv[0], "install-multiple")) {
- if (argc < 2) return usage("install-multiple requires an argument");
+ if (argc < 2) return syntax_error("install-multiple requires an argument");
return install_multiple_app(transport_type, serial, argc, argv);
}
else if (!strcmp(argv[0], "uninstall")) {
- if (argc < 2) return usage("uninstall requires an argument");
+ if (argc < 2) return syntax_error("uninstall requires an argument");
if (_use_legacy_install()) {
return uninstall_app_legacy(transport_type, serial, argc, argv);
}
@@ -1860,12 +1710,14 @@
// A local path or "android"/"data" arg was specified.
src = argv[1];
} else {
- return usage("usage: adb sync [-l] [PARTITION]");
+ return syntax_error("adb sync [-l] [PARTITION]");
}
+ if (src == "all") src = "";
+
if (src != "" &&
src != "system" && src != "data" && src != "vendor" && src != "oem") {
- return usage("don't know how to sync %s partition", src.c_str());
+ return syntax_error("don't know how to sync %s partition", src.c_str());
}
std::string system_src_path = product_file("system");
@@ -1917,7 +1769,7 @@
return restore(argc, argv);
}
else if (!strcmp(argv[0], "keygen")) {
- if (argc != 2) return usage("keygen requires an argument");
+ if (argc != 2) return syntax_error("keygen requires an argument");
// Always print key generation information for keygen command.
adb_trace_enable(AUTH);
return adb_auth_keygen(argv[1]);
@@ -1960,7 +1812,7 @@
return adb_query_command("host:host-features");
} else if (!strcmp(argv[0], "reconnect")) {
if (argc == 1) {
- return adb_query_command("host:reconnect");
+ return adb_query_command(format_host_command(argv[0], transport_type, serial));
} else if (argc == 2) {
if (!strcmp(argv[1], "device")) {
std::string err;
@@ -1970,12 +1822,12 @@
std::string err;
return adb_query_command("host:reconnect-offline");
} else {
- return usage("usage: adb reconnect [device|offline]");
+ return syntax_error("adb reconnect [device|offline]");
}
}
}
- usage("unknown command %s", argv[0]);
+ syntax_error("unknown command %s", argv[0]);
return 1;
}
@@ -2002,19 +1854,18 @@
// The last argument must be the APK file
const char* file = argv[argc - 1];
if (!android::base::EndsWithIgnoreCase(file, ".apk")) {
- fprintf(stderr, "Filename doesn't end .apk: %s\n", file);
- return EXIT_FAILURE;
+ return syntax_error("filename doesn't end .apk: %s", file);
}
struct stat sb;
if (stat(file, &sb) == -1) {
- fprintf(stderr, "Failed to stat %s: %s\n", file, strerror(errno));
+ fprintf(stderr, "adb: failed to stat %s: %s\n", file, strerror(errno));
return 1;
}
int localFd = adb_open(file, O_RDONLY);
if (localFd < 0) {
- fprintf(stderr, "Failed to open %s: %s\n", file, strerror(errno));
+ fprintf(stderr, "adb: failed to open %s: %s\n", file, strerror(errno));
return 1;
}
@@ -2032,7 +1883,7 @@
int remoteFd = adb_connect(cmd, &error);
if (remoteFd < 0) {
- fprintf(stderr, "Connect error for write: %s\n", error.c_str());
+ fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
adb_close(localFd);
return 1;
}
@@ -2044,12 +1895,12 @@
adb_close(localFd);
adb_close(remoteFd);
- if (strncmp("Success", buf, 7)) {
- fprintf(stderr, "Failed to install %s: %s", file, buf);
- return 1;
+ if (!strncmp("Success", buf, 7)) {
+ fputs(buf, stdout);
+ return 0;
}
- fputs(buf, stderr);
- return 0;
+ fprintf(stderr, "adb: failed to install %s: %s", file, buf);
+ return 1;
}
static int install_multiple_app(TransportType transport, const char* serial, int argc,
@@ -2071,10 +1922,7 @@
}
}
- if (first_apk == -1) {
- fprintf(stderr, "No APK file on command line\n");
- return 1;
- }
+ if (first_apk == -1) return syntax_error("need APK file on command line");
std::string install_cmd;
if (_use_legacy_install()) {
@@ -2092,7 +1940,7 @@
std::string error;
int fd = adb_connect(cmd, &error);
if (fd < 0) {
- fprintf(stderr, "Connect error for create: %s\n", error.c_str());
+ fprintf(stderr, "adb: connect error for create: %s\n", error.c_str());
return EXIT_FAILURE;
}
char buf[BUFSIZ];
@@ -2109,7 +1957,7 @@
}
}
if (session_id < 0) {
- fprintf(stderr, "Failed to create session\n");
+ fprintf(stderr, "adb: failed to create session\n");
fputs(buf, stderr);
return EXIT_FAILURE;
}
@@ -2120,7 +1968,7 @@
const char* file = argv[i];
struct stat sb;
if (stat(file, &sb) == -1) {
- fprintf(stderr, "Failed to stat %s: %s\n", file, strerror(errno));
+ fprintf(stderr, "adb: failed to stat %s: %s\n", file, strerror(errno));
success = 0;
goto finalize_session;
}
@@ -2132,7 +1980,7 @@
int localFd = adb_open(file, O_RDONLY);
if (localFd < 0) {
- fprintf(stderr, "Failed to open %s: %s\n", file, strerror(errno));
+ fprintf(stderr, "adb: failed to open %s: %s\n", file, strerror(errno));
success = 0;
goto finalize_session;
}
@@ -2140,7 +1988,7 @@
std::string error;
int remoteFd = adb_connect(cmd, &error);
if (remoteFd < 0) {
- fprintf(stderr, "Connect error for write: %s\n", error.c_str());
+ fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
adb_close(localFd);
success = 0;
goto finalize_session;
@@ -2153,7 +2001,7 @@
adb_close(remoteFd);
if (strncmp("Success", buf, 7)) {
- fprintf(stderr, "Failed to write %s\n", file);
+ fprintf(stderr, "adb: failed to write %s\n", file);
fputs(buf, stderr);
success = 0;
goto finalize_session;
@@ -2167,20 +2015,19 @@
install_cmd.c_str(), success ? "commit" : "abandon", session_id);
fd = adb_connect(service, &error);
if (fd < 0) {
- fprintf(stderr, "Connect error for finalize: %s\n", error.c_str());
+ fprintf(stderr, "adb: connect error for finalize: %s\n", error.c_str());
return EXIT_FAILURE;
}
read_status_line(fd, buf, sizeof(buf));
adb_close(fd);
if (!strncmp("Success", buf, 7)) {
- fputs(buf, stderr);
+ fputs(buf, stdout);
return 0;
- } else {
- fprintf(stderr, "Failed to finalize session\n");
- fputs(buf, stderr);
- return EXIT_FAILURE;
}
+ fprintf(stderr, "adb: failed to finalize session\n");
+ fputs(buf, stderr);
+ return EXIT_FAILURE;
}
static int pm_command(TransportType transport, const char* serial, int argc, const char** argv) {
@@ -2238,16 +2085,13 @@
}
}
- if (last_apk == -1) {
- fprintf(stderr, "No APK file on command line\n");
- return EXIT_FAILURE;
- }
+ if (last_apk == -1) return syntax_error("need APK file on command line");
int result = -1;
std::vector<const char*> apk_file = {argv[last_apk]};
std::string apk_dest = android::base::StringPrintf(
where, android::base::Basename(argv[last_apk]).c_str());
- if (!do_sync_push(apk_file, apk_dest.c_str())) goto cleanup_apk;
+ 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);
diff --git a/adb/commandline.h b/adb/commandline.h
index 0cf655c..9ba69a3 100644
--- a/adb/commandline.h
+++ b/adb/commandline.h
@@ -87,7 +87,6 @@
extern DefaultStandardStreamsCallback DEFAULT_STANDARD_STREAMS_CALLBACK;
int adb_commandline(int argc, const char** argv);
-int usage();
// Connects to the device "shell" service with |command| and prints the
// resulting output.
diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp
index 7da94ce..1c94298 100644
--- a/adb/daemon/main.cpp
+++ b/adb/daemon/main.cpp
@@ -32,10 +32,10 @@
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <libminijail.h>
+#include <log/log_properties.h>
#include <scoped_minijail.h>
#include <private/android_filesystem_config.h>
-#include <private/android_logger.h>
#include "debuggerd/handler.h"
#include "selinux/android.h"
@@ -107,10 +107,10 @@
// AID_SDCARD_RW to allow writing to the SD card
// AID_NET_BW_STATS to read out qtaguid statistics
// AID_READPROC for reading /proc entries across UID boundaries
- gid_t groups[] = {AID_ADB, AID_LOG, AID_INPUT,
- AID_INET, AID_NET_BT, AID_NET_BT_ADMIN,
- AID_SDCARD_R, AID_SDCARD_RW, AID_NET_BW_STATS,
- AID_READPROC};
+ // AID_UHID for using 'hid' command to read/write to /dev/uhid
+ gid_t groups[] = {AID_ADB, AID_LOG, AID_INPUT, AID_INET,
+ AID_NET_BT, AID_NET_BT_ADMIN, AID_SDCARD_R, AID_SDCARD_RW,
+ AID_NET_BW_STATS, AID_READPROC, AID_UHID};
minijail_set_supplementary_gids(jail.get(), arraysize(groups), groups);
// Don't listen on a port (default 5037) if running in secure mode.
@@ -233,9 +233,8 @@
adb_device_banner = optarg;
break;
case 'v':
- printf("Android Debug Bridge Daemon version %d.%d.%d %s\n",
- ADB_VERSION_MAJOR, ADB_VERSION_MINOR, ADB_SERVER_VERSION,
- ADB_REVISION);
+ printf("Android Debug Bridge Daemon version %d.%d.%d (%s)\n", ADB_VERSION_MAJOR,
+ ADB_VERSION_MINOR, ADB_SERVER_VERSION, ADB_VERSION);
return 0;
default:
// getopt already prints "adbd: invalid option -- %c" for us.
diff --git a/adb/daemon/mdns.cpp b/adb/daemon/mdns.cpp
index 7811143..849378f 100644
--- a/adb/daemon/mdns.cpp
+++ b/adb/daemon/mdns.cpp
@@ -17,12 +17,14 @@
#include "adb_mdns.h"
#include "sysdeps.h"
-#include <chrono>
#include <dns_sd.h>
#include <endian.h>
-#include <mutex>
#include <unistd.h>
+#include <chrono>
+#include <mutex>
+#include <thread>
+
#include <android-base/logging.h>
#include <android-base/properties.h>
@@ -58,7 +60,7 @@
}
}
-static void setup_mdns_thread(void* /* unused */) {
+static void setup_mdns_thread() {
start_mdns();
std::lock_guard<std::mutex> lock(mdns_lock);
@@ -88,7 +90,7 @@
void setup_mdns(int port_in) {
port = port_in;
- adb_thread_create(setup_mdns_thread, nullptr, nullptr);
+ std::thread(setup_mdns_thread).detach();
// TODO: Make this more robust against a hard kill.
atexit(teardown_mdns);
diff --git a/adb/daemon/usb.cpp b/adb/daemon/usb.cpp
index 92e9039..d3b2f3d 100644
--- a/adb/daemon/usb.cpp
+++ b/adb/daemon/usb.cpp
@@ -64,7 +64,7 @@
#define FUNCTIONFS_ENDPOINT_ALLOC _IOR('g', 231, __u32)
-static constexpr size_t ENDPOINT_ALLOC_RETRIES = 2;
+static constexpr size_t ENDPOINT_ALLOC_RETRIES = 10;
static int dummy_fd = -1;
@@ -451,9 +451,7 @@
h->close = usb_ffs_close;
D("[ usb_init - starting thread ]");
- if (!adb_thread_create(usb_ffs_open_thread, h)) {
- fatal_errno("[ cannot create usb thread ]\n");
- }
+ std::thread(usb_ffs_open_thread, h).detach();
}
void usb_init() {
diff --git a/adb/diagnose_usb.cpp b/adb/diagnose_usb.cpp
index 0f067b0..9f721bf 100644
--- a/adb/diagnose_usb.cpp
+++ b/adb/diagnose_usb.cpp
@@ -25,13 +25,14 @@
#if defined(__linux__)
#include <grp.h>
+#include <pwd.h>
#endif
static const char kPermissionsHelpUrl[] = "http://developer.android.com/tools/device.html";
-// Returns a message describing any potential problems we find with udev, or nullptr if we can't
-// find plugdev information (i.e. udev is not installed).
-static const char* GetUdevProblem() {
+// Returns a message describing any potential problems we find with udev, or an empty string if we
+// can't find plugdev information (i.e. udev is not installed).
+static std::string GetUdevProblem() {
#if defined(__linux__)
errno = 0;
group* plugdev_group = getgrnam("plugdev");
@@ -41,43 +42,45 @@
perror("failed to read plugdev group info");
}
// We can't give any generally useful advice here, just let the caller print the help URL.
- return nullptr;
+ return "";
}
- // getgroups(2) indicates that the group_member() may not check the egid so we check it
+ // getgroups(2) indicates that the GNU group_member(3) may not check the egid so we check it
// additionally just to be sure.
if (group_member(plugdev_group->gr_gid) || getegid() == plugdev_group->gr_gid) {
// The user is in plugdev so the problem is likely with the udev rules.
- return "verify udev rules";
+ return "user in plugdev group; are your udev rules wrong?";
}
- return "udev requires plugdev group membership";
+ passwd* pwd = getpwuid(getuid());
+ return android::base::StringPrintf("user %s is not in the plugdev group",
+ pwd ? pwd->pw_name : "?");
#else
- return nullptr;
+ return "";
#endif
}
// Short help text must be a single line, and will look something like:
-// no permissions (reason); see <URL>
+//
+// no permissions (reason); see [URL]
std::string UsbNoPermissionsShortHelpText() {
std::string help_text = "no permissions";
- const char* problem = GetUdevProblem();
- if (problem != nullptr) {
- help_text += android::base::StringPrintf(" (%s)", problem);
- }
+ std::string problem(GetUdevProblem());
+ if (!problem.empty()) help_text += " (" + problem + ")";
return android::base::StringPrintf("%s; see [%s]", help_text.c_str(), kPermissionsHelpUrl);
}
-// Long help text can span multiple lines and should provide more detailed information.
+// Long help text can span multiple lines but doesn't currently provide more detailed information:
+//
+// insufficient permissions for device: reason
+// See [URL] for more information
std::string UsbNoPermissionsLongHelpText() {
std::string header = "insufficient permissions for device";
- const char* problem = GetUdevProblem();
- if (problem != nullptr) {
- header += android::base::StringPrintf(": %s", problem);
- }
+ std::string problem(GetUdevProblem());
+ if (!problem.empty()) header += ": " + problem;
- return android::base::StringPrintf("%s.\nSee [%s] for more information.",
- header.c_str(), kPermissionsHelpUrl);
+ return android::base::StringPrintf("%s\nSee [%s] for more information", header.c_str(),
+ kPermissionsHelpUrl);
}
diff --git a/adb/fdevent.cpp b/adb/fdevent.cpp
index 04cd865..b28de4b 100644
--- a/adb/fdevent.cpp
+++ b/adb/fdevent.cpp
@@ -26,15 +26,19 @@
#include <unistd.h>
#include <atomic>
+#include <functional>
#include <list>
+#include <mutex>
#include <unordered_map>
#include <vector>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
+#include <android-base/thread_annotations.h>
#include "adb_io.h"
#include "adb_trace.h"
+#include "adb_unique_fd.h"
#include "adb_utils.h"
#if !ADB_HOST
@@ -75,13 +79,17 @@
static bool main_thread_valid;
static unsigned long main_thread_id;
-static void check_main_thread() {
+static auto& run_queue_notify_fd = *new unique_fd();
+static auto& run_queue_mutex = *new std::mutex();
+static auto& run_queue GUARDED_BY(run_queue_mutex) = *new std::vector<std::function<void()>>();
+
+void check_main_thread() {
if (main_thread_valid) {
CHECK_EQ(main_thread_id, adb_thread_id());
}
}
-static void set_main_thread() {
+void set_main_thread() {
main_thread_valid = true;
main_thread_id = adb_thread_id();
}
@@ -112,8 +120,7 @@
return android::base::StringPrintf("(fdevent %d %s)", fde->fd, state.c_str());
}
-fdevent *fdevent_create(int fd, fd_func func, void *arg)
-{
+fdevent* fdevent_create(int fd, fd_func func, void* arg) {
check_main_thread();
fdevent *fde = (fdevent*) malloc(sizeof(fdevent));
if(fde == 0) return 0;
@@ -122,8 +129,7 @@
return fde;
}
-void fdevent_destroy(fdevent *fde)
-{
+void fdevent_destroy(fdevent* fde) {
check_main_thread();
if(fde == 0) return;
if(!(fde->state & FDE_CREATED)) {
@@ -278,8 +284,7 @@
}
}
-static void fdevent_call_fdfunc(fdevent* fde)
-{
+static void fdevent_call_fdfunc(fdevent* fde) {
unsigned events = fde->events;
fde->events = 0;
CHECK(fde->state & FDE_PENDING);
@@ -292,10 +297,7 @@
#include <sys/ioctl.h>
-static void fdevent_subproc_event_func(int fd, unsigned ev,
- void* /* userdata */)
-{
-
+static void fdevent_subproc_event_func(int fd, unsigned ev, void* /* userdata */) {
D("subproc handling on fd = %d, ev = %x", fd, ev);
CHECK_GE(fd, 0);
@@ -342,8 +344,7 @@
}
}
-void fdevent_subproc_setup()
-{
+static void fdevent_subproc_setup() {
int s[2];
if(adb_socketpair(s)) {
@@ -358,12 +359,63 @@
}
#endif // !ADB_HOST
-void fdevent_loop()
-{
+static void fdevent_run_flush() REQUIRES(run_queue_mutex) {
+ for (auto& f : run_queue) {
+ f();
+ }
+ run_queue.clear();
+}
+
+static void fdevent_run_func(int fd, unsigned ev, void* /* userdata */) {
+ CHECK_GE(fd, 0);
+ CHECK(ev & FDE_READ);
+
+ char buf[1024];
+
+ // Empty the fd.
+ if (adb_read(fd, buf, sizeof(buf)) == -1) {
+ PLOG(FATAL) << "failed to empty run queue notify fd";
+ }
+
+ std::lock_guard<std::mutex> lock(run_queue_mutex);
+ fdevent_run_flush();
+}
+
+static void fdevent_run_setup() {
+ std::lock_guard<std::mutex> lock(run_queue_mutex);
+ CHECK(run_queue_notify_fd.get() == -1);
+ int s[2];
+ if (adb_socketpair(s) != 0) {
+ PLOG(FATAL) << "failed to create run queue notify socketpair";
+ }
+
+ run_queue_notify_fd.reset(s[0]);
+ fdevent* fde = fdevent_create(s[1], fdevent_run_func, nullptr);
+ CHECK(fde != nullptr);
+ fdevent_add(fde, FDE_READ);
+
+ fdevent_run_flush();
+}
+
+void fdevent_run_on_main_thread(std::function<void()> fn) {
+ std::lock_guard<std::mutex> lock(run_queue_mutex);
+ run_queue.push_back(std::move(fn));
+
+ // run_queue_notify_fd could still be -1 if we're called before fdevent has finished setting up.
+ // In that case, rely on the setup code to flush the queue without a notification being needed.
+ if (run_queue_notify_fd != -1) {
+ if (adb_write(run_queue_notify_fd.get(), "", 1) != 1) {
+ PLOG(FATAL) << "failed to write to run queue notify fd";
+ }
+ }
+}
+
+void fdevent_loop() {
set_main_thread();
#if !ADB_HOST
fdevent_subproc_setup();
#endif // !ADB_HOST
+ fdevent_run_setup();
while (true) {
if (terminate_loop) {
@@ -393,6 +445,11 @@
void fdevent_reset() {
g_poll_node_map.clear();
g_pending_list.clear();
+
+ std::lock_guard<std::mutex> lock(run_queue_mutex);
+ run_queue_notify_fd.reset();
+ run_queue.clear();
+
main_thread_valid = false;
terminate_loop = false;
}
diff --git a/adb/fdevent.h b/adb/fdevent.h
index 207f9b7..896400a 100644
--- a/adb/fdevent.h
+++ b/adb/fdevent.h
@@ -20,6 +20,8 @@
#include <stddef.h>
#include <stdint.h> /* for int64_t */
+#include <functional>
+
/* events that may be observed */
#define FDE_READ 0x0001
#define FDE_WRITE 0x0002
@@ -76,9 +78,15 @@
*/
void fdevent_loop();
+void check_main_thread();
+
+// Queue an operation to run on the main thread.
+void fdevent_run_on_main_thread(std::function<void()> fn);
+
// The following functions are used only for tests.
void fdevent_terminate_loop();
size_t fdevent_installed_count();
void fdevent_reset();
+void set_main_thread();
#endif
diff --git a/adb/fdevent_test.cpp b/adb/fdevent_test.cpp
index c933ed5..86e0209 100644
--- a/adb/fdevent_test.cpp
+++ b/adb/fdevent_test.cpp
@@ -21,6 +21,7 @@
#include <limits>
#include <queue>
#include <string>
+#include <thread>
#include <vector>
#include "adb_io.h"
@@ -77,9 +78,9 @@
};
TEST_F(FdeventTest, fdevent_terminate) {
- adb_thread_t thread;
PrepareThread();
- ASSERT_TRUE(adb_thread_create([](void*) { fdevent_loop(); }, nullptr, &thread));
+
+ std::thread thread(fdevent_loop);
TerminateThread(thread);
}
@@ -112,7 +113,6 @@
int fd_pair2[2];
ASSERT_EQ(0, adb_socketpair(fd_pair1));
ASSERT_EQ(0, adb_socketpair(fd_pair2));
- adb_thread_t thread;
ThreadArg thread_arg;
thread_arg.first_read_fd = fd_pair1[0];
thread_arg.last_write_fd = fd_pair2[1];
@@ -121,8 +121,7 @@
int reader = fd_pair2[0];
PrepareThread();
- ASSERT_TRUE(adb_thread_create(reinterpret_cast<void (*)(void*)>(FdEventThreadFunc), &thread_arg,
- &thread));
+ std::thread thread(FdEventThreadFunc, &thread_arg);
for (size_t i = 0; i < MESSAGE_LOOP_COUNT; ++i) {
std::string read_buffer = MESSAGE;
@@ -152,7 +151,7 @@
}
}
-static void InvalidFdThreadFunc(void*) {
+static void InvalidFdThreadFunc() {
const int INVALID_READ_FD = std::numeric_limits<int>::max() - 1;
size_t happened_event_count = 0;
InvalidFdArg read_arg;
@@ -171,7 +170,27 @@
}
TEST_F(FdeventTest, invalid_fd) {
- adb_thread_t thread;
- ASSERT_TRUE(adb_thread_create(InvalidFdThreadFunc, nullptr, &thread));
- ASSERT_TRUE(adb_thread_join(thread));
+ std::thread thread(InvalidFdThreadFunc);
+ thread.join();
+}
+
+TEST_F(FdeventTest, run_on_main_thread) {
+ std::vector<int> vec;
+
+ PrepareThread();
+ std::thread thread(fdevent_loop);
+
+ for (int i = 0; i < 100; ++i) {
+ fdevent_run_on_main_thread([i, &vec]() {
+ check_main_thread();
+ vec.push_back(i);
+ });
+ }
+
+ TerminateThread(thread);
+
+ ASSERT_EQ(100u, vec.size());
+ for (int i = 0; i < 100; ++i) {
+ ASSERT_EQ(i, vec[i]);
+ }
}
diff --git a/adb/fdevent_test.h b/adb/fdevent_test.h
index ef65b74..5ca49ac 100644
--- a/adb/fdevent_test.h
+++ b/adb/fdevent_test.h
@@ -16,6 +16,8 @@
#include <gtest/gtest.h>
+#include <thread>
+
#include "socket.h"
#include "sysdeps.h"
@@ -51,18 +53,18 @@
size_t GetAdditionalLocalSocketCount() {
#if ADB_HOST
- // dummy socket installed in PrepareThread()
- return 1;
-#else
- // dummy socket and one more socket installed in fdevent_subproc_setup()
+ // dummy socket installed in PrepareThread() + fdevent_run_on_main_thread socket
return 2;
+#else
+ // dummy socket + fdevent_run_on_main_thread + fdevent_subproc_setup() sockets
+ return 3;
#endif
}
- void TerminateThread(adb_thread_t thread) {
+ void TerminateThread(std::thread& thread) {
fdevent_terminate_loop();
ASSERT_TRUE(WriteFdExactly(dummy, "", 1));
- ASSERT_TRUE(adb_thread_join(thread));
+ thread.join();
ASSERT_EQ(0, adb_close(dummy));
}
};
diff --git a/adb/file_sync_client.cpp b/adb/file_sync_client.cpp
index 22bd2f2..2576fb1 100644
--- a/adb/file_sync_client.cpp
+++ b/adb/file_sync_client.cpp
@@ -674,11 +674,22 @@
return true;
}
-static bool sync_send(SyncConnection& sc, const char* lpath, const char* rpath,
- unsigned mtime, mode_t mode)
-{
+static bool sync_send(SyncConnection& sc, const char* lpath, const char* rpath, unsigned mtime,
+ mode_t mode, bool sync) {
std::string path_and_mode = android::base::StringPrintf("%s,%d", rpath, mode);
+ if (sync) {
+ struct stat st;
+ if (sync_lstat(sc, rpath, &st)) {
+ // For links, we cannot update the atime/mtime.
+ if ((S_ISREG(mode & st.st_mode) && st.st_mtime == static_cast<time_t>(mtime)) ||
+ (S_ISLNK(mode & st.st_mode) && st.st_mtime >= static_cast<time_t>(mtime))) {
+ sc.RecordFilesSkipped(1);
+ return true;
+ }
+ }
+ }
+
if (S_ISLNK(mode)) {
#if !defined(_WIN32)
char buf[PATH_MAX];
@@ -902,7 +913,7 @@
if (list_only) {
sc.Println("would push: %s -> %s", ci.lpath.c_str(), ci.rpath.c_str());
} else {
- if (!sync_send(sc, ci.lpath.c_str(), ci.rpath.c_str(), ci.time, ci.mode)) {
+ if (!sync_send(sc, ci.lpath.c_str(), ci.rpath.c_str(), ci.time, ci.mode, false)) {
return false;
}
}
@@ -916,7 +927,7 @@
return true;
}
-bool do_sync_push(const std::vector<const char*>& srcs, const char* dst) {
+bool do_sync_push(const std::vector<const char*>& srcs, const char* dst, bool sync) {
SyncConnection sc;
if (!sc.IsValid()) return false;
@@ -981,7 +992,7 @@
dst_dir.append(android::base::Basename(src_path));
}
- success &= copy_local_dir_remote(sc, src_path, dst_dir.c_str(), false, false);
+ success &= copy_local_dir_remote(sc, src_path, dst_dir.c_str(), sync, false);
continue;
} else if (!should_push_file(st.st_mode)) {
sc.Warning("skipping special file '%s' (mode = 0o%o)", src_path, st.st_mode);
@@ -1002,7 +1013,7 @@
sc.NewTransfer();
sc.SetExpectedTotalBytes(st.st_size);
- success &= sync_send(sc, src_path, dst_path, st.st_mtime, st.st_mode);
+ success &= sync_send(sc, src_path, dst_path, st.st_mtime, st.st_mode, sync);
sc.ReportTransferRate(src_path, TransferDirection::push);
}
diff --git a/adb/file_sync_service.h b/adb/file_sync_service.h
index 90f1965..6606efd 100644
--- a/adb/file_sync_service.h
+++ b/adb/file_sync_service.h
@@ -81,7 +81,7 @@
void file_sync_service(int fd, void* cookie);
bool do_sync_ls(const char* path);
-bool do_sync_push(const std::vector<const char*>& srcs, const char* dst);
+bool do_sync_push(const std::vector<const char*>& srcs, const char* dst, bool sync);
bool do_sync_pull(const std::vector<const char*>& srcs, const char* dst,
bool copy_attrs, const char* name=nullptr);
diff --git a/adb/services.cpp b/adb/services.cpp
index 47f0a03..9605e6e 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -31,6 +31,8 @@
#include <unistd.h>
#endif
+#include <thread>
+
#include <android-base/file.h>
#include <android-base/parsenetaddress.h>
#include <android-base/stringprintf.h>
@@ -41,7 +43,7 @@
#include <android-base/properties.h>
#include <bootloader_message/bootloader_message.h>
#include <cutils/android_reboot.h>
-#include <private/android_logger.h>
+#include <log/log_properties.h>
#endif
#include "adb.h"
@@ -259,13 +261,7 @@
sti->cookie = cookie;
sti->fd = s[1];
- if (!adb_thread_create(service_bootstrap_func, sti)) {
- free(sti);
- adb_close(s[0]);
- adb_close(s[1]);
- printf("cannot create service thread\n");
- return -1;
- }
+ std::thread(service_bootstrap_func, sti).detach();
D("service thread started, %d:%d",s[0], s[1]);
return s[0];
@@ -351,7 +347,7 @@
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);
- if (t != nullptr && (sinfo->state == kCsAny || sinfo->state == t->connection_state)) {
+ if (t != nullptr && (sinfo->state == kCsAny || sinfo->state == t->GetConnectionState())) {
SendOkay(fd);
break;
} else if (!is_ambiguous) {
diff --git a/adb/set_verity_enable_state_service.cpp b/adb/set_verity_enable_state_service.cpp
index 76b156d..253d14a 100644
--- a/adb/set_verity_enable_state_service.cpp
+++ b/adb/set_verity_enable_state_service.cpp
@@ -20,13 +20,14 @@
#include <fcntl.h>
#include <inttypes.h>
+#include <libavb_user/libavb_user.h>
#include <stdarg.h>
#include <stdio.h>
#include <sys/stat.h>
#include "android-base/properties.h"
#include "android-base/stringprintf.h"
-#include <private/android_logger.h>
+#include <log/log_properties.h>
#include "adb.h"
#include "adb_io.h"
@@ -45,13 +46,12 @@
#endif
/* Turn verity on/off */
-static int set_verity_enabled_state(int fd, const char *block_device,
- const char* mount_point, bool enable)
-{
+static bool set_verity_enabled_state(int fd, const char* block_device, const char* mount_point,
+ bool enable) {
if (!make_block_device_writable(block_device)) {
WriteFdFmt(fd, "Could not make block device %s writable (%s).\n",
block_device, strerror(errno));
- return -1;
+ return false;
}
fec::io fh(block_device, O_RDWR);
@@ -59,70 +59,142 @@
if (!fh) {
WriteFdFmt(fd, "Could not open block device %s (%s).\n", block_device, strerror(errno));
WriteFdFmt(fd, "Maybe run adb root?\n");
- return -1;
+ return false;
}
fec_verity_metadata metadata;
if (!fh.get_verity_metadata(metadata)) {
WriteFdFmt(fd, "Couldn't find verity metadata!\n");
- return -1;
+ return false;
}
if (!enable && metadata.disabled) {
WriteFdFmt(fd, "Verity already disabled on %s\n", mount_point);
- return -1;
+ return false;
}
if (enable && !metadata.disabled) {
WriteFdFmt(fd, "Verity already enabled on %s\n", mount_point);
- return -1;
+ return false;
}
if (!fh.set_verity_status(enable)) {
WriteFdFmt(fd, "Could not set verity %s flag on device %s with error %s\n",
enable ? "enabled" : "disabled",
block_device, strerror(errno));
- return -1;
+ return false;
}
WriteFdFmt(fd, "Verity %s on %s\n", enable ? "enabled" : "disabled", mount_point);
- return 0;
+ return true;
+}
+
+/* Helper function to get A/B suffix, if any. If the device isn't
+ * using A/B the empty string is returned. Otherwise either "_a",
+ * "_b", ... is returned.
+ *
+ * Note that since sometime in O androidboot.slot_suffix is deprecated
+ * and androidboot.slot should be used instead. Since bootloaders may
+ * be out of sync with the OS, we check both and for extra safety
+ * prepend a leading underscore if there isn't one already.
+ */
+static std::string get_ab_suffix() {
+ std::string ab_suffix = android::base::GetProperty("ro.boot.slot_suffix", "");
+ if (ab_suffix == "") {
+ ab_suffix = android::base::GetProperty("ro.boot.slot", "");
+ }
+ if (ab_suffix.size() > 0 && ab_suffix[0] != '_') {
+ ab_suffix = std::string("_") + ab_suffix;
+ }
+ return ab_suffix;
+}
+
+/* Use AVB to turn verity on/off */
+static bool set_avb_verity_enabled_state(int fd, AvbOps* ops, bool enable_verity) {
+ std::string ab_suffix = get_ab_suffix();
+
+ bool verity_enabled;
+ if (!avb_user_verity_get(ops, ab_suffix.c_str(), &verity_enabled)) {
+ WriteFdFmt(fd, "Error getting verity state\n");
+ return false;
+ }
+
+ if ((verity_enabled && enable_verity) || (!verity_enabled && !enable_verity)) {
+ WriteFdFmt(fd, "verity is already %s\n", verity_enabled ? "enabled" : "disabled");
+ return false;
+ }
+
+ if (!avb_user_verity_set(ops, ab_suffix.c_str(), enable_verity)) {
+ WriteFdFmt(fd, "Error setting verity\n");
+ return false;
+ }
+
+ WriteFdFmt(fd, "Successfully %s verity\n", enable_verity ? "enabled" : "disabled");
+ return true;
}
void set_verity_enabled_state_service(int fd, void* cookie) {
unique_fd closer(fd);
+ bool any_changed = false;
bool enable = (cookie != NULL);
- if (!kAllowDisableVerity) {
- WriteFdFmt(fd, "%s-verity only works for userdebug builds\n",
- enable ? "enable" : "disable");
+
+ // Figure out if we're using VB1.0 or VB2.0 (aka AVB) - by
+ // contract, androidboot.vbmeta.digest is set by the bootloader
+ // when using AVB).
+ bool using_avb = !android::base::GetProperty("ro.boot.vbmeta.digest", "").empty();
+
+ // If using AVB, dm-verity is used on any build so we want it to
+ // be possible to disable/enable on any build (except USER). For
+ // VB1.0 dm-verity is only enabled on certain builds.
+ if (!using_avb) {
+ if (!kAllowDisableVerity) {
+ WriteFdFmt(fd, "%s-verity only works for userdebug builds\n",
+ enable ? "enable" : "disable");
+ }
+
+ if (!android::base::GetBoolProperty("ro.secure", false)) {
+ WriteFdFmt(fd, "verity not enabled - ENG build\n");
+ return;
+ }
}
- if (!android::base::GetBoolProperty("ro.secure", false)) {
- WriteFdFmt(fd, "verity not enabled - ENG build\n");
- return;
- }
+ // Should never be possible to disable dm-verity on a USER build
+ // regardless of using AVB or VB1.0.
if (!__android_log_is_debuggable()) {
WriteFdFmt(fd, "verity cannot be disabled/enabled - USER build\n");
return;
}
- // read all fstab entries at once from all sources
- fstab = fs_mgr_read_fstab_default();
- if (!fstab) {
- WriteFdFmt(fd, "Failed to read fstab\nMaybe run adb root?\n");
- return;
- }
+ if (using_avb) {
+ // Yep, the system is using AVB.
+ AvbOps* ops = avb_ops_user_new();
+ if (ops == nullptr) {
+ WriteFdFmt(fd, "Error getting AVB ops\n");
+ return;
+ }
+ if (set_avb_verity_enabled_state(fd, ops, enable)) {
+ any_changed = true;
+ }
+ avb_ops_user_free(ops);
+ } else {
+ // Not using AVB - assume VB1.0.
- // Loop through entries looking for ones that vold manages.
- bool any_changed = false;
- for (int i = 0; i < fstab->num_entries; i++) {
- if (fs_mgr_is_verified(&fstab->recs[i])) {
- if (!set_verity_enabled_state(fd, fstab->recs[i].blk_device,
- fstab->recs[i].mount_point,
- enable)) {
- any_changed = true;
+ // read all fstab entries at once from all sources
+ fstab = fs_mgr_read_fstab_default();
+ if (!fstab) {
+ WriteFdFmt(fd, "Failed to read fstab\nMaybe run adb root?\n");
+ return;
+ }
+
+ // Loop through entries looking for ones that vold manages.
+ for (int i = 0; i < fstab->num_entries; i++) {
+ if (fs_mgr_is_verified(&fstab->recs[i])) {
+ if (set_verity_enabled_state(fd, fstab->recs[i].blk_device,
+ fstab->recs[i].mount_point, enable)) {
+ any_changed = true;
+ }
}
}
}
diff --git a/adb/shell_service.cpp b/adb/shell_service.cpp
index d4f334b..ee821f8 100644
--- a/adb/shell_service.cpp
+++ b/adb/shell_service.cpp
@@ -90,6 +90,7 @@
#include <memory>
#include <string>
+#include <thread>
#include <unordered_map>
#include <vector>
@@ -392,12 +393,7 @@
bool Subprocess::StartThread(std::unique_ptr<Subprocess> subprocess, std::string* error) {
Subprocess* raw = subprocess.release();
- if (!adb_thread_create(ThreadHandler, raw)) {
- *error =
- android::base::StringPrintf("failed to create subprocess thread: %s", strerror(errno));
- kill(raw->pid_, SIGKILL);
- return false;
- }
+ std::thread(ThreadHandler, raw).detach();
return true;
}
diff --git a/adb/socket_test.cpp b/adb/socket_test.cpp
index f56f7f7..f7c66db 100644
--- a/adb/socket_test.cpp
+++ b/adb/socket_test.cpp
@@ -42,10 +42,6 @@
class LocalSocketTest : public FdeventTest {};
-static void FdEventThreadFunc(void*) {
- fdevent_loop();
-}
-
constexpr auto SLEEP_FOR_FDEVENT = 100ms;
TEST_F(LocalSocketTest, smoke) {
@@ -88,8 +84,7 @@
connect(prev_tail, end);
PrepareThread();
- adb_thread_t thread;
- ASSERT_TRUE(adb_thread_create(FdEventThreadFunc, nullptr, &thread));
+ std::thread thread(fdevent_loop);
for (size_t i = 0; i < MESSAGE_LOOP_COUNT; ++i) {
std::string read_buffer = MESSAGE;
@@ -152,9 +147,7 @@
arg.cause_close_fd = cause_close_fd[1];
PrepareThread();
- adb_thread_t thread;
- ASSERT_TRUE(adb_thread_create(reinterpret_cast<void (*)(void*)>(CloseWithPacketThreadFunc),
- &arg, &thread));
+ std::thread thread(CloseWithPacketThreadFunc, &arg);
// Wait until the fdevent_loop() starts.
std::this_thread::sleep_for(SLEEP_FOR_FDEVENT);
ASSERT_EQ(0, adb_close(cause_close_fd[0]));
@@ -177,9 +170,7 @@
arg.cause_close_fd = cause_close_fd[1];
PrepareThread();
- adb_thread_t thread;
- ASSERT_TRUE(adb_thread_create(reinterpret_cast<void (*)(void*)>(CloseWithPacketThreadFunc),
- &arg, &thread));
+ std::thread thread(CloseWithPacketThreadFunc, &arg);
// Wait until the fdevent_loop() starts.
std::this_thread::sleep_for(SLEEP_FOR_FDEVENT);
ASSERT_EQ(0, adb_close(cause_close_fd[0]));
@@ -211,10 +202,7 @@
arg.cause_close_fd = cause_close_fd[1];
PrepareThread();
- adb_thread_t thread;
- ASSERT_TRUE(adb_thread_create(reinterpret_cast<void (*)(void*)>(CloseWithPacketThreadFunc),
- &arg, &thread));
-
+ std::thread thread(CloseWithPacketThreadFunc, &arg);
// Wait until the fdevent_loop() starts.
std::this_thread::sleep_for(SLEEP_FOR_FDEVENT);
EXPECT_EQ(2u + GetAdditionalLocalSocketCount(), fdevent_installed_count());
@@ -252,9 +240,7 @@
int listen_fd = network_inaddr_any_server(5038, SOCK_STREAM, &error);
ASSERT_GE(listen_fd, 0);
- adb_thread_t client_thread;
- ASSERT_TRUE(adb_thread_create(reinterpret_cast<void (*)(void*)>(ClientThreadFunc), nullptr,
- &client_thread));
+ std::thread client_thread(ClientThreadFunc);
int accept_fd = adb_socket_accept(listen_fd, nullptr, nullptr);
ASSERT_GE(accept_fd, 0);
@@ -262,16 +248,14 @@
arg.socket_fd = accept_fd;
PrepareThread();
- adb_thread_t thread;
- ASSERT_TRUE(adb_thread_create(reinterpret_cast<void (*)(void*)>(CloseRdHupSocketThreadFunc),
- &arg, &thread));
+ std::thread thread(CloseRdHupSocketThreadFunc, &arg);
// Wait until the fdevent_loop() starts.
std::this_thread::sleep_for(SLEEP_FOR_FDEVENT);
EXPECT_EQ(1u + GetAdditionalLocalSocketCount(), fdevent_installed_count());
// Wait until the client closes its socket.
- ASSERT_TRUE(adb_thread_join(client_thread));
+ client_thread.join();
std::this_thread::sleep_for(SLEEP_FOR_FDEVENT);
ASSERT_EQ(GetAdditionalLocalSocketCount(), fdevent_installed_count());
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index c05903f..14ad1ff 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -32,7 +32,7 @@
#if !ADB_HOST
#include <android-base/properties.h>
-#include <private/android_logger.h>
+#include <log/log_properties.h>
#endif
#include "adb.h"
@@ -794,7 +794,7 @@
if (!s->transport) {
SendFail(s->peer->fd, "device offline (no transport)");
goto fail;
- } else if (s->transport->connection_state == kCsOffline) {
+ } else if (s->transport->GetConnectionState() == kCsOffline) {
/* if there's no remote we fail the connection
** right here and terminate it
*/
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index f95a855..49c7847 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -35,6 +35,7 @@
#include <android-base/utf8.h>
#include "sysdeps/errno.h"
+#include "sysdeps/network.h"
#include "sysdeps/stat.h"
/*
@@ -98,64 +99,6 @@
return c == '\\' || c == '/';
}
-typedef void (*adb_thread_func_t)(void* arg);
-typedef HANDLE adb_thread_t;
-
-struct adb_winthread_args {
- adb_thread_func_t func;
- void* arg;
-};
-
-static unsigned __stdcall adb_winthread_wrapper(void* heap_args) {
- // Move the arguments from the heap onto the thread's stack.
- adb_winthread_args thread_args = *static_cast<adb_winthread_args*>(heap_args);
- delete static_cast<adb_winthread_args*>(heap_args);
- thread_args.func(thread_args.arg);
- return 0;
-}
-
-static __inline__ bool adb_thread_create(adb_thread_func_t func, void* arg,
- adb_thread_t* thread = nullptr) {
- adb_winthread_args* args = new adb_winthread_args{.func = func, .arg = arg};
- uintptr_t handle = _beginthreadex(nullptr, 0, adb_winthread_wrapper, args, 0, nullptr);
- if (handle != static_cast<uintptr_t>(0)) {
- if (thread) {
- *thread = reinterpret_cast<HANDLE>(handle);
- } else {
- CloseHandle(thread);
- }
- return true;
- }
- return false;
-}
-
-static __inline__ bool adb_thread_join(adb_thread_t thread) {
- switch (WaitForSingleObject(thread, INFINITE)) {
- case WAIT_OBJECT_0:
- CloseHandle(thread);
- return true;
-
- case WAIT_FAILED:
- fprintf(stderr, "adb_thread_join failed: %s\n",
- android::base::SystemErrorCodeToString(GetLastError()).c_str());
- break;
-
- default:
- abort();
- }
-
- return false;
-}
-
-static __inline__ bool adb_thread_detach(adb_thread_t thread) {
- CloseHandle(thread);
- return true;
-}
-
-static __inline__ void __attribute__((noreturn)) adb_thread_exit() {
- _endthreadex(0);
-}
-
static __inline__ int adb_thread_setname(const std::string& name) {
// TODO: See https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx for how to set
// the thread name in Windows. Unfortunately, it only works during debugging, but
@@ -163,14 +106,6 @@
return 0;
}
-static __inline__ adb_thread_t adb_thread_self() {
- return GetCurrentThread();
-}
-
-static __inline__ bool adb_thread_equal(adb_thread_t lhs, adb_thread_t rhs) {
- return GetThreadId(lhs) == GetThreadId(rhs);
-}
-
static __inline__ unsigned long adb_thread_id()
{
return GetCurrentThreadId();
@@ -248,8 +183,6 @@
int unix_isatty(int fd);
#define isatty ___xxx_isatty
-int network_loopback_client(int port, int type, std::string* error);
-int network_loopback_server(int port, int type, std::string* error);
int network_inaddr_any_server(int port, int type, std::string* error);
inline int network_local_client(const char* name, int namespace_id, int type, std::string* error) {
@@ -587,17 +520,6 @@
return fd;
}
-inline int network_loopback_client(int port, int type, std::string* error) {
- return _fd_set_error_str(socket_network_client("localhost", port, type), error);
-}
-
-inline int network_loopback_server(int port, int type, std::string* error) {
- int fd = socket_loopback_server(port, type);
- if (fd < 0 && errno == EAFNOSUPPORT)
- return _fd_set_error_str(socket_loopback_server6(port, type), error);
- return _fd_set_error_str(fd, error);
-}
-
inline int network_inaddr_any_server(int port, int type, std::string* error) {
return _fd_set_error_str(socket_inaddr_any_server(port, type), error);
}
@@ -656,55 +578,6 @@
#define unix_write adb_write
#define unix_close adb_close
-// Win32 is limited to DWORDs for thread return values; limit the POSIX systems to this as well to
-// ensure compatibility.
-typedef void (*adb_thread_func_t)(void* arg);
-typedef pthread_t adb_thread_t;
-
-struct adb_pthread_args {
- adb_thread_func_t func;
- void* arg;
-};
-
-static void* adb_pthread_wrapper(void* heap_args) {
- // Move the arguments from the heap onto the thread's stack.
- adb_pthread_args thread_args = *reinterpret_cast<adb_pthread_args*>(heap_args);
- delete static_cast<adb_pthread_args*>(heap_args);
- thread_args.func(thread_args.arg);
- return nullptr;
-}
-
-static __inline__ bool adb_thread_create(adb_thread_func_t start, void* arg,
- adb_thread_t* thread = nullptr) {
- pthread_t temp;
- pthread_attr_t attr;
- pthread_attr_init(&attr);
- pthread_attr_setdetachstate(&attr, thread ? PTHREAD_CREATE_JOINABLE : PTHREAD_CREATE_DETACHED);
- auto* pthread_args = new adb_pthread_args{.func = start, .arg = arg};
- errno = pthread_create(&temp, &attr, adb_pthread_wrapper, pthread_args);
- if (errno == 0) {
- if (thread) {
- *thread = temp;
- }
- return true;
- }
- return false;
-}
-
-static __inline__ bool adb_thread_join(adb_thread_t thread) {
- errno = pthread_join(thread, nullptr);
- return errno == 0;
-}
-
-static __inline__ bool adb_thread_detach(adb_thread_t thread) {
- errno = pthread_detach(thread);
- return errno == 0;
-}
-
-static __inline__ void __attribute__((noreturn)) adb_thread_exit() {
- pthread_exit(nullptr);
-}
-
static __inline__ int adb_thread_setname(const std::string& name) {
#ifdef __APPLE__
return pthread_setname_np(name.c_str());
diff --git a/include/system/window.h b/adb/sysdeps/network.h
similarity index 70%
copy from include/system/window.h
copy to adb/sysdeps/network.h
index efa10d6..83ce371 100644
--- a/include/system/window.h
+++ b/adb/sysdeps/network.h
@@ -1,5 +1,7 @@
+#pragma once
+
/*
- * Copyright (C) 2011 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.
@@ -14,9 +16,7 @@
* limitations under the License.
*/
-#ifndef SYSTEM_CORE_INCLUDE_ANDROID_WINDOW_H
-#define SYSTEM_CORE_INCLUDE_ANDROID_WINDOW_H
+#include <string>
-#include <system/window-deprecated.h>
-
-#endif /* SYSTEM_CORE_INCLUDE_ANDROID_WINDOW_H */
+int network_loopback_client(int port, int type, std::string* error);
+int network_loopback_server(int port, int type, std::string* error);
diff --git a/adb/sysdeps/posix/network.cpp b/adb/sysdeps/posix/network.cpp
new file mode 100644
index 0000000..45da5af
--- /dev/null
+++ b/adb/sysdeps/posix/network.cpp
@@ -0,0 +1,127 @@
+/*
+ * 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 "sysdeps/network.h"
+
+#include <errno.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+
+#include <string>
+
+#include "adb_unique_fd.h"
+
+static void set_error(std::string* error) {
+ if (error) {
+ *error = strerror(errno);
+ }
+}
+
+static sockaddr* loopback_addr4(sockaddr_storage* addr, socklen_t* addrlen, int port) {
+ struct sockaddr_in* addr4 = reinterpret_cast<sockaddr_in*>(addr);
+ *addrlen = sizeof(*addr4);
+
+ addr4->sin_family = AF_INET;
+ addr4->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ addr4->sin_port = htons(port);
+ return reinterpret_cast<sockaddr*>(addr);
+}
+
+static sockaddr* loopback_addr6(sockaddr_storage* addr, socklen_t* addrlen, int port) {
+ struct sockaddr_in6* addr6 = reinterpret_cast<sockaddr_in6*>(addr);
+ *addrlen = sizeof(*addr6);
+
+ addr6->sin6_family = AF_INET6;
+ addr6->sin6_addr = in6addr_loopback;
+ addr6->sin6_port = htons(port);
+ return reinterpret_cast<sockaddr*>(addr);
+}
+
+static int _network_loopback_client(bool ipv6, int port, int type, std::string* error) {
+ unique_fd s(socket(ipv6 ? AF_INET6 : AF_INET, type, 0));
+ if (s == -1) {
+ set_error(error);
+ return -1;
+ }
+
+ struct sockaddr_storage addr_storage = {};
+ socklen_t addrlen = sizeof(addr_storage);
+ sockaddr* addr = (ipv6 ? loopback_addr6 : loopback_addr4)(&addr_storage, &addrlen, 0);
+
+ if (bind(s.get(), addr, addrlen) != 0) {
+ set_error(error);
+ return -1;
+ }
+
+ addr = (ipv6 ? loopback_addr6 : loopback_addr4)(&addr_storage, &addrlen, port);
+
+ if (connect(s.get(), addr, addrlen) != 0) {
+ set_error(error);
+ return -1;
+ }
+
+ return s.release();
+}
+
+int network_loopback_client(int port, int type, std::string* error) {
+ // Try IPv4 first, use IPv6 as a fallback.
+ int rc = _network_loopback_client(false, port, type, error);
+ if (rc == -1) {
+ return _network_loopback_client(true, port, type, error);
+ }
+ return rc;
+}
+
+static int _network_loopback_server(bool ipv6, int port, int type, std::string* error) {
+ unique_fd s(socket(ipv6 ? AF_INET6 : AF_INET, type, 0));
+ if (s == -1) {
+ set_error(error);
+ return -1;
+ }
+
+ int n = 1;
+ setsockopt(s.get(), SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n));
+
+ struct sockaddr_storage addr_storage = {};
+ socklen_t addrlen = sizeof(addr_storage);
+ sockaddr* addr = (ipv6 ? loopback_addr6 : loopback_addr4)(&addr_storage, &addrlen, port);
+
+ if (bind(s, addr, addrlen) != 0) {
+ set_error(error);
+ return -1;
+ }
+
+ if (type == SOCK_STREAM || type == SOCK_SEQPACKET) {
+ // Arbitrarily selected value, ported from libcutils.
+ if (listen(s, 4) != 0) {
+ set_error(error);
+ return -1;
+ }
+ }
+
+ return s.release();
+}
+
+int network_loopback_server(int port, int type, std::string* error) {
+ int rc = _network_loopback_server(false, port, type, error);
+
+ // Only attempt to listen on IPv6 if IPv4 is unavailable.
+ // We don't want to start an IPv6 server if there's already an IPv4 one running.
+ if (rc == -1 && (errno == EADDRNOTAVAIL || errno == EAFNOSUPPORT)) {
+ return _network_loopback_server(true, port, type, error);
+ }
+ return rc;
+}
diff --git a/adb/sysdeps_test.cpp b/adb/sysdeps_test.cpp
index 9007e75..fd19882 100644
--- a/adb/sysdeps_test.cpp
+++ b/adb/sysdeps_test.cpp
@@ -25,54 +25,6 @@
#include "sysdeps.h"
#include "sysdeps/chrono.h"
-static void increment_atomic_int(void* c) {
- std::this_thread::sleep_for(1s);
- reinterpret_cast<std::atomic<int>*>(c)->fetch_add(1);
-}
-
-TEST(sysdeps_thread, smoke) {
- std::atomic<int> counter(0);
-
- for (int i = 0; i < 100; ++i) {
- ASSERT_TRUE(adb_thread_create(increment_atomic_int, &counter));
- }
-
- std::this_thread::sleep_for(2s);
- ASSERT_EQ(100, counter.load());
-}
-
-TEST(sysdeps_thread, join) {
- std::atomic<int> counter(0);
- std::vector<adb_thread_t> threads(500);
- for (size_t i = 0; i < threads.size(); ++i) {
- ASSERT_TRUE(adb_thread_create(increment_atomic_int, &counter, &threads[i]));
- }
-
- int current = counter.load();
- ASSERT_GE(current, 0);
- // Make sure that adb_thread_create actually creates threads, and doesn't do something silly
- // like synchronously run the function passed in. The sleep in increment_atomic_int should be
- // enough to keep this from being flakey.
- ASSERT_LT(current, 500);
-
- for (const auto& thread : threads) {
- ASSERT_TRUE(adb_thread_join(thread));
- }
-
- ASSERT_EQ(500, counter.load());
-}
-
-TEST(sysdeps_thread, exit) {
- adb_thread_t thread;
- ASSERT_TRUE(adb_thread_create(
- [](void*) {
- adb_thread_exit();
- for (;;) continue;
- },
- nullptr, &thread));
- ASSERT_TRUE(adb_thread_join(thread));
-}
-
TEST(sysdeps_socketpair, smoke) {
int fds[2];
ASSERT_EQ(0, adb_socketpair(fds)) << strerror(errno);
@@ -249,65 +201,22 @@
}
}
-TEST(sysdeps_mutex, mutex_smoke) {
- static std::atomic<bool> finished(false);
- static std::mutex &m = *new std::mutex();
- m.lock();
- ASSERT_FALSE(m.try_lock());
- adb_thread_create([](void*) {
- ASSERT_FALSE(m.try_lock());
- m.lock();
- finished.store(true);
- std::this_thread::sleep_for(200ms);
- m.unlock();
- }, nullptr);
-
- ASSERT_FALSE(finished.load());
- std::this_thread::sleep_for(100ms);
- ASSERT_FALSE(finished.load());
- m.unlock();
- std::this_thread::sleep_for(100ms);
- m.lock();
- ASSERT_TRUE(finished.load());
- m.unlock();
-}
-
-TEST(sysdeps_mutex, recursive_mutex_smoke) {
- static std::recursive_mutex &m = *new std::recursive_mutex();
-
- m.lock();
- ASSERT_TRUE(m.try_lock());
- m.unlock();
-
- adb_thread_create([](void*) {
- ASSERT_FALSE(m.try_lock());
- m.lock();
- std::this_thread::sleep_for(500ms);
- m.unlock();
- }, nullptr);
-
- std::this_thread::sleep_for(100ms);
- m.unlock();
- std::this_thread::sleep_for(100ms);
- ASSERT_FALSE(m.try_lock());
- m.lock();
- m.unlock();
-}
-
TEST(sysdeps_condition_variable, smoke) {
static std::mutex &m = *new std::mutex;
static std::condition_variable &cond = *new std::condition_variable;
static volatile bool flag = false;
std::unique_lock<std::mutex> lock(m);
- adb_thread_create([](void*) {
+ std::thread thread([]() {
m.lock();
flag = true;
cond.notify_one();
m.unlock();
- }, nullptr);
+ });
while (!flag) {
cond.wait(lock);
}
+
+ thread.join();
}
diff --git a/adb/test_device.py b/adb/test_device.py
index e76aaed..737d0c2 100644
--- a/adb/test_device.py
+++ b/adb/test_device.py
@@ -1130,8 +1130,18 @@
if host_dir is not None:
shutil.rmtree(host_dir)
+ def verify_sync(self, device, temp_files, device_dir):
+ """Verifies that a list of temp files was synced to the device."""
+ # Confirm that every file on the device mirrors that on the host.
+ for temp_file in temp_files:
+ device_full_path = posixpath.join(
+ device_dir, temp_file.base_name)
+ dev_md5, _ = device.shell(
+ [get_md5_prog(self.device), device_full_path])[0].split()
+ self.assertEqual(temp_file.checksum, dev_md5)
+
def test_sync(self):
- """Sync a randomly generated directory of files to specified device."""
+ """Sync a host directory to the data partition."""
try:
base_dir = tempfile.mkdtemp()
@@ -1141,27 +1151,50 @@
os.makedirs(full_dir_path)
# Create 32 random files within the host mirror.
- temp_files = make_random_host_files(in_dir=full_dir_path, num_files=32)
+ temp_files = make_random_host_files(
+ in_dir=full_dir_path, num_files=32)
- # Clean up any trash on the device.
- device = adb.get_device(product=base_dir)
+ # Clean up any stale files on the device.
+ device = adb.get_device() # pylint: disable=no-member
device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ old_product_out = os.environ.get('ANDROID_PRODUCT_OUT')
+ os.environ['ANDROID_PRODUCT_OUT'] = base_dir
device.sync('data')
+ if old_product_out is None:
+ del os.environ['ANDROID_PRODUCT_OUT']
+ else:
+ os.environ['ANDROID_PRODUCT_OUT'] = old_product_out
- # Confirm that every file on the device mirrors that on the host.
- for temp_file in temp_files:
- device_full_path = posixpath.join(self.DEVICE_TEMP_DIR,
- temp_file.base_name)
- dev_md5, _ = device.shell(
- [get_md5_prog(self.device), device_full_path])[0].split()
- self.assertEqual(temp_file.checksum, dev_md5)
+ self.verify_sync(device, temp_files, self.DEVICE_TEMP_DIR)
- self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ #self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
finally:
if base_dir is not None:
shutil.rmtree(base_dir)
+ def test_push_sync(self):
+ """Sync a host directory to a specific path."""
+
+ try:
+ temp_dir = tempfile.mkdtemp()
+ temp_files = make_random_host_files(in_dir=temp_dir, num_files=32)
+
+ device_dir = posixpath.join(self.DEVICE_TEMP_DIR, 'sync_src_dst')
+
+ # Clean up any stale files on the device.
+ device = adb.get_device() # pylint: disable=no-member
+ device.shell(['rm', '-rf', device_dir])
+
+ device.push(temp_dir, device_dir, sync=True)
+
+ self.verify_sync(device, temp_files, device_dir)
+
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ finally:
+ if temp_dir is not None:
+ shutil.rmtree(temp_dir)
+
def test_unicode_paths(self):
"""Ensure that we can support non-ASCII paths, even on Windows."""
name = u'로보카 폴리'
@@ -1188,6 +1221,97 @@
self.device.shell(['rm', '-f', '/data/local/tmp/adb-test-*'])
+class DeviceOfflineTest(DeviceTest):
+ def _get_device_state(self, serialno):
+ output = subprocess.check_output(self.device.adb_cmd + ['devices'])
+ for line in output.split('\n'):
+ m = re.match('(\S+)\s+(\S+)', line)
+ if m and m.group(1) == serialno:
+ return m.group(2)
+ return None
+
+ def test_killed_when_pushing_a_large_file(self):
+ """
+ While running adb push with a large file, kill adb server.
+ Occasionally the device becomes offline. Because the device is still
+ reading data without realizing that the adb server has been restarted.
+ Test if we can bring the device online automatically now.
+ http://b/32952319
+ """
+ serialno = subprocess.check_output(self.device.adb_cmd + ['get-serialno']).strip()
+ # 1. Push a large file
+ file_path = 'tmp_large_file'
+ try:
+ fh = open(file_path, 'w')
+ fh.write('\0' * (100 * 1024 * 1024))
+ fh.close()
+ subproc = subprocess.Popen(self.device.adb_cmd + ['push', file_path, '/data/local/tmp'])
+ time.sleep(0.1)
+ # 2. Kill the adb server
+ subprocess.check_call(self.device.adb_cmd + ['kill-server'])
+ subproc.terminate()
+ finally:
+ try:
+ os.unlink(file_path)
+ except:
+ pass
+ # 3. See if the device still exist.
+ # Sleep to wait for the adb server exit.
+ time.sleep(0.5)
+ # 4. The device should be online
+ self.assertEqual(self._get_device_state(serialno), 'device')
+
+ def test_killed_when_pulling_a_large_file(self):
+ """
+ While running adb pull with a large file, kill adb server.
+ Occasionally the device can't be connected. Because the device is trying to
+ send a message larger than what is expected by the adb server.
+ Test if we can bring the device online automatically now.
+ """
+ serialno = subprocess.check_output(self.device.adb_cmd + ['get-serialno']).strip()
+ file_path = 'tmp_large_file'
+ try:
+ # 1. Create a large file on device.
+ self.device.shell(['dd', 'if=/dev/zero', 'of=/data/local/tmp/tmp_large_file',
+ 'bs=1000000', 'count=100'])
+ # 2. Pull the large file on host.
+ subproc = subprocess.Popen(self.device.adb_cmd +
+ ['pull','/data/local/tmp/tmp_large_file', file_path])
+ time.sleep(0.1)
+ # 3. Kill the adb server
+ subprocess.check_call(self.device.adb_cmd + ['kill-server'])
+ subproc.terminate()
+ finally:
+ try:
+ os.unlink(file_path)
+ except:
+ pass
+ # 4. See if the device still exist.
+ # Sleep to wait for the adb server exit.
+ time.sleep(0.5)
+ self.assertEqual(self._get_device_state(serialno), 'device')
+
+
+ def test_packet_size_regression(self):
+ """Test for http://b/37783561
+
+ Receiving packets of a length divisible by 512 but not 1024 resulted in
+ the adb client waiting indefinitely for more input.
+ """
+ # The values that trigger things are 507 (512 - 5 bytes from shell protocol) + 1024*n
+ # Probe some surrounding values as well, for the hell of it.
+ for length in [506, 507, 508, 1018, 1019, 1020, 1530, 1531, 1532]:
+ cmd = ['dd', 'if=/dev/zero', 'bs={}'.format(length), 'count=1', '2>/dev/null;'
+ 'echo', 'foo']
+ rc, stdout, _ = self.device.shell_nocheck(cmd)
+
+ self.assertEqual(0, rc)
+
+ # Output should be '\0' * length, followed by "foo\n"
+ self.assertEqual(length, len(stdout) - 4)
+ self.assertEqual(stdout, "\0" * length + "foo\n")
+
+
def main():
random.seed(0)
if len(adb.get_devices()) > 0:
diff --git a/adb/transport.cpp b/adb/transport.cpp
index c951f5b..308ee8d 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -29,9 +29,11 @@
#include <algorithm>
#include <list>
#include <mutex>
+#include <thread>
#include <android-base/logging.h>
#include <android-base/parsenetaddress.h>
+#include <android-base/quick_exit.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
@@ -40,6 +42,7 @@
#include "adb_trace.h"
#include "adb_utils.h"
#include "diagnose_usb.h"
+#include "fdevent.h"
static void transport_unref(atransport *t);
@@ -52,6 +55,7 @@
const char* const kFeatureCmd = "cmd";
const char* const kFeatureStat2 = "stat_v2";
const char* const kFeatureLibusb = "libusb";
+const char* const kFeaturePushSync = "push_sync";
static std::string dump_packet(const char* name, const char* func, apacket* p) {
unsigned command = p->msg.command;
@@ -208,6 +212,11 @@
put_apacket(p);
break;
}
+#if ADB_HOST
+ if (p->msg.command == 0) {
+ continue;
+ }
+#endif
}
D("%s: received remote packet, sending to transport", t->serial);
@@ -270,7 +279,11 @@
if (active) {
D("%s: transport got packet, sending to remote", t->serial);
ATRACE_NAME("write_transport write_remote");
- t->write_to_remote(p, t);
+ if (t->Write(p) != 0) {
+ D("%s: remote write failed for transport", t->serial);
+ put_apacket(p);
+ break;
+ }
} else {
D("%s: transport ignoring packet while offline", t->serial);
}
@@ -388,8 +401,27 @@
return &tracker->socket;
}
+// 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);
+ for (const auto& t : transport_list) {
+ if (!fn(t)) {
+ return false;
+ }
+ }
+ for (const auto& t : pending_list) {
+ if (!fn(t)) {
+ return false;
+ }
+ }
+ return true;
+}
+
// Call this function each time the transport list has changed.
void update_transports() {
+ update_transport_status();
+
+ // Notify `adb track-devices` clients.
std::string transports = list_transports(false);
device_tracker* tracker = device_tracker_list;
@@ -492,7 +524,7 @@
}
/* don't create transport threads for inaccessible devices */
- if (t->connection_state != kCsNoPerm) {
+ if (t->GetConnectionState() != kCsNoPerm) {
/* initial references are the two threads */
t->ref_count = 2;
@@ -509,13 +541,8 @@
fdevent_set(&(t->transport_fde), FDE_READ);
- if (!adb_thread_create(write_transport_thread, t)) {
- fatal_errno("cannot create write_transport thread");
- }
-
- if (!adb_thread_create(read_transport_thread, t)) {
- fatal_errno("cannot create read_transport thread");
- }
+ std::thread(write_transport_thread, t).detach();
+ std::thread(read_transport_thread, t).detach();
}
{
@@ -544,6 +571,14 @@
fdevent_set(&transport_registration_fde, FDE_READ);
}
+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);
+ for (auto t : transport_list) {
+ t->Kick();
+ }
+}
+
/* the fdevent select pump is single threaded */
static void register_transport(atransport* transport) {
tmsg m;
@@ -604,7 +639,7 @@
}
atransport* acquire_one_transport(TransportType type, const char* serial, bool* is_ambiguous,
- std::string* error_out) {
+ std::string* error_out, bool accept_any_state) {
atransport* result = nullptr;
if (serial) {
@@ -619,7 +654,7 @@
std::unique_lock<std::mutex> lock(transport_lock);
for (const auto& t : transport_list) {
- if (t->connection_state == kCsNoPerm) {
+ if (t->GetConnectionState() == kCsNoPerm) {
#if ADB_HOST
*error_out = UsbNoPermissionsLongHelpText();
#endif
@@ -668,7 +703,7 @@
lock.unlock();
// Don't return unauthorized devices; the caller can't do anything with them.
- if (result && result->connection_state == kCsUnauthorized) {
+ if (result && result->GetConnectionState() == kCsUnauthorized && !accept_any_state) {
*error_out = "device unauthorized.\n";
char* ADB_VENDOR_KEYS = getenv("ADB_VENDOR_KEYS");
*error_out += "This adb server's $ADB_VENDOR_KEYS is ";
@@ -680,7 +715,7 @@
}
// Don't return offline devices; the caller can't do anything with them.
- if (result && result->connection_state == kCsOffline) {
+ if (result && result->GetConnectionState() == kCsOffline && !accept_any_state) {
*error_out = "device offline";
result = nullptr;
}
@@ -692,16 +727,38 @@
return result;
}
+int atransport::Write(apacket* p) {
+#if ADB_HOST
+ std::lock_guard<std::mutex> lock(write_msg_lock_);
+#endif
+ return write_func_(p, this);
+}
+
void atransport::Kick() {
if (!kicked_) {
kicked_ = true;
CHECK(kick_func_ != nullptr);
+#if ADB_HOST
+ // On host, adb server should avoid writing part of a packet, so don't
+ // kick a transport whiling writing a packet.
+ std::lock_guard<std::mutex> lock(write_msg_lock_);
+#endif
kick_func_(this);
}
}
+ConnectionState atransport::GetConnectionState() const {
+ return connection_state_;
+}
+
+void atransport::SetConnectionState(ConnectionState state) {
+ check_main_thread();
+ connection_state_ = state;
+}
+
const std::string atransport::connection_state_name() const {
- switch (connection_state) {
+ ConnectionState state = GetConnectionState();
+ switch (state) {
case kCsOffline:
return "offline";
case kCsBootloader:
@@ -967,10 +1024,10 @@
void register_usb_transport(usb_handle* usb, const char* serial, const char* devpath,
unsigned writeable) {
- atransport* t = new atransport();
+ atransport* t = new atransport((writeable ? kCsOffline : kCsNoPerm));
D("transport: %p init'ing for usb_handle %p (sn='%s')", t, usb, serial ? serial : "");
- init_usb_transport(t, usb, (writeable ? kCsOffline : kCsNoPerm));
+ init_usb_transport(t, usb);
if (serial) {
t->serial = strdup(serial);
}
@@ -991,29 +1048,27 @@
void unregister_usb_transport(usb_handle* usb) {
std::lock_guard<std::mutex> lock(transport_lock);
transport_list.remove_if(
- [usb](atransport* t) { return t->usb == usb && t->connection_state == kCsNoPerm; });
+ [usb](atransport* t) { return t->usb == usb && t->GetConnectionState() == kCsNoPerm; });
}
-int check_header(apacket* p, atransport* t) {
+bool check_header(apacket* p, atransport* t) {
if (p->msg.magic != (p->msg.command ^ 0xffffffff)) {
- VLOG(RWX) << "check_header(): invalid magic";
- return -1;
+ VLOG(RWX) << "check_header(): invalid magic command = " << std::hex << p->msg.command
+ << ", magic = " << p->msg.magic;
+ return false;
}
if (p->msg.data_length > t->get_max_payload()) {
VLOG(RWX) << "check_header(): " << p->msg.data_length
<< " atransport::max_payload = " << t->get_max_payload();
- return -1;
+ return false;
}
- return 0;
+ return true;
}
-int check_data(apacket* p) {
- if (calculate_apacket_checksum(p) != p->msg.data_check) {
- return -1;
- }
- return 0;
+bool check_data(apacket* p) {
+ return calculate_apacket_checksum(p) == p->msg.data_check;
}
#if ADB_HOST
@@ -1024,4 +1079,11 @@
keys_.pop_front();
return result;
}
+bool atransport::SetSendConnectOnError() {
+ if (has_send_connect_on_error_) {
+ return false;
+ }
+ has_send_connect_on_error_ = true;
+ return true;
+}
#endif
diff --git a/adb/transport.h b/adb/transport.h
index 4d97fc7..57fc988 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -19,10 +19,12 @@
#include <sys/types.h>
+#include <atomic>
#include <deque>
#include <functional>
#include <list>
#include <memory>
+#include <mutex>
#include <string>
#include <unordered_set>
@@ -49,6 +51,8 @@
extern const char* const kFeatureStat2;
// The server is running with libusb enabled.
extern const char* const kFeatureLibusb;
+// The server supports `push --sync`.
+extern const char* const kFeaturePushSync;
class atransport {
public:
@@ -57,31 +61,35 @@
// 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() {
+ atransport(ConnectionState state = kCsOffline) : connection_state_(state) {
transport_fde = {};
protocol_version = A_VERSION;
max_payload = MAX_PAYLOAD;
}
-
virtual ~atransport() {}
int (*read_from_remote)(apacket* p, atransport* t) = nullptr;
- int (*write_to_remote)(apacket* p, atransport* t) = nullptr;
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_;
}
+ int Write(apacket* p);
void Kick();
+ // ConnectionState can be read by all threads, but can only be written in the main thread.
+ ConnectionState GetConnectionState() const;
+ void SetConnectionState(ConnectionState state);
+
int fd = -1;
int transport_socket = -1;
fdevent transport_fde;
size_t ref_count = 0;
uint32_t sync_token = 0;
- ConnectionState connection_state = kCsOffline;
bool online = false;
TransportType type = kTransportAny;
@@ -114,11 +122,13 @@
#if ADB_HOST
std::shared_ptr<RSA> NextKey();
+ bool SetSendConnectOnError();
#endif
char token[TOKEN_SIZE] = {};
size_t failed_auth_attempts = 0;
+ const std::string serial_name() const { return serial ? serial : "<unknown>"; }
const std::string connection_state_name() const;
void update_version(int version, size_t payload);
@@ -157,6 +167,7 @@
int local_port_for_emulator_ = -1;
bool kicked_ = false;
void (*kick_func_)(atransport*) = nullptr;
+ int (*write_func_)(apacket*, atransport*) = nullptr;
// A set of features transmitted in the banner with the initial connection.
// This is stored in the banner as 'features=feature0,feature1,etc'.
@@ -167,8 +178,11 @@
// A list of adisconnect callbacks called when the transport is kicked.
std::list<adisconnect*> disconnects_;
+ std::atomic<ConnectionState> connection_state_;
#if ADB_HOST
std::deque<std::shared_ptr<RSA>> keys_;
+ std::mutex write_msg_lock_;
+ bool has_send_connect_on_error_ = false;
#endif
DISALLOW_COPY_AND_ASSIGN(atransport);
@@ -181,16 +195,21 @@
* 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);
+atransport* acquire_one_transport(TransportType type, const char* serial, bool* is_ambiguous,
+ std::string* error_out, bool accept_any_state = false);
void kick_transport(atransport* t);
void update_transports(void);
+// Iterates across all of the current and pending transports.
+// Stops iteration and returns false if fn returns false, otherwise returns true.
+bool iterate_transports(std::function<bool(const atransport*)> fn);
+
void init_transport_registration(void);
void init_mdns_transport_discovery(void);
std::string list_transports(bool long_listing);
atransport* find_transport(const char* serial);
void kick_all_tcp_devices();
+void kick_all_transports();
void register_usb_transport(usb_handle* h, const char* serial,
const char* devpath, unsigned writeable);
@@ -204,8 +223,8 @@
// This should only be used for transports with connection_state == kCsNoPerm.
void unregister_usb_transport(usb_handle* usb);
-int check_header(apacket* p, atransport* t);
-int check_data(apacket* p);
+bool check_header(apacket* p, atransport* t);
+bool check_data(apacket* p);
void close_usb_devices();
void close_usb_devices(std::function<bool(const atransport*)> predicate);
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index 12b98ba..809ed89 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -62,22 +62,22 @@
static int remote_read(apacket *p, atransport *t)
{
- if(!ReadFdExactly(t->sfd, &p->msg, sizeof(amessage))){
+ if (!ReadFdExactly(t->sfd, &p->msg, sizeof(amessage))) {
D("remote local: read terminated (message)");
return -1;
}
- if(check_header(p, t)) {
+ if (!check_header(p, t)) {
D("bad header: terminated (data)");
return -1;
}
- if(!ReadFdExactly(t->sfd, p->data, p->msg.data_length)){
+ if (!ReadFdExactly(t->sfd, p->data, p->msg.data_length)) {
D("remote local: terminated (data)");
return -1;
}
- if(check_data(p)) {
+ if (!check_data(p)) {
D("bad data: terminated (data)");
return -1;
}
@@ -199,7 +199,7 @@
std::mutex &retry_ports_lock = *new std::mutex;
std::condition_variable &retry_ports_cond = *new std::condition_variable;
-static void client_socket_thread(void* x) {
+static void client_socket_thread(int) {
adb_thread_setname("client_socket_thread");
D("transport: client_socket_thread() starting");
PollAllLocalPortsForEmulator();
@@ -244,9 +244,8 @@
#else // ADB_HOST
-static void server_socket_thread(void* arg) {
+static void server_socket_thread(int port) {
int serverfd, fd;
- int port = (int) (uintptr_t) arg;
adb_thread_setname("server socket");
D("transport: server_socket_thread() starting");
@@ -325,7 +324,7 @@
* the transport registration is completed. That's why we need to send the
* 'start' request after the transport is registered.
*/
-static void qemu_socket_thread(void* arg) {
+static void qemu_socket_thread(int port) {
/* 'accept' request to the adb QEMUD service. */
static const char _accept_req[] = "accept";
/* 'start' request to the adb QEMUD service. */
@@ -333,7 +332,6 @@
/* 'ok' reply from the adb QEMUD service. */
static const char _ok_resp[] = "ok";
- const int port = (int) (uintptr_t) arg;
int fd;
char tmp[256];
char con_name[32];
@@ -350,7 +348,7 @@
/* This could be an older version of the emulator, that doesn't
* implement adb QEMUD service. Fall back to the old TCP way. */
D("adb service is not available. Falling back to TCP socket.");
- adb_thread_create(server_socket_thread, arg);
+ std::thread(server_socket_thread, port).detach();
return;
}
@@ -394,7 +392,7 @@
void local_init(int port)
{
- adb_thread_func_t func;
+ void (*func)(int);
const char* debug_name = "";
#if ADB_HOST
@@ -414,9 +412,7 @@
#endif // !ADB_HOST
D("transport: local %s init", debug_name);
- if (!adb_thread_create(func, (void *) (uintptr_t) port)) {
- fatal_errno("cannot create local socket %s thread", debug_name);
- }
+ std::thread(func, port).detach();
}
static void remote_kick(atransport *t)
@@ -519,12 +515,11 @@
int fail = 0;
t->SetKickFunction(remote_kick);
+ t->SetWriteFunction(remote_write);
t->close = remote_close;
t->read_from_remote = remote_read;
- t->write_to_remote = remote_write;
t->sfd = s;
t->sync_token = 1;
- t->connection_state = kCsOffline;
t->type = kTransportLocal;
#if ADB_HOST
diff --git a/adb/transport_mdns.cpp b/adb/transport_mdns.cpp
index e49b1c6..3603f09 100644
--- a/adb/transport_mdns.cpp
+++ b/adb/transport_mdns.cpp
@@ -24,6 +24,8 @@
#include <arpa/inet.h>
#endif
+#include <thread>
+
#include <android-base/stringprintf.h>
#include <dns_sd.h>
@@ -262,19 +264,22 @@
}
}
-void init_mdns_transport_discovery(void) {
- DNSServiceErrorType errorCode =
- DNSServiceBrowse(&service_ref, 0, 0, kADBServiceType, nullptr,
- register_mdns_transport, nullptr);
+void init_mdns_transport_discovery_thread(void) {
+ DNSServiceErrorType errorCode = DNSServiceBrowse(&service_ref, 0, 0, kADBServiceType, nullptr,
+ register_mdns_transport, nullptr);
if (errorCode != kDNSServiceErr_NoError) {
D("Got %d initiating mDNS browse.", errorCode);
return;
}
- fdevent_install(&service_ref_fde,
- adb_DNSServiceRefSockFD(service_ref),
- pump_service_ref,
- &service_ref);
- fdevent_set(&service_ref_fde, FDE_READ);
+ fdevent_run_on_main_thread([]() {
+ fdevent_install(&service_ref_fde, adb_DNSServiceRefSockFD(service_ref), pump_service_ref,
+ &service_ref);
+ fdevent_set(&service_ref_fde, FDE_READ);
+ });
+}
+
+void init_mdns_transport_discovery(void) {
+ std::thread(init_mdns_transport_discovery_thread).detach();
}
diff --git a/adb/transport_test.cpp b/adb/transport_test.cpp
index 8b38e03..68689d4 100644
--- a/adb/transport_test.cpp
+++ b/adb/transport_test.cpp
@@ -94,12 +94,13 @@
}
TEST(transport, parse_banner_no_features) {
+ set_main_thread();
atransport t;
parse_banner("host::", &t);
ASSERT_EQ(0U, t.features().size());
- ASSERT_EQ(kCsHost, t.connection_state);
+ ASSERT_EQ(kCsHost, t.GetConnectionState());
ASSERT_EQ(nullptr, t.product);
ASSERT_EQ(nullptr, t.model);
@@ -113,7 +114,7 @@
"host::ro.product.name=foo;ro.product.model=bar;ro.product.device=baz;";
parse_banner(banner, &t);
- ASSERT_EQ(kCsHost, t.connection_state);
+ ASSERT_EQ(kCsHost, t.GetConnectionState());
ASSERT_EQ(0U, t.features().size());
@@ -130,7 +131,7 @@
"features=woodly,doodly";
parse_banner(banner, &t);
- ASSERT_EQ(kCsHost, t.connection_state);
+ ASSERT_EQ(kCsHost, t.GetConnectionState());
ASSERT_EQ(2U, t.features().size());
ASSERT_TRUE(t.has_feature("woodly"));
diff --git a/adb/transport_usb.cpp b/adb/transport_usb.cpp
index 516b4f2..47094b8 100644
--- a/adb/transport_usb.cpp
+++ b/adb/transport_usb.cpp
@@ -25,43 +25,136 @@
#include "adb.h"
+#if ADB_HOST
+
+// Call usb_read using a buffer having a multiple of usb_get_max_packet_size() bytes
+// to avoid overflow. See http://libusb.sourceforge.net/api-1.0/packetoverflow.html.
+static int UsbReadMessage(usb_handle* h, amessage* msg) {
+ D("UsbReadMessage");
+
+ size_t usb_packet_size = usb_get_max_packet_size(h);
+ CHECK(usb_packet_size >= sizeof(*msg));
+ CHECK(usb_packet_size < 4096);
+
+ char buffer[4096];
+ int n = usb_read(h, buffer, usb_packet_size);
+ if (n != sizeof(*msg)) {
+ D("usb_read returned unexpected length %d (expected %zu)", n, sizeof(*msg));
+ return -1;
+ }
+ memcpy(msg, buffer, sizeof(*msg));
+ return n;
+}
+
+// Call usb_read using a buffer having a multiple of usb_get_max_packet_size() bytes
+// to avoid overflow. See http://libusb.sourceforge.net/api-1.0/packetoverflow.html.
+static int UsbReadPayload(usb_handle* h, apacket* p) {
+ D("UsbReadPayload(%d)", p->msg.data_length);
+
+ size_t usb_packet_size = usb_get_max_packet_size(h);
+ CHECK(sizeof(p->data) % usb_packet_size == 0);
+
+ // Round the data length up to the nearest packet size boundary.
+ // The device won't send a zero packet for packet size aligned payloads,
+ // so don't read any more packets than needed.
+ size_t len = p->msg.data_length;
+ size_t rem_size = len % usb_packet_size;
+ if (rem_size) {
+ len += usb_packet_size - rem_size;
+ }
+ CHECK(len <= sizeof(p->data));
+ return usb_read(h, &p->data, len);
+}
+
+static int remote_read(apacket* p, atransport* t) {
+ int n = UsbReadMessage(t->usb, &p->msg);
+ if (n < 0) {
+ D("remote usb: read terminated (message)");
+ return -1;
+ }
+ if (static_cast<size_t>(n) != sizeof(p->msg) || !check_header(p, t)) {
+ D("remote usb: check_header failed, skip it");
+ goto err_msg;
+ }
+ if (t->GetConnectionState() == kCsOffline) {
+ // If we read a wrong msg header declaring a large message payload, don't read its payload.
+ // Otherwise we may miss true messages from the device.
+ if (p->msg.command != A_CNXN && p->msg.command != A_AUTH) {
+ goto err_msg;
+ }
+ }
+ if (p->msg.data_length) {
+ n = UsbReadPayload(t->usb, p);
+ if (n < 0) {
+ D("remote usb: terminated (data)");
+ return -1;
+ }
+ if (static_cast<uint32_t>(n) != p->msg.data_length) {
+ D("remote usb: read payload failed (need %u bytes, give %d bytes), skip it",
+ p->msg.data_length, n);
+ goto err_msg;
+ }
+ }
+ if (!check_data(p)) {
+ D("remote usb: check_data failed, skip it");
+ goto err_msg;
+ }
+ return 0;
+
+err_msg:
+ p->msg.command = 0;
+ if (t->GetConnectionState() == kCsOffline) {
+ // If the data toggle of ep_out on device and ep_in on host are not the same, we may receive
+ // an error message. In this case, resend one A_CNXN message to connect the device.
+ if (t->SetSendConnectOnError()) {
+ SendConnectOnHost(t);
+ }
+ }
+ return 0;
+}
+
+#else
+
+// On Android devices, we rely on the kernel to provide buffered read.
+// So we can recover automatically from EOVERFLOW.
static int remote_read(apacket *p, atransport *t)
{
- if(usb_read(t->usb, &p->msg, sizeof(amessage))){
+ if (usb_read(t->usb, &p->msg, sizeof(amessage))) {
D("remote usb: read terminated (message)");
return -1;
}
- if(check_header(p, t)) {
+ if (!check_header(p, t)) {
D("remote usb: check_header failed");
return -1;
}
- if(p->msg.data_length) {
- if(usb_read(t->usb, p->data, p->msg.data_length)){
+ if (p->msg.data_length) {
+ if (usb_read(t->usb, p->data, p->msg.data_length)) {
D("remote usb: terminated (data)");
return -1;
}
}
- if(check_data(p)) {
+ if (!check_data(p)) {
D("remote usb: check_data failed");
return -1;
}
return 0;
}
+#endif
static int remote_write(apacket *p, atransport *t)
{
unsigned size = p->msg.data_length;
- if(usb_write(t->usb, &p->msg, sizeof(amessage))) {
+ if (usb_write(t->usb, &p->msg, sizeof(amessage))) {
D("remote usb: 1 - write terminated");
return -1;
}
if(p->msg.data_length == 0) return 0;
- if(usb_write(t->usb, &p->data, size)) {
+ if (usb_write(t->usb, &p->data, size)) {
D("remote usb: 2 - write terminated");
return -1;
}
@@ -75,20 +168,17 @@
t->usb = 0;
}
-static void remote_kick(atransport *t)
-{
+static void remote_kick(atransport* t) {
usb_kick(t->usb);
}
-void init_usb_transport(atransport *t, usb_handle *h, ConnectionState state)
-{
+void init_usb_transport(atransport* t, usb_handle* h) {
D("transport: usb");
t->close = remote_close;
t->SetKickFunction(remote_kick);
+ t->SetWriteFunction(remote_write);
t->read_from_remote = remote_read;
- t->write_to_remote = remote_write;
t->sync_token = 1;
- t->connection_state = state;
t->type = kTransportUsb;
t->usb = h;
}
diff --git a/adb/usb.h b/adb/usb.h
index ba70de4..f428ede 100644
--- a/adb/usb.h
+++ b/adb/usb.h
@@ -16,14 +16,18 @@
#pragma once
+#include <sys/types.h>
+
// USB host/client interface.
#define ADB_USB_INTERFACE(handle_ref_type) \
void usb_init(); \
+ void usb_cleanup(); \
int usb_write(handle_ref_type h, const void* data, int len); \
int usb_read(handle_ref_type h, void* data, int len); \
int usb_close(handle_ref_type h); \
- void usb_kick(handle_ref_type h)
+ void usb_kick(handle_ref_type h); \
+ size_t usb_get_max_packet_size(handle_ref_type)
#if defined(_WIN32) || !ADB_HOST
// Windows and the daemon have a single implementation.
diff --git a/base/Android.bp b/base/Android.bp
index 121da42..b636dc3 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -20,8 +20,25 @@
"-Werror",
]
+cc_library_headers {
+ name: "libbase_headers",
+ vendor_available: true,
+ host_supported: true,
+ export_include_dirs: ["include"],
+
+ target: {
+ linux_bionic: {
+ enabled: true,
+ },
+ windows: {
+ enabled: true,
+ },
+ },
+}
+
cc_library {
name: "libbase",
+ vendor_available: true,
clang: true,
host_supported: true,
srcs: [
@@ -33,9 +50,13 @@
"strings.cpp",
"test_utils.cpp",
],
- local_include_dirs: ["include"],
+
+ header_libs: [
+ "libbase_headers",
+ ],
+ export_header_lib_headers: ["libbase_headers"],
+
cppflags: libbase_cppflags,
- export_include_dirs: ["include"],
shared_libs: ["liblog"],
target: {
android: {
@@ -98,6 +119,7 @@
"parseint_test.cpp",
"parsenetaddress_test.cpp",
"quick_exit_test.cpp",
+ "scopeguard_test.cpp",
"stringprintf_test.cpp",
"strings_test.cpp",
"test_main.cpp",
diff --git a/base/file.cpp b/base/file.cpp
index d4e5894..a2f2887 100644
--- a/base/file.cpp
+++ b/base/file.cpp
@@ -28,16 +28,18 @@
#include <string>
#include <vector>
-#include "android-base/macros.h" // For TEMP_FAILURE_RETRY on Darwin.
#include "android-base/logging.h"
+#include "android-base/macros.h" // For TEMP_FAILURE_RETRY on Darwin.
+#include "android-base/unique_fd.h"
#include "android-base/utf8.h"
-#include "utils/Compat.h"
#if defined(__APPLE__)
#include <mach-o/dyld.h>
#endif
#if defined(_WIN32)
#include <windows.h>
+#define O_CLOEXEC O_NOINHERIT
+#define O_NOFOLLOW 0
#endif
namespace android {
@@ -69,13 +71,11 @@
content->clear();
int flags = O_RDONLY | O_CLOEXEC | O_BINARY | (follow_symlinks ? 0 : O_NOFOLLOW);
- int fd = TEMP_FAILURE_RETRY(open(path.c_str(), flags));
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), flags)));
if (fd == -1) {
return false;
}
- bool result = ReadFdToString(fd, content);
- close(fd);
- return result;
+ return ReadFdToString(fd, content);
}
bool WriteStringToFd(const std::string& content, int fd) {
@@ -106,7 +106,7 @@
bool follow_symlinks) {
int flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_BINARY |
(follow_symlinks ? 0 : O_NOFOLLOW);
- int fd = TEMP_FAILURE_RETRY(open(path.c_str(), flags, mode));
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), flags, mode)));
if (fd == -1) {
PLOG(ERROR) << "android::WriteStringToFile open failed";
return false;
@@ -126,7 +126,6 @@
PLOG(ERROR) << "android::WriteStringToFile write failed";
return CleanUpAfterFailedWrite(path);
}
- close(fd);
return true;
}
#endif
@@ -135,14 +134,11 @@
bool follow_symlinks) {
int flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_BINARY |
(follow_symlinks ? 0 : O_NOFOLLOW);
- int fd = TEMP_FAILURE_RETRY(open(path.c_str(), flags, DEFFILEMODE));
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), flags, 0666)));
if (fd == -1) {
return false;
}
-
- bool result = WriteStringToFd(content, fd);
- close(fd);
- return result || CleanUpAfterFailedWrite(path);
+ return WriteStringToFd(content, fd) || CleanUpAfterFailedWrite(path);
}
bool ReadFully(int fd, void* data, size_t byte_count) {
diff --git a/base/include/android-base/logging.h b/base/include/android-base/logging.h
index e78edbb..cf4a624 100644
--- a/base/include/android-base/logging.h
+++ b/base/include/android-base/logging.h
@@ -164,6 +164,24 @@
using ::android::base::FATAL; \
return (severity); }())
+#ifdef __clang_analyzer__
+// Clang's static analyzer does not see the conditional statement inside
+// LogMessage's destructor that will abort on FATAL severity.
+#define ABORT_AFTER_LOG_FATAL for (;; abort())
+
+struct LogAbortAfterFullExpr {
+ ~LogAbortAfterFullExpr() __attribute__((noreturn)) { abort(); }
+ explicit operator bool() const { return false; }
+};
+// Provides an expression that evaluates to the truthiness of `x`, automatically
+// aborting if `c` is true.
+#define ABORT_AFTER_LOG_EXPR_IF(c, x) (((c) && ::android::base::LogAbortAfterFullExpr()) || (x))
+#else
+#define ABORT_AFTER_LOG_FATAL
+#define ABORT_AFTER_LOG_EXPR_IF(c, x) (x)
+#endif
+#define ABORT_AFTER_LOG_FATAL_EXPR(x) ABORT_AFTER_LOG_EXPR_IF(true, x)
+
// Defines whether the given severity will be logged or silently swallowed.
#define WOULD_LOG(severity) \
UNLIKELY((SEVERITY_LAMBDA(severity)) >= ::android::base::GetMinimumLogSeverity())
@@ -190,54 +208,47 @@
// LOG(FATAL) << "We didn't expect to reach here";
#define LOG(severity) LOG_TO(DEFAULT, severity)
+// Checks if we want to log something, and sets up appropriate RAII objects if
+// so.
+// Note: DO NOT USE DIRECTLY. This is an implementation detail.
+#define LOGGING_PREAMBLE(severity) \
+ (WOULD_LOG(severity) && \
+ ABORT_AFTER_LOG_EXPR_IF((SEVERITY_LAMBDA(severity)) == ::android::base::FATAL, true) && \
+ ::android::base::ErrnoRestorer())
+
// Logs a message to logcat with the specified log ID on Android otherwise to
// stderr. If the severity is FATAL it also causes an abort.
-// Use an if-else statement instead of just an if statement here. So if there is a
-// else statement after LOG() macro, it won't bind to the if statement in the macro.
-// do-while(0) statement doesn't work here. Because we need to support << operator
-// following the macro, like "LOG(DEBUG) << xxx;".
-
-#define LOG_TO(dest, severity) \
- WOULD_LOG(severity) && \
- ::android::base::ErrnoRestorer() && \
- LOG_STREAM_TO(dest, severity)
+// Use an expression here so we can support the << operator following the macro,
+// like "LOG(DEBUG) << xxx;".
+#define LOG_TO(dest, severity) LOGGING_PREAMBLE(severity) && LOG_STREAM_TO(dest, severity)
// A variant of LOG that also logs the current errno value. To be used when
// library calls fail.
#define PLOG(severity) PLOG_TO(DEFAULT, severity)
// Behaves like PLOG, but logs to the specified log ID.
-#define PLOG_TO(dest, severity) \
- WOULD_LOG(SEVERITY_LAMBDA(severity)) && \
- ::android::base::ErrnoRestorer() && \
- ::android::base::LogMessage(__FILE__, __LINE__, \
- ::android::base::dest, \
- SEVERITY_LAMBDA(severity), errno).stream()
+#define PLOG_TO(dest, severity) \
+ LOGGING_PREAMBLE(severity) && \
+ ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::dest, \
+ SEVERITY_LAMBDA(severity), errno) \
+ .stream()
// Marker that code is yet to be implemented.
#define UNIMPLEMENTED(level) \
LOG(level) << __PRETTY_FUNCTION__ << " unimplemented "
-#ifdef __clang_analyzer__
-// ClangL static analyzer does not see the conditional statement inside
-// LogMessage's destructor that will abort on FATAL severity.
-#define ABORT_AFTER_LOG_FATAL for (;;abort())
-#else
-#define ABORT_AFTER_LOG_FATAL
-#endif
-
// Check whether condition x holds and LOG(FATAL) if not. The value of the
// expression x is only evaluated once. Extra logging can be appended using <<
// after. For example:
//
// CHECK(false == true) results in a log message of
// "Check failed: false == true".
-#define CHECK(x) \
- LIKELY((x)) || \
- ABORT_AFTER_LOG_FATAL \
- ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT, \
- ::android::base::FATAL, -1).stream() \
- << "Check failed: " #x << " "
+#define CHECK(x) \
+ LIKELY((x)) || ABORT_AFTER_LOG_FATAL_EXPR(false) || \
+ ::android::base::LogMessage( \
+ __FILE__, __LINE__, ::android::base::DEFAULT, ::android::base::FATAL, \
+ -1).stream() \
+ << "Check failed: " #x << " "
// Helper for CHECK_xx(x,y) macros.
#define CHECK_OP(LHS, RHS, OP) \
@@ -304,7 +315,7 @@
// DCHECKs are debug variants of CHECKs only enabled in debug builds. Generally
// CHECK should be used unless profiling identifies a CHECK as being in
// performance critical code.
-#if defined(NDEBUG)
+#if defined(NDEBUG) && !defined(__clang_analyzer__)
static constexpr bool kEnableDChecks = false;
#else
static constexpr bool kEnableDChecks = true;
@@ -328,7 +339,7 @@
if (::android::base::kEnableDChecks) CHECK_STREQ(s1, s2)
#define DCHECK_STRNE(s1, s2) \
if (::android::base::kEnableDChecks) CHECK_STRNE(s1, s2)
-#if defined(NDEBUG)
+#if defined(NDEBUG) && !defined(__clang_analyzer__)
#define DCHECK_CONSTEXPR(x, out, dummy)
#else
#define DCHECK_CONSTEXPR(x, out, dummy) CHECK_CONSTEXPR(x, out, dummy)
diff --git a/base/include/android-base/scopeguard.h b/base/include/android-base/scopeguard.h
new file mode 100644
index 0000000..abcf4bc
--- /dev/null
+++ b/base/include/android-base/scopeguard.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_BASE_SCOPEGUARD_H
+#define ANDROID_BASE_SCOPEGUARD_H
+
+#include <utility> // for std::move
+
+namespace android {
+namespace base {
+
+template <typename F>
+class ScopeGuard {
+ public:
+ ScopeGuard(F f) : f_(f), active_(true) {}
+
+ ScopeGuard(ScopeGuard&& that) : f_(std::move(that.f_)), active_(that.active_) {
+ that.active_ = false;
+ }
+
+ ~ScopeGuard() {
+ if (active_) f_();
+ }
+
+ ScopeGuard() = delete;
+ ScopeGuard(const ScopeGuard&) = delete;
+ void operator=(const ScopeGuard&) = delete;
+ void operator=(ScopeGuard&& that) = delete;
+
+ void Disable() { active_ = false; }
+
+ bool active() const { return active_; }
+
+ private:
+ F f_;
+ bool active_;
+};
+
+template <typename T>
+ScopeGuard<T> make_scope_guard(T f) {
+ return ScopeGuard<T>(f);
+}
+
+} // namespace base
+} // namespace android
+
+#endif // ANDROID_BASE_SCOPEGUARD_H
diff --git a/base/scopeguard_test.cpp b/base/scopeguard_test.cpp
new file mode 100644
index 0000000..e11154a
--- /dev/null
+++ b/base/scopeguard_test.cpp
@@ -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 "android-base/scopeguard.h"
+
+#include <utility>
+
+#include <gtest/gtest.h>
+
+TEST(scopeguard, normal) {
+ bool guarded_var = true;
+ {
+ auto scopeguard = android::base::make_scope_guard([&guarded_var] { guarded_var = false; });
+ }
+ ASSERT_FALSE(guarded_var);
+}
+
+TEST(scopeguard, disabled) {
+ bool guarded_var = true;
+ {
+ auto scopeguard = android::base::make_scope_guard([&guarded_var] { guarded_var = false; });
+ scopeguard.Disable();
+ }
+ ASSERT_TRUE(guarded_var);
+}
+
+TEST(scopeguard, moved) {
+ int guarded_var = true;
+ auto scopeguard = android::base::make_scope_guard([&guarded_var] { guarded_var = false; });
+ { decltype(scopeguard) new_guard(std::move(scopeguard)); }
+ EXPECT_FALSE(scopeguard.active());
+ ASSERT_FALSE(guarded_var);
+}
diff --git a/base/utf8.cpp b/base/utf8.cpp
old mode 100755
new mode 100644
diff --git a/base/utf8_test.cpp b/base/utf8_test.cpp
old mode 100755
new mode 100644
diff --git a/bootstat/.clang-format b/bootstat/.clang-format
new file mode 120000
index 0000000..fd0645f
--- /dev/null
+++ b/bootstat/.clang-format
@@ -0,0 +1 @@
+../.clang-format-2
\ No newline at end of file
diff --git a/bootstat/Android.bp b/bootstat/Android.bp
index 95c9af5..bc90a6e 100644
--- a/bootstat/Android.bp
+++ b/bootstat/Android.bp
@@ -74,6 +74,7 @@
// -----------------------------------------------------------------------------
cc_test {
name: "bootstat_tests",
+ test_suites: ["device-tests"],
defaults: ["bootstat_defaults"],
host_supported: true,
static_libs: [
diff --git a/init/AndroidTest.xml b/bootstat/AndroidTest.xml
similarity index 83%
rename from init/AndroidTest.xml
rename to bootstat/AndroidTest.xml
index 3de69ed..f3783fa 100644
--- a/init/AndroidTest.xml
+++ b/bootstat/AndroidTest.xml
@@ -13,14 +13,14 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<configuration description="Config for init_tests">
+<configuration description="Config for bootstat_tests">
<target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
<option name="cleanup" value="true" />
- <option name="push" value="init_tests->/data/local/tmp/init_tests" />
+ <option name="push" value="bootstat_tests->/data/local/tmp/bootstat_tests" />
</target_preparer>
<option name="test-suite-tag" value="apct" />
<test class="com.android.tradefed.testtype.GTest" >
<option name="native-test-device-path" value="/data/local/tmp" />
- <option name="module-name" value="init_tests" />
+ <option name="module-name" value="bootstat_tests" />
</test>
-</configuration>
+</configuration>
\ No newline at end of file
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 6f25d96..a4c2160 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -201,8 +201,10 @@
BootEventRecordStore boot_event_store;
BootEventRecordStore::BootEventRecord record;
- if (!boot_event_store.GetBootEvent(kBuildDateKey, &record) ||
- build_date != record.second) {
+ if (!boot_event_store.GetBootEvent(kBuildDateKey, &record)) {
+ boot_complete_prefix = "factory_reset_" + boot_complete_prefix;
+ boot_event_store.AddBootEventWithValue(kBuildDateKey, build_date);
+ } else if (build_date != record.second) {
boot_complete_prefix = "ota_" + boot_complete_prefix;
boot_event_store.AddBootEventWithValue(kBuildDateKey, build_date);
}
@@ -221,43 +223,78 @@
}
}
-// Parses and records the set of bootloader stages and associated boot times
-// from the ro.boot.boottime system property.
-void RecordBootloaderTimings(BootEventRecordStore* boot_event_store) {
- // |ro.boot.boottime| is of the form 'stage1:time1,...,stageN:timeN'.
+// A map from bootloader timing stage to the time that stage took during boot.
+typedef std::map<std::string, int32_t> BootloaderTimingMap;
+
+// Returns a mapping from bootloader stage names to the time those stages
+// took to boot.
+const BootloaderTimingMap GetBootLoaderTimings() {
+ BootloaderTimingMap timings;
+
+ // |ro.boot.boottime| is of the form 'stage1:time1,...,stageN:timeN',
+ // where timeN is in milliseconds.
std::string value = GetProperty("ro.boot.boottime");
if (value.empty()) {
// ro.boot.boottime is not reported on all devices.
- return;
+ return BootloaderTimingMap();
}
- int32_t total_time = 0;
auto stages = android::base::Split(value, ",");
- for (auto const &stageTiming : stages) {
+ for (const auto& stageTiming : stages) {
// |stageTiming| is of the form 'stage:time'.
auto stageTimingValues = android::base::Split(stageTiming, ":");
- DCHECK_EQ(2, stageTimingValues.size());
+ DCHECK_EQ(2U, stageTimingValues.size());
std::string stageName = stageTimingValues[0];
int32_t time_ms;
if (android::base::ParseInt(stageTimingValues[1], &time_ms)) {
- total_time += time_ms;
- boot_event_store->AddBootEventWithValue(
- "boottime.bootloader." + stageName, time_ms);
+ timings[stageName] = time_ms;
}
}
+ return timings;
+}
+
+// Parses and records the set of bootloader stages and associated boot times
+// from the ro.boot.boottime system property.
+void RecordBootloaderTimings(BootEventRecordStore* boot_event_store,
+ const BootloaderTimingMap& bootloader_timings) {
+ int32_t total_time = 0;
+ for (const auto& timing : bootloader_timings) {
+ total_time += timing.second;
+ boot_event_store->AddBootEventWithValue("boottime.bootloader." + timing.first, timing.second);
+ }
+
boot_event_store->AddBootEventWithValue("boottime.bootloader.total", total_time);
}
+// Records the closest estimation to the absolute device boot time, i.e.,
+// from power on to boot_complete, including bootloader times.
+void RecordAbsoluteBootTime(BootEventRecordStore* boot_event_store,
+ const BootloaderTimingMap& bootloader_timings,
+ std::chrono::milliseconds uptime) {
+ int32_t bootloader_time_ms = 0;
+
+ for (const auto& timing : bootloader_timings) {
+ if (timing.first.compare("SW") != 0) {
+ bootloader_time_ms += timing.second;
+ }
+ }
+
+ auto bootloader_duration = std::chrono::milliseconds(bootloader_time_ms);
+ auto absolute_total =
+ std::chrono::duration_cast<std::chrono::seconds>(bootloader_duration + uptime);
+ boot_event_store->AddBootEventWithValue("absolute_boot_time", absolute_total.count());
+}
+
// Records several metrics related to the time it takes to boot the device,
// including disambiguating boot time on encrypted or non-encrypted devices.
void RecordBootComplete() {
BootEventRecordStore boot_event_store;
BootEventRecordStore::BootEventRecord record;
- auto uptime = std::chrono::duration_cast<std::chrono::seconds>(
- android::base::boot_clock::now().time_since_epoch());
+ auto time_since_epoch = android::base::boot_clock::now().time_since_epoch();
+ auto uptime = std::chrono::duration_cast<std::chrono::seconds>(time_since_epoch);
time_t current_time_utc = time(nullptr);
if (boot_event_store.GetBootEvent("last_boot_time_utc", &record)) {
@@ -290,7 +327,6 @@
std::chrono::seconds boot_complete = std::chrono::seconds(uptime.count() - record.second);
boot_event_store.AddBootEventWithValue(boot_complete_prefix + "_post_decrypt",
boot_complete.count());
-
} else {
boot_event_store.AddBootEventWithValue(boot_complete_prefix + "_no_encryption",
uptime.count());
@@ -304,7 +340,11 @@
RecordInitBootTimeProp(&boot_event_store, "ro.boottime.init.selinux");
RecordInitBootTimeProp(&boot_event_store, "ro.boottime.init.cold_boot_wait");
- RecordBootloaderTimings(&boot_event_store);
+ const BootloaderTimingMap bootloader_timings = GetBootLoaderTimings();
+ RecordBootloaderTimings(&boot_event_store, bootloader_timings);
+
+ auto uptime_ms = std::chrono::duration_cast<std::chrono::milliseconds>(time_since_epoch);
+ RecordAbsoluteBootTime(&boot_event_store, bootloader_timings, uptime_ms);
}
// Records the boot_reason metric by querying the ro.boot.bootreason system
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 4783d6e..3a80b50 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -1,6 +1,5 @@
cc_defaults {
name: "debuggerd_defaults",
- defaults: ["linux_bionic_supported"],
cflags: [
"-Wall",
"-Wextra",
@@ -9,27 +8,45 @@
"-Os",
],
- // util.cpp gets async signal safe logging via libc_logging,
- // which defines its interface in bionic private headers.
- include_dirs: ["bionic/libc"],
-
local_include_dirs: ["include"],
}
+cc_library_shared {
+ name: "libtombstoned_client",
+ defaults: ["debuggerd_defaults"],
+ srcs: [
+ "tombstoned/tombstoned_client.cpp",
+ "util.cpp",
+ ],
+
+ static_libs: [
+ "libasync_safe"
+ ],
+
+ shared_libs: [
+ "libcutils",
+ "libbase",
+ ],
+
+ export_include_dirs: ["tombstoned/include"]
+}
+
// Utility library to tombstoned and get an output fd.
cc_library_static {
- name: "libtombstoned_client",
+ name: "libtombstoned_client_static",
defaults: ["debuggerd_defaults"],
srcs: [
- "tombstoned_client.cpp",
+ "tombstoned/tombstoned_client.cpp",
"util.cpp",
],
whole_static_libs: [
- "libc_logging",
+ "libasync_safe",
"libcutils",
"libbase",
],
+
+ export_include_dirs: ["tombstoned/include"]
}
// Core implementation, linked into libdebuggerd_handler and the dynamic linker.
@@ -39,7 +56,7 @@
srcs: ["handler/debuggerd_handler.cpp"],
whole_static_libs: [
- "libc_logging",
+ "libasync_safe",
"libdebuggerd",
],
@@ -69,7 +86,8 @@
whole_static_libs: [
"libdebuggerd_handler_core",
- "libtombstoned_client",
+ "libtombstoned_client_static",
+ "libasync_safe",
"libbase",
"libdebuggerd",
"libbacktrace",
@@ -163,9 +181,8 @@
srcs: [
"client/debuggerd_client_test.cpp",
"debuggerd_test.cpp",
- "tombstoned_client.cpp",
- "util.cpp"
],
+ static_libs: ["libasync_safe", "libtombstoned_client_static"],
},
},
@@ -174,11 +191,11 @@
"libbase",
"libcutils",
"libdebuggerd_client",
+ "liblog"
],
static_libs: [
"libdebuggerd",
- "libc_logging",
],
local_include_dirs: [
@@ -215,7 +232,7 @@
},
static_libs: [
- "libtombstoned_client",
+ "libtombstoned_client_static",
"libdebuggerd",
"libcutils",
],
@@ -225,7 +242,6 @@
"libbase",
"liblog",
"libprocinfo",
- "libselinux",
],
}
diff --git a/debuggerd/client/debuggerd_client.cpp b/debuggerd/client/debuggerd_client.cpp
index 3b84853..4ce038c 100644
--- a/debuggerd/client/debuggerd_client.cpp
+++ b/debuggerd/client/debuggerd_client.cpp
@@ -31,9 +31,10 @@
#include <android-base/stringprintf.h>
#include <android-base/unique_fd.h>
#include <cutils/sockets.h>
-#include <debuggerd/handler.h>
-#include <debuggerd/protocol.h>
-#include <debuggerd/util.h>
+
+#include "debuggerd/handler.h"
+#include "protocol.h"
+#include "util.h"
using namespace std::chrono_literals;
@@ -140,7 +141,9 @@
}
bool backtrace = dump_type == kDebuggerdBacktrace;
- send_signal(pid, backtrace);
+ if (!send_signal(pid, backtrace)) {
+ return false;
+ }
rc = TEMP_FAILURE_RETRY(recv(set_timeout(sockfd.get()), &response, sizeof(response), MSG_TRUNC));
if (rc == 0) {
diff --git a/debuggerd/client/debuggerd_client_test.cpp b/debuggerd/client/debuggerd_client_test.cpp
index aff03e5..8f97db1 100644
--- a/debuggerd/client/debuggerd_client_test.cpp
+++ b/debuggerd/client/debuggerd_client_test.cpp
@@ -31,7 +31,7 @@
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
-#include <debuggerd/util.h>
+#include "util.h"
using namespace std::chrono_literals;
using android::base::unique_fd;
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index 88f390b..be28079 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -42,16 +42,15 @@
#include <cutils/sockets.h>
#include <log/log.h>
#include <procinfo/process.h>
-#include <selinux/selinux.h>
#include "backtrace.h"
#include "tombstone.h"
#include "utility.h"
#include "debuggerd/handler.h"
-#include "debuggerd/protocol.h"
-#include "debuggerd/tombstoned.h"
-#include "debuggerd/util.h"
+#include "protocol.h"
+#include "tombstoned/tombstoned.h"
+#include "util.h"
using android::base::unique_fd;
using android::base::ReadFileToString;
diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp
index 492e9f0..4997dd6 100644
--- a/debuggerd/debuggerd.cpp
+++ b/debuggerd/debuggerd.cpp
@@ -27,8 +27,8 @@
#include <android-base/parseint.h>
#include <android-base/unique_fd.h>
#include <debuggerd/client.h>
-#include <debuggerd/util.h>
#include <selinux/selinux.h>
+#include "util.h"
using android::base::unique_fd;
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index b705e27..f17724a 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -29,17 +29,19 @@
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/macros.h>
#include <android-base/parseint.h>
#include <android-base/properties.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <cutils/sockets.h>
-#include <debuggerd/handler.h>
-#include <debuggerd/protocol.h>
-#include <debuggerd/tombstoned.h>
-#include <debuggerd/util.h>
#include <gtest/gtest.h>
+#include "debuggerd/handler.h"
+#include "protocol.h"
+#include "tombstoned/tombstoned.h"
+#include "util.h"
+
using namespace std::chrono_literals;
using android::base::unique_fd;
@@ -78,6 +80,14 @@
} \
} while (0)
+#define ASSERT_NOT_MATCH(str, pattern) \
+ do { \
+ std::regex r((pattern)); \
+ if (std::regex_search((str), r)) { \
+ FAIL() << "regex mismatch: expected to not find " << (pattern) << " in: \n" << (str); \
+ } \
+ } while (0)
+
static void tombstoned_intercept(pid_t target_pid, unique_fd* intercept_fd, unique_fd* output_fd) {
intercept_fd->reset(socket_local_client(kTombstonedInterceptSocketName,
ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET));
@@ -141,7 +151,7 @@
// Returns -1 if we fail to read a response from tombstoned, otherwise the received return code.
void FinishIntercept(int* result);
- void StartProcess(std::function<void()> function);
+ void StartProcess(std::function<void()> function, std::function<pid_t()> forker = fork);
void StartCrasher(const std::string& crash_type);
void FinishCrasher();
void AssertDeath(int signo);
@@ -187,14 +197,14 @@
}
}
-void CrasherTest::StartProcess(std::function<void()> function) {
+void CrasherTest::StartProcess(std::function<void()> function, std::function<pid_t()> forker) {
unique_fd read_pipe;
unique_fd crasher_read_pipe;
if (!Pipe(&crasher_read_pipe, &crasher_pipe)) {
FAIL() << "failed to create pipe: " << strerror(errno);
}
- crasher_pid = fork();
+ crasher_pid = forker();
if (crasher_pid == -1) {
FAIL() << "fork failed: " << strerror(errno);
} else if (crasher_pid == 0) {
@@ -226,12 +236,14 @@
FAIL() << "failed to wait for crasher: " << strerror(errno);
}
- if (WIFEXITED(status)) {
- FAIL() << "crasher failed to exec: " << strerror(WEXITSTATUS(status));
- } else if (!WIFSIGNALED(status)) {
- FAIL() << "crasher didn't terminate via a signal";
+ if (signo == 0) {
+ ASSERT_TRUE(WIFEXITED(status));
+ ASSERT_EQ(0, WEXITSTATUS(signo));
+ } else {
+ ASSERT_FALSE(WIFEXITED(status));
+ ASSERT_TRUE(WIFSIGNALED(status)) << "crasher didn't terminate via a signal";
+ ASSERT_EQ(signo, WTERMSIG(status));
}
- ASSERT_EQ(signo, WTERMSIG(status));
crasher_pid = -1;
}
@@ -336,6 +348,26 @@
ASSERT_MATCH(result, R"(Abort message: 'abort message goes here')");
}
+TEST_F(CrasherTest, abort_message_backtrace) {
+ int intercept_result;
+ unique_fd output_fd;
+ StartProcess([]() {
+ android_set_abort_message("not actually aborting");
+ raise(DEBUGGER_SIGNAL);
+ exit(0);
+ });
+ StartIntercept(&output_fd);
+ FinishCrasher();
+ AssertDeath(0);
+ FinishIntercept(&intercept_result);
+
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+ std::string result;
+ ConsumeFd(std::move(output_fd), &result);
+ ASSERT_NOT_MATCH(result, R"(Abort message:)");
+}
+
TEST_F(CrasherTest, intercept_timeout) {
int intercept_result;
unique_fd output_fd;
@@ -497,6 +529,37 @@
ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(tgkill)");
}
+TEST_F(CrasherTest, fake_pid) {
+ int intercept_result;
+ unique_fd output_fd;
+
+ // Prime the getpid/gettid caches.
+ UNUSED(getpid());
+ UNUSED(gettid());
+
+ std::function<pid_t()> clone_fn = []() {
+ return syscall(__NR_clone, SIGCHLD, nullptr, nullptr, nullptr, nullptr);
+ };
+ StartProcess(
+ []() {
+ ASSERT_NE(getpid(), syscall(__NR_getpid));
+ ASSERT_NE(gettid(), syscall(__NR_gettid));
+ raise(SIGSEGV);
+ },
+ clone_fn);
+
+ StartIntercept(&output_fd);
+ FinishCrasher();
+ AssertDeath(SIGSEGV);
+ FinishIntercept(&intercept_result);
+
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+ std::string result;
+ ConsumeFd(std::move(output_fd), &result);
+ ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(tgkill)");
+}
+
TEST(crash_dump, zombie) {
pid_t forkpid = fork();
diff --git a/debuggerd/handler/debuggerd_fallback.cpp b/debuggerd/handler/debuggerd_fallback.cpp
index 5c6c59c..a9c9862 100644
--- a/debuggerd/handler/debuggerd_fallback.cpp
+++ b/debuggerd/handler/debuggerd_fallback.cpp
@@ -39,16 +39,15 @@
#include <android-base/file.h>
#include <android-base/unique_fd.h>
+#include <async_safe/log.h>
#include "debuggerd/handler.h"
-#include "debuggerd/tombstoned.h"
-#include "debuggerd/util.h"
+#include "tombstoned/tombstoned.h"
+#include "util.h"
#include "backtrace.h"
#include "tombstone.h"
-#include "private/libc_logging.h"
-
using android::base::unique_fd;
extern "C" void __linker_enable_fallback_allocator();
@@ -81,7 +80,7 @@
DIR* dir = opendir(buf);
if (!dir) {
- __libc_format_log(ANDROID_LOG_ERROR, "libc", "failed to open %s: %s", buf, strerror(errno));
+ async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to open %s: %s", buf, strerror(errno));
return;
}
@@ -145,7 +144,8 @@
static pthread_mutex_t trace_mutex = PTHREAD_MUTEX_INITIALIZER;
int ret = pthread_mutex_trylock(&trace_mutex);
if (ret != 0) {
- __libc_format_log(ANDROID_LOG_INFO, "libc", "pthread_mutex_try_lock failed: %s", strerror(ret));
+ async_safe_format_log(ANDROID_LOG_INFO, "libc", "pthread_mutex_try_lock failed: %s",
+ strerror(ret));
return;
}
@@ -167,7 +167,8 @@
// receiving our signal.
unique_fd pipe_read, pipe_write;
if (!Pipe(&pipe_read, &pipe_write)) {
- __libc_format_log(ANDROID_LOG_ERROR, "libc", "failed to create pipe: %s", strerror(errno));
+ async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to create pipe: %s",
+ strerror(errno));
return false;
}
@@ -180,8 +181,8 @@
siginfo.si_uid = getuid();
if (syscall(__NR_rt_tgsigqueueinfo, getpid(), tid, DEBUGGER_SIGNAL, &siginfo) != 0) {
- __libc_format_log(ANDROID_LOG_ERROR, "libc", "failed to send trace signal to %d: %s", tid,
- strerror(errno));
+ async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to send trace signal to %d: %s",
+ tid, strerror(errno));
return false;
}
@@ -209,7 +210,7 @@
static pthread_mutex_t crash_mutex = PTHREAD_MUTEX_INITIALIZER;
int ret = pthread_mutex_lock(&crash_mutex);
if (ret != 0) {
- __libc_format_log(ANDROID_LOG_INFO, "libc", "pthread_mutex_lock failed: %s", strerror(ret));
+ async_safe_format_log(ANDROID_LOG_INFO, "libc", "pthread_mutex_lock failed: %s", strerror(ret));
return;
}
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index cd00dc5..8fd6e11 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -48,8 +48,7 @@
#include <sys/wait.h>
#include <unistd.h>
-#include "private/bionic_futex.h"
-#include "private/libc_logging.h"
+#include <async_safe/log.h>
// see man(2) prctl, specifically the section about PR_GET_NAME
#define MAX_TASK_NAME_LEN (16)
@@ -62,6 +61,20 @@
#define CRASH_DUMP_PATH "/system/bin/" CRASH_DUMP_NAME
+// Wrappers that directly invoke the respective syscalls, in case the cached values are invalid.
+#pragma GCC poison getpid gettid
+static pid_t __getpid() {
+ return syscall(__NR_getpid);
+}
+
+static pid_t __gettid() {
+ return syscall(__NR_gettid);
+}
+
+static inline void futex_wait(volatile void* ftx, int value) {
+ syscall(__NR_futex, ftx, FUTEX_WAIT, value, nullptr, nullptr, 0);
+}
+
class ErrnoRestorer {
public:
ErrnoRestorer() : saved_errno_(errno) {
@@ -82,11 +95,12 @@
// Mutex to ensure only one crashing thread dumps itself.
static pthread_mutex_t crash_mutex = PTHREAD_MUTEX_INITIALIZER;
-// Don't use __libc_fatal because it exits via abort, which might put us back into a signal handler.
+// Don't use async_safe_fatal because it exits via abort, which might put us back into
+// a signal handler.
static void __noreturn __printflike(1, 2) fatal(const char* fmt, ...) {
va_list args;
va_start(args, fmt);
- __libc_format_log_va_list(ANDROID_LOG_FATAL, "libc", fmt, args);
+ async_safe_format_log_va_list(ANDROID_LOG_FATAL, "libc", fmt, args);
_exit(1);
}
@@ -96,7 +110,7 @@
va_start(args, fmt);
char buf[4096];
- __libc_format_buffer_va_list(buf, sizeof(buf), fmt, args);
+ async_safe_format_buffer_va_list(buf, sizeof(buf), fmt, args);
fatal("%s: %s", buf, strerror(err));
}
@@ -120,8 +134,8 @@
}
if (signum == DEBUGGER_SIGNAL) {
- __libc_format_log(ANDROID_LOG_INFO, "libc", "Requested dump for tid %d (%s)", gettid(),
- thread_name);
+ async_safe_format_log(ANDROID_LOG_INFO, "libc", "Requested dump for tid %d (%s)", __gettid(),
+ thread_name);
return;
}
@@ -166,14 +180,14 @@
char addr_desc[32]; // ", fault addr 0x1234"
addr_desc[0] = code_desc[0] = 0;
if (info != nullptr) {
- __libc_format_buffer(code_desc, sizeof(code_desc), ", code %d", info->si_code);
+ async_safe_format_buffer(code_desc, sizeof(code_desc), ", code %d", info->si_code);
if (has_address) {
- __libc_format_buffer(addr_desc, sizeof(addr_desc), ", fault addr %p", info->si_addr);
+ async_safe_format_buffer(addr_desc, sizeof(addr_desc), ", fault addr %p", info->si_addr);
}
}
- __libc_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);
+ 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);
}
/*
@@ -182,8 +196,8 @@
static bool have_siginfo(int signum) {
struct sigaction old_action;
if (sigaction(signum, nullptr, &old_action) < 0) {
- __libc_format_log(ANDROID_LOG_WARN, "libc", "Failed testing for SA_SIGINFO: %s",
- strerror(errno));
+ async_safe_format_log(ANDROID_LOG_WARN, "libc", "Failed testing for SA_SIGINFO: %s",
+ strerror(errno));
return false;
}
return (old_action.sa_flags & SA_SIGINFO) != 0;
@@ -207,7 +221,7 @@
capdata[1].inheritable = capdata[1].permitted;
if (capset(&capheader, &capdata[0]) == -1) {
- __libc_format_log(ANDROID_LOG_ERROR, "libc", "capset failed: %s", strerror(errno));
+ async_safe_format_log(ANDROID_LOG_ERROR, "libc", "capset failed: %s", strerror(errno));
}
}
@@ -217,8 +231,8 @@
for (unsigned long i = 0; i < 64; ++i) {
if (capmask & (1ULL << i)) {
if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, i, 0, 0) != 0) {
- __libc_format_log(ANDROID_LOG_ERROR, "libc", "failed to raise ambient capability %lu: %s",
- i, strerror(errno));
+ async_safe_format_log(ANDROID_LOG_ERROR, "libc",
+ "failed to raise ambient capability %lu: %s", i, strerror(errno));
}
}
}
@@ -260,8 +274,8 @@
// Don't use fork(2) to avoid calling pthread_atfork handlers.
int forkpid = clone(nullptr, nullptr, 0, nullptr);
if (forkpid == -1) {
- __libc_format_log(ANDROID_LOG_FATAL, "libc", "failed to fork in debuggerd signal handler: %s",
- strerror(errno));
+ async_safe_format_log(ANDROID_LOG_FATAL, "libc",
+ "failed to fork in debuggerd signal handler: %s", strerror(errno));
} else if (forkpid == 0) {
TEMP_FAILURE_RETRY(dup2(pipefds[1], STDOUT_FILENO));
close(pipefds[0]);
@@ -271,8 +285,9 @@
char main_tid[10];
char pseudothread_tid[10];
- __libc_format_buffer(main_tid, sizeof(main_tid), "%d", thread_info->crashing_tid);
- __libc_format_buffer(pseudothread_tid, sizeof(pseudothread_tid), "%d", thread_info->pseudothread_tid);
+ async_safe_format_buffer(main_tid, sizeof(main_tid), "%d", thread_info->crashing_tid);
+ async_safe_format_buffer(pseudothread_tid, sizeof(pseudothread_tid), "%d",
+ thread_info->pseudothread_tid);
execl(CRASH_DUMP_PATH, CRASH_DUMP_NAME, main_tid, pseudothread_tid, nullptr);
@@ -282,15 +297,16 @@
char buf[4];
ssize_t rc = TEMP_FAILURE_RETRY(read(pipefds[0], &buf, sizeof(buf)));
if (rc == -1) {
- __libc_format_log(ANDROID_LOG_FATAL, "libc", "read of IPC pipe failed: %s", strerror(errno));
+ async_safe_format_log(ANDROID_LOG_FATAL, "libc", "read of IPC pipe failed: %s",
+ strerror(errno));
} else if (rc == 0) {
- __libc_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper failed to exec");
+ async_safe_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper failed to exec");
} else if (rc != 1) {
- __libc_format_log(ANDROID_LOG_FATAL, "libc",
- "read of IPC pipe returned unexpected value: %zd", rc);
+ async_safe_format_log(ANDROID_LOG_FATAL, "libc",
+ "read of IPC pipe returned unexpected value: %zd", rc);
} else {
if (buf[0] != '\1') {
- __libc_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper reported failure");
+ async_safe_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper reported failure");
} else {
thread_info->crash_dump_started = true;
}
@@ -300,10 +316,10 @@
// Don't leave a zombie child.
int status;
if (TEMP_FAILURE_RETRY(waitpid(forkpid, &status, 0)) == -1) {
- __libc_format_log(ANDROID_LOG_FATAL, "libc", "failed to wait for crash_dump helper: %s",
- strerror(errno));
+ async_safe_format_log(ANDROID_LOG_FATAL, "libc", "failed to wait for crash_dump helper: %s",
+ strerror(errno));
} else if (WIFSTOPPED(status) || WIFSIGNALED(status)) {
- __libc_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper crashed or stopped");
+ async_safe_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper crashed or stopped");
thread_info->crash_dump_started = false;
}
}
@@ -331,7 +347,7 @@
// rt_tgsigqueueinfo(2) to preserve SA_SIGINFO) will cause it to be delivered
// when our signal handler returns.
if (crash_dump_started || info->si_signo != DEBUGGER_SIGNAL) {
- int rc = syscall(SYS_rt_tgsigqueueinfo, getpid(), gettid(), info->si_signo, info);
+ int rc = syscall(SYS_rt_tgsigqueueinfo, __getpid(), __gettid(), info->si_signo, info);
if (rc != 0) {
fatal_errno("failed to resend signal during crash");
}
@@ -356,7 +372,7 @@
memset(&si, 0, sizeof(si));
si.si_signo = signal_number;
si.si_code = SI_USER;
- si.si_pid = getpid();
+ si.si_pid = __getpid();
si.si_uid = getuid();
info = &si;
} else if (info->si_code >= 0 || info->si_code == SI_TKILL) {
@@ -383,21 +399,22 @@
// Only allow one thread to handle a signal at a time.
int ret = pthread_mutex_lock(&crash_mutex);
if (ret != 0) {
- __libc_format_log(ANDROID_LOG_INFO, "libc", "pthread_mutex_lock failed: %s", strerror(ret));
+ async_safe_format_log(ANDROID_LOG_INFO, "libc", "pthread_mutex_lock failed: %s", strerror(ret));
return;
}
log_signal_summary(signal_number, info);
- // Populate si_value with the abort message address, if found.
- if (abort_message) {
+ // If this was a fatal crash, populate si_value with the abort message address if possible.
+ // Note that applications can set an abort message without aborting.
+ if (abort_message && signal_number != DEBUGGER_SIGNAL) {
info->si_value.sival_ptr = abort_message;
}
debugger_thread_info thread_info = {
.crash_dump_started = false,
.pseudothread_tid = -1,
- .crashing_tid = gettid(),
+ .crashing_tid = __gettid(),
.signal_number = signal_number,
.info = info
};
@@ -418,10 +435,10 @@
}
// Wait for the child to start...
- __futex_wait(&thread_info.pseudothread_tid, -1, nullptr);
+ futex_wait(&thread_info.pseudothread_tid, -1);
// and then wait for it to finish.
- __futex_wait(&thread_info.pseudothread_tid, child_pid, nullptr);
+ futex_wait(&thread_info.pseudothread_tid, child_pid);
// Restore PR_SET_DUMPABLE to its original value.
if (prctl(PR_SET_DUMPABLE, orig_dumpable) != 0) {
diff --git a/debuggerd/libdebuggerd/arm/machine.cpp b/debuggerd/libdebuggerd/arm/machine.cpp
index 78c2306..ac833ae 100644
--- a/debuggerd/libdebuggerd/arm/machine.cpp
+++ b/debuggerd/libdebuggerd/arm/machine.cpp
@@ -48,6 +48,21 @@
}
}
+#define DUMP_GP_REGISTERS(log, reg_prefix) \
+ _LOG(log, logtype::REGISTERS, " r0 %08x r1 %08x r2 %08x r3 %08x\n", \
+ static_cast<uint32_t>(reg_prefix##r0), static_cast<uint32_t>(reg_prefix##r1), \
+ static_cast<uint32_t>(reg_prefix##r2), static_cast<uint32_t>(reg_prefix##r3)); \
+ _LOG(log, logtype::REGISTERS, " r4 %08x r5 %08x r6 %08x r7 %08x\n", \
+ static_cast<uint32_t>(reg_prefix##r4), static_cast<uint32_t>(reg_prefix##r5), \
+ static_cast<uint32_t>(reg_prefix##r6), static_cast<uint32_t>(reg_prefix##r7)); \
+ _LOG(log, logtype::REGISTERS, " r8 %08x r9 %08x sl %08x fp %08x\n", \
+ static_cast<uint32_t>(reg_prefix##r8), static_cast<uint32_t>(reg_prefix##r9), \
+ static_cast<uint32_t>(reg_prefix##r10), static_cast<uint32_t>(reg_prefix##fp)); \
+ _LOG(log, logtype::REGISTERS, " ip %08x sp %08x lr %08x pc %08x cpsr %08x\n", \
+ static_cast<uint32_t>(reg_prefix##ip), static_cast<uint32_t>(reg_prefix##sp), \
+ static_cast<uint32_t>(reg_prefix##lr), static_cast<uint32_t>(reg_prefix##pc), \
+ static_cast<uint32_t>(reg_prefix##cpsr))
+
void dump_registers(log_t* log, pid_t tid) {
pt_regs r;
if (ptrace(PTRACE_GETREGS, tid, 0, &r)) {
@@ -55,19 +70,7 @@
return;
}
- _LOG(log, logtype::REGISTERS, " r0 %08x r1 %08x r2 %08x r3 %08x\n",
- static_cast<uint32_t>(r.ARM_r0), static_cast<uint32_t>(r.ARM_r1),
- static_cast<uint32_t>(r.ARM_r2), static_cast<uint32_t>(r.ARM_r3));
- _LOG(log, logtype::REGISTERS, " r4 %08x r5 %08x r6 %08x r7 %08x\n",
- static_cast<uint32_t>(r.ARM_r4), static_cast<uint32_t>(r.ARM_r5),
- static_cast<uint32_t>(r.ARM_r6), static_cast<uint32_t>(r.ARM_r7));
- _LOG(log, logtype::REGISTERS, " r8 %08x r9 %08x sl %08x fp %08x\n",
- static_cast<uint32_t>(r.ARM_r8), static_cast<uint32_t>(r.ARM_r9),
- static_cast<uint32_t>(r.ARM_r10), static_cast<uint32_t>(r.ARM_fp));
- _LOG(log, logtype::REGISTERS, " ip %08x sp %08x lr %08x pc %08x cpsr %08x\n",
- static_cast<uint32_t>(r.ARM_ip), static_cast<uint32_t>(r.ARM_sp),
- static_cast<uint32_t>(r.ARM_lr), static_cast<uint32_t>(r.ARM_pc),
- static_cast<uint32_t>(r.ARM_cpsr));
+ DUMP_GP_REGISTERS(log, r.ARM_);
user_vfp vfp_regs;
if (ptrace(PTRACE_GETVFPREGS, tid, 0, &vfp_regs)) {
@@ -81,3 +84,7 @@
}
_LOG(log, logtype::FP_REGISTERS, " scr %08lx\n", vfp_regs.fpscr);
}
+
+void dump_registers(log_t* log, const ucontext_t* uc) {
+ DUMP_GP_REGISTERS(log, uc->uc_mcontext.arm_);
+}
diff --git a/debuggerd/libdebuggerd/arm64/machine.cpp b/debuggerd/libdebuggerd/arm64/machine.cpp
index e7bf79a..fa73c99a 100644
--- a/debuggerd/libdebuggerd/arm64/machine.cpp
+++ b/debuggerd/libdebuggerd/arm64/machine.cpp
@@ -52,6 +52,17 @@
}
}
+#define DUMP_GP_REGISTERS(log) \
+ for (int i = 0; i < 28; i += 4) { \
+ const char* fmt = " x%-2d %016llx x%-2d %016llx x%-2d %016llx x%-2d %016llx\n"; \
+ _LOG(log, logtype::REGISTERS, fmt, i, r.regs[i], i + 1, r.regs[i + 1], i + 2, r.regs[i + 2], \
+ i + 3, r.regs[i + 3]); \
+ } \
+ _LOG(log, logtype::REGISTERS, " x28 %016llx x29 %016llx x30 %016llx\n", r.regs[28], \
+ r.regs[29], r.regs[30]); \
+ _LOG(log, logtype::REGISTERS, " sp %016llx pc %016llx pstate %016llx\n", r.sp, r.pc, \
+ r.pstate)
+
void dump_registers(log_t* log, pid_t tid) {
struct user_pt_regs r;
struct iovec io;
@@ -63,20 +74,7 @@
return;
}
- for (int i = 0; i < 28; i += 4) {
- _LOG(log, logtype::REGISTERS,
- " x%-2d %016llx x%-2d %016llx x%-2d %016llx x%-2d %016llx\n",
- i, r.regs[i],
- i+1, r.regs[i+1],
- i+2, r.regs[i+2],
- i+3, r.regs[i+3]);
- }
-
- _LOG(log, logtype::REGISTERS, " x28 %016llx x29 %016llx x30 %016llx\n",
- r.regs[28], r.regs[29], r.regs[30]);
-
- _LOG(log, logtype::REGISTERS, " sp %016llx pc %016llx pstate %016llx\n",
- r.sp, r.pc, r.pstate);
+ DUMP_GP_REGISTERS(log);
struct user_fpsimd_state f;
io.iov_base = &f;
@@ -99,3 +97,8 @@
}
_LOG(log, logtype::FP_REGISTERS, " fpsr %08x fpcr %08x\n", f.fpsr, f.fpcr);
}
+
+void dump_registers(log_t* log, const ucontext_t* ucontext) {
+ const mcontext_t& r = ucontext->uc_mcontext;
+ DUMP_GP_REGISTERS(log);
+}
diff --git a/debuggerd/libdebuggerd/include/machine.h b/debuggerd/libdebuggerd/include/machine.h
index e65b147..5e56682 100644
--- a/debuggerd/libdebuggerd/include/machine.h
+++ b/debuggerd/libdebuggerd/include/machine.h
@@ -25,5 +25,6 @@
void dump_memory_and_code(log_t* log, Backtrace* backtrace);
void dump_registers(log_t* log, pid_t tid);
+void dump_registers(log_t* log, const ucontext_t* uc);
#endif // _DEBUGGERD_MACHINE_H
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index 0c38449..edc7be5 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -470,6 +470,11 @@
}
}
+// Weak noop implementation, real implementations are in <arch>/machine.cpp.
+__attribute__((weak)) void dump_registers(log_t* log, const ucontext_t*) {
+ _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,
uintptr_t abort_msg_address, bool primary_thread) {
@@ -749,11 +754,15 @@
read_with_default("/proc/self/comm", thread_name, sizeof(thread_name), "<unknown>");
read_with_default("/proc/self/cmdline", process_name, sizeof(process_name), "<unknown>");
+ _LOG(&log, logtype::HEADER, "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n");
+ dump_header_info(&log);
dump_thread_info(&log, pid, tid, thread_name, process_name);
dump_signal_info(&log, siginfo);
std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, tid));
dump_abort_message(backtrace.get(), &log, abort_msg_address);
+ dump_registers(&log, ucontext);
+
// TODO: Dump registers from the ucontext.
if (backtrace->Unwind(0, ucontext)) {
dump_backtrace_and_stack(backtrace.get(), &log);
diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp
index 22fde5e..7f450e6 100644
--- a/debuggerd/libdebuggerd/utility.cpp
+++ b/debuggerd/libdebuggerd/utility.cpp
@@ -22,16 +22,22 @@
#include <signal.h>
#include <string.h>
#include <sys/ptrace.h>
+#include <sys/uio.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <backtrace/Backtrace.h>
#include <log/log.h>
+using android::base::unique_fd;
+
// Whitelist output desired in the logcat output.
bool is_allowed_in_logcat(enum logtype ltype) {
if ((ltype == HEADER)
@@ -42,6 +48,19 @@
return false;
}
+static bool should_write_to_kmsg() {
+ // Write to kmsg if tombstoned isn't up, and we're able to do so.
+ if (!android::base::GetBoolProperty("ro.debuggable", false)) {
+ return false;
+ }
+
+ if (android::base::GetProperty("init.svc.tombstoned", "") == "running") {
+ return false;
+ }
+
+ return true;
+}
+
__attribute__((__weak__, visibility("default")))
void _LOG(log_t* log, enum logtype ltype, const char* fmt, ...) {
bool write_to_tombstone = (log->tfd != -1);
@@ -49,6 +68,7 @@
&& log->crashed_tid != -1
&& log->current_tid != -1
&& (log->crashed_tid == log->current_tid);
+ static bool write_to_kmsg = should_write_to_kmsg();
char buf[512];
va_list ap;
@@ -70,6 +90,30 @@
if (log->amfd_data != nullptr) {
*log->amfd_data += buf;
}
+
+ if (write_to_kmsg) {
+ unique_fd kmsg_fd(open("/dev/kmsg_debug", O_WRONLY | O_APPEND | O_CLOEXEC));
+ if (kmsg_fd.get() >= 0) {
+ // Our output might contain newlines which would otherwise be handled by the android logger.
+ // Split the lines up ourselves before sending to the kernel logger.
+ if (buf[len - 1] == '\n') {
+ buf[len - 1] = '\0';
+ }
+
+ std::vector<std::string> fragments = android::base::Split(buf, "\n");
+ for (const std::string& fragment : fragments) {
+ static constexpr char prefix[] = "<3>DEBUG: ";
+ struct iovec iov[3];
+ iov[0].iov_base = const_cast<char*>(prefix);
+ iov[0].iov_len = strlen(prefix);
+ iov[1].iov_base = const_cast<char*>(fragment.c_str());
+ iov[1].iov_len = fragment.length();
+ iov[2].iov_base = const_cast<char*>("\n");
+ iov[2].iov_len = 1;
+ TEMP_FAILURE_RETRY(writev(kmsg_fd.get(), iov, 3));
+ }
+ }
+ }
}
}
@@ -205,7 +249,7 @@
}
void read_with_default(const char* path, char* buf, size_t len, const char* default_value) {
- android::base::unique_fd fd(open(path, O_RDONLY));
+ unique_fd fd(open(path, O_RDONLY | O_CLOEXEC));
if (fd != -1) {
int rc = TEMP_FAILURE_RETRY(read(fd.get(), buf, len - 1));
if (rc != -1) {
diff --git a/debuggerd/include/debuggerd/protocol.h b/debuggerd/protocol.h
similarity index 96%
rename from debuggerd/include/debuggerd/protocol.h
rename to debuggerd/protocol.h
index 0756876..144efc8 100644
--- a/debuggerd/include/debuggerd/protocol.h
+++ b/debuggerd/protocol.h
@@ -21,6 +21,7 @@
// Sockets in the ANDROID_SOCKET_NAMESPACE_RESERVED namespace.
// Both sockets are SOCK_SEQPACKET sockets, so no explicit length field is needed.
constexpr char kTombstonedCrashSocketName[] = "tombstoned_crash";
+constexpr char kTombstonedJavaTraceSocketName[] = "tombstoned_java_trace";
constexpr char kTombstonedInterceptSocketName[] = "tombstoned_intercept";
enum class CrashPacketType : uint8_t {
diff --git a/debuggerd/include/debuggerd/tombstoned.h b/debuggerd/tombstoned/include/tombstoned/tombstoned.h
similarity index 89%
rename from debuggerd/include/debuggerd/tombstoned.h
rename to debuggerd/tombstoned/include/tombstoned/tombstoned.h
index d158d50..908517d 100644
--- a/debuggerd/include/debuggerd/tombstoned.h
+++ b/debuggerd/tombstoned/include/tombstoned/tombstoned.h
@@ -21,6 +21,6 @@
#include <android-base/unique_fd.h>
bool tombstoned_connect(pid_t pid, android::base::unique_fd* tombstoned_socket,
- android::base::unique_fd* output_fd);
+ android::base::unique_fd* output_fd, bool is_native_crash = true);
bool tombstoned_notify_completion(int tombstoned_socket);
diff --git a/debuggerd/tombstoned/intercept_manager.cpp b/debuggerd/tombstoned/intercept_manager.cpp
index dff942c..4d4eb9e 100644
--- a/debuggerd/tombstoned/intercept_manager.cpp
+++ b/debuggerd/tombstoned/intercept_manager.cpp
@@ -28,8 +28,8 @@
#include <android-base/unique_fd.h>
#include <cutils/sockets.h>
-#include "debuggerd/protocol.h"
-#include "debuggerd/util.h"
+#include "protocol.h"
+#include "util.h"
using android::base::unique_fd;
diff --git a/debuggerd/tombstoned/tombstoned.cpp b/debuggerd/tombstoned/tombstoned.cpp
index 2248a21..05df9f2 100644
--- a/debuggerd/tombstoned/tombstoned.cpp
+++ b/debuggerd/tombstoned/tombstoned.cpp
@@ -35,8 +35,8 @@
#include <cutils/sockets.h>
#include "debuggerd/handler.h"
-#include "debuggerd/protocol.h"
-#include "debuggerd/util.h"
+#include "protocol.h"
+#include "util.h"
#include "intercept_manager.h"
@@ -50,86 +50,154 @@
kCrashStatusQueued,
};
+struct Crash;
+
+class CrashType {
+ public:
+ CrashType(const std::string& dir_path, const std::string& file_name_prefix, size_t max_artifacts,
+ size_t max_concurrent_dumps)
+ : file_name_prefix_(file_name_prefix),
+ dir_path_(dir_path),
+ dir_fd_(open(dir_path.c_str(), O_DIRECTORY | O_RDONLY | O_CLOEXEC)),
+ max_artifacts_(max_artifacts),
+ next_artifact_(0),
+ max_concurrent_dumps_(max_concurrent_dumps),
+ num_concurrent_dumps_(0) {
+ if (dir_fd_ == -1) {
+ PLOG(FATAL) << "failed to open directory: " << dir_path;
+ }
+
+ // NOTE: If max_artifacts_ <= max_concurrent_dumps_, then theoretically the
+ // same filename could be handed out to multiple processes.
+ CHECK(max_artifacts_ > max_concurrent_dumps_);
+
+ find_oldest_artifact();
+ }
+
+ unique_fd get_output_fd() {
+ unique_fd result;
+ char buf[PATH_MAX];
+ snprintf(buf, sizeof(buf), "%s%02d", file_name_prefix_.c_str(), next_artifact_);
+ // Unlink and create the file, instead of using O_TRUNC, to avoid two processes
+ // interleaving their output in case we ever get into that situation.
+ if (unlinkat(dir_fd_, buf, 0) != 0 && errno != ENOENT) {
+ PLOG(FATAL) << "failed to unlink tombstone at " << dir_path_ << buf;
+ }
+
+ result.reset(openat(dir_fd_, buf, O_CREAT | O_EXCL | O_WRONLY | O_APPEND | O_CLOEXEC, 0640));
+ if (result == -1) {
+ PLOG(FATAL) << "failed to create tombstone at " << dir_path_ << buf;
+ }
+
+ next_artifact_ = (next_artifact_ + 1) % max_artifacts_;
+ return result;
+ }
+
+ bool maybe_enqueue_crash(Crash* crash) {
+ if (num_concurrent_dumps_ == max_concurrent_dumps_) {
+ queued_requests_.push_back(crash);
+ return true;
+ }
+
+ return false;
+ }
+
+ void maybe_dequeue_crashes(void (*handler)(Crash* crash)) {
+ while (!queued_requests_.empty() && num_concurrent_dumps_ < max_concurrent_dumps_) {
+ Crash* next_crash = queued_requests_.front();
+ queued_requests_.pop_front();
+ handler(next_crash);
+ }
+ }
+
+ void on_crash_started() { ++num_concurrent_dumps_; }
+
+ void on_crash_completed() { --num_concurrent_dumps_; }
+
+ static CrashType* const tombstone;
+ static CrashType* const java_trace;
+
+ private:
+ void find_oldest_artifact() {
+ size_t oldest_tombstone = 0;
+ time_t oldest_time = std::numeric_limits<time_t>::max();
+
+ for (size_t i = 0; i < max_artifacts_; ++i) {
+ std::string path = android::base::StringPrintf("%s/%s%02zu", dir_path_.c_str(),
+ file_name_prefix_.c_str(), i);
+ struct stat st;
+ if (stat(path.c_str(), &st) != 0) {
+ if (errno == ENOENT) {
+ oldest_tombstone = i;
+ break;
+ } else {
+ PLOG(ERROR) << "failed to stat " << path;
+ continue;
+ }
+ }
+
+ if (st.st_mtime < oldest_time) {
+ oldest_tombstone = i;
+ oldest_time = st.st_mtime;
+ }
+ }
+
+ next_artifact_ = oldest_tombstone;
+ }
+
+ const std::string file_name_prefix_;
+
+ const std::string dir_path_;
+ const int dir_fd_;
+
+ const size_t max_artifacts_;
+ int next_artifact_;
+
+ const size_t max_concurrent_dumps_;
+ size_t num_concurrent_dumps_;
+
+ std::deque<Crash*> queued_requests_;
+
+ DISALLOW_COPY_AND_ASSIGN(CrashType);
+};
+
+// Whether java trace dumps are produced via tombstoned.
+static constexpr bool kJavaTraceDumpsEnabled = false;
+
+/* static */ CrashType* const CrashType::tombstone =
+ new CrashType("/data/tombstones", "tombstone_" /* file_name_prefix */, 10 /* max_artifacts */,
+ 1 /* max_concurrent_dumps */);
+
+/* static */ CrashType* const CrashType::java_trace =
+ (kJavaTraceDumpsEnabled ? new CrashType("/data/anr", "anr_" /* file_name_prefix */,
+ 64 /* max_artifacts */, 4 /* max_concurrent_dumps */)
+ : nullptr);
+
// Ownership of Crash is a bit messy.
// It's either owned by an active event that must have a timeout, or owned by
// queued_requests, in the case that multiple crashes come in at the same time.
struct Crash {
- ~Crash() {
- event_free(crash_event);
- }
+ ~Crash() { event_free(crash_event); }
unique_fd crash_fd;
pid_t crash_pid;
event* crash_event = nullptr;
+
+ // Not owned by |Crash|.
+ CrashType* crash_type = nullptr;
};
-static constexpr char kTombstoneDirectory[] = "/data/tombstones/";
-static constexpr size_t kTombstoneCount = 10;
-static int tombstone_directory_fd = -1;
-static int next_tombstone = 0;
-
-static constexpr size_t kMaxConcurrentDumps = 1;
-static size_t num_concurrent_dumps = 0;
-
-static std::deque<Crash*> queued_requests;
-
// Forward declare the callbacks so they can be placed in a sensible order.
static void crash_accept_cb(evconnlistener* listener, evutil_socket_t sockfd, sockaddr*, int, void*);
static void crash_request_cb(evutil_socket_t sockfd, short ev, void* arg);
static void crash_completed_cb(evutil_socket_t sockfd, short ev, void* arg);
-static void find_oldest_tombstone() {
- size_t oldest_tombstone = 0;
- time_t oldest_time = std::numeric_limits<time_t>::max();
-
- for (size_t i = 0; i < kTombstoneCount; ++i) {
- std::string path = android::base::StringPrintf("%stombstone_%02zu", kTombstoneDirectory, i);
- struct stat st;
- if (stat(path.c_str(), &st) != 0) {
- if (errno == ENOENT) {
- oldest_tombstone = i;
- break;
- } else {
- PLOG(ERROR) << "failed to stat " << path;
- continue;
- }
- }
-
- if (st.st_mtime < oldest_time) {
- oldest_tombstone = i;
- oldest_time = st.st_mtime;
- }
- }
-
- next_tombstone = oldest_tombstone;
-}
-
-static unique_fd get_tombstone_fd() {
- // If kMaxConcurrentDumps is greater than 1, then theoretically the same
- // filename could be handed out to multiple processes. Unlink and create the
- // file, instead of using O_TRUNC, to avoid two processes interleaving their
- // output.
- unique_fd result;
- char buf[PATH_MAX];
- snprintf(buf, sizeof(buf), "tombstone_%02d", next_tombstone);
- if (unlinkat(tombstone_directory_fd, buf, 0) != 0 && errno != ENOENT) {
- PLOG(FATAL) << "failed to unlink tombstone at " << kTombstoneDirectory << buf;
- }
-
- result.reset(
- openat(tombstone_directory_fd, buf, O_CREAT | O_EXCL | O_WRONLY | O_APPEND | O_CLOEXEC, 0640));
- if (result == -1) {
- PLOG(FATAL) << "failed to create tombstone at " << kTombstoneDirectory << buf;
- }
-
- next_tombstone = (next_tombstone + 1) % kTombstoneCount;
- return result;
-}
-
static void perform_request(Crash* crash) {
unique_fd output_fd;
- if (!intercept_manager->GetIntercept(crash->crash_pid, &output_fd)) {
- output_fd = get_tombstone_fd();
+ // Note that java traces are not interceptible.
+ if ((crash->crash_type == CrashType::java_trace) ||
+ !intercept_manager->GetIntercept(crash->crash_pid, &output_fd)) {
+ output_fd = crash->crash_type->get_output_fd();
}
TombstonedCrashPacket response = {
@@ -152,23 +220,15 @@
event_add(crash->crash_event, &timeout);
}
- ++num_concurrent_dumps;
+ crash->crash_type->on_crash_started();
return;
fail:
delete crash;
}
-static void dequeue_requests() {
- while (!queued_requests.empty() && num_concurrent_dumps < kMaxConcurrentDumps) {
- Crash* next_crash = queued_requests.front();
- queued_requests.pop_front();
- perform_request(next_crash);
- }
-}
-
static void crash_accept_cb(evconnlistener* listener, evutil_socket_t sockfd, sockaddr*, int,
- void*) {
+ void* crash_type) {
event_base* base = evconnlistener_get_base(listener);
Crash* crash = new Crash();
@@ -176,12 +236,15 @@
event* crash_event = event_new(base, sockfd, EV_TIMEOUT | EV_READ, crash_request_cb, crash);
crash->crash_fd.reset(sockfd);
crash->crash_event = crash_event;
+ crash->crash_type = static_cast<CrashType*>(crash_type);
event_add(crash_event, &timeout);
}
static void crash_request_cb(evutil_socket_t sockfd, short ev, void* arg) {
ssize_t rc;
Crash* crash = static_cast<Crash*>(arg);
+ CrashType* type = crash->crash_type;
+
TombstonedCrashPacket request = {};
if ((ev & EV_TIMEOUT) != 0) {
@@ -208,12 +271,27 @@
goto fail;
}
- crash->crash_pid = request.packet.dump_request.pid;
+ if (type == CrashType::tombstone) {
+ crash->crash_pid = request.packet.dump_request.pid;
+ } else {
+ // Requests for java traces are sent from untrusted processes, so we
+ // must not trust the PID sent down with the request. Instead, we ask the
+ // kernel.
+ ucred cr = {};
+ socklen_t len = sizeof(cr);
+ int ret = getsockopt(sockfd, SOL_SOCKET, SO_PEERCRED, &cr, &len);
+ if (ret != 0) {
+ PLOG(ERROR) << "Failed to getsockopt(..SO_PEERCRED)";
+ goto fail;
+ }
+
+ crash->crash_pid = cr.pid;
+ }
+
LOG(INFO) << "received crash request for pid " << crash->crash_pid;
- if (num_concurrent_dumps == kMaxConcurrentDumps) {
+ if (type->maybe_enqueue_crash(crash)) {
LOG(INFO) << "enqueueing crash request for pid " << crash->crash_pid;
- queued_requests.push_back(crash);
} else {
perform_request(crash);
}
@@ -229,7 +307,7 @@
Crash* crash = static_cast<Crash*>(arg);
TombstonedCrashPacket request = {};
- --num_concurrent_dumps;
+ crash->crash_type->on_crash_completed();
if ((ev & EV_READ) == 0) {
goto fail;
@@ -252,10 +330,11 @@
}
fail:
+ CrashType* type = crash->crash_type;
delete crash;
// If there's something queued up, let them proceed.
- dequeue_requests();
+ type->maybe_dequeue_crashes(perform_request);
}
int main(int, char* []) {
@@ -269,13 +348,6 @@
};
debuggerd_register_handlers(&action);
- tombstone_directory_fd = open(kTombstoneDirectory, O_DIRECTORY | O_RDONLY | O_CLOEXEC);
- if (tombstone_directory_fd == -1) {
- PLOG(FATAL) << "failed to open tombstone directory";
- }
-
- find_oldest_tombstone();
-
int intercept_socket = android_get_control_socket(kTombstonedInterceptSocketName);
int crash_socket = android_get_control_socket(kTombstonedCrashSocketName);
@@ -293,10 +365,24 @@
intercept_manager = new InterceptManager(base, intercept_socket);
- evconnlistener* listener =
- evconnlistener_new(base, crash_accept_cb, nullptr, -1, LEV_OPT_CLOSE_ON_FREE, crash_socket);
- if (!listener) {
- LOG(FATAL) << "failed to create evconnlistener";
+ evconnlistener* tombstone_listener = evconnlistener_new(
+ base, crash_accept_cb, CrashType::tombstone, -1, LEV_OPT_CLOSE_ON_FREE, crash_socket);
+ if (!tombstone_listener) {
+ LOG(FATAL) << "failed to create evconnlistener for tombstones.";
+ }
+
+ if (kJavaTraceDumpsEnabled) {
+ const int java_trace_socket = android_get_control_socket(kTombstonedJavaTraceSocketName);
+ if (java_trace_socket == -1) {
+ PLOG(FATAL) << "failed to get socket from init";
+ }
+
+ evutil_make_socket_nonblocking(java_trace_socket);
+ evconnlistener* java_trace_listener = evconnlistener_new(
+ base, crash_accept_cb, CrashType::java_trace, -1, LEV_OPT_CLOSE_ON_FREE, java_trace_socket);
+ if (!java_trace_listener) {
+ LOG(FATAL) << "failed to create evconnlistener for java traces.";
+ }
}
LOG(INFO) << "tombstoned successfully initialized";
diff --git a/debuggerd/tombstoned/tombstoned.rc b/debuggerd/tombstoned/tombstoned.rc
index b8345ca..53ef01c 100644
--- a/debuggerd/tombstoned/tombstoned.rc
+++ b/debuggerd/tombstoned/tombstoned.rc
@@ -7,4 +7,5 @@
socket tombstoned_crash seqpacket 0666 system system
socket tombstoned_intercept seqpacket 0666 system system
+ socket tombstoned_java_trace seqpacket 0666 system system
writepid /dev/cpuset/system-background/tasks
diff --git a/debuggerd/tombstoned_client.cpp b/debuggerd/tombstoned/tombstoned_client.cpp
similarity index 65%
rename from debuggerd/tombstoned_client.cpp
rename to debuggerd/tombstoned/tombstoned_client.cpp
index 03b4a20..39dc6eb 100644
--- a/debuggerd/tombstoned_client.cpp
+++ b/debuggerd/tombstoned/tombstoned_client.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "debuggerd/tombstoned.h"
+#include "tombstoned/tombstoned.h"
#include <fcntl.h>
#include <unistd.h>
@@ -22,20 +22,22 @@
#include <utility>
#include <android-base/unique_fd.h>
+#include <async_safe/log.h>
#include <cutils/sockets.h>
-#include "debuggerd/protocol.h"
-#include "debuggerd/util.h"
-#include "private/libc_logging.h"
+#include "protocol.h"
+#include "util.h"
using android::base::unique_fd;
-bool tombstoned_connect(pid_t pid, unique_fd* tombstoned_socket, unique_fd* output_fd) {
- unique_fd sockfd(socket_local_client(kTombstonedCrashSocketName,
- ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET));
+bool tombstoned_connect(pid_t pid, unique_fd* tombstoned_socket, unique_fd* output_fd,
+ bool is_native_crash) {
+ unique_fd sockfd(socket_local_client(
+ (is_native_crash ? kTombstonedCrashSocketName : kTombstonedJavaTraceSocketName),
+ ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET));
if (sockfd == -1) {
- __libc_format_log(ANDROID_LOG_ERROR, "libc", "failed to connect to tombstoned: %s",
- strerror(errno));
+ async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to connect to tombstoned: %s",
+ strerror(errno));
return false;
}
@@ -43,22 +45,22 @@
packet.packet_type = CrashPacketType::kDumpRequest;
packet.packet.dump_request.pid = pid;
if (TEMP_FAILURE_RETRY(write(sockfd, &packet, sizeof(packet))) != sizeof(packet)) {
- __libc_format_log(ANDROID_LOG_ERROR, "libc", "failed to write DumpRequest packet: %s",
- strerror(errno));
+ async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to write DumpRequest packet: %s",
+ strerror(errno));
return false;
}
unique_fd tmp_output_fd;
ssize_t rc = recv_fd(sockfd, &packet, sizeof(packet), &tmp_output_fd);
if (rc == -1) {
- __libc_format_log(ANDROID_LOG_ERROR, "libc",
- "failed to read response to DumpRequest packet: %s", strerror(errno));
+ async_safe_format_log(ANDROID_LOG_ERROR, "libc",
+ "failed to read response to DumpRequest packet: %s", strerror(errno));
return false;
} else if (rc != sizeof(packet)) {
- __libc_format_log(
- ANDROID_LOG_ERROR, "libc",
- "received DumpRequest response packet of incorrect length (expected %zu, got %zd)",
- sizeof(packet), rc);
+ async_safe_format_log(
+ ANDROID_LOG_ERROR, "libc",
+ "received DumpRequest response packet of incorrect length (expected %zu, got %zd)",
+ sizeof(packet), rc);
return false;
}
@@ -67,8 +69,8 @@
// a regular fd, and writing to an fd with O_APPEND).
int flags = fcntl(tmp_output_fd.get(), F_GETFL);
if (fcntl(tmp_output_fd.get(), F_SETFL, flags | O_APPEND) != 0) {
- __libc_format_log(ANDROID_LOG_WARN, "libc", "failed to set output fd flags: %s",
- strerror(errno));
+ async_safe_format_log(ANDROID_LOG_WARN, "libc", "failed to set output fd flags: %s",
+ strerror(errno));
}
*tombstoned_socket = std::move(sockfd);
diff --git a/debuggerd/util.cpp b/debuggerd/util.cpp
index 4c015d7..c6a997b 100644
--- a/debuggerd/util.cpp
+++ b/debuggerd/util.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "debuggerd/util.h"
+#include "util.h"
#include <sys/socket.h>
@@ -22,9 +22,7 @@
#include <android-base/unique_fd.h>
#include <cutils/sockets.h>
-#include <debuggerd/protocol.h>
-
-#include "private/libc_logging.h"
+#include "protocol.h"
using android::base::unique_fd;
diff --git a/debuggerd/include/debuggerd/util.h b/debuggerd/util.h
similarity index 100%
rename from debuggerd/include/debuggerd/util.h
rename to debuggerd/util.h
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index 5610cc0..80def73 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -14,10 +14,12 @@
LOCAL_PATH:= $(call my-dir)
-fastboot_version := $(shell git -C $(LOCAL_PATH) rev-parse --short=12 HEAD 2>/dev/null)-android
+include $(LOCAL_PATH)/../platform_tools_tool_version.mk
include $(CLEAR_VARS)
+LOCAL_CFLAGS += -DFASTBOOT_VERSION="\"$(tool_version)\""
+
LOCAL_C_INCLUDES := \
$(LOCAL_PATH)/../adb \
$(LOCAL_PATH)/../mkbootimg \
@@ -39,8 +41,6 @@
LOCAL_MODULE_HOST_OS := darwin linux windows
LOCAL_CFLAGS += -Wall -Wextra -Werror -Wunreachable-code
-LOCAL_CFLAGS += -DFASTBOOT_REVISION='"$(fastboot_version)"'
-
LOCAL_SRC_FILES_linux := usb_linux.cpp
LOCAL_STATIC_LIBRARIES_linux := libselinux
@@ -51,7 +51,7 @@
LOCAL_SRC_FILES_windows := usb_windows.cpp
LOCAL_STATIC_LIBRARIES_windows := AdbWinApi
-LOCAL_REQUIRED_MODULES_windows := AdbWinApi
+LOCAL_REQUIRED_MODULES_windows := AdbWinApi AdbWinUsbApi
LOCAL_LDLIBS_windows := -lws2_32
LOCAL_C_INCLUDES_windows := development/host/windows/usb/api
diff --git a/fastboot/README.md b/fastboot/README.md
index 022d34b..ec7dcb4 100644
--- a/fastboot/README.md
+++ b/fastboot/README.md
@@ -126,6 +126,16 @@
space in RAM or "FAIL" if not. The size of
the download is remembered.
+ upload Read data from memory which was staged by the last
+ command, e.g. an oem command. The client will reply
+ with "DATA%08x" if it is ready to send %08x bytes of
+ data. If no data was staged in the last command,
+ the client must reply with "FAIL". After the client
+ successfully sends %08x bytes, the client shall send
+ a single packet starting with "OKAY". Clients
+ should not support "upload" unless it supports an
+ oem command that requires "upload" capabilities.
+
verify:%08x Send a digital signature to verify the downloaded
data. Required if the bootloader is "secure"
otherwise "flash" and "boot" will be ignored.
diff --git a/fastboot/engine.cpp b/fastboot/engine.cpp
index 728dcb8..7e10cc9 100644
--- a/fastboot/engine.cpp
+++ b/fastboot/engine.cpp
@@ -44,6 +44,8 @@
#define OP_NOTICE 4
#define OP_DOWNLOAD_SPARSE 5
#define OP_WAIT_FOR_DISCONNECT 6
+#define OP_DOWNLOAD_FD 7
+#define OP_UPLOAD 8
typedef struct Action Action;
@@ -56,6 +58,7 @@
char cmd[CMD_SIZE];
const char* prod;
void* data;
+ int fd;
// The protocol only supports 32-bit sizes, so you'll have to break
// anything larger into chunks.
@@ -142,7 +145,20 @@
a->msg = mkmsg("erasing '%s'", ptn);
}
-void fb_queue_flash(const char *ptn, void *data, unsigned sz)
+void fb_queue_flash_fd(const char *ptn, int fd, uint32_t sz)
+{
+ Action *a;
+
+ a = queue_action(OP_DOWNLOAD_FD, "");
+ a->fd = fd;
+ a->size = sz;
+ a->msg = mkmsg("sending '%s' (%d KB)", ptn, sz / 1024);
+
+ a = queue_action(OP_COMMAND, "flash:%s", ptn);
+ a->msg = mkmsg("writing '%s'", ptn);
+}
+
+void fb_queue_flash(const char *ptn, void *data, uint32_t sz)
{
Action *a;
@@ -155,7 +171,7 @@
a->msg = mkmsg("writing '%s'", ptn);
}
-void fb_queue_flash_sparse(const char* ptn, struct sparse_file* s, unsigned sz, size_t current,
+void fb_queue_flash_sparse(const char* ptn, struct sparse_file* s, uint32_t sz, size_t current,
size_t total) {
Action *a;
@@ -242,6 +258,12 @@
return cb_check(a, status, resp, 1);
}
+static char* xstrdup(const char* s) {
+ char* result = strdup(s);
+ if (!result) die("out of memory");
+ return result;
+}
+
void fb_queue_require(const char *prod, const char *var,
bool invert, size_t nvalues, const char **value)
{
@@ -260,16 +282,14 @@
fprintf(stderr, "%s FAILED (%s)\n", a->cmd, resp);
return status;
}
- fprintf(stderr, "%s: %s\n", (char*) a->data, resp);
+ fprintf(stderr, "%s: %s\n", static_cast<const char*>(a->data), resp);
+ free(static_cast<char*>(a->data));
return 0;
}
-void fb_queue_display(const char *var, const char *prettyname)
-{
- Action *a;
- a = queue_action(OP_QUERY, "getvar:%s", var);
- a->data = strdup(prettyname);
- if (a->data == nullptr) die("out of memory");
+void fb_queue_display(const char* var, const char* prettyname) {
+ Action* a = queue_action(OP_QUERY, "getvar:%s", var);
+ a->data = xstrdup(prettyname);
a->func = cb_display;
}
@@ -282,11 +302,9 @@
return 0;
}
-void fb_queue_query_save(const char *var, char *dest, unsigned dest_size)
-{
- Action *a;
- a = queue_action(OP_QUERY, "getvar:%s", var);
- a->data = (void *)dest;
+void fb_queue_query_save(const char* var, char* dest, uint32_t dest_size) {
+ Action* a = queue_action(OP_QUERY, "getvar:%s", var);
+ a->data = dest;
a->size = dest_size;
a->func = cb_save;
}
@@ -309,7 +327,7 @@
a->msg = msg;
}
-void fb_queue_download(const char *name, void *data, unsigned size)
+void fb_queue_download(const char *name, void *data, uint32_t size)
{
Action *a = queue_action(OP_DOWNLOAD, "");
a->data = data;
@@ -317,8 +335,22 @@
a->msg = mkmsg("downloading '%s'", name);
}
-void fb_queue_notice(const char *notice)
+void fb_queue_download_fd(const char *name, int fd, uint32_t sz)
{
+ Action *a;
+ a = queue_action(OP_DOWNLOAD_FD, "");
+ a->fd = fd;
+ a->size = sz;
+ a->msg = mkmsg("sending '%s' (%d KB)", name, sz / 1024);
+}
+
+void fb_queue_upload(const char* outfile) {
+ Action* a = queue_action(OP_UPLOAD, "");
+ a->data = xstrdup(outfile);
+ a->msg = mkmsg("uploading '%s'", outfile);
+}
+
+void fb_queue_notice(const char* notice) {
Action *a = queue_action(OP_NOTICE, "");
a->data = (void*) notice;
}
@@ -328,11 +360,11 @@
queue_action(OP_WAIT_FOR_DISCONNECT, "");
}
-int fb_execute_queue(Transport* transport)
+int64_t fb_execute_queue(Transport* transport)
{
Action *a;
char resp[FB_RESPONSE_SZ+1];
- int status = 0;
+ int64_t status = 0;
a = action_list;
if (!a)
@@ -351,6 +383,10 @@
status = fb_download_data(transport, a->data, a->size);
status = a->func(a, status, status ? fb_get_error().c_str() : "");
if (status) break;
+ } else if (a->op == OP_DOWNLOAD_FD) {
+ status = fb_download_data_fd(transport, a->fd, a->size);
+ status = a->func(a, status, status ? fb_get_error().c_str() : "");
+ if (status) break;
} else if (a->op == OP_COMMAND) {
status = fb_command(transport, a->cmd);
status = a->func(a, status, status ? fb_get_error().c_str() : "");
@@ -367,6 +403,9 @@
if (status) break;
} else if (a->op == OP_WAIT_FOR_DISCONNECT) {
transport->WaitForDisconnect();
+ } else if (a->op == OP_UPLOAD) {
+ status = fb_upload_data(transport, reinterpret_cast<char*>(a->data));
+ status = a->func(a, status, status ? fb_get_error().c_str() : "");
} else {
die("bogus action");
}
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 704dc43..3e890c7 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -26,8 +26,6 @@
* SUCH DAMAGE.
*/
-#define _LARGEFILE64_SOURCE
-
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
@@ -55,6 +53,7 @@
#include <android-base/parsenetaddress.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
#include <sparse/sparse.h>
#include <ziparchive/zip_archive.h>
@@ -67,6 +66,8 @@
#include "udp.h"
#include "usb.h"
+using android::base::unique_fd;
+
#ifndef O_BINARY
#define O_BINARY 0
#endif
@@ -74,10 +75,13 @@
char cur_product[FB_RESPONSE_SZ + 1];
static const char* serial = nullptr;
-static const char* product = nullptr;
static const char* cmdline = nullptr;
static unsigned short vendor_id = 0;
static int long_listing = 0;
+// Don't resparse files in too-big chunks.
+// libsparse will support INT_MAX, but this results in large allocations, so
+// let's keep it at 1GB to avoid memory pressure on the host.
+static constexpr int64_t RESPARSE_LIMIT = 1 * 1024 * 1024 * 1024;
static int64_t sparse_limit = -1;
static int64_t target_sparse_limit = -1;
@@ -91,7 +95,7 @@
static const std::string convert_fbe_marker_filename("convert_fbe");
enum fb_buffer_type {
- FB_BUFFER,
+ FB_BUFFER_FD,
FB_BUFFER_SPARSE,
};
@@ -99,66 +103,51 @@
enum fb_buffer_type type;
void* data;
int64_t sz;
+ int fd;
};
static struct {
- char img_name[17];
- char sig_name[17];
- char part_name[9];
+ const char* nickname;
+ const char* img_name;
+ const char* sig_name;
+ const char* part_name;
bool is_optional;
bool is_secondary;
} images[] = {
- {"boot.img", "boot.sig", "boot", false, false},
- {"boot_other.img", "boot.sig", "boot", true, true},
- {"recovery.img", "recovery.sig", "recovery", true, false},
- {"system.img", "system.sig", "system", false, false},
- {"system_other.img", "system.sig", "system", true, true},
- {"vendor.img", "vendor.sig", "vendor", true, false},
- {"vendor_other.img", "vendor.sig", "vendor", true, true},
- {"vbmeta.img", "vbmeta.sig", "vbmeta", true, false},
+ // clang-format off
+ { "boot", "boot.img", "boot.sig", "boot", false, false },
+ { nullptr, "boot_other.img", "boot.sig", "boot", true, true },
+ { "dtbo", "dtbo.img", "dtbo.sig", "dtbo", true, false },
+ { "dts", "dt.img", "dt.sig", "dts", true, false },
+ { "recovery", "recovery.img", "recovery.sig", "recovery", true, false },
+ { "system", "system.img", "system.sig", "system", false, false },
+ { nullptr, "system_other.img", "system.sig", "system", true, true },
+ { "vbmeta", "vbmeta.img", "vbmeta.sig", "vbmeta", true, false },
+ { "vendor", "vendor.img", "vendor.sig", "vendor", true, false },
+ { nullptr, "vendor_other.img", "vendor.sig", "vendor", true, true },
+ // clang-format on
};
-static std::string find_item_given_name(const char* img_name, const char* product) {
- if(product) {
- std::string path = android::base::GetExecutablePath();
- path.erase(path.find_last_of('/'));
- return android::base::StringPrintf("%s/../../../target/product/%s/%s",
- path.c_str(), product, img_name);
- }
-
- char *dir = getenv("ANDROID_PRODUCT_OUT");
+static std::string find_item_given_name(const char* img_name) {
+ char* dir = getenv("ANDROID_PRODUCT_OUT");
if (dir == nullptr || dir[0] == '\0') {
- die("neither -p product specified nor ANDROID_PRODUCT_OUT set");
+ die("ANDROID_PRODUCT_OUT not set");
}
-
return android::base::StringPrintf("%s/%s", dir, img_name);
}
-std::string find_item(const char* item, const char* product) {
- const char *fn;
-
- if (!strcmp(item,"boot")) {
- fn = "boot.img";
- } else if(!strcmp(item,"recovery")) {
- fn = "recovery.img";
- } else if(!strcmp(item,"system")) {
- fn = "system.img";
- } else if(!strcmp(item,"vendor")) {
- fn = "vendor.img";
- } else if(!strcmp(item,"vbmeta")) {
- fn = "vbmeta.img";
- } else if(!strcmp(item,"userdata")) {
- fn = "userdata.img";
- } else if(!strcmp(item,"cache")) {
- fn = "cache.img";
- } else if(!strcmp(item,"info")) {
- fn = "android-info.txt";
- } else {
- fprintf(stderr,"unknown partition '%s'\n", item);
- return "";
+static std::string find_item(const std::string& item) {
+ for (size_t i = 0; i < arraysize(images); ++i) {
+ if (images[i].nickname && item == images[i].nickname) {
+ return find_item_given_name(images[i].img_name);
+ }
}
- return find_item_given_name(fn, product);
+ if (item == "userdata") return find_item_given_name("userdata.img");
+ if (item == "cache") return find_item_given_name("cache.img");
+
+ fprintf(stderr, "unknown partition '%s'\n", item.c_str());
+ return "";
}
static int64_t get_file_size(int fd) {
@@ -317,8 +306,21 @@
usb_open(list_devices_callback);
}
-static void usage() {
- fprintf(stderr,
+static void syntax_error(const char* fmt, ...) {
+ fprintf(stderr, "fastboot: usage: ");
+
+ va_list ap;
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+
+ fprintf(stderr, "\n");
+ exit(1);
+}
+
+static int show_help() {
+ // clang-format off
+ fprintf(stdout,
/* 1234567890123456789012345678901234567890123456789012345678901234567890123456 */
"usage: fastboot [ <option> ] <command>\n"
"\n"
@@ -356,13 +358,20 @@
" set_active <slot> Sets the active slot. If slots are\n"
" not supported, this does nothing.\n"
" boot <kernel> [ <ramdisk> [ <second> ] ] Download and boot kernel.\n"
- " flash:raw boot <kernel> [ <ramdisk> [ <second> ] ]\n"
+ " flash:raw <bootable-partition> <kernel> [ <ramdisk> [ <second> ] ]\n"
" Create bootimage and flash it.\n"
" devices [-l] List all connected devices [with\n"
" device paths].\n"
" continue Continue with autoboot.\n"
" reboot [bootloader|emergency] Reboot device [into bootloader or emergency mode].\n"
" reboot-bootloader Reboot device into bootloader.\n"
+ " oem <parameter1> ... <parameterN> Executes oem specific command.\n"
+ " stage <infile> Sends contents of <infile> to stage for\n"
+ " the next command. Supported only on\n"
+ " Android Things devices.\n"
+ " get_staged <outfile> Receives data to <outfile> staged by the\n"
+ " last command. Supported only on Android\n"
+ " Things devices.\n"
" help Show this help message.\n"
"\n"
"options:\n"
@@ -375,7 +384,6 @@
" For ethernet, provide an address in the\n"
" form <protocol>:<hostname>[:port] where\n"
" <protocol> is either tcp or udp.\n"
- " -p <product> Specify product name.\n"
" -c <cmdline> Override kernel commandline.\n"
" -i <vendor id> Specify a custom USB vendor id.\n"
" -b, --base <base_addr> Specify a custom kernel base\n"
@@ -419,31 +427,22 @@
" --version Display version.\n"
" -h, --help show this message.\n"
);
+ // clang-format off
+ return 0;
}
-static void* load_bootable_image(const char* kernel, const char* ramdisk,
- const char* secondstage, int64_t* sz,
+static void* load_bootable_image(const std::string& kernel, const std::string& ramdisk,
+ const std::string& second_stage, int64_t* sz,
const char* cmdline) {
- if (kernel == nullptr) {
- fprintf(stderr, "no image specified\n");
- return 0;
- }
-
int64_t ksize;
- void* kdata = load_file(kernel, &ksize);
- if (kdata == nullptr) {
- fprintf(stderr, "cannot load '%s': %s\n", kernel, strerror(errno));
- return 0;
- }
+ void* kdata = load_file(kernel.c_str(), &ksize);
+ if (kdata == nullptr) die("cannot load '%s': %s\n", kernel.c_str(), strerror(errno));
// Is this actually a boot image?
- if(!memcmp(kdata, BOOT_MAGIC, BOOT_MAGIC_SIZE)) {
+ if (!memcmp(kdata, BOOT_MAGIC, BOOT_MAGIC_SIZE)) {
if (cmdline) bootimg_set_cmdline((boot_img_hdr*) kdata, cmdline);
- if (ramdisk) {
- fprintf(stderr, "cannot boot a boot.img *and* ramdisk\n");
- return 0;
- }
+ if (!ramdisk.empty()) die("cannot boot a boot.img *and* ramdisk\n");
*sz = ksize;
return kdata;
@@ -451,22 +450,16 @@
void* rdata = nullptr;
int64_t rsize = 0;
- if (ramdisk) {
- rdata = load_file(ramdisk, &rsize);
- if (rdata == nullptr) {
- fprintf(stderr,"cannot load '%s': %s\n", ramdisk, strerror(errno));
- return 0;
- }
+ if (!ramdisk.empty()) {
+ rdata = load_file(ramdisk.c_str(), &rsize);
+ if (rdata == nullptr) die("cannot load '%s': %s\n", ramdisk.c_str(), strerror(errno));
}
void* sdata = nullptr;
int64_t ssize = 0;
- if (secondstage) {
- sdata = load_file(secondstage, &ssize);
- if (sdata == nullptr) {
- fprintf(stderr,"cannot load '%s': %s\n", secondstage, strerror(errno));
- return 0;
- }
+ if (!second_stage.empty()) {
+ sdata = load_file(second_stage.c_str(), &ssize);
+ if (sdata == nullptr) die("cannot load '%s': %s\n", second_stage.c_str(), strerror(errno));
}
fprintf(stderr,"creating boot image...\n");
@@ -475,10 +468,8 @@
rdata, rsize, ramdisk_offset,
sdata, ssize, second_offset,
page_size, base_addr, tags_offset, &bsize);
- if (bdata == nullptr) {
- fprintf(stderr,"failed to create boot.img\n");
- return 0;
- }
+ if (bdata == nullptr) die("failed to create boot.img\n");
+
if (cmdline) bootimg_set_cmdline((boot_img_hdr*) bdata, cmdline);
fprintf(stderr, "creating boot image - %" PRId64 " bytes\n", bsize);
*sz = bsize;
@@ -486,8 +477,7 @@
return bdata;
}
-static void* unzip_file(ZipArchiveHandle zip, const char* entry_name, int64_t* sz)
-{
+static void* unzip_file(ZipArchiveHandle zip, const char* entry_name, int64_t* sz) {
ZipString zip_entry_name(entry_name);
ZipEntry zip_entry;
if (FindEntry(zip, zip_entry_name, &zip_entry) != 0) {
@@ -497,6 +487,7 @@
*sz = zip_entry.uncompressed_length;
+ fprintf(stderr, "extracting %s (%" PRId64 " MB)...\n", entry_name, *sz / 1024 / 1024);
uint8_t* data = reinterpret_cast<uint8_t*>(malloc(zip_entry.uncompressed_length));
if (data == nullptr) {
fprintf(stderr, "failed to allocate %" PRId64 " bytes for '%s'\n", *sz, entry_name);
@@ -545,22 +536,39 @@
return "";
}
+static int make_temporary_fd() {
+ // TODO: reimplement to avoid leaking a FILE*.
+ return fileno(tmpfile());
+}
+
#else
+static std::string make_temporary_template() {
+ const char* tmpdir = getenv("TMPDIR");
+ if (tmpdir == nullptr) tmpdir = P_tmpdir;
+ return std::string(tmpdir) + "/fastboot_userdata_XXXXXX";
+}
+
static std::string make_temporary_directory() {
- const char *tmpdir = getenv("TMPDIR");
- if (tmpdir == nullptr) {
- tmpdir = P_tmpdir;
- }
- std::string result = std::string(tmpdir) + "/fastboot_userdata_XXXXXX";
- if (mkdtemp(&result[0]) == NULL) {
- fprintf(stderr, "Unable to create temporary directory: %s\n",
- strerror(errno));
+ std::string result(make_temporary_template());
+ if (mkdtemp(&result[0]) == nullptr) {
+ fprintf(stderr, "Unable to create temporary directory: %s\n", strerror(errno));
return "";
}
return result;
}
+static int make_temporary_fd() {
+ std::string path_template(make_temporary_template());
+ int fd = mkstemp(&path_template[0]);
+ if (fd == -1) {
+ fprintf(stderr, "Unable to create temporary file: %s\n", strerror(errno));
+ return -1;
+ }
+ unlink(path_template.c_str());
+ return fd;
+}
+
#endif
static std::string create_fbemarker_tmpdir() {
@@ -594,9 +602,9 @@
}
}
-static int unzip_to_file(ZipArchiveHandle zip, char* entry_name) {
- FILE* fp = tmpfile();
- if (fp == nullptr) {
+static int unzip_to_file(ZipArchiveHandle zip, const char* entry_name) {
+ unique_fd fd(make_temporary_fd());
+ if (fd == -1) {
fprintf(stderr, "failed to create temporary file for '%s': %s\n",
entry_name, strerror(errno));
return -1;
@@ -606,21 +614,20 @@
ZipEntry zip_entry;
if (FindEntry(zip, zip_entry_name, &zip_entry) != 0) {
fprintf(stderr, "archive does not contain '%s'\n", entry_name);
- fclose(fp);
return -1;
}
- int fd = fileno(fp);
+ fprintf(stderr, "extracting %s (%" PRIu32 " MB)...\n", entry_name,
+ zip_entry.uncompressed_length / 1024 / 1024);
int error = ExtractEntryToFile(zip, &zip_entry, fd);
if (error != 0) {
fprintf(stderr, "failed to extract '%s': %s\n", entry_name, ErrorCodeString(error));
- fclose(fp);
return -1;
}
lseek(fd, 0, SEEK_SET);
// TODO: We're leaking 'fp' here.
- return fd;
+ return fd.release();
}
static char *strip(char *s)
@@ -789,7 +796,7 @@
}
if (size > limit) {
- return limit;
+ return std::min(limit, RESPARSE_LIMIT);
}
return 0;
@@ -822,10 +829,9 @@
buf->type = FB_BUFFER_SPARSE;
buf->data = s;
} else {
- void* data = load_fd(fd, &sz);
- if (data == nullptr) return -1;
- buf->type = FB_BUFFER;
- buf->data = data;
+ buf->type = FB_BUFFER_FD;
+ buf->data = nullptr;
+ buf->fd = fd;
buf->sz = sz;
}
@@ -833,11 +839,22 @@
}
static bool load_buf(Transport* transport, const char* fname, struct fastboot_buffer* buf) {
- int fd = open(fname, O_RDONLY | O_BINARY);
+ unique_fd fd(TEMP_FAILURE_RETRY(open(fname, O_RDONLY | O_BINARY)));
+
if (fd == -1) {
return false;
}
- return load_buf_fd(transport, fd, buf);
+
+ struct stat s;
+ if (fstat(fd, &s)) {
+ return false;
+ }
+ if (!S_ISREG(s.st_mode)) {
+ errno = S_ISDIR(s.st_mode) ? EISDIR : EINVAL;
+ return false;
+ }
+
+ return load_buf_fd(transport, fd.release(), buf);
}
static void flash_buf(const char *pname, struct fastboot_buffer *buf)
@@ -860,9 +877,8 @@
}
break;
}
-
- case FB_BUFFER:
- fb_queue_flash(pname, buf->data, buf->sz);
+ case FB_BUFFER_FD:
+ fb_queue_flash_fd(pname, buf->fd, buf->sz);
break;
default:
die("unknown buffer type: %d", buf->type);
@@ -1040,9 +1056,9 @@
flash_buf(pname, &buf);
}
-static void do_update_signature(ZipArchiveHandle zip, char* fn) {
+static void do_update_signature(ZipArchiveHandle zip, const char* filename) {
int64_t sz;
- void* data = unzip_file(zip, fn, &sz);
+ void* data = unzip_file(zip, filename, &sz);
if (data == nullptr) return;
fb_queue_download("signature", data, sz);
fb_queue_command("signature", "installing signature");
@@ -1134,7 +1150,7 @@
}
flash_buf(partition.c_str(), &buf);
/* not closing the fd here since the sparse code keeps the fd around
- * but hasn't mmaped data yet. The tmpfile will get cleaned up when the
+ * but hasn't mmaped data yet. The temporary file will get cleaned up when the
* program exits.
*/
};
@@ -1169,7 +1185,7 @@
fb_queue_query_save("product", cur_product, sizeof(cur_product));
- fname = find_item("info", product);
+ fname = find_item_given_name("android-info.txt");
if (fname.empty()) die("cannot find android-info.txt");
int64_t sz;
@@ -1201,7 +1217,7 @@
slot = slot_override.c_str();
}
if (!slot) continue;
- fname = find_item_given_name(images[i].img_name, product);
+ fname = find_item_given_name(images[i].img_name);
fastboot_buffer buf;
if (!load_buf(transport, fname.c_str(), &buf)) {
if (images[i].is_optional) continue;
@@ -1225,41 +1241,33 @@
}
}
-#define skip(n) do { argc -= (n); argv += (n); } while (0)
-#define require(n) do { if (argc < (n)) {usage(); exit(1);}} while (0)
-
-static int do_bypass_unlock_command(int argc, char **argv)
-{
- if (argc <= 2) return 0;
- skip(2);
-
- /*
- * Process unlock_bootloader, we have to load the message file
- * and send that to the remote device.
- */
- require(1);
-
- int64_t sz;
- void* data = load_file(*argv, &sz);
- if (data == nullptr) die("could not load '%s': %s", *argv, strerror(errno));
- fb_queue_download("unlock_message", data, sz);
- fb_queue_command("flashing unlock_bootloader", "unlocking bootloader");
- skip(1);
- return 0;
+static std::string next_arg(std::vector<std::string>* args) {
+ if (args->empty()) syntax_error("expected argument");
+ std::string result = args->front();
+ args->erase(args->begin());
+ return result;
}
-static int do_oem_command(int argc, char** argv) {
- if (argc <= 1) return 0;
+static void do_bypass_unlock_command(std::vector<std::string>* args) {
+ if (args->empty()) syntax_error("missing unlock_bootloader request");
- std::string command;
- while (argc > 0) {
- command += *argv;
- skip(1);
- if (argc != 0) command += " ";
+ std::string filename = next_arg(args);
+
+ int64_t sz;
+ void* data = load_file(filename.c_str(), &sz);
+ if (data == nullptr) die("could not load '%s': %s", filename.c_str(), strerror(errno));
+ fb_queue_download("unlock_message", data, sz);
+ fb_queue_command("flashing unlock_bootloader", "unlocking bootloader");
+}
+
+static void do_oem_command(const std::string& cmd, std::vector<std::string>* args) {
+ if (args->empty()) syntax_error("empty oem command");
+
+ std::string command(cmd);
+ while (!args->empty()) {
+ command += " " + next_arg(args);
}
-
fb_queue_command(command.c_str(), "");
- return 0;
}
static int64_t parse_num(const char *arg)
@@ -1335,7 +1343,7 @@
static void fb_perform_format(Transport* transport,
const char* partition, int skip_if_not_supported,
- const char* type_override, const char* size_override,
+ const std::string& type_override, const std::string& size_override,
const std::string& initial_dir) {
std::string partition_type, partition_size;
@@ -1356,10 +1364,10 @@
errMsg = "Can't determine partition type.\n";
goto failed;
}
- if (type_override) {
+ if (!type_override.empty()) {
if (partition_type != type_override) {
fprintf(stderr, "Warning: %s type is %s, but %s was requested for formatting.\n",
- partition, partition_type.c_str(), type_override);
+ partition, partition_type.c_str(), type_override.c_str());
}
partition_type = type_override;
}
@@ -1368,10 +1376,10 @@
errMsg = "Unable to get partition size\n";
goto failed;
}
- if (size_override) {
+ if (!size_override.empty()) {
if (partition_size != size_override) {
fprintf(stderr, "Warning: %s size is %s, but %s was requested for formatting.\n",
- partition, partition_size.c_str(), size_override);
+ partition, partition_size.c_str(), size_override.c_str());
}
partition_size = size_override;
}
@@ -1395,7 +1403,8 @@
return;
}
- fd = fileno(tmpfile());
+ fd = make_temporary_fd();
+ if (fd == -1) return;
unsigned eraseBlkSize, logicalBlkSize;
eraseBlkSize = fb_get_flash_block_size(transport, "erase-block-size");
@@ -1467,7 +1476,7 @@
serial = getenv("ANDROID_SERIAL");
while (1) {
- int c = getopt_long(argc, argv, "wub:k:n:r:t:s:S:lp:c:i:m:ha::", longopts, &longindex);
+ int c = getopt_long(argc, argv, "wub:k:n:r:t:s:S:lc:i:m:ha::", longopts, &longindex);
if (c < 0) {
break;
}
@@ -1485,8 +1494,7 @@
cmdline = optarg;
break;
case 'h':
- usage();
- return 1;
+ return show_help();
case 'i': {
char *endptr = nullptr;
unsigned long val;
@@ -1507,9 +1515,6 @@
page_size = (unsigned)strtoul(optarg, nullptr, 0);
if (!page_size) die("invalid page size");
break;
- case 'p':
- product = optarg;
- break;
case 'r':
ramdisk_offset = strtoul(optarg, 0, 16);
break;
@@ -1538,7 +1543,7 @@
setvbuf(stdout, nullptr, _IONBF, 0);
setvbuf(stderr, nullptr, _IONBF, 0);
} else if (strcmp("version", longopts[longindex].name) == 0) {
- fprintf(stdout, "fastboot version %s\n", FASTBOOT_REVISION);
+ fprintf(stdout, "fastboot version %s\n", FASTBOOT_VERSION);
fprintf(stdout, "Installed as %s\n", android::base::GetExecutablePath().c_str());
return 0;
} else if (strcmp("slot", longopts[longindex].name) == 0) {
@@ -1566,20 +1571,15 @@
argc -= optind;
argv += optind;
- if (argc == 0 && !wants_wipe && !wants_set_active) {
- usage();
- return 1;
- }
+ if (argc == 0 && !wants_wipe && !wants_set_active) syntax_error("no command");
if (argc > 0 && !strcmp(*argv, "devices")) {
- skip(1);
list_devices();
return 0;
}
if (argc > 0 && !strcmp(*argv, "help")) {
- usage();
- return 0;
+ return show_help();
}
Transport* transport = open_device();
@@ -1608,17 +1608,19 @@
}
}
- while (argc > 0) {
- if (!strcmp(*argv, "getvar")) {
- require(2);
- fb_queue_display(argv[1], argv[1]);
- skip(2);
- } else if(!strcmp(*argv, "erase")) {
- require(2);
+ std::vector<std::string> args(argv, argv + argc);
+ while (!args.empty()) {
+ std::string command = next_arg(&args);
- auto erase = [&](const std::string &partition) {
+ if (command == "getvar") {
+ std::string variable = next_arg(&args);
+ fb_queue_display(variable.c_str(), variable.c_str());
+ } else if (command == "erase") {
+ std::string partition = next_arg(&args);
+ auto erase = [&](const std::string& partition) {
std::string partition_type;
- if (fb_getvar(transport, std::string("partition-type:") + argv[1], &partition_type) &&
+ if (fb_getvar(transport, std::string("partition-type:") + partition,
+ &partition_type) &&
fs_get_generator(partition_type) != nullptr) {
fprintf(stderr, "******** Did you mean to fastboot format this %s partition?\n",
partition_type.c_str());
@@ -1626,106 +1628,79 @@
fb_queue_erase(partition.c_str());
};
- do_for_partitions(transport, argv[1], slot_override, erase, true);
- skip(2);
- } else if(!strncmp(*argv, "format", strlen("format"))) {
- char *overrides;
- char *type_override = nullptr;
- char *size_override = nullptr;
- require(2);
- /*
- * Parsing for: "format[:[type][:[size]]]"
- * Some valid things:
- * - select ontly the size, and leave default fs type:
- * format::0x4000000 userdata
- * - default fs type and size:
- * format userdata
- * format:: userdata
- */
- overrides = strchr(*argv, ':');
- if (overrides) {
- overrides++;
- size_override = strchr(overrides, ':');
- if (size_override) {
- size_override[0] = '\0';
- size_override++;
- }
- type_override = overrides;
- }
- if (type_override && !type_override[0]) type_override = nullptr;
- if (size_override && !size_override[0]) size_override = nullptr;
+ do_for_partitions(transport, partition, slot_override, erase, true);
+ } else if (android::base::StartsWith(command, "format")) {
+ // Parsing for: "format[:[type][:[size]]]"
+ // Some valid things:
+ // - select only the size, and leave default fs type:
+ // format::0x4000000 userdata
+ // - default fs type and size:
+ // format userdata
+ // format:: userdata
+ std::vector<std::string> pieces = android::base::Split(command, ":");
+ std::string type_override;
+ if (pieces.size() > 1) type_override = pieces[1].c_str();
+ std::string size_override;
+ if (pieces.size() > 2) size_override = pieces[2].c_str();
- auto format = [&](const std::string &partition) {
+ std::string partition = next_arg(&args);
+
+ auto format = [&](const std::string& partition) {
if (erase_first && needs_erase(transport, partition.c_str())) {
fb_queue_erase(partition.c_str());
}
- fb_perform_format(transport, partition.c_str(), 0,
- type_override, size_override, "");
+ fb_perform_format(transport, partition.c_str(), 0, type_override, size_override,
+ "");
};
- do_for_partitions(transport, argv[1], slot_override, format, true);
- skip(2);
- } else if(!strcmp(*argv, "signature")) {
- require(2);
- data = load_file(argv[1], &sz);
- if (data == nullptr) die("could not load '%s': %s", argv[1], strerror(errno));
+ do_for_partitions(transport, partition.c_str(), slot_override, format, true);
+ } else if (command == "signature") {
+ std::string filename = next_arg(&args);
+ data = load_file(filename.c_str(), &sz);
+ if (data == nullptr) die("could not load '%s': %s", filename.c_str(), strerror(errno));
if (sz != 256) die("signature must be 256 bytes");
fb_queue_download("signature", data, sz);
fb_queue_command("signature", "installing signature");
- skip(2);
- } else if(!strcmp(*argv, "reboot")) {
+ } else if (command == "reboot") {
wants_reboot = true;
- skip(1);
- if (argc > 0) {
- if (!strcmp(*argv, "bootloader")) {
+
+ if (args.size() == 1) {
+ std::string what = next_arg(&args);
+ if (what == "bootloader") {
wants_reboot = false;
wants_reboot_bootloader = true;
- skip(1);
- } else if (!strcmp(*argv, "emergency")) {
+ } else if (what == "emergency") {
wants_reboot = false;
wants_reboot_emergency = true;
- skip(1);
+ } else {
+ syntax_error("unknown reboot target %s", what.c_str());
}
+
}
- require(0);
- } else if(!strcmp(*argv, "reboot-bootloader")) {
+ if (!args.empty()) syntax_error("junk after reboot command");
+ } else if (command == "reboot-bootloader") {
wants_reboot_bootloader = true;
- skip(1);
- } else if (!strcmp(*argv, "continue")) {
+ } else if (command == "continue") {
fb_queue_command("continue", "resuming boot");
- skip(1);
- } else if(!strcmp(*argv, "boot")) {
- char *kname = 0;
- char *rname = 0;
- char *sname = 0;
- skip(1);
- if (argc > 0) {
- kname = argv[0];
- skip(1);
- }
- if (argc > 0) {
- rname = argv[0];
- skip(1);
- }
- if (argc > 0) {
- sname = argv[0];
- skip(1);
- }
- data = load_bootable_image(kname, rname, sname, &sz, cmdline);
- if (data == 0) return 1;
+ } else if (command == "boot") {
+ std::string kernel = next_arg(&args);
+ std::string ramdisk;
+ if (!args.empty()) ramdisk = next_arg(&args);
+ std::string second_stage;
+ if (!args.empty()) second_stage = next_arg(&args);
+
+ data = load_bootable_image(kernel, ramdisk, second_stage, &sz, cmdline);
fb_queue_download("boot.img", data, sz);
fb_queue_command("boot", "booting");
- } else if(!strcmp(*argv, "flash")) {
- char* pname = argv[1];
+ } else if (command == "flash") {
+ std::string pname = next_arg(&args);
+
std::string fname;
- require(2);
- if (argc > 2) {
- fname = argv[2];
- skip(3);
+ if (!args.empty()) {
+ fname = next_arg(&args);
} else {
- fname = find_item(pname, product);
- skip(2);
+ fname = find_item(pname);
}
- if (fname.empty()) die("cannot determine image filename for '%s'", pname);
+ if (fname.empty()) die("cannot determine image filename for '%s'", pname.c_str());
auto flash = [&](const std::string &partition) {
if (erase_first && needs_erase(transport, partition.c_str())) {
@@ -1733,29 +1708,21 @@
}
do_flash(transport, partition.c_str(), fname.c_str());
};
- do_for_partitions(transport, pname, slot_override, flash, true);
- } else if(!strcmp(*argv, "flash:raw")) {
- char *kname = argv[2];
- char *rname = 0;
- char *sname = 0;
- require(3);
- skip(3);
- if (argc > 0) {
- rname = argv[0];
- skip(1);
- }
- if (argc > 0) {
- sname = argv[0];
- skip(1);
- }
- data = load_bootable_image(kname, rname, sname, &sz, cmdline);
- if (data == 0) die("cannot load bootable image");
- auto flashraw = [&](const std::string &partition) {
+ do_for_partitions(transport, pname.c_str(), slot_override, flash, true);
+ } else if (command == "flash:raw") {
+ std::string partition = next_arg(&args);
+ std::string kernel = next_arg(&args);
+ std::string ramdisk;
+ if (!args.empty()) ramdisk = next_arg(&args);
+ std::string second_stage;
+ if (!args.empty()) second_stage = next_arg(&args);
+
+ data = load_bootable_image(kernel, ramdisk, second_stage, &sz, cmdline);
+ auto flashraw = [&](const std::string& partition) {
fb_queue_flash(partition.c_str(), data, sz);
};
- do_for_partitions(transport, argv[1], slot_override, flashraw, true);
- } else if(!strcmp(*argv, "flashall")) {
- skip(1);
+ do_for_partitions(transport, partition, slot_override, flashraw, true);
+ } else if (command == "flashall") {
if (slot_override == "all") {
fprintf(stderr, "Warning: slot set to 'all'. Secondary slots will not be flashed.\n");
do_flashall(transport, slot_override, erase_first, true);
@@ -1763,22 +1730,21 @@
do_flashall(transport, slot_override, erase_first, skip_secondary);
}
wants_reboot = true;
- } else if(!strcmp(*argv, "update")) {
+ } else if (command == "update") {
bool slot_all = (slot_override == "all");
if (slot_all) {
fprintf(stderr, "Warning: slot set to 'all'. Secondary slots will not be flashed.\n");
}
- if (argc > 1) {
- do_update(transport, argv[1], slot_override, erase_first, skip_secondary || slot_all);
- skip(2);
- } else {
- do_update(transport, "update.zip", slot_override, erase_first, skip_secondary || slot_all);
- skip(1);
+ std::string filename = "update.zip";
+ if (!args.empty()) {
+ filename = next_arg(&args);
}
+ do_update(transport, filename.c_str(), slot_override, erase_first,
+ skip_secondary || slot_all);
wants_reboot = true;
- } else if(!strcmp(*argv, "set_active")) {
- require(2);
- std::string slot = verify_slot(transport, std::string(argv[1]), false);
+ } else if (command == "set_active") {
+ std::string slot = verify_slot(transport, next_arg(&args), false);
+
// Legacy support: verify_slot() removes leading underscores, we need to put them back
// in for old bootloaders. Legacy bootloaders do not have the slot-count variable but
// do have slot-suffixes.
@@ -1788,28 +1754,36 @@
slot = "_" + slot;
}
fb_set_active(slot.c_str());
- skip(2);
- } else if(!strcmp(*argv, "oem")) {
- argc = do_oem_command(argc, argv);
- } else if(!strcmp(*argv, "flashing")) {
- if (argc == 2 && (!strcmp(*(argv+1), "unlock") ||
- !strcmp(*(argv+1), "lock") ||
- !strcmp(*(argv+1), "unlock_critical") ||
- !strcmp(*(argv+1), "lock_critical") ||
- !strcmp(*(argv+1), "get_unlock_ability") ||
- !strcmp(*(argv+1), "get_unlock_bootloader_nonce") ||
- !strcmp(*(argv+1), "lock_bootloader"))) {
- argc = do_oem_command(argc, argv);
- } else
- if (argc == 3 && !strcmp(*(argv+1), "unlock_bootloader")) {
- argc = do_bypass_unlock_command(argc, argv);
+ } else if (command == "stage") {
+ std::string filename = next_arg(&args);
+
+ struct fastboot_buffer buf;
+ if (!load_buf(transport, filename.c_str(), &buf) || buf.type != FB_BUFFER_FD) {
+ die("cannot load '%s'", filename.c_str());
+ }
+ fb_queue_download_fd(filename.c_str(), buf.fd, buf.sz);
+ } else if (command == "get_staged") {
+ std::string filename = next_arg(&args);
+ fb_queue_upload(filename.c_str());
+ } else if (command == "oem") {
+ do_oem_command("oem", &args);
+ } else if (command == "flashing") {
+ if (args.empty()) {
+ syntax_error("missing 'flashing' command");
+ } else if (args.size() == 1 && (args[0] == "unlock" || args[0] == "lock" ||
+ args[0] == "unlock_critical" ||
+ args[0] == "lock_critical" ||
+ args[0] == "get_unlock_ability" ||
+ args[0] == "get_unlock_bootloader_nonce" ||
+ args[0] == "lock_bootloader")) {
+ do_oem_command("flashing", &args);
+ } else if (args.size() == 2 && args[0] == "unlock_bootloader") {
+ do_bypass_unlock_command(&args);
} else {
- usage();
- return 1;
+ syntax_error("unknown 'flashing' command %s", args[0].c_str());
}
} else {
- usage();
- return 1;
+ syntax_error("unknown command %s", command.c_str());
}
}
@@ -1822,17 +1796,17 @@
if (initial_userdata_dir.empty()) {
return 1;
}
- fb_perform_format(transport, "userdata", 1, nullptr, nullptr, initial_userdata_dir);
+ fb_perform_format(transport, "userdata", 1, "", "", initial_userdata_dir);
delete_fbemarker_tmpdir(initial_userdata_dir);
} else {
- fb_perform_format(transport, "userdata", 1, nullptr, nullptr, "");
+ fb_perform_format(transport, "userdata", 1, "", "", "");
}
std::string cache_type;
if (fb_getvar(transport, "partition-type:cache", &cache_type) && !cache_type.empty()) {
fprintf(stderr, "wiping cache...\n");
fb_queue_erase("cache");
- fb_perform_format(transport, "cache", 1, nullptr, nullptr, "");
+ fb_perform_format(transport, "cache", 1, "", "", "");
}
}
if (wants_set_active) {
diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h
index 6699b6a..e3c60ae 100644
--- a/fastboot/fastboot.h
+++ b/fastboot/fastboot.h
@@ -41,8 +41,10 @@
/* protocol.c - fastboot protocol */
int fb_command(Transport* transport, const char* cmd);
int fb_command_response(Transport* transport, const char* cmd, char* response);
-int fb_download_data(Transport* transport, const void* data, uint32_t size);
+int64_t fb_download_data(Transport* transport, const void* data, uint32_t size);
+int64_t fb_download_data_fd(Transport* transport, int fd, uint32_t size);
int fb_download_data_sparse(Transport* transport, struct sparse_file* s);
+int64_t fb_upload_data(Transport* transport, const char* outfile);
const std::string fb_get_error();
#define FB_COMMAND_SZ 64
@@ -51,6 +53,7 @@
/* engine.c - high level command queue engine */
bool fb_getvar(Transport* transport, const std::string& key, std::string* value);
void fb_queue_flash(const char *ptn, void *data, uint32_t sz);
+void fb_queue_flash_fd(const char *ptn, int fd, uint32_t sz);
void fb_queue_flash_sparse(const char* ptn, struct sparse_file* s, uint32_t sz, size_t current,
size_t total);
void fb_queue_erase(const char *ptn);
@@ -62,9 +65,11 @@
void fb_queue_reboot(void);
void fb_queue_command(const char *cmd, const char *msg);
void fb_queue_download(const char *name, void *data, uint32_t size);
+void fb_queue_download_fd(const char *name, int fd, uint32_t sz);
+void fb_queue_upload(const char* outfile);
void fb_queue_notice(const char *notice);
void fb_queue_wait_for_disconnect(void);
-int fb_execute_queue(Transport* transport);
+int64_t fb_execute_queue(Transport* transport);
void fb_set_active(const char *slot);
/* util stuff */
diff --git a/fastboot/protocol.cpp b/fastboot/protocol.cpp
index bfa83b0..dcdf8f0 100644
--- a/fastboot/protocol.cpp
+++ b/fastboot/protocol.cpp
@@ -29,26 +29,34 @@
#define round_down(a, b) \
({ typeof(a) _a = (a); typeof(b) _b = (b); _a - (_a % _b); })
+#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <algorithm>
+#include <vector>
+#include <android-base/file.h>
#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
#include <sparse/sparse.h>
+#include <utils/FileMap.h>
#include "fastboot.h"
#include "transport.h"
static std::string g_error;
+using android::base::unique_fd;
+using android::base::WriteStringToFile;
+
const std::string fb_get_error() {
return g_error;
}
-static int check_response(Transport* transport, uint32_t size, char* response) {
+static int64_t check_response(Transport* transport, uint32_t size, char* response) {
char status[65];
while (true) {
@@ -105,7 +113,7 @@
return -1;
}
-static int _command_start(Transport* transport, const char* cmd, uint32_t size, char* response) {
+static int64_t _command_start(Transport* transport, const char* cmd, uint32_t size, char* response) {
size_t cmdsize = strlen(cmd);
if (cmdsize > 64) {
g_error = android::base::StringPrintf("command too large (%zu)", cmdsize);
@@ -125,37 +133,51 @@
return check_response(transport, size, response);
}
-static int _command_data(Transport* transport, const void* data, uint32_t size) {
- int r = transport->Write(data, size);
+static int64_t _command_write_data(Transport* transport, const void* data, uint32_t size) {
+ int64_t r = transport->Write(data, size);
if (r < 0) {
- g_error = android::base::StringPrintf("data transfer failure (%s)", strerror(errno));
+ g_error = android::base::StringPrintf("data write failure (%s)", strerror(errno));
transport->Close();
return -1;
}
- if (r != ((int) size)) {
- g_error = "data transfer failure (short transfer)";
+ if (r != static_cast<int64_t>(size)) {
+ g_error = "data write failure (short transfer)";
transport->Close();
return -1;
}
return r;
}
-static int _command_end(Transport* transport) {
+static int64_t _command_read_data(Transport* transport, void* data, uint32_t size) {
+ int64_t r = transport->Read(data, size);
+ if (r < 0) {
+ g_error = android::base::StringPrintf("data read failure (%s)", strerror(errno));
+ transport->Close();
+ return -1;
+ }
+ if (r != (static_cast<int64_t>(size))) {
+ g_error = "data read failure (short transfer)";
+ transport->Close();
+ return -1;
+ }
+ return r;
+}
+
+static int64_t _command_end(Transport* transport) {
return check_response(transport, 0, 0) < 0 ? -1 : 0;
}
-static int _command_send(Transport* transport, const char* cmd, const void* data, uint32_t size,
+static int64_t _command_send(Transport* transport, const char* cmd, const void* data, uint32_t size,
char* response) {
if (size == 0) {
return -1;
}
- int r = _command_start(transport, cmd, size, response);
+ int64_t r = _command_start(transport, cmd, size, response);
if (r < 0) {
return -1;
}
-
- r = _command_data(transport, data, size);
+ r = _command_write_data(transport, data, size);
if (r < 0) {
return -1;
}
@@ -168,6 +190,39 @@
return size;
}
+static int64_t _command_send_fd(Transport* transport, const char* cmd, int fd, uint32_t size,
+ char* response) {
+ static constexpr uint32_t MAX_MAP_SIZE = 512 * 1024 * 1024;
+ off64_t offset = 0;
+ uint32_t remaining = size;
+
+ if (_command_start(transport, cmd, size, response) < 0) {
+ return -1;
+ }
+
+ while (remaining) {
+ android::FileMap filemap;
+ size_t len = std::min(remaining, MAX_MAP_SIZE);
+
+ if (!filemap.create(NULL, fd, offset, len, true)) {
+ return -1;
+ }
+
+ if (_command_write_data(transport, filemap.getDataPtr(), len) < 0) {
+ return -1;
+ }
+
+ remaining -= len;
+ offset += len;
+ }
+
+ if (_command_end(transport) < 0) {
+ return -1;
+ }
+
+ return size;
+}
+
static int _command_send_no_data(Transport* transport, const char* cmd, char* response) {
return _command_start(transport, cmd, 0, response);
}
@@ -180,10 +235,36 @@
return _command_send_no_data(transport, cmd, response);
}
-int fb_download_data(Transport* transport, const void* data, uint32_t size) {
- char cmd[64];
- snprintf(cmd, sizeof(cmd), "download:%08x", size);
- return _command_send(transport, cmd, data, size, 0) < 0 ? -1 : 0;
+int64_t fb_download_data(Transport* transport, const void* data, uint32_t size) {
+ std::string cmd(android::base::StringPrintf("download:%08x", size));
+ return _command_send(transport, cmd.c_str(), data, size, 0) < 0 ? -1 : 0;
+}
+
+int64_t fb_download_data_fd(Transport* transport, int fd, uint32_t size) {
+ std::string cmd(android::base::StringPrintf("download:%08x", size));
+ return _command_send_fd(transport, cmd.c_str(), fd, size, 0) < 0 ? -1 : 0;
+}
+
+int64_t fb_upload_data(Transport* transport, const char* outfile) {
+ // positive return value is the upload size sent by the device
+ int64_t r = _command_start(transport, "upload", std::numeric_limits<int32_t>::max(), nullptr);
+ if (r <= 0) {
+ g_error = android::base::StringPrintf("command start failed (%s)", strerror(errno));
+ return r;
+ }
+
+ std::string data;
+ data.resize(r);
+ if ((r = _command_read_data(transport, &data[0], data.size())) == -1) {
+ return r;
+ }
+
+ if (!WriteStringToFile(data, outfile, true)) {
+ g_error = android::base::StringPrintf("write to '%s' failed", outfile);
+ return -1;
+ }
+
+ return _command_end(transport);
}
#define TRANSPORT_BUF_SIZE 1024
@@ -207,7 +288,7 @@
}
if (transport_buf_len == TRANSPORT_BUF_SIZE) {
- r = _command_data(transport, transport_buf, TRANSPORT_BUF_SIZE);
+ r = _command_write_data(transport, transport_buf, TRANSPORT_BUF_SIZE);
if (r != TRANSPORT_BUF_SIZE) {
return -1;
}
@@ -220,7 +301,7 @@
return -1;
}
to_write = round_down(len, TRANSPORT_BUF_SIZE);
- r = _command_data(transport, ptr, to_write);
+ r = _command_write_data(transport, ptr, to_write);
if (r != to_write) {
return -1;
}
@@ -242,7 +323,8 @@
static int fb_download_data_sparse_flush(Transport* transport) {
if (transport_buf_len > 0) {
- if (_command_data(transport, transport_buf, transport_buf_len) != transport_buf_len) {
+ int64_t r = _command_write_data(transport, transport_buf, transport_buf_len);
+ if (r != static_cast<int64_t>(transport_buf_len)) {
return -1;
}
transport_buf_len = 0;
@@ -256,9 +338,8 @@
return -1;
}
- char cmd[64];
- snprintf(cmd, sizeof(cmd), "download:%08x", size);
- int r = _command_start(transport, cmd, size, 0);
+ std::string cmd(android::base::StringPrintf("download:%08x", size));
+ int r = _command_start(transport, cmd.c_str(), size, 0);
if (r < 0) {
return -1;
}
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
new file mode 100644
index 0000000..0af6159
--- /dev/null
+++ b/fs_mgr/Android.bp
@@ -0,0 +1,62 @@
+//
+// 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_defaults {
+ name: "fs_mgr_defaults",
+ sanitize: {
+ misc_undefined: ["integer"],
+ },
+ local_include_dirs: ["include/"],
+ cppflags: ["-Werror"],
+ static_libs: [
+ "liblogwrap",
+ "libfec",
+ "libfec_rs",
+ "libbase",
+ "libcrypto_utils",
+ "libcrypto",
+ "libext4_utils",
+ "libsquashfs_utils",
+ "libselinux",
+ "libavb",
+ ],
+}
+
+cc_library_static {
+ name: "libfs_mgr",
+ defaults: ["fs_mgr_defaults"],
+ export_include_dirs: ["include"],
+ include_dirs: ["system/vold"],
+ srcs: [
+ "fs_mgr.cpp",
+ "fs_mgr_dm_ioctl.cpp",
+ "fs_mgr_format.cpp",
+ "fs_mgr_fstab.cpp",
+ "fs_mgr_slotselect.cpp",
+ "fs_mgr_verity.cpp",
+ "fs_mgr_avb.cpp",
+ "fs_mgr_avb_ops.cpp",
+ "fs_mgr_boot_config.cpp",
+ ],
+ product_variables: {
+ debuggable: {
+ cppflags: ["-DALLOW_ADBD_DISABLE_VERITY=1"],
+ },
+ eng: {
+ cppflags: ["-DALLOW_SKIP_SECURE_CHECK=1"],
+ },
+ },
+}
diff --git a/fs_mgr/Android.mk b/fs_mgr/Android.mk
index 2863a26..f3ca724 100644
--- a/fs_mgr/Android.mk
+++ b/fs_mgr/Android.mk
@@ -17,35 +17,6 @@
include $(CLEAR_VARS)
LOCAL_CLANG := true
LOCAL_SANITIZE := integer
-LOCAL_SRC_FILES:= \
- fs_mgr.cpp \
- fs_mgr_dm_ioctl.cpp \
- fs_mgr_format.cpp \
- fs_mgr_fstab.cpp \
- fs_mgr_slotselect.cpp \
- fs_mgr_verity.cpp \
- fs_mgr_avb.cpp \
- fs_mgr_avb_ops.cpp \
- fs_mgr_boot_config.cpp
-LOCAL_C_INCLUDES := \
- $(LOCAL_PATH)/include \
- system/vold \
- system/extras/ext4_utils
-LOCAL_MODULE:= libfs_mgr
-LOCAL_STATIC_LIBRARIES := $(common_static_libraries)
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_CFLAGS := -Werror
-ifneq (,$(filter userdebug,$(TARGET_BUILD_VARIANT)))
-LOCAL_CFLAGS += -DALLOW_ADBD_DISABLE_VERITY=1
-endif
-ifneq (,$(filter eng,$(TARGET_BUILD_VARIANT)))
-LOCAL_CFLAGS += -DALLOW_SKIP_SECURE_CHECK=1
-endif
-include $(BUILD_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_CLANG := true
-LOCAL_SANITIZE := integer
LOCAL_SRC_FILES:= fs_mgr_main.cpp
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
LOCAL_MODULE:= fs_mgr
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 6636be2..c189eee 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -31,7 +31,10 @@
#include <time.h>
#include <unistd.h>
+#include <memory>
+
#include <android-base/file.h>
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/unique_fd.h>
#include <cutils/android_reboot.h>
@@ -44,11 +47,14 @@
#include <ext4_utils/wipe.h>
#include <linux/fs.h>
#include <linux/loop.h>
+#include <linux/magic.h>
+#include <log/log_properties.h>
#include <logwrap/logwrap.h>
-#include <private/android_logger.h> // for __android_log_is_debuggable()
+#include "fs_mgr.h"
+#include "fs_mgr_avb.h"
#include "fs_mgr_priv.h"
-#include "fs_mgr_priv_avb.h"
+#include "fs_mgr_priv_dm_ioctl.h"
#define KEY_LOC_PROP "ro.crypto.keyfile.userdata"
#define KEY_IN_FOOTER "footer"
@@ -67,17 +73,18 @@
// record fs stat
enum FsStatFlags {
- FS_STAT_IS_EXT4 = 0x0001,
+ FS_STAT_IS_EXT4 = 0x0001,
FS_STAT_NEW_IMAGE_VERSION = 0x0002,
- FS_STAT_E2FSCK_F_ALWAYS = 0x0004,
- FS_STAT_UNCLEAN_SHUTDOWN = 0x0008,
- FS_STAT_QUOTA_ENABLED = 0x0010,
- FS_STAT_TUNE2FS_FAILED = 0x0020,
- FS_STAT_RO_MOUNT_FAILED = 0x0040,
+ FS_STAT_E2FSCK_F_ALWAYS = 0x0004,
+ FS_STAT_UNCLEAN_SHUTDOWN = 0x0008,
+ FS_STAT_QUOTA_ENABLED = 0x0010,
+ FS_STAT_TUNE2FS_FAILED = 0x0020,
+ FS_STAT_RO_MOUNT_FAILED = 0x0040,
FS_STAT_RO_UNMOUNT_FAILED = 0x0080,
FS_STAT_FULL_MOUNT_FAILED = 0x0100,
- FS_STAT_E2FSCK_FAILED = 0x0200,
- FS_STAT_E2FSCK_FS_FIXED = 0x0400,
+ FS_STAT_E2FSCK_FAILED = 0x0200,
+ FS_STAT_E2FSCK_FS_FIXED = 0x0400,
+ FS_STAT_EXT4_INVALID_MAGIC = 0x0800,
};
/*
@@ -121,21 +128,26 @@
}
}
+static bool should_force_check(int fs_stat) {
+ return fs_stat & (FS_STAT_E2FSCK_F_ALWAYS | FS_STAT_UNCLEAN_SHUTDOWN | FS_STAT_QUOTA_ENABLED |
+ FS_STAT_TUNE2FS_FAILED | FS_STAT_RO_MOUNT_FAILED | FS_STAT_RO_UNMOUNT_FAILED |
+ FS_STAT_FULL_MOUNT_FAILED | FS_STAT_E2FSCK_FAILED);
+}
+
static void check_fs(const char *blk_device, char *fs_type, char *target, int *fs_stat)
{
int status;
int ret;
long tmpmnt_flags = MS_NOATIME | MS_NOEXEC | MS_NOSUID;
char tmpmnt_opts[64] = "errors=remount-ro";
- const char *e2fsck_argv[] = {
- E2FSCK_BIN,
- "-f",
- "-y",
- blk_device
- };
+ const char* e2fsck_argv[] = {E2FSCK_BIN, "-y", blk_device};
+ const char* e2fsck_forced_argv[] = {E2FSCK_BIN, "-f", "-y", blk_device};
/* Check for the types of filesystems we know how to check */
if (!strcmp(fs_type, "ext2") || !strcmp(fs_type, "ext3") || !strcmp(fs_type, "ext4")) {
+ if (*fs_stat & FS_STAT_EXT4_INVALID_MAGIC) { // will fail, so do not try
+ return;
+ }
/*
* First try to mount and unmount the filesystem. We do this because
* the kernel is more efficient than e2fsck in running the journal and
@@ -149,32 +161,35 @@
* filesytsem due to an error, e2fsck is still run to do a full check
* fix the filesystem.
*/
- errno = 0;
- if (!strcmp(fs_type, "ext4")) {
- // This option is only valid with ext4
- strlcat(tmpmnt_opts, ",nomblk_io_submit", sizeof(tmpmnt_opts));
- }
- ret = mount(blk_device, target, fs_type, tmpmnt_flags, tmpmnt_opts);
- PINFO << __FUNCTION__ << "(): mount(" << blk_device << "," << target
- << "," << fs_type << ")=" << ret;
- if (!ret) {
- int i;
- for (i = 0; i < 5; i++) {
- // Try to umount 5 times before continuing on.
- // Should we try rebooting if all attempts fail?
- int result = umount(target);
- if (result == 0) {
- LINFO << __FUNCTION__ << "(): unmount(" << target
- << ") succeeded";
- break;
- }
- *fs_stat |= FS_STAT_RO_UNMOUNT_FAILED;
- PERROR << __FUNCTION__ << "(): umount(" << target << ")="
- << result;
- sleep(1);
+ if (!(*fs_stat & FS_STAT_FULL_MOUNT_FAILED)) { // already tried if full mount failed
+ errno = 0;
+ if (!strcmp(fs_type, "ext4")) {
+ // This option is only valid with ext4
+ strlcat(tmpmnt_opts, ",nomblk_io_submit", sizeof(tmpmnt_opts));
}
- } else {
- *fs_stat |= FS_STAT_RO_MOUNT_FAILED;
+ ret = mount(blk_device, target, fs_type, tmpmnt_flags, tmpmnt_opts);
+ PINFO << __FUNCTION__ << "(): mount(" << blk_device << "," << target << "," << fs_type
+ << ")=" << ret;
+ if (!ret) {
+ bool umounted = false;
+ int retry_count = 5;
+ while (retry_count-- > 0) {
+ umounted = umount(target) == 0;
+ if (umounted) {
+ LINFO << __FUNCTION__ << "(): unmount(" << target << ") succeeded";
+ break;
+ }
+ PERROR << __FUNCTION__ << "(): umount(" << target << ") failed";
+ if (retry_count) sleep(1);
+ }
+ if (!umounted) {
+ // boot may fail but continue and leave it to later stage for now.
+ PERROR << __FUNCTION__ << "(): umount(" << target << ") timed out";
+ *fs_stat |= FS_STAT_RO_UNMOUNT_FAILED;
+ }
+ } else {
+ *fs_stat |= FS_STAT_RO_MOUNT_FAILED;
+ }
}
/*
@@ -186,14 +201,15 @@
<< " (executable not in system image)";
} else {
LINFO << "Running " << E2FSCK_BIN << " on " << blk_device;
-
- *fs_stat |= FS_STAT_E2FSCK_F_ALWAYS;
- ret = android_fork_execvp_ext(ARRAY_SIZE(e2fsck_argv),
- const_cast<char **>(e2fsck_argv),
- &status, true, LOG_KLOG | LOG_FILE,
- true,
- const_cast<char *>(FSCK_LOG_FILE),
- NULL, 0);
+ if (should_force_check(*fs_stat)) {
+ ret = android_fork_execvp_ext(
+ ARRAY_SIZE(e2fsck_forced_argv), const_cast<char**>(e2fsck_forced_argv), &status,
+ true, LOG_KLOG | LOG_FILE, true, const_cast<char*>(FSCK_LOG_FILE), NULL, 0);
+ } else {
+ ret = android_fork_execvp_ext(
+ ARRAY_SIZE(e2fsck_argv), const_cast<char**>(e2fsck_argv), &status, true,
+ LOG_KLOG | LOG_FILE, true, const_cast<char*>(FSCK_LOG_FILE), NULL, 0);
+ }
if (ret < 0) {
/* No need to check for error in fork, we can't really handle it now */
@@ -282,8 +298,16 @@
PERROR << "Can't read '" << blk_device << "' super block";
return force_check;
}
+ if (sb.s_magic != EXT4_SUPER_MAGIC) {
+ LINFO << "Invalid ext4 magic:0x" << std::hex << sb.s_magic << "," << blk_device;
+ *fs_stat |= FS_STAT_EXT4_INVALID_MAGIC;
+ return 0; // not a valid fs, tune2fs, fsck, and mount will all fail.
+ }
*fs_stat |= FS_STAT_IS_EXT4;
- //TODO check if it is new version or not
+ LINFO << "superblock s_max_mnt_count:" << sb.s_max_mnt_count << "," << blk_device;
+ if (sb.s_max_mnt_count == 0xffff) { // -1 (int16) in ext2, but uint16 in ext4
+ *fs_stat |= FS_STAT_NEW_IMAGE_VERSION;
+ }
if ((sb.s_feature_incompat & EXT4_FEATURE_INCOMPAT_RECOVER) != 0 ||
(sb.s_state & EXT4_VALID_FS) == 0) {
LINFO << __FUNCTION__ << "(): was not clealy shutdown, state flag:"
@@ -539,7 +563,13 @@
int force_check = do_quota_with_shutdown_check(fstab->recs[i].blk_device,
fstab->recs[i].fs_type,
&fstab->recs[i], &fs_stat);
-
+ if (fs_stat & FS_STAT_EXT4_INVALID_MAGIC) {
+ LERROR << __FUNCTION__ << "(): skipping mount, invalid ext4, mountpoint="
+ << fstab->recs[i].mount_point << " rec[" << i
+ << "].fs_type=" << fstab->recs[i].fs_type;
+ mount_errno = EINVAL; // continue bootup for FDE
+ continue;
+ }
if ((fstab->recs[i].fs_mgr_flags & MF_CHECK) || force_check) {
check_fs(fstab->recs[i].blk_device, fstab->recs[i].fs_type,
fstab->recs[i].mount_point, &fs_stat);
@@ -550,21 +580,31 @@
&fstab->recs[i], &fs_stat);
}
- if (!__mount(fstab->recs[i].blk_device, fstab->recs[i].mount_point, &fstab->recs[i])) {
- *attempted_idx = i;
- mounted = 1;
- if (i != start_idx) {
- LERROR << __FUNCTION__ << "(): Mounted "
- << fstab->recs[i].blk_device << " on "
- << fstab->recs[i].mount_point << " with fs_type="
- << fstab->recs[i].fs_type << " instead of "
- << fstab->recs[start_idx].fs_type;
- }
- } else {
- fs_stat |= FS_STAT_FULL_MOUNT_FAILED;
- /* back up the first errno for crypto decisions */
- if (mount_errno == 0) {
- mount_errno = errno;
+ int retry_count = 2;
+ while (retry_count-- > 0) {
+ if (!__mount(fstab->recs[i].blk_device, fstab->recs[i].mount_point,
+ &fstab->recs[i])) {
+ *attempted_idx = i;
+ mounted = 1;
+ if (i != start_idx) {
+ LERROR << __FUNCTION__ << "(): Mounted " << fstab->recs[i].blk_device
+ << " on " << fstab->recs[i].mount_point
+ << " with fs_type=" << fstab->recs[i].fs_type << " instead of "
+ << fstab->recs[start_idx].fs_type;
+ }
+ fs_stat &= ~FS_STAT_FULL_MOUNT_FAILED;
+ mount_errno = 0;
+ break;
+ } else {
+ if (retry_count <= 0) break; // run check_fs only once
+ fs_stat |= FS_STAT_FULL_MOUNT_FAILED;
+ /* back up the first errno for crypto decisions */
+ if (mount_errno == 0) {
+ mount_errno = errno;
+ }
+ // retry after fsck
+ check_fs(fstab->recs[i].blk_device, fstab->recs[i].fs_type,
+ fstab->recs[i].mount_point, &fs_stat);
}
}
log_fs_stat(fstab->recs[i].blk_device, fs_stat);
@@ -681,6 +721,12 @@
return false;
}
+static bool should_use_metadata_encryption(const struct fstab_rec* rec) {
+ if (!(rec->fs_mgr_flags & (MF_FILEENCRYPTION | MF_FORCEFDEORFBE))) return false;
+ if (!(rec->fs_mgr_flags & MF_KEYDIRECTORY)) return false;
+ return true;
+}
+
// Check to see if a mountable volume has encryption requirements
static int handle_encryptable(const struct fstab_rec* rec)
{
@@ -693,8 +739,14 @@
<< " - allow continue unencrypted";
return FS_MGR_MNTALL_DEV_NOT_ENCRYPTED;
}
+ } else if (should_use_metadata_encryption(rec)) {
+ if (umount(rec->mount_point) == 0) {
+ return FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION;
+ } else {
+ PERROR << "Could not umount " << rec->mount_point << " - fail since can't encrypt";
+ return FS_MGR_MNTALL_FAIL;
+ }
} else if (rec->fs_mgr_flags & (MF_FILEENCRYPTION | MF_FORCEFDEORFBE)) {
- // Deal with file level encryption
LINFO << rec->mount_point << " is file encrypted";
return FS_MGR_MNTALL_DEV_FILE_ENCRYPTED;
} else if (fs_mgr_is_encryptable(rec)) {
@@ -747,15 +799,10 @@
int mret = -1;
int mount_errno = 0;
int attempted_idx = -1;
- int avb_ret = FS_MGR_SETUP_AVB_FAIL;
+ FsManagerAvbUniquePtr avb_handle(nullptr);
if (!fstab) {
- return -1;
- }
-
- if (fs_mgr_is_avb_used() &&
- (avb_ret = fs_mgr_load_vbmeta_images(fstab)) == FS_MGR_SETUP_AVB_FAIL) {
- return -1;
+ return FS_MGR_MNTALL_FAIL;
}
for (i = 0; i < fstab->num_entries; i++) {
@@ -796,16 +843,15 @@
wait_for_file(fstab->recs[i].blk_device, WAIT_TIMEOUT);
}
- if (fs_mgr_is_avb_used() && (fstab->recs[i].fs_mgr_flags & MF_AVB)) {
- /* If HASHTREE_DISABLED is set (cf. 'adb disable-verity'), we
- * should set up the device without using dm-verity.
- * The actual mounting still take place in the following
- * mount_with_alternatives().
- */
- if (avb_ret == FS_MGR_SETUP_AVB_HASHTREE_DISABLED) {
- LINFO << "AVB HASHTREE disabled";
- } else if (fs_mgr_setup_avb(&fstab->recs[i]) !=
- FS_MGR_SETUP_AVB_SUCCESS) {
+ if (fstab->recs[i].fs_mgr_flags & MF_AVB) {
+ if (!avb_handle) {
+ avb_handle = FsManagerAvbHandle::Open(*fstab);
+ if (!avb_handle) {
+ LERROR << "Failed to open FsManagerAvbHandle";
+ return FS_MGR_MNTALL_FAIL;
+ }
+ }
+ if (!avb_handle->SetUpAvb(&fstab->recs[i], true /* wait_for_verity_dev */)) {
LERROR << "Failed to set up AVB on partition: "
<< fstab->recs[i].mount_point << ", skipping!";
/* Skips mounting the device. */
@@ -849,7 +895,6 @@
continue;
}
- /* mount(2) returned an error, handle the encryptable/formattable case */
bool wiped = partition_wiped(fstab->recs[top_idx].blk_device);
bool crypt_footer = false;
if (mret && mount_errno != EBUSY && mount_errno != EACCES &&
@@ -889,6 +934,8 @@
continue;
}
}
+
+ /* mount(2) returned an error, handle the encryptable/formattable case */
if (mret && mount_errno != EBUSY && mount_errno != EACCES &&
fs_mgr_is_encryptable(&fstab->recs[attempted_idx])) {
if (wiped) {
@@ -914,29 +961,32 @@
}
}
encryptable = FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED;
+ } else if (mret && mount_errno != EBUSY && mount_errno != EACCES &&
+ should_use_metadata_encryption(&fstab->recs[attempted_idx])) {
+ encryptable = FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED;
} else {
+ // fs_options might be null so we cannot use PERROR << directly.
+ // Use StringPrintf to output "(null)" instead.
if (fs_mgr_is_nofail(&fstab->recs[attempted_idx])) {
- PERROR << "Ignoring failure to mount an un-encryptable or wiped partition on"
- << fstab->recs[attempted_idx].blk_device << " at "
- << fstab->recs[attempted_idx].mount_point << " options: "
- << fstab->recs[attempted_idx].fs_options;
+ PERROR << android::base::StringPrintf(
+ "Ignoring failure to mount an un-encryptable or wiped "
+ "partition on %s at %s options: %s",
+ fstab->recs[attempted_idx].blk_device, fstab->recs[attempted_idx].mount_point,
+ fstab->recs[attempted_idx].fs_options);
} else {
- PERROR << "Failed to mount an un-encryptable or wiped partition on"
- << fstab->recs[attempted_idx].blk_device << " at "
- << fstab->recs[attempted_idx].mount_point << " options: "
- << fstab->recs[attempted_idx].fs_options;
+ PERROR << android::base::StringPrintf(
+ "Failed to mount an un-encryptable or wiped partition "
+ "on %s at %s options: %s",
+ fstab->recs[attempted_idx].blk_device, fstab->recs[attempted_idx].mount_point,
+ fstab->recs[attempted_idx].fs_options);
++error_count;
}
continue;
}
}
- if (fs_mgr_is_avb_used()) {
- fs_mgr_unload_vbmeta_images();
- }
-
if (error_count) {
- return -1;
+ return FS_MGR_MNTALL_FAIL;
} else {
return encryptable;
}
@@ -969,19 +1019,13 @@
char *tmp_mount_point)
{
int i = 0;
- int ret = FS_MGR_DOMNT_FAILED;
int mount_errors = 0;
int first_mount_errno = 0;
- char *m;
- int avb_ret = FS_MGR_SETUP_AVB_FAIL;
+ char* mount_point;
+ FsManagerAvbUniquePtr avb_handle(nullptr);
if (!fstab) {
- return ret;
- }
-
- if (fs_mgr_is_avb_used() &&
- (avb_ret = fs_mgr_load_vbmeta_images(fstab)) == FS_MGR_SETUP_AVB_FAIL) {
- return ret;
+ return FS_MGR_DOMNT_FAILED;
}
for (i = 0; i < fstab->num_entries; i++) {
@@ -996,7 +1040,7 @@
!strcmp(fstab->recs[i].fs_type, "mtd")) {
LERROR << "Cannot mount filesystem of type "
<< fstab->recs[i].fs_type << " on " << n_blk_device;
- goto out;
+ return FS_MGR_DOMNT_FAILED;
}
/* First check the filesystem if requested */
@@ -1005,8 +1049,7 @@
}
int fs_stat = 0;
- int force_check = do_quota_with_shutdown_check(fstab->recs[i].blk_device,
- fstab->recs[i].fs_type,
+ int force_check = do_quota_with_shutdown_check(n_blk_device, fstab->recs[i].fs_type,
&fstab->recs[i], &fs_stat);
if ((fstab->recs[i].fs_mgr_flags & MF_CHECK) || force_check) {
@@ -1018,16 +1061,15 @@
do_reserved_size(n_blk_device, fstab->recs[i].fs_type, &fstab->recs[i], &fs_stat);
}
- if (fs_mgr_is_avb_used() && (fstab->recs[i].fs_mgr_flags & MF_AVB)) {
- /* If HASHTREE_DISABLED is set (cf. 'adb disable-verity'), we
- * should set up the device without using dm-verity.
- * The actual mounting still take place in the following
- * mount_with_alternatives().
- */
- if (avb_ret == FS_MGR_SETUP_AVB_HASHTREE_DISABLED) {
- LINFO << "AVB HASHTREE disabled";
- } else if (fs_mgr_setup_avb(&fstab->recs[i]) !=
- FS_MGR_SETUP_AVB_SUCCESS) {
+ if (fstab->recs[i].fs_mgr_flags & MF_AVB) {
+ if (!avb_handle) {
+ avb_handle = FsManagerAvbHandle::Open(*fstab);
+ if (!avb_handle) {
+ LERROR << "Failed to open FsManagerAvbHandle";
+ return FS_MGR_DOMNT_FAILED;
+ }
+ }
+ if (!avb_handle->SetUpAvb(&fstab->recs[i], true /* wait_for_verity_dev */)) {
LERROR << "Failed to set up AVB on partition: "
<< fstab->recs[i].mount_point << ", skipping!";
/* Skips mounting the device. */
@@ -1045,41 +1087,36 @@
/* Now mount it where requested */
if (tmp_mount_point) {
- m = tmp_mount_point;
+ mount_point = tmp_mount_point;
} else {
- m = fstab->recs[i].mount_point;
+ mount_point = fstab->recs[i].mount_point;
}
- if (__mount(n_blk_device, m, &fstab->recs[i])) {
- if (!first_mount_errno) first_mount_errno = errno;
- mount_errors++;
- fs_stat |= FS_STAT_FULL_MOUNT_FAILED;
- log_fs_stat(fstab->recs[i].blk_device, fs_stat);
- continue;
- } else {
- ret = 0;
- log_fs_stat(fstab->recs[i].blk_device, fs_stat);
- goto out;
+ int retry_count = 2;
+ while (retry_count-- > 0) {
+ if (!__mount(n_blk_device, mount_point, &fstab->recs[i])) {
+ fs_stat &= ~FS_STAT_FULL_MOUNT_FAILED;
+ return FS_MGR_DOMNT_SUCCESS;
+ } else {
+ if (retry_count <= 0) break; // run check_fs only once
+ if (!first_mount_errno) first_mount_errno = errno;
+ mount_errors++;
+ fs_stat |= FS_STAT_FULL_MOUNT_FAILED;
+ // try again after fsck
+ check_fs(n_blk_device, fstab->recs[i].fs_type, fstab->recs[i].mount_point, &fs_stat);
+ }
}
- }
- if (mount_errors) {
- PERROR << "Cannot mount filesystem on " << n_blk_device
- << " at " << m;
- if (first_mount_errno == EBUSY) {
- ret = FS_MGR_DOMNT_BUSY;
- } else {
- ret = FS_MGR_DOMNT_FAILED;
- }
- } else {
- /* We didn't find a match, say so and return an error */
- LERROR << "Cannot find mount point " << fstab->recs[i].mount_point
- << " in fstab";
+ log_fs_stat(fstab->recs[i].blk_device, fs_stat);
}
-out:
- if (fs_mgr_is_avb_used()) {
- fs_mgr_unload_vbmeta_images();
+ // Reach here means the mount attempt fails.
+ if (mount_errors) {
+ PERROR << "Cannot mount filesystem on " << n_blk_device << " at " << mount_point;
+ if (first_mount_errno == EBUSY) return FS_MGR_DOMNT_BUSY;
+ } else {
+ /* We didn't find a match, say so and return an error */
+ LERROR << "Cannot find mount point " << n_name << " in fstab";
}
- return ret;
+ return FS_MGR_DOMNT_FAILED;
}
/*
@@ -1213,46 +1250,143 @@
return ret;
}
-/*
- * key_loc must be at least PROPERTY_VALUE_MAX bytes long
- *
- * real_blk_device must be at least PROPERTY_VALUE_MAX bytes long
- */
-int fs_mgr_get_crypt_info(struct fstab *fstab, char *key_loc, char *real_blk_device, int size)
-{
- int i = 0;
+struct fstab_rec const* fs_mgr_get_crypt_entry(struct fstab const* fstab) {
+ int i;
if (!fstab) {
- return -1;
- }
- /* Initialize return values to null strings */
- if (key_loc) {
- *key_loc = '\0';
- }
- if (real_blk_device) {
- *real_blk_device = '\0';
+ return NULL;
}
/* Look for the encryptable partition to find the data */
for (i = 0; i < fstab->num_entries; i++) {
/* Don't deal with vold managed enryptable partitions here */
- if (fstab->recs[i].fs_mgr_flags & MF_VOLDMANAGED) {
- continue;
+ if (!(fstab->recs[i].fs_mgr_flags & MF_VOLDMANAGED) &&
+ (fstab->recs[i].fs_mgr_flags &
+ (MF_CRYPT | MF_FORCECRYPT | MF_FORCEFDEORFBE | MF_FILEENCRYPTION))) {
+ return &fstab->recs[i];
}
- if (!(fstab->recs[i].fs_mgr_flags
- & (MF_CRYPT | MF_FORCECRYPT | MF_FORCEFDEORFBE))) {
- continue;
- }
+ }
+ return NULL;
+}
- /* We found a match */
- if (key_loc) {
- strlcpy(key_loc, fstab->recs[i].key_loc, size);
+/*
+ * key_loc must be at least PROPERTY_VALUE_MAX bytes long
+ *
+ * real_blk_device must be at least PROPERTY_VALUE_MAX bytes long
+ */
+void fs_mgr_get_crypt_info(struct fstab* fstab, char* key_loc, char* real_blk_device, size_t size) {
+ struct fstab_rec const* rec = fs_mgr_get_crypt_entry(fstab);
+ if (key_loc) {
+ if (rec) {
+ strlcpy(key_loc, rec->key_loc, size);
+ } else {
+ *key_loc = '\0';
}
- if (real_blk_device) {
- strlcpy(real_blk_device, fstab->recs[i].blk_device, size);
+ }
+ if (real_blk_device) {
+ if (rec) {
+ strlcpy(real_blk_device, rec->blk_device, size);
+ } else {
+ *real_blk_device = '\0';
}
- break;
+ }
+}
+
+bool fs_mgr_load_verity_state(int* mode) {
+ /* return the default mode, unless any of the verified partitions are in
+ * logging mode, in which case return that */
+ *mode = VERITY_MODE_DEFAULT;
+
+ std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
+ fs_mgr_free_fstab);
+ if (!fstab) {
+ LERROR << "Failed to read default fstab";
+ return false;
}
- return 0;
+ for (int i = 0; i < fstab->num_entries; i++) {
+ if (fs_mgr_is_avb(&fstab->recs[i])) {
+ *mode = VERITY_MODE_RESTART; // avb only supports restart mode.
+ break;
+ } else if (!fs_mgr_is_verified(&fstab->recs[i])) {
+ continue;
+ }
+
+ int current;
+ if (load_verity_state(&fstab->recs[i], ¤t) < 0) {
+ continue;
+ }
+ if (current != VERITY_MODE_DEFAULT) {
+ *mode = current;
+ break;
+ }
+ }
+
+ return true;
+}
+
+bool fs_mgr_update_verity_state(fs_mgr_verity_state_callback callback) {
+ if (!callback) {
+ return false;
+ }
+
+ int mode;
+ if (!fs_mgr_load_verity_state(&mode)) {
+ return false;
+ }
+
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open("/dev/device-mapper", O_RDWR | O_CLOEXEC)));
+ if (fd == -1) {
+ PERROR << "Error opening device mapper";
+ return false;
+ }
+
+ std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
+ fs_mgr_free_fstab);
+ if (!fstab) {
+ LERROR << "Failed to read default fstab";
+ return false;
+ }
+
+ alignas(dm_ioctl) char buffer[DM_BUF_SIZE];
+ struct dm_ioctl* io = (struct dm_ioctl*)buffer;
+ bool system_root = android::base::GetProperty("ro.build.system_root_image", "") == "true";
+
+ for (int i = 0; i < fstab->num_entries; i++) {
+ if (!fs_mgr_is_verified(&fstab->recs[i]) && !fs_mgr_is_avb(&fstab->recs[i])) {
+ continue;
+ }
+
+ std::string mount_point;
+ if (system_root && !strcmp(fstab->recs[i].mount_point, "/")) {
+ // In AVB, the dm device name is vroot instead of system.
+ mount_point = fs_mgr_is_avb(&fstab->recs[i]) ? "vroot" : "system";
+ } else {
+ mount_point = basename(fstab->recs[i].mount_point);
+ }
+
+ fs_mgr_verity_ioctl_init(io, mount_point, 0);
+
+ const char* status;
+ if (ioctl(fd, DM_TABLE_STATUS, io)) {
+ if (fstab->recs[i].fs_mgr_flags & MF_VERIFYATBOOT) {
+ status = "V";
+ } else {
+ PERROR << "Failed to query DM_TABLE_STATUS for " << mount_point.c_str();
+ continue;
+ }
+ }
+
+ status = &buffer[io->data_start + sizeof(struct dm_target_spec)];
+
+ // To be consistent in vboot 1.0 and vboot 2.0 (AVB), change the mount_point
+ // back to 'system' for the callback. So it has property [partition.system.verified]
+ // instead of [partition.vroot.verified].
+ if (mount_point == "vroot") mount_point = "system";
+ if (*status == 'C' || *status == 'V') {
+ callback(&fstab->recs[i], mount_point.c_str(), mode, *status);
+ }
+ }
+
+ return true;
}
diff --git a/fs_mgr/fs_mgr_avb.cpp b/fs_mgr/fs_mgr_avb.cpp
index 7512eb9..6618003 100644
--- a/fs_mgr/fs_mgr_avb.cpp
+++ b/fs_mgr/fs_mgr_avb.cpp
@@ -14,95 +14,32 @@
* limitations under the License.
*/
-#include <errno.h>
+#include "fs_mgr_avb.h"
+
#include <fcntl.h>
-#include <inttypes.h>
#include <libgen.h>
-#include <stdio.h>
#include <string.h>
-#include <sys/stat.h>
+#include <sys/ioctl.h>
#include <sys/types.h>
-#include <unistd.h>
+
+#include <sstream>
+#include <string>
#include <vector>
#include <android-base/file.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 <cutils/properties.h>
#include <libavb/libavb.h>
-#include <openssl/sha.h>
-#include <sys/ioctl.h>
-#include <utils/Compat.h>
#include "fs_mgr.h"
-#include "fs_mgr_avb_ops.h"
#include "fs_mgr_priv.h"
-#include "fs_mgr_priv_avb.h"
+#include "fs_mgr_priv_avb_ops.h"
#include "fs_mgr_priv_dm_ioctl.h"
#include "fs_mgr_priv_sha.h"
-/* The format of dm-verity construction parameters:
- * <version> <dev> <hash_dev> <data_block_size> <hash_block_size>
- * <num_data_blocks> <hash_start_block> <algorithm> <digest> <salt>
- */
-#define VERITY_TABLE_FORMAT \
- "%u %s %s %u %u " \
- "%" PRIu64 " %" PRIu64 " %s %s %s "
-
-#define VERITY_TABLE_PARAMS(hashtree_desc, blk_device, digest, salt) \
- hashtree_desc.dm_verity_version, blk_device, blk_device, hashtree_desc.data_block_size, \
- hashtree_desc.hash_block_size, \
- hashtree_desc.image_size / hashtree_desc.data_block_size, /* num_data_blocks. */ \
- hashtree_desc.tree_offset / hashtree_desc.hash_block_size, /* hash_start_block. */ \
- (char*)hashtree_desc.hash_algorithm, digest, salt
-
-#define VERITY_TABLE_OPT_RESTART "restart_on_corruption"
-#define VERITY_TABLE_OPT_IGNZERO "ignore_zero_blocks"
-
-/* The default format of dm-verity optional parameters:
- * <#opt_params> ignore_zero_blocks restart_on_corruption
- */
-#define VERITY_TABLE_OPT_DEFAULT_FORMAT "2 %s %s"
-#define VERITY_TABLE_OPT_DEFAULT_PARAMS VERITY_TABLE_OPT_IGNZERO, VERITY_TABLE_OPT_RESTART
-
-/* The FEC (forward error correction) format of dm-verity optional parameters:
- * <#opt_params> use_fec_from_device <fec_dev>
- * fec_roots <num> fec_blocks <num> fec_start <offset>
- * ignore_zero_blocks restart_on_corruption
- */
-#define VERITY_TABLE_OPT_FEC_FORMAT \
- "10 use_fec_from_device %s fec_roots %u fec_blocks %" PRIu64 " fec_start %" PRIu64 " %s %s"
-
-/* Note that fec_blocks is the size that FEC covers, *not* the
- * size of the FEC data. Since we use FEC for everything up until
- * the FEC data, it's the same as the offset (fec_start).
- */
-#define VERITY_TABLE_OPT_FEC_PARAMS(hashtree_desc, blk_device) \
- blk_device, hashtree_desc.fec_num_roots, \
- hashtree_desc.fec_offset / hashtree_desc.data_block_size, /* fec_blocks */ \
- hashtree_desc.fec_offset / hashtree_desc.data_block_size, /* fec_start */ \
- VERITY_TABLE_OPT_IGNZERO, VERITY_TABLE_OPT_RESTART
-
-AvbSlotVerifyData* fs_mgr_avb_verify_data = nullptr;
-AvbOps* fs_mgr_avb_ops = nullptr;
-
-enum HashAlgorithm {
- kInvalid = 0,
- kSHA256 = 1,
- kSHA512 = 2,
-};
-
-struct androidboot_vbmeta {
- HashAlgorithm hash_alg;
- uint8_t digest[SHA512_DIGEST_LENGTH];
- size_t vbmeta_size;
- bool allow_verification_error;
-};
-
-androidboot_vbmeta fs_mgr_vbmeta_prop;
-
static inline bool nibble_value(const char& c, uint8_t* value) {
FS_MGR_CHECK(value != nullptr);
@@ -159,27 +96,78 @@
return hex;
}
-static bool load_vbmeta_prop(androidboot_vbmeta* vbmeta_prop) {
- FS_MGR_CHECK(vbmeta_prop != nullptr);
+template <typename Hasher>
+static std::pair<size_t, bool> verify_vbmeta_digest(const AvbSlotVerifyData& verify_data,
+ const uint8_t* expected_digest) {
+ size_t total_size = 0;
+ Hasher hasher;
+ for (size_t n = 0; n < verify_data.num_vbmeta_images; n++) {
+ hasher.update(verify_data.vbmeta_images[n].vbmeta_data,
+ verify_data.vbmeta_images[n].vbmeta_size);
+ total_size += verify_data.vbmeta_images[n].vbmeta_size;
+ }
+ bool matched = (memcmp(hasher.finalize(), expected_digest, Hasher::DIGEST_SIZE) == 0);
+
+ return std::make_pair(total_size, matched);
+}
+
+// Reads the following values from kernel cmdline and provides the
+// VerifyVbmetaImages() to verify AvbSlotVerifyData.
+// - androidboot.vbmeta.device_state
+// - androidboot.vbmeta.hash_alg
+// - androidboot.vbmeta.size
+// - androidboot.vbmeta.digest
+class FsManagerAvbVerifier {
+ public:
+ // The factory method to return a unique_ptr<FsManagerAvbVerifier>
+ static std::unique_ptr<FsManagerAvbVerifier> Create();
+ bool VerifyVbmetaImages(const AvbSlotVerifyData& verify_data);
+ bool IsDeviceUnlocked() { return is_device_unlocked_; }
+
+ protected:
+ FsManagerAvbVerifier() = default;
+
+ private:
+ enum HashAlgorithm {
+ kInvalid = 0,
+ kSHA256 = 1,
+ kSHA512 = 2,
+ };
+
+ HashAlgorithm hash_alg_;
+ uint8_t digest_[SHA512_DIGEST_LENGTH];
+ size_t vbmeta_size_;
+ bool is_device_unlocked_;
+};
+
+std::unique_ptr<FsManagerAvbVerifier> FsManagerAvbVerifier::Create() {
std::string cmdline;
- android::base::ReadFileToString("/proc/cmdline", &cmdline);
+ if (!android::base::ReadFileToString("/proc/cmdline", &cmdline)) {
+ PERROR << "Failed to read /proc/cmdline";
+ return nullptr;
+ }
- std::string hash_alg;
+ std::unique_ptr<FsManagerAvbVerifier> avb_verifier(new FsManagerAvbVerifier());
+ if (!avb_verifier) {
+ LERROR << "Failed to create unique_ptr<FsManagerAvbVerifier>";
+ return nullptr;
+ }
+
std::string digest;
-
+ std::string hash_alg;
for (const auto& entry : android::base::Split(android::base::Trim(cmdline), " ")) {
std::vector<std::string> pieces = android::base::Split(entry, "=");
const std::string& key = pieces[0];
const std::string& value = pieces[1];
if (key == "androidboot.vbmeta.device_state") {
- vbmeta_prop->allow_verification_error = (value == "unlocked");
+ avb_verifier->is_device_unlocked_ = (value == "unlocked");
} else if (key == "androidboot.vbmeta.hash_alg") {
hash_alg = value;
} else if (key == "androidboot.vbmeta.size") {
- if (!android::base::ParseUint(value.c_str(), &vbmeta_prop->vbmeta_size)) {
- return false;
+ if (!android::base::ParseUint(value.c_str(), &avb_verifier->vbmeta_size_)) {
+ return nullptr;
}
} else if (key == "androidboot.vbmeta.digest") {
digest = value;
@@ -190,48 +178,31 @@
size_t expected_digest_size = 0;
if (hash_alg == "sha256") {
expected_digest_size = SHA256_DIGEST_LENGTH * 2;
- vbmeta_prop->hash_alg = kSHA256;
+ avb_verifier->hash_alg_ = kSHA256;
} else if (hash_alg == "sha512") {
expected_digest_size = SHA512_DIGEST_LENGTH * 2;
- vbmeta_prop->hash_alg = kSHA512;
+ avb_verifier->hash_alg_ = kSHA512;
} else {
LERROR << "Unknown hash algorithm: " << hash_alg.c_str();
- return false;
+ return nullptr;
}
// Reads digest.
if (digest.size() != expected_digest_size) {
LERROR << "Unexpected digest size: " << digest.size()
<< " (expected: " << expected_digest_size << ")";
- return false;
+ return nullptr;
}
- if (!hex_to_bytes(vbmeta_prop->digest, sizeof(vbmeta_prop->digest), digest)) {
+ if (!hex_to_bytes(avb_verifier->digest_, sizeof(avb_verifier->digest_), digest)) {
LERROR << "Hash digest contains non-hexidecimal character: " << digest.c_str();
- return false;
+ return nullptr;
}
- return true;
+ return avb_verifier;
}
-template <typename Hasher>
-static std::pair<size_t, bool> verify_vbmeta_digest(const AvbSlotVerifyData& verify_data,
- const androidboot_vbmeta& vbmeta_prop) {
- size_t total_size = 0;
- Hasher hasher;
- for (size_t n = 0; n < verify_data.num_vbmeta_images; n++) {
- hasher.update(verify_data.vbmeta_images[n].vbmeta_data,
- verify_data.vbmeta_images[n].vbmeta_size);
- total_size += verify_data.vbmeta_images[n].vbmeta_size;
- }
-
- bool matched = (memcmp(hasher.finalize(), vbmeta_prop.digest, Hasher::DIGEST_SIZE) == 0);
-
- return std::make_pair(total_size, matched);
-}
-
-static bool verify_vbmeta_images(const AvbSlotVerifyData& verify_data,
- const androidboot_vbmeta& vbmeta_prop) {
+bool FsManagerAvbVerifier::VerifyVbmetaImages(const AvbSlotVerifyData& verify_data) {
if (verify_data.num_vbmeta_images == 0) {
LERROR << "No vbmeta images";
return false;
@@ -240,17 +211,17 @@
size_t total_size = 0;
bool digest_matched = false;
- if (vbmeta_prop.hash_alg == kSHA256) {
+ if (hash_alg_ == kSHA256) {
std::tie(total_size, digest_matched) =
- verify_vbmeta_digest<SHA256Hasher>(verify_data, vbmeta_prop);
- } else if (vbmeta_prop.hash_alg == kSHA512) {
+ verify_vbmeta_digest<SHA256Hasher>(verify_data, digest_);
+ } else if (hash_alg_ == kSHA512) {
std::tie(total_size, digest_matched) =
- verify_vbmeta_digest<SHA512Hasher>(verify_data, vbmeta_prop);
+ verify_vbmeta_digest<SHA512Hasher>(verify_data, digest_);
}
- if (total_size != vbmeta_prop.vbmeta_size) {
- LERROR << "total vbmeta size mismatch: " << total_size
- << " (expected: " << vbmeta_prop.vbmeta_size << ")";
+ if (total_size != vbmeta_size_) {
+ LERROR << "total vbmeta size mismatch: " << total_size << " (expected: " << vbmeta_size_
+ << ")";
return false;
}
@@ -262,10 +233,81 @@
return true;
}
-static bool hashtree_load_verity_table(struct dm_ioctl* io, const std::string& dm_device_name,
- int fd, const std::string& blk_device,
- const AvbHashtreeDescriptor& hashtree_desc,
- const std::string& salt, const std::string& root_digest) {
+// Constructs dm-verity arguments for sending DM_TABLE_LOAD ioctl to kernel.
+// See the following link for more details:
+// https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity
+static std::string construct_verity_table(const AvbHashtreeDescriptor& hashtree_desc,
+ const std::string& salt, const std::string& root_digest,
+ const std::string& blk_device) {
+ // Loads androidboot.veritymode from kernel cmdline.
+ std::string verity_mode;
+ if (!fs_mgr_get_boot_config("veritymode", &verity_mode)) {
+ verity_mode = "enforcing"; // Defaults to enforcing when it's absent.
+ }
+
+ // Converts veritymode to the format used in kernel.
+ std::string dm_verity_mode;
+ if (verity_mode == "enforcing") {
+ dm_verity_mode = "restart_on_corruption";
+ } else if (verity_mode == "logging") {
+ dm_verity_mode = "ignore_corruption";
+ } else if (verity_mode != "eio") { // Default dm_verity_mode is eio.
+ LERROR << "Unknown androidboot.veritymode: " << verity_mode;
+ return "";
+ }
+
+ // dm-verity construction parameters:
+ // <version> <dev> <hash_dev>
+ // <data_block_size> <hash_block_size>
+ // <num_data_blocks> <hash_start_block>
+ // <algorithm> <digest> <salt>
+ // [<#opt_params> <opt_params>]
+ std::ostringstream verity_table;
+ verity_table << hashtree_desc.dm_verity_version << " " << blk_device << " " << blk_device << " "
+ << hashtree_desc.data_block_size << " " << hashtree_desc.hash_block_size << " "
+ << hashtree_desc.image_size / hashtree_desc.data_block_size << " "
+ << hashtree_desc.tree_offset / hashtree_desc.hash_block_size << " "
+ << hashtree_desc.hash_algorithm << " " << root_digest << " " << salt;
+
+ // Continued from the above optional parameters:
+ // [<#opt_params> <opt_params>]
+ int optional_argc = 0;
+ std::ostringstream optional_args;
+
+ // dm-verity optional parameters for FEC (forward error correction):
+ // use_fec_from_device <fec_dev>
+ // fec_roots <num>
+ // fec_blocks <num>
+ // fec_start <offset>
+ if (hashtree_desc.fec_size > 0) {
+ // Note that fec_blocks is the size that FEC covers, *NOT* the
+ // size of the FEC data. Since we use FEC for everything up until
+ // the FEC data, it's the same as the offset (fec_start).
+ optional_argc += 8;
+ // clang-format off
+ optional_args << "use_fec_from_device " << blk_device
+ << " fec_roots " << hashtree_desc.fec_num_roots
+ << " fec_blocks " << hashtree_desc.fec_offset / hashtree_desc.data_block_size
+ << " fec_start " << hashtree_desc.fec_offset / hashtree_desc.data_block_size
+ << " ";
+ // clang-format on
+ }
+
+ if (!dm_verity_mode.empty()) {
+ optional_argc += 1;
+ optional_args << dm_verity_mode << " ";
+ }
+
+ // Always use ignore_zero_blocks.
+ optional_argc += 1;
+ optional_args << "ignore_zero_blocks";
+
+ verity_table << " " << optional_argc << " " << optional_args.str();
+ return verity_table.str();
+}
+
+static bool load_verity_table(struct dm_ioctl* io, const std::string& dm_device_name, int fd,
+ uint64_t image_size, const std::string& verity_table) {
fs_mgr_verity_ioctl_init(io, dm_device_name, DM_STATUS_TABLE_FLAG);
// The buffer consists of [dm_ioctl][dm_target_spec][verity_params].
@@ -276,35 +318,25 @@
io->target_count = 1;
dm_target->status = 0;
dm_target->sector_start = 0;
- dm_target->length = hashtree_desc.image_size / 512;
+ dm_target->length = image_size / 512;
strcpy(dm_target->target_type, "verity");
// Builds the verity params.
char* verity_params = buffer + sizeof(struct dm_ioctl) + sizeof(struct dm_target_spec);
size_t bufsize = DM_BUF_SIZE - (verity_params - buffer);
- int res = 0;
- if (hashtree_desc.fec_size > 0) {
- res = snprintf(verity_params, bufsize, VERITY_TABLE_FORMAT VERITY_TABLE_OPT_FEC_FORMAT,
- VERITY_TABLE_PARAMS(hashtree_desc, blk_device.c_str(), root_digest.c_str(),
- salt.c_str()),
- VERITY_TABLE_OPT_FEC_PARAMS(hashtree_desc, blk_device.c_str()));
- } else {
- res = snprintf(verity_params, bufsize, VERITY_TABLE_FORMAT VERITY_TABLE_OPT_DEFAULT_FORMAT,
- VERITY_TABLE_PARAMS(hashtree_desc, blk_device.c_str(), root_digest.c_str(),
- salt.c_str()),
- VERITY_TABLE_OPT_DEFAULT_PARAMS);
- }
+ LINFO << "Loading verity table: '" << verity_table << "'";
- if (res < 0 || (size_t)res >= bufsize) {
- LERROR << "Error building verity table; insufficient buffer size?";
+ // Copies verity_table to verity_params (including the terminating null byte).
+ if (verity_table.size() > bufsize - 1) {
+ LERROR << "Verity table size too large: " << verity_table.size()
+ << " (max allowable size: " << bufsize - 1 << ")";
return false;
}
-
- LINFO << "Loading verity table: '" << verity_params << "'";
+ memcpy(verity_params, verity_table.c_str(), verity_table.size() + 1);
// Sets ext target boundary.
- verity_params += strlen(verity_params) + 1;
+ verity_params += verity_table.size() + 1;
verity_params = (char*)(((unsigned long)verity_params + 7) & ~7);
dm_target->next = verity_params - buffer;
@@ -319,7 +351,8 @@
static bool hashtree_dm_verity_setup(struct fstab_rec* fstab_entry,
const AvbHashtreeDescriptor& hashtree_desc,
- const std::string& salt, const std::string& root_digest) {
+ const std::string& salt, const std::string& root_digest,
+ bool wait_for_verity_dev) {
// Gets the device mapper fd.
android::base::unique_fd fd(open("/dev/device-mapper", O_RDWR));
if (fd < 0) {
@@ -343,9 +376,15 @@
return false;
}
+ std::string verity_table =
+ construct_verity_table(hashtree_desc, salt, root_digest, fstab_entry->blk_device);
+ if (verity_table.empty()) {
+ LERROR << "Failed to construct verity table.";
+ return false;
+ }
+
// Loads the verity mapping table.
- if (!hashtree_load_verity_table(io, mount_point, fd, std::string(fstab_entry->blk_device),
- hashtree_desc, salt, root_digest)) {
+ if (!load_verity_table(io, mount_point, fd, hashtree_desc.image_size, verity_table)) {
LERROR << "Couldn't load verity table!";
return false;
}
@@ -358,13 +397,12 @@
// Marks the underlying block device as read-only.
fs_mgr_set_blk_ro(fstab_entry->blk_device);
- // TODO(bowgotsai): support verified all partition at boot.
// Updates fstab_rec->blk_device to verity device name.
free(fstab_entry->blk_device);
fstab_entry->blk_device = strdup(verity_blk_name.c_str());
// Makes sure we've set everything up properly.
- if (fs_mgr_test_access(verity_blk_name.c_str()) < 0) {
+ if (wait_for_verity_dev && fs_mgr_test_access(verity_blk_name.c_str()) < 0) {
return false;
}
@@ -408,8 +446,7 @@
continue;
}
if (desc.tag == AVB_DESCRIPTOR_TAG_HASHTREE) {
- desc_partition_name =
- (const uint8_t*)descriptors[j] + sizeof(AvbHashtreeDescriptor);
+ desc_partition_name = (const uint8_t*)descriptors[j] + sizeof(AvbHashtreeDescriptor);
if (!avb_hashtree_descriptor_validate_and_byteswap(
(AvbHashtreeDescriptor*)descriptors[j], out_hashtree_desc)) {
continue;
@@ -441,140 +478,124 @@
return true;
}
-static bool init_is_avb_used() {
- // When AVB is used, boot loader should set androidboot.vbmeta.{hash_alg,
- // size, digest} in kernel cmdline or in device tree. They will then be
- // imported by init process to system properties: ro.boot.vbmeta.{hash_alg, size, digest}.
- //
- // In case of early mount, init properties are not initialized, so we also
- // ensure we look into kernel command line and device tree if the property is
- // not found
- //
- // Checks hash_alg as an indicator for whether AVB is used.
- // We don't have to parse and check all of them here. The check will
- // be done in fs_mgr_load_vbmeta_images() and FS_MGR_SETUP_AVB_FAIL will
- // be returned when there is an error.
-
- std::string hash_alg;
- if (!fs_mgr_get_boot_config("vbmeta.hash_alg", &hash_alg)) {
- return false;
- }
- if (hash_alg == "sha256" || hash_alg == "sha512") {
- return true;
- }
- return false;
+FsManagerAvbUniquePtr FsManagerAvbHandle::Open(const fstab& fstab) {
+ FsManagerAvbOps avb_ops(fstab);
+ return DoOpen(&avb_ops);
}
-bool fs_mgr_is_avb_used() {
- static bool result = init_is_avb_used();
- return result;
+FsManagerAvbUniquePtr FsManagerAvbHandle::Open(ByNameSymlinkMap&& by_name_symlink_map) {
+ if (by_name_symlink_map.empty()) {
+ LERROR << "Empty by_name_symlink_map when opening FsManagerAvbHandle";
+ return nullptr;
+ }
+ FsManagerAvbOps avb_ops(std::move(by_name_symlink_map));
+ return DoOpen(&avb_ops);
}
-int fs_mgr_load_vbmeta_images(struct fstab* fstab) {
- FS_MGR_CHECK(fstab != nullptr);
-
- // Gets the expected hash value of vbmeta images from
- // kernel cmdline.
- if (!load_vbmeta_prop(&fs_mgr_vbmeta_prop)) {
- return FS_MGR_SETUP_AVB_FAIL;
+FsManagerAvbUniquePtr FsManagerAvbHandle::DoOpen(FsManagerAvbOps* avb_ops) {
+ // Gets the expected hash value of vbmeta images from kernel cmdline.
+ std::unique_ptr<FsManagerAvbVerifier> avb_verifier = FsManagerAvbVerifier::Create();
+ if (!avb_verifier) {
+ LERROR << "Failed to create FsManagerAvbVerifier";
+ return nullptr;
}
- fs_mgr_avb_ops = fs_mgr_dummy_avb_ops_new(fstab);
- if (fs_mgr_avb_ops == nullptr) {
- LERROR << "Failed to allocate dummy avb_ops";
- return FS_MGR_SETUP_AVB_FAIL;
+ FsManagerAvbUniquePtr avb_handle(new FsManagerAvbHandle());
+ if (!avb_handle) {
+ LERROR << "Failed to allocate FsManagerAvbHandle";
+ return nullptr;
}
- // Invokes avb_slot_verify() to load and verify all vbmeta images.
- // Sets requested_partitions to nullptr as it's to copy the contents
- // of HASH partitions into fs_mgr_avb_verify_data, which is not required as
- // fs_mgr only deals with HASHTREE partitions.
- const char *requested_partitions[] = {nullptr};
- std::string ab_suffix;
- std::string slot;
- if (fs_mgr_get_boot_config("slot", &slot)) {
- ab_suffix = "_" + slot;
- } else {
- // remove slot_suffix once bootloaders update to new androidboot.slot param
- fs_mgr_get_boot_config("slot_suffix", &ab_suffix);
- }
+ AvbSlotVerifyFlags flags = avb_verifier->IsDeviceUnlocked()
+ ? AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR
+ : AVB_SLOT_VERIFY_FLAGS_NONE;
AvbSlotVerifyResult verify_result =
- avb_slot_verify(fs_mgr_avb_ops, requested_partitions, ab_suffix.c_str(),
- fs_mgr_vbmeta_prop.allow_verification_error, &fs_mgr_avb_verify_data);
+ avb_ops->AvbSlotVerify(fs_mgr_get_slot_suffix(), flags, &avb_handle->avb_slot_data_);
// Only allow two verify results:
// - AVB_SLOT_VERIFY_RESULT_OK.
// - AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION (for UNLOCKED state).
- if (verify_result == AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION) {
- if (!fs_mgr_vbmeta_prop.allow_verification_error) {
- LERROR << "ERROR_VERIFICATION isn't allowed";
- goto fail;
- }
- } else if (verify_result != AVB_SLOT_VERIFY_RESULT_OK) {
- LERROR << "avb_slot_verify failed, result: " << verify_result;
- goto fail;
+ // If the device is UNLOCKED, i.e., |allow_verification_error| is true for
+ // AvbSlotVerify(), then the following return values are all non-fatal:
+ // * AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION
+ // * AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED
+ // * AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX
+ // The latter two results were checked by bootloader prior to start fs_mgr so
+ // we just need to handle the first result here. See *dummy* operations in
+ // FsManagerAvbOps and the comments in external/avb/libavb/avb_slot_verify.h
+ // for more details.
+ switch (verify_result) {
+ case AVB_SLOT_VERIFY_RESULT_OK:
+ avb_handle->status_ = kFsManagerAvbHandleSuccess;
+ break;
+ case AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION:
+ if (!avb_verifier->IsDeviceUnlocked()) {
+ LERROR << "ERROR_VERIFICATION isn't allowed when the device is LOCKED";
+ return nullptr;
+ }
+ avb_handle->status_ = kFsManagerAvbHandleErrorVerification;
+ break;
+ default:
+ LERROR << "avb_slot_verify failed, result: " << verify_result;
+ return nullptr;
}
// Verifies vbmeta images against the digest passed from bootloader.
- if (!verify_vbmeta_images(*fs_mgr_avb_verify_data, fs_mgr_vbmeta_prop)) {
- LERROR << "verify_vbmeta_images failed";
- goto fail;
- } else {
- // Checks whether FLAGS_HASHTREE_DISABLED is set.
- AvbVBMetaImageHeader vbmeta_header;
- avb_vbmeta_image_header_to_host_byte_order(
- (AvbVBMetaImageHeader*)fs_mgr_avb_verify_data->vbmeta_images[0].vbmeta_data,
- &vbmeta_header);
-
- bool hashtree_disabled =
- ((AvbVBMetaImageFlags)vbmeta_header.flags & AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED);
- if (hashtree_disabled) {
- return FS_MGR_SETUP_AVB_HASHTREE_DISABLED;
- }
+ if (!avb_verifier->VerifyVbmetaImages(*avb_handle->avb_slot_data_)) {
+ LERROR << "VerifyVbmetaImages failed";
+ return nullptr;
}
- if (verify_result == AVB_SLOT_VERIFY_RESULT_OK) {
- return FS_MGR_SETUP_AVB_SUCCESS;
+ // Sets the MAJOR.MINOR for init to set it into "ro.boot.avb_version".
+ avb_handle->avb_version_ =
+ android::base::StringPrintf("%d.%d", AVB_VERSION_MAJOR, AVB_VERSION_MINOR);
+
+ // Checks whether FLAGS_HASHTREE_DISABLED is set.
+ AvbVBMetaImageHeader vbmeta_header;
+ avb_vbmeta_image_header_to_host_byte_order(
+ (AvbVBMetaImageHeader*)avb_handle->avb_slot_data_->vbmeta_images[0].vbmeta_data,
+ &vbmeta_header);
+
+ bool hashtree_disabled =
+ ((AvbVBMetaImageFlags)vbmeta_header.flags & AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED);
+ if (hashtree_disabled) {
+ avb_handle->status_ = kFsManagerAvbHandleHashtreeDisabled;
}
-fail:
- fs_mgr_unload_vbmeta_images();
- return FS_MGR_SETUP_AVB_FAIL;
+ LINFO << "Returning avb_handle with status: " << avb_handle->status_;
+ return avb_handle;
}
-void fs_mgr_unload_vbmeta_images() {
- if (fs_mgr_avb_verify_data != nullptr) {
- avb_slot_verify_data_free(fs_mgr_avb_verify_data);
+bool FsManagerAvbHandle::SetUpAvb(struct fstab_rec* fstab_entry, bool wait_for_verity_dev) {
+ if (!fstab_entry) return false;
+ if (!avb_slot_data_ || avb_slot_data_->num_vbmeta_images < 1) {
+ return false;
}
- if (fs_mgr_avb_ops != nullptr) {
- fs_mgr_dummy_avb_ops_free(fs_mgr_avb_ops);
- }
-}
-
-int fs_mgr_setup_avb(struct fstab_rec* fstab_entry) {
- if (!fstab_entry || !fs_mgr_avb_verify_data || fs_mgr_avb_verify_data->num_vbmeta_images < 1) {
- return FS_MGR_SETUP_AVB_FAIL;
+ if (status_ == kFsManagerAvbHandleUninitialized) return false;
+ if (status_ == kFsManagerAvbHandleHashtreeDisabled) {
+ LINFO << "AVB HASHTREE disabled on:" << fstab_entry->mount_point;
+ return true;
}
std::string partition_name(basename(fstab_entry->mount_point));
if (!avb_validate_utf8((const uint8_t*)partition_name.c_str(), partition_name.length())) {
LERROR << "Partition name: " << partition_name.c_str() << " is not valid UTF-8.";
- return FS_MGR_SETUP_AVB_FAIL;
+ return false;
}
AvbHashtreeDescriptor hashtree_descriptor;
std::string salt;
std::string root_digest;
- if (!get_hashtree_descriptor(partition_name, *fs_mgr_avb_verify_data, &hashtree_descriptor,
- &salt, &root_digest)) {
- return FS_MGR_SETUP_AVB_FAIL;
+ if (!get_hashtree_descriptor(partition_name, *avb_slot_data_, &hashtree_descriptor, &salt,
+ &root_digest)) {
+ return false;
}
// Converts HASHTREE descriptor to verity_table_params.
- if (!hashtree_dm_verity_setup(fstab_entry, hashtree_descriptor, salt, root_digest)) {
- return FS_MGR_SETUP_AVB_FAIL;
+ if (!hashtree_dm_verity_setup(fstab_entry, hashtree_descriptor, salt, root_digest,
+ wait_for_verity_dev)) {
+ return false;
}
-
- return FS_MGR_SETUP_AVB_SUCCESS;
+ return true;
}
diff --git a/fs_mgr/fs_mgr_avb_ops.cpp b/fs_mgr/fs_mgr_avb_ops.cpp
index 8e49663..512839b 100644
--- a/fs_mgr/fs_mgr_avb_ops.cpp
+++ b/fs_mgr/fs_mgr_avb_ops.cpp
@@ -22,6 +22,8 @@
* SOFTWARE.
*/
+#include "fs_mgr_priv_avb_ops.h"
+
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
@@ -36,94 +38,12 @@
#include <utils/Compat.h>
#include "fs_mgr.h"
-#include "fs_mgr_avb_ops.h"
#include "fs_mgr_priv.h"
-static std::string fstab_by_name_prefix;
-
-static std::string extract_by_name_prefix(struct fstab* fstab) {
- // In AVB, we can assume that there's an entry for the /misc mount
- // point in the fstab file and use that to get the device file for
- // the misc partition. The device needs not to have an actual /misc
- // partition. Then returns the prefix by removing the trailing "misc":
- //
- // - /dev/block/platform/soc.0/7824900.sdhci/by-name/misc ->
- // - /dev/block/platform/soc.0/7824900.sdhci/by-name/
-
- struct fstab_rec* fstab_entry = fs_mgr_get_entry_for_mount_point(fstab, "/misc");
- if (fstab_entry == nullptr) {
- LERROR << "/misc mount point not found in fstab";
- return "";
- }
-
- std::string full_path(fstab_entry->blk_device);
- size_t end_slash = full_path.find_last_of("/");
-
- return full_path.substr(0, end_slash + 1);
-}
-
-static AvbIOResult read_from_partition(AvbOps* ops ATTRIBUTE_UNUSED, const char* partition,
- int64_t offset, size_t num_bytes, void* buffer,
- size_t* out_num_read) {
- // The input |partition| name is with ab_suffix, e.g. system_a.
- // Slot suffix (e.g. _a) will be appended to the device file path
- // for partitions having 'slotselect' optin in fstab file, but it
- // won't be appended to the mount point.
- //
- // Appends |partition| to the fstab_by_name_prefix, which is obtained
- // by removing the trailing "misc" from the device file of /misc mount
- // point. e.g.,
- //
- // - /dev/block/platform/soc.0/7824900.sdhci/by-name/ ->
- // - /dev/block/platform/soc.0/7824900.sdhci/by-name/system_a
-
- std::string path = fstab_by_name_prefix + partition;
-
- // Ensures the device path (a symlink created by init) is ready to
- // access. fs_mgr_test_access() will test a few iterations if the
- // path doesn't exist yet.
- if (fs_mgr_test_access(path.c_str()) < 0) {
- return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
- }
-
- android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
-
- if (fd < 0) {
- PERROR << "Failed to open " << path.c_str();
- return AVB_IO_RESULT_ERROR_IO;
- }
-
- // If offset is negative, interprets its absolute value as the
- // number of bytes from the end of the partition.
- if (offset < 0) {
- off64_t total_size = lseek64(fd, 0, SEEK_END);
- if (total_size == -1) {
- LERROR << "Failed to lseek64 to end of the partition";
- return AVB_IO_RESULT_ERROR_IO;
- }
- offset = total_size + offset;
- // Repositions the offset to the beginning.
- if (lseek64(fd, 0, SEEK_SET) == -1) {
- LERROR << "Failed to lseek64 to the beginning of the partition";
- return AVB_IO_RESULT_ERROR_IO;
- }
- }
-
- // On Linux, we never get partial reads from block devices (except
- // for EOF).
- ssize_t num_read = TEMP_FAILURE_RETRY(pread64(fd, buffer, num_bytes, offset));
-
- if (num_read < 0 || (size_t)num_read != num_bytes) {
- PERROR << "Failed to read " << num_bytes << " bytes from " << path.c_str() << " offset "
- << offset;
- return AVB_IO_RESULT_ERROR_IO;
- }
-
- if (out_num_read != nullptr) {
- *out_num_read = num_read;
- }
-
- return AVB_IO_RESULT_OK;
+static AvbIOResult read_from_partition(AvbOps* ops, const char* partition, int64_t offset,
+ size_t num_bytes, void* buffer, size_t* out_num_read) {
+ return FsManagerAvbOps::GetInstanceFromAvbOps(ops)->ReadFromPartition(
+ partition, offset, num_bytes, buffer, out_num_read);
}
static AvbIOResult dummy_read_rollback_index(AvbOps* ops ATTRIBUTE_UNUSED,
@@ -145,7 +65,6 @@
// Addtionally, user-space should check
// androidboot.vbmeta.{hash_alg, size, digest} against the digest
// of all vbmeta images after invoking avb_slot_verify().
-
*out_is_trusted = true;
return AVB_IO_RESULT_OK;
}
@@ -170,28 +89,103 @@
return AVB_IO_RESULT_OK;
}
-AvbOps* fs_mgr_dummy_avb_ops_new(struct fstab* fstab) {
- AvbOps* ops;
+void FsManagerAvbOps::InitializeAvbOps() {
+ // We only need to provide the implementation of read_from_partition()
+ // operation since that's all what is being used by the avb_slot_verify().
+ // Other I/O operations are only required in bootloader but not in
+ // user-space so we set them as dummy operations. Also zero the entire
+ // struct so operations added in the future will be set to NULL.
+ memset(&avb_ops_, 0, sizeof(AvbOps));
+ avb_ops_.read_from_partition = read_from_partition;
+ avb_ops_.read_rollback_index = dummy_read_rollback_index;
+ avb_ops_.validate_vbmeta_public_key = dummy_validate_vbmeta_public_key;
+ avb_ops_.read_is_device_unlocked = dummy_read_is_device_unlocked;
+ avb_ops_.get_unique_guid_for_partition = dummy_get_unique_guid_for_partition;
- fstab_by_name_prefix = extract_by_name_prefix(fstab);
- if (fstab_by_name_prefix.empty()) return nullptr;
-
- ops = (AvbOps*)calloc(1, sizeof(AvbOps));
- if (ops == nullptr) {
- LERROR << "Error allocating memory for AvbOps";
- return nullptr;
- }
-
- // We only need these operations since that's all what is being used
- // by the avb_slot_verify(); Most of them are dummy operations because
- // they're only required in bootloader but not required in user-space.
- ops->read_from_partition = read_from_partition;
- ops->read_rollback_index = dummy_read_rollback_index;
- ops->validate_vbmeta_public_key = dummy_validate_vbmeta_public_key;
- ops->read_is_device_unlocked = dummy_read_is_device_unlocked;
- ops->get_unique_guid_for_partition = dummy_get_unique_guid_for_partition;
-
- return ops;
+ // Sets user_data for GetInstanceFromAvbOps() to convert it back to FsManagerAvbOps.
+ avb_ops_.user_data = this;
}
-void fs_mgr_dummy_avb_ops_free(AvbOps* ops) { free(ops); }
+FsManagerAvbOps::FsManagerAvbOps(std::map<std::string, std::string>&& by_name_symlink_map)
+ : by_name_symlink_map_(std::move(by_name_symlink_map)) {
+ InitializeAvbOps();
+}
+
+FsManagerAvbOps::FsManagerAvbOps(const fstab& fstab) {
+ // Constructs the by-name symlink map for each fstab record.
+ // /dev/block/platform/soc.0/7824900.sdhci/by-name/system_a =>
+ // by_name_symlink_map_["system_a"] = "/dev/block/platform/soc.0/7824900.sdhci/by-name/system_a"
+ for (int i = 0; i < fstab.num_entries; i++) {
+ std::string partition_name = basename(fstab.recs[i].blk_device);
+ by_name_symlink_map_[partition_name] = fstab.recs[i].blk_device;
+ }
+ InitializeAvbOps();
+}
+
+AvbIOResult FsManagerAvbOps::ReadFromPartition(const char* partition, int64_t offset,
+ size_t num_bytes, void* buffer,
+ size_t* out_num_read) {
+ const auto iter = by_name_symlink_map_.find(partition);
+ if (iter == by_name_symlink_map_.end()) {
+ LERROR << "by-name symlink not found for partition: '" << partition << "'";
+ return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
+ }
+ std::string path = iter->second;
+
+ // Ensures the device path (a symlink created by init) is ready to
+ // access. fs_mgr_test_access() will test a few iterations if the
+ // path doesn't exist yet.
+ if (fs_mgr_test_access(path.c_str()) < 0) {
+ return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
+ }
+
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
+ if (fd < 0) {
+ PERROR << "Failed to open " << path;
+ return AVB_IO_RESULT_ERROR_IO;
+ }
+
+ // If offset is negative, interprets its absolute value as the
+ // number of bytes from the end of the partition.
+ if (offset < 0) {
+ off64_t total_size = lseek64(fd, 0, SEEK_END);
+ if (total_size == -1) {
+ PERROR << "Failed to lseek64 to end of the partition";
+ return AVB_IO_RESULT_ERROR_IO;
+ }
+ offset = total_size + offset;
+ // Repositions the offset to the beginning.
+ if (lseek64(fd, 0, SEEK_SET) == -1) {
+ PERROR << "Failed to lseek64 to the beginning of the partition";
+ return AVB_IO_RESULT_ERROR_IO;
+ }
+ }
+
+ // On Linux, we never get partial reads from block devices (except
+ // for EOF).
+ ssize_t num_read = TEMP_FAILURE_RETRY(pread64(fd, buffer, num_bytes, offset));
+ if (num_read < 0 || (size_t)num_read != num_bytes) {
+ PERROR << "Failed to read " << num_bytes << " bytes from " << path << " offset " << offset;
+ return AVB_IO_RESULT_ERROR_IO;
+ }
+
+ if (out_num_read != nullptr) {
+ *out_num_read = num_read;
+ }
+
+ return AVB_IO_RESULT_OK;
+}
+
+AvbSlotVerifyResult FsManagerAvbOps::AvbSlotVerify(const std::string& ab_suffix,
+ AvbSlotVerifyFlags flags,
+ AvbSlotVerifyData** out_data) {
+ // Invokes avb_slot_verify() to load and verify all vbmeta images.
+ // Sets requested_partitions to nullptr as it's to copy the contents
+ // of HASH partitions into handle>avb_slot_data_, which is not required as
+ // fs_mgr only deals with HASHTREE partitions.
+ const char* requested_partitions[] = {nullptr};
+ // The |hashtree_error_mode| field doesn't matter as it only
+ // influences the generated kernel cmdline parameters.
+ return avb_slot_verify(&avb_ops_, requested_partitions, ab_suffix.c_str(), flags,
+ AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE, out_data);
+}
diff --git a/fs_mgr/fs_mgr_avb_ops.h b/fs_mgr/fs_mgr_avb_ops.h
deleted file mode 100644
index bfdec9a..0000000
--- a/fs_mgr/fs_mgr_avb_ops.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation
- * files (the "Software"), to deal in the Software without
- * restriction, including without limitation the rights to use, copy,
- * modify, merge, publish, distribute, sublicense, and/or sell copies
- * of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-#ifndef __CORE_FS_MGR_AVB_OPS_H
-#define __CORE_FS_MGR_AVB_OPS_H
-
-#include <libavb/libavb.h>
-
-#include "fs_mgr.h"
-
-__BEGIN_DECLS
-
-/* Allocates a "dummy" AvbOps instance solely for use in user-space.
- * Returns nullptr on OOM.
- *
- * It mainly provides read_from_partitions() for user-space to get
- * AvbSlotVerifyData.vbmeta_images[] and the caller MUST check their
- * integrity against the androidboot.vbmeta.{hash_alg, size, digest}
- * values from /proc/cmdline, e.g. verify_vbmeta_images()
- * in fs_mgr_avb.cpp.
- *
- * Other I/O operations are only required in boot loader so we set
- * them as dummy operations here.
- * - Will allow any public key for signing.
- * - returns 0 for any rollback index location.
- * - returns device is unlocked regardless of the actual state.
- * - returns a dummy guid for any partition.
- *
- * Frees with fs_mgr_dummy_avb_ops_free().
- */
-AvbOps* fs_mgr_dummy_avb_ops_new(struct fstab* fstab);
-
-/* Frees an AvbOps instance previously allocated with fs_mgr_avb_ops_new(). */
-void fs_mgr_dummy_avb_ops_free(AvbOps* ops);
-
-__END_DECLS
-
-#endif /* __CORE_FS_MGR_AVB_OPS_H */
diff --git a/fs_mgr/fs_mgr_boot_config.cpp b/fs_mgr/fs_mgr_boot_config.cpp
index cffa6ce..ab5beed 100644
--- a/fs_mgr/fs_mgr_boot_config.cpp
+++ b/fs_mgr/fs_mgr_boot_config.cpp
@@ -55,8 +55,6 @@
if (android::base::ReadFileToString(file_name, out_val)) {
return true;
}
-
- LINFO << "Error finding '" << key << "' in device tree";
}
return false;
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index a9a0df7..23bd664 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -31,6 +31,7 @@
struct fs_mgr_flag_values {
char *key_loc;
+ char* key_dir;
char *verity_loc;
long long part_length;
char *label;
@@ -70,34 +71,35 @@
};
static struct flag_list fs_mgr_flags[] = {
- { "wait", MF_WAIT },
- { "check", MF_CHECK },
- { "encryptable=", MF_CRYPT },
- { "forceencrypt=", MF_FORCECRYPT },
- { "fileencryption=", MF_FILEENCRYPTION },
- { "forcefdeorfbe=", MF_FORCEFDEORFBE },
- { "nonremovable", MF_NONREMOVABLE },
- { "voldmanaged=", MF_VOLDMANAGED},
- { "length=", MF_LENGTH },
- { "recoveryonly", MF_RECOVERYONLY },
- { "swapprio=", MF_SWAPPRIO },
- { "zramsize=", MF_ZRAMSIZE },
- { "max_comp_streams=", MF_MAX_COMP_STREAMS },
- { "verifyatboot", MF_VERIFYATBOOT },
- { "verify", MF_VERIFY },
- { "avb", MF_AVB },
- { "noemulatedsd", MF_NOEMULATEDSD },
- { "notrim", MF_NOTRIM },
- { "formattable", MF_FORMATTABLE },
- { "slotselect", MF_SLOTSELECT },
- { "nofail", MF_NOFAIL },
- { "latemount", MF_LATEMOUNT },
- { "reservedsize=", MF_RESERVEDSIZE },
- { "quota", MF_QUOTA },
- { "eraseblk=", MF_ERASEBLKSIZE },
- { "logicalblk=", MF_LOGICALBLKSIZE },
- { "defaults", 0 },
- { 0, 0 },
+ {"wait", MF_WAIT},
+ {"check", MF_CHECK},
+ {"encryptable=", MF_CRYPT},
+ {"forceencrypt=", MF_FORCECRYPT},
+ {"fileencryption=", MF_FILEENCRYPTION},
+ {"forcefdeorfbe=", MF_FORCEFDEORFBE},
+ {"keydirectory=", MF_KEYDIRECTORY},
+ {"nonremovable", MF_NONREMOVABLE},
+ {"voldmanaged=", MF_VOLDMANAGED},
+ {"length=", MF_LENGTH},
+ {"recoveryonly", MF_RECOVERYONLY},
+ {"swapprio=", MF_SWAPPRIO},
+ {"zramsize=", MF_ZRAMSIZE},
+ {"max_comp_streams=", MF_MAX_COMP_STREAMS},
+ {"verifyatboot", MF_VERIFYATBOOT},
+ {"verify", MF_VERIFY},
+ {"avb", MF_AVB},
+ {"noemulatedsd", MF_NOEMULATEDSD},
+ {"notrim", MF_NOTRIM},
+ {"formattable", MF_FORMATTABLE},
+ {"slotselect", MF_SLOTSELECT},
+ {"nofail", MF_NOFAIL},
+ {"latemount", MF_LATEMOUNT},
+ {"reservedsize=", MF_RESERVEDSIZE},
+ {"quota", MF_QUOTA},
+ {"eraseblk=", MF_ERASEBLKSIZE},
+ {"logicalblk=", MF_LOGICALBLKSIZE},
+ {"defaults", 0},
+ {0, 0},
};
#define EM_AES_256_XTS 1
@@ -266,6 +268,11 @@
} else {
flag_vals->file_names_mode = EM_AES_256_CTS;
}
+ } else if ((fl[i].flag == MF_KEYDIRECTORY) && flag_vals) {
+ /* The metadata flag is followed by an = and the
+ * directory for the keys. Get it and return it.
+ */
+ flag_vals->key_dir = strdup(strchr(p, '=') + 1);
} else if ((fl[i].flag == MF_LENGTH) && flag_vals) {
/* The length flag is followed by an = and the
* size of the partition. Get it and return it.
@@ -397,6 +404,15 @@
std::vector<std::string> fstab_entry;
std::string file_name;
std::string value;
+ // skip a partition entry if the status property is present and not set to ok
+ file_name = android::base::StringPrintf("%s/%s/status", fstabdir_name.c_str(), dp->d_name);
+ if (read_dt_file(file_name, &value)) {
+ if (value != "okay" && value != "ok") {
+ LINFO << "dt_fstab: Skip disabled entry for partition " << dp->d_name;
+ continue;
+ }
+ }
+
file_name = android::base::StringPrintf("%s/%s/dev", fstabdir_name.c_str(), dp->d_name);
if (!read_dt_file(file_name, &value)) {
LERROR << "dt_fstab: Failed to find device for partition " << dp->d_name;
@@ -557,6 +573,7 @@
fstab->recs[cnt].fs_mgr_flags = parse_flags(p, fs_mgr_flags,
&flag_vals, NULL, 0);
fstab->recs[cnt].key_loc = flag_vals.key_loc;
+ fstab->recs[cnt].key_dir = flag_vals.key_dir;
fstab->recs[cnt].verity_loc = flag_vals.verity_loc;
fstab->recs[cnt].length = flag_vals.part_length;
fstab->recs[cnt].label = flag_vals.label;
@@ -572,7 +589,7 @@
cnt++;
}
/* If an A/B partition, modify block device to be the real block device */
- if (fs_mgr_update_for_slotselect(fstab) != 0) {
+ if (!fs_mgr_update_for_slotselect(fstab)) {
LERROR << "Error updating for slotselect";
goto err;
}
@@ -716,6 +733,7 @@
free(fstab->recs[i].fs_type);
free(fstab->recs[i].fs_options);
free(fstab->recs[i].key_loc);
+ free(fstab->recs[i].key_dir);
free(fstab->recs[i].label);
}
@@ -814,6 +832,11 @@
return fstab->fs_mgr_flags & MF_VERIFY;
}
+int fs_mgr_is_avb(const struct fstab_rec *fstab)
+{
+ return fstab->fs_mgr_flags & MF_AVB;
+}
+
int fs_mgr_is_verifyatboot(const struct fstab_rec *fstab)
{
return fstab->fs_mgr_flags & MF_VERIFYATBOOT;
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index 377d2ec..3ca507b 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -41,8 +41,6 @@
#define PWARNING PLOG(WARNING) << FS_MGR_TAG
#define PERROR PLOG(ERROR) << FS_MGR_TAG
-__BEGIN_DECLS
-
#define CRYPTO_TMPFS_OPTIONS "size=256m,mode=0771,uid=1000,gid=1000"
#define WAIT_TIMEOUT 20
@@ -109,15 +107,15 @@
#define MF_ERASEBLKSIZE 0x800000
#define MF_LOGICALBLKSIZE 0X1000000
#define MF_AVB 0X2000000
+#define MF_KEYDIRECTORY 0X4000000
#define DM_BUF_SIZE 4096
int fs_mgr_set_blk_ro(const char *blockdev);
int fs_mgr_test_access(const char *device);
-int fs_mgr_update_for_slotselect(struct fstab *fstab);
+bool fs_mgr_update_for_slotselect(struct fstab *fstab);
bool is_dt_compatible();
bool is_device_secure();
-
-__END_DECLS
+int load_verity_state(struct fstab_rec* fstab, int* mode);
#endif /* __CORE_FS_MGR_PRIV_H */
diff --git a/fs_mgr/fs_mgr_priv_avb.h b/fs_mgr/fs_mgr_priv_avb.h
deleted file mode 100644
index dce9f61..0000000
--- a/fs_mgr/fs_mgr_priv_avb.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * 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 __CORE_FS_MGR_PRIV_AVB_H
-#define __CORE_FS_MGR_PRIV_AVB_H
-
-#ifndef __cplusplus
-#include <stdbool.h>
-#endif
-
-#include "fs_mgr.h"
-
-__BEGIN_DECLS
-
-#define FS_MGR_SETUP_AVB_HASHTREE_DISABLED (-2)
-#define FS_MGR_SETUP_AVB_FAIL (-1)
-#define FS_MGR_SETUP_AVB_SUCCESS 0
-
-bool fs_mgr_is_avb_used();
-
-/* Gets AVB metadata through external/avb/libavb for all partitions:
- * AvbSlotVerifyData.vbmeta_images[] and checks their integrity
- * against the androidboot.vbmeta.{hash_alg, size, digest} values
- * from /proc/cmdline.
- *
- * Return values:
- * - FS_MGR_SETUP_AVB_SUCCESS: the metadata cab be trusted.
- * - FS_MGR_SETUP_AVB_FAIL: any error when reading and verifying the
- * metadata, e.g. I/O error, digest value mismatch, size mismatch.
- * - FS_MGR_SETUP_AVB_HASHTREE_DISABLED: to support the existing
- * 'adb disable-verity' feature in Android. It's very helpful for
- * developers to make the filesystem writable to allow replacing
- * binaries on the device.
- */
-int fs_mgr_load_vbmeta_images(struct fstab* fstab);
-
-void fs_mgr_unload_vbmeta_images();
-
-int fs_mgr_setup_avb(struct fstab_rec* fstab_entry);
-
-__END_DECLS
-
-#endif /* __CORE_FS_MGR_PRIV_AVB_H */
diff --git a/fs_mgr/fs_mgr_priv_avb_ops.h b/fs_mgr/fs_mgr_priv_avb_ops.h
new file mode 100644
index 0000000..d1ef2e9
--- /dev/null
+++ b/fs_mgr/fs_mgr_priv_avb_ops.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef __CORE_FS_MGR_PRIV_AVB_OPS_H
+#define __CORE_FS_MGR_PRIV_AVB_OPS_H
+
+#include <map>
+#include <string>
+
+#include <libavb/libavb.h>
+
+#include "fs_mgr.h"
+
+// This class provides C++ bindings to interact with libavb, a small
+// self-contained piece of code that's intended to be used in bootloaders.
+// It mainly contains two functions:
+// - ReadFromPartition(): to read AVB metadata from a given partition.
+// It provides the implementation of AvbOps.read_from_partition() when
+// reading metadata through libavb.
+// - AvbSlotVerify(): the C++ binding of libavb->avb_slot_verify() to
+// read and verify the metadata and store it into the out_data parameter.
+// The caller MUST check the integrity of metadata against the
+// androidboot.vbmeta.{hash_alg, size, digest} values from /proc/cmdline.
+// e.g., see class FsManagerAvbVerifier for more details.
+//
+class FsManagerAvbOps {
+ public:
+ FsManagerAvbOps(const fstab& fstab);
+ FsManagerAvbOps(std::map<std::string, std::string>&& by_name_symlink_map);
+
+ static FsManagerAvbOps* GetInstanceFromAvbOps(AvbOps* ops) {
+ return reinterpret_cast<FsManagerAvbOps*>(ops->user_data);
+ }
+
+ AvbIOResult ReadFromPartition(const char* partition, int64_t offset, size_t num_bytes,
+ void* buffer, size_t* out_num_read);
+
+ AvbSlotVerifyResult AvbSlotVerify(const std::string& ab_suffix, AvbSlotVerifyFlags flags,
+ AvbSlotVerifyData** out_data);
+
+ private:
+ void InitializeAvbOps();
+
+ AvbOps avb_ops_;
+ std::map<std::string, std::string> by_name_symlink_map_;
+};
+#endif /* __CORE_FS_MGR_PRIV_AVB_OPS_H */
diff --git a/fs_mgr/fs_mgr_priv_sha.h b/fs_mgr/fs_mgr_priv_sha.h
index 882411b..5b53eea 100644
--- a/fs_mgr/fs_mgr_priv_sha.h
+++ b/fs_mgr/fs_mgr_priv_sha.h
@@ -20,16 +20,18 @@
#include <openssl/sha.h>
class SHA256Hasher {
- private:
+ private:
SHA256_CTX sha256_ctx;
uint8_t hash[SHA256_DIGEST_LENGTH];
- public:
+ public:
enum { DIGEST_SIZE = SHA256_DIGEST_LENGTH };
SHA256Hasher() { SHA256_Init(&sha256_ctx); }
- void update(const void* data, size_t data_size) { SHA256_Update(&sha256_ctx, data, data_size); }
+ void update(const uint8_t* data, size_t data_size) {
+ SHA256_Update(&sha256_ctx, data, data_size);
+ }
const uint8_t* finalize() {
SHA256_Final(hash, &sha256_ctx);
@@ -38,11 +40,11 @@
};
class SHA512Hasher {
- private:
+ private:
SHA512_CTX sha512_ctx;
uint8_t hash[SHA512_DIGEST_LENGTH];
- public:
+ public:
enum { DIGEST_SIZE = SHA512_DIGEST_LENGTH };
SHA512Hasher() { SHA512_Init(&sha512_ctx); }
diff --git a/fs_mgr/fs_mgr_slotselect.cpp b/fs_mgr/fs_mgr_slotselect.cpp
index 7a45473..9ca15e2 100644
--- a/fs_mgr/fs_mgr_slotselect.cpp
+++ b/fs_mgr/fs_mgr_slotselect.cpp
@@ -16,37 +16,47 @@
#include <stdio.h>
+#include <string>
+
#include "fs_mgr.h"
#include "fs_mgr_priv.h"
-// Updates |fstab| for slot_suffix. Returns 0 on success, -1 on error.
-int fs_mgr_update_for_slotselect(struct fstab *fstab)
-{
+// Returns "_a" or "_b" based on two possible values in kernel cmdline:
+// - androidboot.slot = a or b OR
+// - androidboot.slot_suffix = _a or _b
+// TODO: remove slot_suffix once it's deprecated.
+std::string fs_mgr_get_slot_suffix() {
+ std::string slot;
+ std::string ab_suffix;
+
+ if (fs_mgr_get_boot_config("slot", &slot)) {
+ ab_suffix = "_" + slot;
+ } else if (!fs_mgr_get_boot_config("slot_suffix", &ab_suffix)) {
+ ab_suffix = "";
+ }
+ return ab_suffix;
+}
+
+// Updates |fstab| for slot_suffix. Returns true on success, false on error.
+bool fs_mgr_update_for_slotselect(struct fstab *fstab) {
int n;
- int got_suffix = 0;
- std::string suffix;
+ std::string ab_suffix;
for (n = 0; n < fstab->num_entries; n++) {
if (fstab->recs[n].fs_mgr_flags & MF_SLOTSELECT) {
char *tmp;
-
- if (!got_suffix) {
- std::string slot;
- if (fs_mgr_get_boot_config("slot", &slot)) {
- suffix = "_" + slot;
- } else if (!fs_mgr_get_boot_config("slot_suffix", &suffix)) {
- // remove slot_suffix once bootloaders update to new androidboot.slot param
- return -1;
- }
+ if (ab_suffix.empty()) {
+ ab_suffix = fs_mgr_get_slot_suffix();
+ // Returns false as non A/B devices should not have MF_SLOTSELECT.
+ if (ab_suffix.empty()) return false;
}
-
- if (asprintf(&tmp, "%s%s", fstab->recs[n].blk_device, suffix.c_str()) > 0) {
+ if (asprintf(&tmp, "%s%s", fstab->recs[n].blk_device, ab_suffix.c_str()) > 0) {
free(fstab->recs[n].blk_device);
fstab->recs[n].blk_device = tmp;
} else {
- return -1;
+ return false;
}
}
}
- return 0;
+ return true;
}
diff --git a/fs_mgr/fs_mgr_verity.cpp b/fs_mgr/fs_mgr_verity.cpp
index 8c7a8ca..5fa10bc 100644
--- a/fs_mgr/fs_mgr_verity.cpp
+++ b/fs_mgr/fs_mgr_verity.cpp
@@ -653,8 +653,7 @@
offset);
}
-static int load_verity_state(struct fstab_rec *fstab, int *mode)
-{
+int load_verity_state(struct fstab_rec* fstab, int* mode) {
int match = 0;
off64_t offset = 0;
@@ -664,7 +663,7 @@
/* use the kernel parameter if set */
std::string veritymode;
if (fs_mgr_get_boot_config("veritymode", &veritymode)) {
- if (veritymode.compare("enforcing")) {
+ if (veritymode == "enforcing") {
*mode = VERITY_MODE_DEFAULT;
}
return 0;
@@ -690,129 +689,6 @@
return read_verity_state(fstab->verity_loc, offset, mode);
}
-int fs_mgr_load_verity_state(int *mode)
-{
- int rc = -1;
- int i;
- int current;
- struct fstab *fstab = NULL;
-
- /* return the default mode, unless any of the verified partitions are in
- * logging mode, in which case return that */
- *mode = VERITY_MODE_DEFAULT;
-
- fstab = fs_mgr_read_fstab_default();
- if (!fstab) {
- LERROR << "Failed to read default fstab";
- goto out;
- }
-
- for (i = 0; i < fstab->num_entries; i++) {
- if (!fs_mgr_is_verified(&fstab->recs[i])) {
- continue;
- }
-
- rc = load_verity_state(&fstab->recs[i], ¤t);
- if (rc < 0) {
- continue;
- }
-
- if (current != VERITY_MODE_DEFAULT) {
- *mode = current;
- break;
- }
- }
-
- rc = 0;
-
-out:
- if (fstab) {
- fs_mgr_free_fstab(fstab);
- }
-
- return rc;
-}
-
-int fs_mgr_update_verity_state(fs_mgr_verity_state_callback callback)
-{
- alignas(dm_ioctl) char buffer[DM_BUF_SIZE];
- bool system_root = false;
- std::string mount_point;
- char propbuf[PROPERTY_VALUE_MAX];
- const char *status;
- int fd = -1;
- int i;
- int mode;
- int rc = -1;
- struct dm_ioctl *io = (struct dm_ioctl *) buffer;
- struct fstab *fstab = NULL;
-
- if (!callback) {
- return -1;
- }
-
- if (fs_mgr_load_verity_state(&mode) == -1) {
- return -1;
- }
-
- fd = TEMP_FAILURE_RETRY(open("/dev/device-mapper", O_RDWR | O_CLOEXEC));
- if (fd == -1) {
- PERROR << "Error opening device mapper";
- goto out;
- }
-
- property_get("ro.build.system_root_image", propbuf, "");
- system_root = !strcmp(propbuf, "true");
- fstab = fs_mgr_read_fstab_default();
- if (!fstab) {
- LERROR << "Failed to read default fstab";
- goto out;
- }
-
- for (i = 0; i < fstab->num_entries; i++) {
- if (!fs_mgr_is_verified(&fstab->recs[i])) {
- continue;
- }
-
- if (system_root && !strcmp(fstab->recs[i].mount_point, "/")) {
- mount_point = "system";
- } else {
- mount_point = basename(fstab->recs[i].mount_point);
- }
-
- fs_mgr_verity_ioctl_init(io, mount_point, 0);
-
- if (ioctl(fd, DM_TABLE_STATUS, io)) {
- if (fstab->recs[i].fs_mgr_flags & MF_VERIFYATBOOT) {
- status = "V";
- } else {
- PERROR << "Failed to query DM_TABLE_STATUS for "
- << mount_point.c_str();
- continue;
- }
- }
-
- status = &buffer[io->data_start + sizeof(struct dm_target_spec)];
-
- if (*status == 'C' || *status == 'V') {
- callback(&fstab->recs[i], mount_point.c_str(), mode, *status);
- }
- }
-
- rc = 0;
-
-out:
- if (fstab) {
- fs_mgr_free_fstab(fstab);
- }
-
- if (fd) {
- close(fd);
- }
-
- return rc;
-}
-
static void update_verity_table_blk_device(char *blk_device, char **table)
{
std::string result, word;
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index 2fd5f65..02a22db 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -22,6 +22,12 @@
#include <stdbool.h>
#include <linux/dm-ioctl.h>
+// C++ only headers
+// TODO: move this into separate header files under include/fs_mgr/*.h
+#ifdef __cplusplus
+#include <string>
+#endif
+
// Magic number at start of verity metadata
#define VERITY_METADATA_MAGIC_NUMBER 0xb001b001
@@ -29,9 +35,7 @@
// turn verity off in userdebug builds.
#define VERITY_METADATA_MAGIC_DISABLE 0x46464f56 // "VOFF"
-#ifdef __cplusplus
-extern "C" {
-#endif
+__BEGIN_DECLS
// Verity modes
enum verity_mode {
@@ -68,6 +72,7 @@
char *fs_options;
int fs_mgr_flags;
char *key_loc;
+ char* key_dir;
char *verity_loc;
long long length;
char *label;
@@ -91,6 +96,8 @@
struct fstab *fs_mgr_read_fstab(const char *fstab_path);
void fs_mgr_free_fstab(struct fstab *fstab);
+#define FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED 7
+#define FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION 6
#define FS_MGR_MNTALL_DEV_FILE_ENCRYPTED 5
#define FS_MGR_MNTALL_DEV_NEEDS_RECOVERY 4
#define FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION 3
@@ -102,16 +109,17 @@
#define FS_MGR_DOMNT_FAILED (-1)
#define FS_MGR_DOMNT_BUSY (-2)
+#define FS_MGR_DOMNT_SUCCESS 0
int fs_mgr_do_mount(struct fstab *fstab, const char *n_name, char *n_blk_device,
char *tmp_mount_point);
int fs_mgr_do_mount_one(struct fstab_rec *rec);
int fs_mgr_do_tmpfs_mount(const char *n_name);
int fs_mgr_unmount_all(struct fstab *fstab);
-int fs_mgr_get_crypt_info(struct fstab *fstab, char *key_loc,
- char *real_blk_device, int size);
-int fs_mgr_load_verity_state(int *mode);
-int fs_mgr_update_verity_state(fs_mgr_verity_state_callback callback);
+struct fstab_rec const* fs_mgr_get_crypt_entry(struct fstab const* fstab);
+void fs_mgr_get_crypt_info(struct fstab* fstab, char* key_loc, char* real_blk_device, size_t size);
+bool fs_mgr_load_verity_state(int* mode);
+bool fs_mgr_update_verity_state(fs_mgr_verity_state_callback callback);
int fs_mgr_add_entry(struct fstab *fstab,
const char *mount_point, const char *fs_type,
const char *blk_device);
@@ -120,6 +128,7 @@
int fs_mgr_is_nonremovable(const struct fstab_rec *fstab);
int fs_mgr_is_verified(const struct fstab_rec *fstab);
int fs_mgr_is_verifyatboot(const struct fstab_rec *fstab);
+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);
void fs_mgr_get_file_encryption_modes(const struct fstab_rec *fstab,
@@ -142,8 +151,12 @@
#define FS_MGR_SETUP_VERITY_SUCCESS 0
int fs_mgr_setup_verity(struct fstab_rec *fstab, bool wait_for_verity_dev);
+__END_DECLS
+
+// C++ only functions
+// TODO: move this into separate header files under include/fs_mgr/*.h
#ifdef __cplusplus
-}
+std::string fs_mgr_get_slot_suffix();
#endif
#endif /* __CORE_FS_MGR_H */
diff --git a/fs_mgr/include/fs_mgr_avb.h b/fs_mgr/include/fs_mgr_avb.h
new file mode 100644
index 0000000..bbafe1a
--- /dev/null
+++ b/fs_mgr/include/fs_mgr_avb.h
@@ -0,0 +1,113 @@
+/*
+ * 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 __CORE_FS_MGR_AVB_H
+#define __CORE_FS_MGR_AVB_H
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include <libavb/libavb.h>
+
+#include "fs_mgr.h"
+
+enum FsManagerAvbHandleStatus {
+ kFsManagerAvbHandleUninitialized = -1,
+ kFsManagerAvbHandleSuccess = 0,
+ kFsManagerAvbHandleHashtreeDisabled = 1,
+ kFsManagerAvbHandleErrorVerification = 2,
+};
+
+class FsManagerAvbOps;
+
+class FsManagerAvbHandle;
+using FsManagerAvbUniquePtr = std::unique_ptr<FsManagerAvbHandle>;
+
+using ByNameSymlinkMap = std::map<std::string, std::string>;
+
+// Provides a factory method to return a unique_ptr pointing to itself and the
+// SetUpAvb() function to extract dm-verity parameters from AVB metadata to
+// load verity table into kernel through ioctl.
+class FsManagerAvbHandle {
+ public:
+ // The factory method to return a FsManagerAvbUniquePtr that holds
+ // the verified AVB (external/avb) metadata of all verified partitions
+ // in avb_slot_data_.vbmeta_images[].
+ //
+ // The metadata is checked against the following values from /proc/cmdline.
+ // - androidboot.vbmeta.{hash_alg, size, digest}.
+ //
+ // A typical usage will be:
+ // - FsManagerAvbUniquePtr handle = FsManagerAvbHandle::Open();
+ //
+ // There are two overloaded Open() functions with a single parameter.
+ // The argument can be a ByNameSymlinkMap describing the mapping from partition
+ // name to by-name symlink, or a fstab file to which the ByNameSymlinkMap is
+ // constructed from. e.g.,
+ // - /dev/block/platform/soc.0/7824900.sdhci/by-name/system_a ->
+ // - ByNameSymlinkMap["system_a"] = "/dev/block/platform/soc.0/7824900.sdhci/by-name/system_a"
+ //
+ // Possible return values:
+ // - nullptr: any error when reading and verifying the metadata,
+ // e.g., I/O error, digest value mismatch, size mismatch, etc.
+ //
+ // - a valid unique_ptr with status kFsMgrAvbHandleHashtreeDisabled:
+ // to support the existing 'adb disable-verity' feature in Android.
+ // It's very helpful for developers to make the filesystem writable to
+ // allow replacing binaries on the device.
+ //
+ // - a valid unique_ptr with status kFsMgrAvbHandleSuccess: the metadata
+ // is verified and can be trusted.
+ //
+ static FsManagerAvbUniquePtr Open(const fstab& fstab);
+ static FsManagerAvbUniquePtr Open(ByNameSymlinkMap&& by_name_symlink_map);
+
+ // Sets up dm-verity on the given fstab entry.
+ // The 'wait_for_verity_dev' parameter makes this function wait for the
+ // verity device to get created before return.
+ // Returns true if the mount point is eligible to mount, it includes:
+ // - status_ is kFsMgrAvbHandleHashtreeDisabled or
+ // - status_ is kFsMgrAvbHandleSuccess and sending ioctl DM_TABLE_LOAD
+ // to load verity table is success.
+ // Otherwise, returns false.
+ bool SetUpAvb(fstab_rec* fstab_entry, bool wait_for_verity_dev);
+
+ bool hashtree_disabled() const { return status_ == kFsManagerAvbHandleHashtreeDisabled; }
+ const std::string& avb_version() const { return avb_version_; }
+
+ FsManagerAvbHandle(const FsManagerAvbHandle&) = delete; // no copy
+ FsManagerAvbHandle& operator=(const FsManagerAvbHandle&) = delete; // no assignment
+
+ FsManagerAvbHandle(FsManagerAvbHandle&&) noexcept = delete; // no move
+ FsManagerAvbHandle& operator=(FsManagerAvbHandle&&) noexcept = delete; // no move assignment
+
+ ~FsManagerAvbHandle() {
+ if (avb_slot_data_) {
+ avb_slot_verify_data_free(avb_slot_data_);
+ }
+ };
+
+ private:
+ FsManagerAvbHandle() : avb_slot_data_(nullptr), status_(kFsManagerAvbHandleUninitialized) {}
+ static FsManagerAvbUniquePtr DoOpen(FsManagerAvbOps* avb_ops);
+
+ AvbSlotVerifyData* avb_slot_data_;
+ FsManagerAvbHandleStatus status_;
+ std::string avb_version_;
+};
+
+#endif /* __CORE_FS_MGR_AVB_H */
diff --git a/include/backtrace b/include/backtrace
new file mode 120000
index 0000000..93ce2b1
--- /dev/null
+++ b/include/backtrace
@@ -0,0 +1 @@
+../libbacktrace/include/backtrace
\ No newline at end of file
diff --git a/include/nativebridge/native_bridge.h b/include/nativebridge/native_bridge.h
index 45266de..9bfc935 100644
--- a/include/nativebridge/native_bridge.h
+++ b/include/nativebridge/native_bridge.h
@@ -116,14 +116,25 @@
// Use NativeBridgeIsSupported() instead in non-namespace scenario.
bool NativeBridgeIsPathSupported(const char* path);
-// Initializes public and anonymous namespace at native bridge side.
+// Initializes anonymous namespace.
+// NativeBridge's peer of android_init_anonymous_namespace() of dynamic linker.
+//
+// The anonymous namespace is used in the case when a NativeBridge implementation
+// cannot identify the caller of dlopen/dlsym which happens for the code not loaded
+// by dynamic linker; for example calls from the mono-compiled code.
//
// Starting with v3, NativeBridge has two scenarios: with/without namespace.
// Should not use in non-namespace scenario.
-bool NativeBridgeInitNamespace(const char* public_ns_sonames,
- const char* anon_ns_library_path);
+bool NativeBridgeInitAnonymousNamespace(const char* public_ns_sonames,
+ const char* anon_ns_library_path);
-// Create a namespace and pass the key of related namespaces to native bridge.
+// Create new namespace in which native libraries will be loaded.
+// NativeBridge's peer of android_create_namespace() of dynamic linker.
+//
+// The libraries in the namespace are searched by folowing order:
+// 1. ld_library_path (Think of this as namespace-local LD_LIBRARY_PATH)
+// 2. In directories specified by DT_RUNPATH of the "needed by" binary.
+// 3. deault_library_path (This of this as namespace-local default library path)
//
// Starting with v3, NativeBridge has two scenarios: with/without namespace.
// Should not use in non-namespace scenario.
@@ -134,12 +145,25 @@
const char* permitted_when_isolated_path,
native_bridge_namespace_t* parent_ns);
+// Creates a link which shares some libraries from one namespace to another.
+// NativeBridge's peer of android_link_namespaces() of dynamic linker.
+//
+// Starting with v3, NativeBridge has two scenarios: with/without namespace.
+// Should not use in non-namespace scenario.
+bool NativeBridgeLinkNamespaces(native_bridge_namespace_t* from, native_bridge_namespace_t* to,
+ const char* shared_libs_sonames);
+
// Load a shared library with namespace key that is supported by the native bridge.
+// NativeBridge's peer of android_dlopen_ext() of dynamic linker, only supports namespace
+// extension.
//
// Starting with v3, NativeBridge has two scenarios: with/without namespace.
// Use NativeBridgeLoadLibrary() instead in non-namespace scenario.
void* NativeBridgeLoadLibraryExt(const char* libpath, int flag, native_bridge_namespace_t* ns);
+// Returns vendor namespace if it is enabled for the device and null otherwise
+native_bridge_namespace_t* NativeBridgeGetVendorNamespace();
+
// Native bridge interfaces to runtime.
struct NativeBridgeCallbacks {
// Version number of the interface.
@@ -152,7 +176,7 @@
// Parameters:
// runtime_cbs [IN] the pointer to NativeBridgeRuntimeCallbacks.
// Returns:
- // true iff initialization was successful.
+ // true if initialization was successful.
bool (*initialize)(const NativeBridgeRuntimeCallbacks* runtime_cbs, const char* private_dir,
const char* instruction_set);
@@ -194,10 +218,10 @@
// instruction set.
//
// Parameters:
- // instruction_set [IN] the instruction set of the app
+ // instruction_set [IN] the instruction set of the app
// Returns:
- // NULL if not supported by native bridge.
- // Otherwise, return all environment values to be set after fork.
+ // NULL if not supported by native bridge.
+ // Otherwise, return all environment values to be set after fork.
const struct NativeBridgeRuntimeValues* (*getAppEnv)(const char* instruction_set);
// Added callbacks in version 2.
@@ -206,9 +230,9 @@
// forwards- or backwards-compatible, and libnativebridge will then stop using it.
//
// Parameters:
- // bridge_version [IN] the version of libnativebridge.
+ // bridge_version [IN] the version of libnativebridge.
// Returns:
- // true iff the native bridge supports the given version of libnativebridge.
+ // true if the native bridge supports the given version of libnativebridge.
bool (*isCompatibleWith)(uint32_t bridge_version);
// A callback to retrieve a native bridge's signal handler for the specified signal. The runtime
@@ -217,12 +241,12 @@
// that will potentially lead to cycles.
//
// Parameters:
- // signal [IN] the signal for which the handler is asked for. Currently, only SIGSEGV is
+ // signal [IN] the signal for which the handler is asked for. Currently, only SIGSEGV is
// supported by the runtime.
// Returns:
- // NULL if the native bridge doesn't use a handler or doesn't want it to be managed by the
- // runtime.
- // Otherwise, a pointer to the signal handler.
+ // NULL if the native bridge doesn't use a handler or doesn't want it to be managed by the
+ // runtime.
+ // Otherwise, a pointer to the signal handler.
NativeBridgeSignalHandlerFn (*getSignalHandler)(int signal);
// Added callbacks in version 3.
@@ -231,7 +255,7 @@
// to zero then the dynamic library is unloaded.
//
// Parameters:
- // handle [IN] the handler of a dynamic library.
+ // handle [IN] the handler of a dynamic library.
//
// Returns:
// 0 on success, and nonzero on error.
@@ -257,33 +281,36 @@
// Use isSupported instead in non-namespace scenario.
bool (*isPathSupported)(const char* library_path);
- // Initializes anonymous namespace at native bridge side and pass the key of
- // two namespaces(default and anonymous) owned by dynamic linker to native bridge.
+ // Initializes anonymous namespace at native bridge side.
+ // NativeBridge's peer of android_init_anonymous_namespace() of dynamic linker.
+ //
+ // The anonymous namespace is used in the case when a NativeBridge implementation
+ // cannot identify the caller of dlopen/dlsym which happens for the code not loaded
+ // by dynamic linker; for example calls from the mono-compiled code.
//
// Parameters:
- // public_ns_sonames [IN] the name of "public" libraries.
- // anon_ns_library_path [IN] the library search path of (anonymous) namespace.
+ // public_ns_sonames [IN] the name of "public" libraries.
+ // anon_ns_library_path [IN] the library search path of (anonymous) namespace.
// Returns:
- // true if the pass is ok.
- // Otherwise, false.
+ // true if the pass is ok.
+ // Otherwise, false.
//
// Starting with v3, NativeBridge has two scenarios: with/without namespace.
// Should not use in non-namespace scenario.
- bool (*initNamespace)(const char* public_ns_sonames,
- const char* anon_ns_library_path);
+ bool (*initAnonymousNamespace)(const char* public_ns_sonames, const char* anon_ns_library_path);
-
- // Create a namespace and pass the key of releated namespaces to native bridge.
+ // Create new namespace in which native libraries will be loaded.
+ // NativeBridge's peer of android_create_namespace() of dynamic linker.
//
// Parameters:
- // name [IN] the name of the namespace.
- // ld_library_path [IN] the first set of library search paths of the namespace.
- // default_library_path [IN] the second set of library search path of the namespace.
- // type [IN] the attribute of the namespace.
- // permitted_when_isolated_path [IN] the permitted path for isolated namespace(if it is).
- // parent_ns [IN] the pointer of the parent namespace to be inherited from.
+ // name [IN] the name of the namespace.
+ // ld_library_path [IN] the first set of library search paths of the namespace.
+ // default_library_path [IN] the second set of library search path of the namespace.
+ // type [IN] the attribute of the namespace.
+ // permitted_when_isolated_path [IN] the permitted path for isolated namespace(if it is).
+ // parent_ns [IN] the pointer of the parent namespace to be inherited from.
// Returns:
- // native_bridge_namespace_t* for created namespace or nullptr in the case of error.
+ // native_bridge_namespace_t* for created namespace or nullptr in the case of error.
//
// Starting with v3, NativeBridge has two scenarios: with/without namespace.
// Should not use in non-namespace scenario.
@@ -294,7 +321,25 @@
const char* permitted_when_isolated_path,
native_bridge_namespace_t* parent_ns);
+ // Creates a link which shares some libraries from one namespace to another.
+ // NativeBridge's peer of android_link_namespaces() of dynamic linker.
+ //
+ // Parameters:
+ // from [IN] the namespace where libraries are accessed.
+ // to [IN] the namespace where libraries are loaded.
+ // shared_libs_sonames [IN] the libraries to be shared.
+ //
+ // Returns:
+ // Whether successed or not.
+ //
+ // Starting with v3, NativeBridge has two scenarios: with/without namespace.
+ // Should not use in non-namespace scenario.
+ bool (*linkNamespaces)(native_bridge_namespace_t* from, native_bridge_namespace_t* to,
+ const char* shared_libs_sonames);
+
// Load a shared library within a namespace.
+ // NativeBridge's peer of android_dlopen_ext() of dynamic linker, only supports namespace
+ // extension.
//
// Parameters:
// libpath [IN] path to the shared library
@@ -306,6 +351,15 @@
// Starting with v3, NativeBridge has two scenarios: with/without namespace.
// Use loadLibrary instead in non-namespace scenario.
void* (*loadLibraryExt)(const char* libpath, int flag, native_bridge_namespace_t* ns);
+
+ // Get native bridge version of vendor namespace.
+ // The vendor namespace is the namespace used to load vendor public libraries.
+ // With O release this namespace can be different from the default namespace.
+ // For the devices without enable vendor namespaces this function should return null
+ //
+ // Returns:
+ // vendor namespace or null if it was not set up for the device
+ native_bridge_namespace_t* (*getVendorNamespace)();
};
// Runtime interfaces to native bridge.
diff --git a/include/system b/include/system
new file mode 120000
index 0000000..91d45be
--- /dev/null
+++ b/include/system
@@ -0,0 +1 @@
+../libsystem/include/system/
\ No newline at end of file
diff --git a/include/system/window-deprecated.h b/include/system/window-deprecated.h
deleted file mode 100644
index d1ef1e7..0000000
--- a/include/system/window-deprecated.h
+++ /dev/null
@@ -1,1092 +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.
- */
-
-/**************************************************************************************************
- **************************************************************************************************
- **** ****
- **** 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 ****
- **** ****
- **************************************************************************************************
- **************************************************************************************************/
-
-#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,
-};
-
-/* 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);
-
-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/include/ziparchive/zip_writer.h b/include/ziparchive/zip_writer.h
index 41ca2e1..08ead48 100644
--- a/include/ziparchive/zip_writer.h
+++ b/include/ziparchive/zip_writer.h
@@ -75,7 +75,8 @@
uint32_t uncompressed_size;
uint16_t last_mod_time;
uint16_t last_mod_date;
- uint32_t local_file_header_offset;
+ uint32_t padding_length;
+ off64_t local_file_header_offset;
};
static const char* ErrorCodeString(int32_t error_code);
@@ -172,6 +173,7 @@
};
FILE* file_;
+ bool seekable_;
off64_t current_offset_;
State state_;
std::vector<FileEntry> files_;
diff --git a/init/Android.bp b/init/Android.bp
new file mode 100644
index 0000000..af1e9d3
--- /dev/null
+++ b/init/Android.bp
@@ -0,0 +1,167 @@
+//
+// 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_defaults {
+ name: "init_defaults",
+ cpp_std: "experimental",
+ sanitize: {
+ misc_undefined: ["integer"],
+ },
+ tidy_checks: ["-misc-forwarding-reference-overload"],
+ cppflags: [
+ "-DLOG_UEVENTS=0",
+ "-Wall",
+ "-Wextra",
+ "-Wno-unused-parameter",
+ "-Werror",
+ "-DALLOW_LOCAL_PROP_OVERRIDE=0",
+ "-DALLOW_PERMISSIVE_SELINUX=0",
+ "-DREBOOT_BOOTLOADER_ON_PANIC=0",
+ "-DWORLD_WRITABLE_KMSG=0",
+ "-DDUMP_ON_UMOUNT_FAILURE=0",
+ "-DSHUTDOWN_ZERO_TIMEOUT=0",
+ ],
+ product_variables: {
+ debuggable: {
+ cppflags: [
+ "-UALLOW_LOCAL_PROP_OVERRIDE",
+ "-DALLOW_LOCAL_PROP_OVERRIDE=1",
+ "-UALLOW_PERMISSIVE_SELINUX",
+ "-DALLOW_PERMISSIVE_SELINUX=1",
+ "-UREBOOT_BOOTLOADER_ON_PANIC",
+ "-DREBOOT_BOOTLOADER_ON_PANIC=1",
+ "-UWORLD_WRITABLE_KMSG",
+ "-DWORLD_WRITABLE_KMSG=1",
+ "-UDUMP_ON_UMOUNT_FAILURE",
+ "-DDUMP_ON_UMOUNT_FAILURE=1",
+ ],
+ },
+ eng: {
+ cppflags: [
+ "-USHUTDOWN_ZERO_TIMEOUT",
+ "-DSHUTDOWN_ZERO_TIMEOUT=1",
+ ],
+ },
+ },
+}
+
+cc_library_static {
+ name: "libinit",
+ defaults: ["init_defaults"],
+ srcs: [
+ "action.cpp",
+ "capabilities.cpp",
+ "descriptors.cpp",
+ "devices.cpp",
+ "firmware_handler.cpp",
+ "import_parser.cpp",
+ "init_parser.cpp",
+ "log.cpp",
+ "parser.cpp",
+ "service.cpp",
+ "uevent_listener.cpp",
+ "ueventd_parser.cpp",
+ "util.cpp",
+ ],
+ whole_static_libs: ["libcap"],
+ static_libs: [
+ "libbase",
+ "libselinux",
+ "liblog",
+ "libprocessgroup",
+ ],
+}
+
+/*
+This is not yet ready, see the below TODOs for what is missing
+
+cc_binary {
+ // TODO: Missing,
+ //LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
+ //LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED)
+
+ name: "init",
+ defaults: ["init_defaults"],
+ static_executable: true,
+ srcs: [
+ "bootchart.cpp",
+ "builtins.cpp",
+ "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",
+ "libfs_mgr",
+ "libfec",
+ "libfec_rs",
+ "libsquashfs_utils",
+ "liblogwrap",
+ "libext4_utils",
+ "libcutils",
+ "libbase",
+ "libc",
+ "libselinux",
+ "liblog",
+ "libcrypto_utils",
+ "libcrypto",
+ "libc++_static",
+ "libdl",
+ "libsparse",
+ "libz",
+ "libprocessgroup",
+ "libavb",
+ "libkeyutils",
+ ],
+ symlinks: [
+ "sbin/ueventd",
+ "sbin/watchdogd",
+ ],
+}
+*/
+
+// Tests
+// ------------------------------------------------------------------------------
+
+cc_test {
+ name: "init_tests",
+ defaults: ["init_defaults"],
+ srcs: [
+ "devices_test.cpp",
+ "init_parser_test.cpp",
+ "init_test.cpp",
+ "property_service_test.cpp",
+ "service_test.cpp",
+ "util_test.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ "libcutils",
+ "libselinux",
+ ],
+ static_libs: ["libinit"],
+}
+
+subdirs = ["*"]
diff --git a/init/Android.mk b/init/Android.mk
index 584a4ce..489d076 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -8,12 +8,16 @@
init_options += \
-DALLOW_LOCAL_PROP_OVERRIDE=1 \
-DALLOW_PERMISSIVE_SELINUX=1 \
- -DREBOOT_BOOTLOADER_ON_PANIC=1
+ -DREBOOT_BOOTLOADER_ON_PANIC=1 \
+ -DWORLD_WRITABLE_KMSG=1 \
+ -DDUMP_ON_UMOUNT_FAILURE=1
else
init_options += \
-DALLOW_LOCAL_PROP_OVERRIDE=0 \
-DALLOW_PERMISSIVE_SELINUX=0 \
- -DREBOOT_BOOTLOADER_ON_PANIC=0
+ -DREBOOT_BOOTLOADER_ON_PANIC=0 \
+ -DWORLD_WRITABLE_KMSG=0 \
+ -DDUMP_ON_UMOUNT_FAILURE=0
endif
ifneq (,$(filter eng,$(TARGET_BUILD_VARIANT)))
@@ -35,60 +39,20 @@
# --
-# If building on Linux, then build unit test for the host.
-ifeq ($(HOST_OS),linux)
include $(CLEAR_VARS)
-LOCAL_CPPFLAGS := $(init_cflags)
-LOCAL_SRC_FILES:= \
- parser/tokenizer.cpp \
-
-LOCAL_MODULE := libinit_parser
-LOCAL_CLANG := true
-include $(BUILD_HOST_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := init_parser_tests
-LOCAL_SRC_FILES := \
- parser/tokenizer_test.cpp \
-
-LOCAL_STATIC_LIBRARIES := libinit_parser
-LOCAL_CLANG := true
-include $(BUILD_HOST_NATIVE_TEST)
-endif
-
-include $(CLEAR_VARS)
-LOCAL_CPPFLAGS := $(init_cflags)
-LOCAL_SRC_FILES:= \
- action.cpp \
- capabilities.cpp \
- descriptors.cpp \
- import_parser.cpp \
- init_parser.cpp \
- log.cpp \
- parser.cpp \
- service.cpp \
- util.cpp \
-
-LOCAL_STATIC_LIBRARIES := libbase libselinux liblog libprocessgroup libnl
-LOCAL_WHOLE_STATIC_LIBRARIES := libcap
-LOCAL_MODULE := libinit
-LOCAL_SANITIZE := integer
-LOCAL_CLANG := true
-include $(BUILD_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
+# b/38002385, work around clang-tidy segmentation fault.
+LOCAL_TIDY_CHECKS := -misc-forwarding-reference-overload
LOCAL_CPPFLAGS := $(init_cflags)
LOCAL_SRC_FILES:= \
bootchart.cpp \
builtins.cpp \
- devices.cpp \
init.cpp \
+ init_first_stage.cpp \
keychords.cpp \
property_service.cpp \
reboot.cpp \
signal_handler.cpp \
ueventd.cpp \
- ueventd_parser.cpp \
watchdogd.cpp \
LOCAL_MODULE:= init
@@ -107,8 +71,8 @@
libfec_rs \
libsquashfs_utils \
liblogwrap \
- libcutils \
libext4_utils \
+ libcutils \
libbase \
libc \
libselinux \
@@ -120,8 +84,8 @@
libsparse \
libz \
libprocessgroup \
- libnl \
- libavb
+ libavb \
+ libkeyutils \
# Create symlinks.
LOCAL_POST_INSTALL_CMD := $(hide) mkdir -p $(TARGET_ROOT_OUT)/sbin; \
@@ -131,29 +95,3 @@
LOCAL_SANITIZE := integer
LOCAL_CLANG := true
include $(BUILD_EXECUTABLE)
-
-
-# Unit tests.
-# =========================================================
-include $(CLEAR_VARS)
-LOCAL_MODULE := init_tests
-LOCAL_COMPATIBILITY_SUITE := device-tests
-LOCAL_SRC_FILES := \
- init_parser_test.cpp \
- property_service_test.cpp \
- util_test.cpp \
-
-LOCAL_SHARED_LIBRARIES += \
- libcutils \
- libbase \
-
-LOCAL_STATIC_LIBRARIES := libinit
-LOCAL_SANITIZE := integer
-LOCAL_CLANG := true
-LOCAL_CPPFLAGS := -Wall -Wextra -Werror
-include $(BUILD_NATIVE_TEST)
-
-
-# Include targets in subdirs.
-# =========================================================
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/init/README.md b/init/README.md
index fc50730..72b6c6b 100644
--- a/init/README.md
+++ b/init/README.md
@@ -16,9 +16,7 @@
or options belong to the section most recently declared. Commands
or options before the first section are ignored.
-Actions and Services have unique names. If a second Action is defined
-with the same name as an existing one, its commands are appended to
-the commands of the existing action. If a second Service is defined
+Services have unique names. If a second Service is defined
with the same name as an existing one, it is ignored and an error
message is logged.
@@ -31,13 +29,21 @@
/init.rc is the primary .rc file and is loaded by the init executable
at the beginning of its execution. It is responsible for the initial
-set up of the system. It imports /init.${ro.hardware}.rc which is the
-primary vendor supplied .rc file.
+set up of the system.
-During the mount\_all command, the init executable loads all of the
-files contained within the /{system,vendor,odm}/etc/init/ directories.
-These directories are intended for all Actions and Services used after
-file system mounting.
+Devices that mount /system, /vendor through the first stage mount mechanism
+load all of the files contained within the
+/{system,vendor,odm}/etc/init/ directories immediately after loading
+the primary /init.rc. This is explained in more details in the
+Imports section of this file.
+
+Legacy devices without the first stage mount mechanism do the following:
+1. /init.rc imports /init.${ro.hardware}.rc which is the primary
+ vendor supplied .rc file.
+2. During the mount\_all command, the init executable loads all of the
+ files contained within the /{system,vendor,odm}/etc/init/ directories.
+ These directories are intended for all Actions and Services used after
+ file system mounting.
One may specify paths in the mount\_all command line to have it import
.rc files at the specified paths instead of the default ones listed above.
@@ -89,7 +95,7 @@
Actions
-------
Actions are named sequences of commands. Actions have a trigger which
-is used to determine when the action should occur. When an event
+is used to determine when the action is executed. When an event
occurs which matches an action's trigger, that action is added to
the tail of a to-be-executed queue (unless it is already on the
queue).
@@ -106,6 +112,34 @@
<command>
<command>
+Actions are added to the queue and executed based on the order that
+the file that contains them was parsed (see the Imports section), then
+sequentially within an individual file.
+
+For example if a file contains:
+
+ on boot
+ setprop a 1
+ setprop b 2
+
+ on boot && property:true=true
+ setprop c 1
+ setprop d 2
+
+ on boot
+ setprop e 1
+ setprop f 2
+
+Then when the `boot` trigger occurs and assuming the property `true`
+equals `true`, then the order of the commands executed will be:
+
+ setprop a 1
+ setprop b 2
+ setprop c 1
+ setprop d 2
+ setprop e 1
+ setprop f 2
+
Services
--------
@@ -276,7 +310,8 @@
`class_start <serviceclass>`
> Start all services of the specified class if they are
- not already running.
+ not already running. See the start entry for more information on
+ starting services.
`class_stop <serviceclass>`
> Stop and disable all services of the specified class if they are
@@ -321,9 +356,9 @@
Init halts executing commands until the forked process exits.
`exec_start <service>`
-> Start service a given service and halt processing of additional init commands
- until it returns. It functions similarly to the `exec` command, but uses an
- existing service definition instead of providing them as arguments.
+> Start a given service and halt the processing of additional init commands
+ until it returns. The command functions similarly to the `exec` command,
+ but uses an existing service definition in place of the exec argument vector.
`export <name> <value>`
> Set the environment variable _name_ equal to _value_ in the
@@ -370,10 +405,6 @@
_options_ include "barrier=1", "noauto\_da\_alloc", "discard", ... as
a comma separated string, eg: barrier=1,noauto\_da\_alloc
-`powerctl`
-> Internal implementation detail used to respond to changes to the
- "sys.powerctl" system property, used to implement rebooting.
-
`restart <service>`
> Stops and restarts a running service, does nothing if the service is currently
restarting, otherwise, it just starts the service.
@@ -405,6 +436,16 @@
`start <service>`
> Start a service running if it is not already running.
+ Note that this is _not_ synchronous, and even if it were, there is
+ no guarantee that the operating system's scheduler will execute the
+ service sufficiently to guarantee anything about the service's status.
+
+> This creates an important consequence that if the service offers
+ functionality to other services, such as providing a
+ communication channel, simply starting this service before those
+ services is _not_ sufficient to guarantee that the channel has
+ been set up before those services ask for it. There must be a
+ separate mechanism to make any such guarantees.
`stop <service>`
> Stop a service from running if it is currently running.
@@ -451,21 +492,54 @@
Imports
-------
-The import keyword is not a command, but rather its own section and is
-handled immediately after the .rc file that contains it has finished
-being parsed. It takes the below form:
-
`import <path>`
> Parse an init config file, extending the current configuration.
If _path_ is a directory, each file in the directory is parsed as
a config file. It is not recursive, nested directories will
not be parsed.
-There are only two times where the init executable imports .rc files:
+The import keyword is not a command, but rather its own section,
+meaning that it does not happen as part of an Action, but rather,
+imports are handled as a file is being parsed and follow the below logic.
- 1. When it imports /init.rc during initial boot
- 2. When it imports /{system,vendor,odm}/etc/init/ or .rc files at specified
- paths during mount_all
+There are only three times where the init executable imports .rc files:
+
+ 1. When it imports /init.rc or the script indicated by the property
+ `ro.boot.init_rc` during initial boot.
+ 2. When it imports /{system,vendor,odm}/etc/init/ for first stage mount
+ devices immediately after importing /init.rc.
+ 3. When it imports /{system,vendor,odm}/etc/init/ or .rc files at specified
+ paths during mount_all.
+
+The order that files are imported is a bit complex for legacy reasons
+and to keep backwards compatibility. It is not strictly guaranteed.
+
+The only correct way to guarantee that a command has been run before a
+different command is to either 1) place it in an Action with an
+earlier executed trigger, or 2) place it in an Action with the same
+trigger within the same file at an earlier line.
+
+Nonetheless, the defacto order for first stage mount devices is:
+1. /init.rc is parsed then recursively each of its imports are
+ parsed.
+2. The contents of /system/etc/init/ are alphabetized and parsed
+ sequentially, with imports happening recursively after each file is
+ parsed.
+3. Step 2 is repeated for /vendor/etc/init then /odm/etc/init
+
+The below pseudocode may explain this more clearly:
+
+ fn Import(file)
+ Parse(file)
+ for (import : file.imports)
+ Import(import)
+
+ Import(/init.rc)
+ Directories = [/system/etc/init, /vendor/etc/init, /odm/etc/init]
+ for (directory : Directories)
+ files = <Alphabetical order of directory's contents>
+ for (file : files)
+ Import(file)
Properties
diff --git a/init/action.cpp b/init/action.cpp
index 2ccf0bc..21abe02 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -16,25 +16,18 @@
#include "action.h"
-#include <errno.h>
-
+#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
-#include "builtins.h"
-#include "error.h"
-#include "init_parser.h"
-#include "log.h"
#include "util.h"
using android::base::Join;
using android::base::StringPrintf;
-Command::Command(BuiltinFunction f, const std::vector<std::string>& args,
- const std::string& filename, int line)
- : func_(f), args_(args), filename_(filename), line_(line) {
-}
+Command::Command(BuiltinFunction f, const std::vector<std::string>& args, int line)
+ : func_(f), args_(args), line_(line) {}
int Command::InvokeFunc() const {
std::vector<std::string> expanded_args;
@@ -54,50 +47,28 @@
return Join(args_, ' ');
}
-std::string Command::BuildSourceString() const {
- if (!filename_.empty()) {
- return StringPrintf(" (%s:%d)", filename_.c_str(), line_);
- } else {
- return std::string();
- }
-}
-
-Action::Action(bool oneshot) : oneshot_(oneshot) {
-}
+Action::Action(bool oneshot, const std::string& filename, int line)
+ : oneshot_(oneshot), filename_(filename), line_(line) {}
const KeywordMap<BuiltinFunction>* Action::function_map_ = nullptr;
-bool Action::AddCommand(const std::vector<std::string>& args,
- const std::string& filename, int line, std::string* err) {
+bool Action::AddCommand(const std::vector<std::string>& args, int line, std::string* err) {
if (!function_map_) {
*err = "no function map available";
return false;
}
- if (args.empty()) {
- *err = "command needed, but not provided";
- return false;
- }
-
- auto function = function_map_->FindFunction(args[0], args.size() - 1, err);
+ auto function = function_map_->FindFunction(args, err);
if (!function) {
return false;
}
- AddCommand(function, args, filename, line);
+ AddCommand(function, args, line);
return true;
}
-void Action::AddCommand(BuiltinFunction f,
- const std::vector<std::string>& args,
- const std::string& filename, int line) {
- commands_.emplace_back(f, args, filename, line);
-}
-
-void Action::CombineAction(const Action& action) {
- for (const auto& c : action.commands_) {
- commands_.emplace_back(c);
- }
+void Action::AddCommand(BuiltinFunction f, const std::vector<std::string>& args, int line) {
+ commands_.emplace_back(f, args, line);
}
std::size_t Action::NumCommands() const {
@@ -127,7 +98,7 @@
android::base::GetMinimumLogSeverity() <= android::base::DEBUG) {
std::string trigger_name = BuildTriggersString();
std::string cmd_str = command.BuildCommandString();
- std::string source = command.BuildSourceString();
+ std::string source = StringPrintf(" (%s:%d)", filename_.c_str(), command.line());
LOG(INFO) << "Command '" << cmd_str << "' action=" << trigger_name << source
<< " returned " << result << " took " << duration_ms << "ms.";
@@ -228,20 +199,17 @@
return found;
}
-bool Action::CheckEventTrigger(const std::string& trigger) const {
- return !event_trigger_.empty() &&
- trigger == event_trigger_ &&
- CheckPropertyTriggers();
+bool Action::CheckEvent(const EventTrigger& event_trigger) const {
+ return event_trigger == event_trigger_ && CheckPropertyTriggers();
}
-bool Action::CheckPropertyTrigger(const std::string& name,
- const std::string& value) const {
+bool Action::CheckEvent(const PropertyChange& property_change) const {
+ const auto& [name, value] = property_change;
return event_trigger_.empty() && CheckPropertyTriggers(name, value);
}
-bool Action::TriggersEqual(const Action& other) const {
- return property_triggers_ == other.property_triggers_ &&
- event_trigger_ == other.event_trigger_;
+bool Action::CheckEvent(const BuiltinAction& builtin_action) const {
+ return this == builtin_action;
}
std::string Action::BuildTriggersString() const {
@@ -267,41 +235,6 @@
}
}
-class EventTrigger : public Trigger {
-public:
- explicit EventTrigger(const std::string& trigger) : trigger_(trigger) {
- }
- bool CheckTriggers(const Action& action) const override {
- return action.CheckEventTrigger(trigger_);
- }
-private:
- const std::string trigger_;
-};
-
-class PropertyTrigger : public Trigger {
-public:
- PropertyTrigger(const std::string& name, const std::string& value)
- : name_(name), value_(value) {
- }
- bool CheckTriggers(const Action& action) const override {
- return action.CheckPropertyTrigger(name_, value_);
- }
-private:
- const std::string name_;
- const std::string value_;
-};
-
-class BuiltinTrigger : public Trigger {
-public:
- explicit BuiltinTrigger(Action* action) : action_(action) {
- }
- bool CheckTriggers(const Action& action) const override {
- return action_ == &action;
- }
-private:
- const Action* action_;
-};
-
ActionManager::ActionManager() : current_command_(0) {
}
@@ -311,56 +244,45 @@
}
void ActionManager::AddAction(std::unique_ptr<Action> action) {
- auto old_action_it =
- std::find_if(actions_.begin(), actions_.end(),
- [&action] (std::unique_ptr<Action>& a) {
- return action->TriggersEqual(*a);
- });
-
- if (old_action_it != actions_.end()) {
- (*old_action_it)->CombineAction(*action);
- } else {
- actions_.emplace_back(std::move(action));
- }
+ actions_.emplace_back(std::move(action));
}
void ActionManager::QueueEventTrigger(const std::string& trigger) {
- trigger_queue_.push(std::make_unique<EventTrigger>(trigger));
+ event_queue_.emplace(trigger);
}
-void ActionManager::QueuePropertyTrigger(const std::string& name,
- const std::string& value) {
- trigger_queue_.push(std::make_unique<PropertyTrigger>(name, value));
+void ActionManager::QueuePropertyChange(const std::string& name, const std::string& value) {
+ event_queue_.emplace(std::make_pair(name, value));
}
-void ActionManager::QueueAllPropertyTriggers() {
- QueuePropertyTrigger("", "");
+void ActionManager::QueueAllPropertyActions() {
+ QueuePropertyChange("", "");
}
-void ActionManager::QueueBuiltinAction(BuiltinFunction func,
- const std::string& name) {
- auto action = std::make_unique<Action>(true);
+void ActionManager::QueueBuiltinAction(BuiltinFunction func, const std::string& name) {
+ auto action = std::make_unique<Action>(true, "<Builtin Action>", 0);
std::vector<std::string> name_vector{name};
if (!action->InitSingleTrigger(name)) {
return;
}
- action->AddCommand(func, name_vector);
+ action->AddCommand(func, name_vector, 0);
- trigger_queue_.push(std::make_unique<BuiltinTrigger>(action.get()));
+ event_queue_.emplace(action.get());
actions_.emplace_back(std::move(action));
}
void ActionManager::ExecuteOneCommand() {
- // Loop through the trigger queue until we have an action to execute
- while (current_executing_actions_.empty() && !trigger_queue_.empty()) {
+ // Loop through the event queue until we have an action to execute
+ while (current_executing_actions_.empty() && !event_queue_.empty()) {
for (const auto& action : actions_) {
- if (trigger_queue_.front()->CheckTriggers(*action)) {
+ if (std::visit([&action](const auto& event) { return action->CheckEvent(event); },
+ event_queue_.front())) {
current_executing_actions_.emplace(action.get());
}
}
- trigger_queue_.pop();
+ event_queue_.pop();
}
if (current_executing_actions_.empty()) {
@@ -371,7 +293,8 @@
if (current_command_ == 0) {
std::string trigger_name = action->BuildTriggersString();
- LOG(INFO) << "processing action (" << trigger_name << ")";
+ LOG(INFO) << "processing action (" << trigger_name << ") from (" << action->filename()
+ << ":" << action->line() << ")";
}
action->ExecuteOneCommand(current_command_);
@@ -393,7 +316,7 @@
}
bool ActionManager::HasMoreCommands() const {
- return !current_executing_actions_.empty() || !trigger_queue_.empty();
+ return !current_executing_actions_.empty() || !event_queue_.empty();
}
void ActionManager::DumpState() const {
@@ -402,15 +325,15 @@
}
}
-bool ActionParser::ParseSection(const std::vector<std::string>& args,
- std::string* err) {
+bool ActionParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
+ int line, std::string* err) {
std::vector<std::string> triggers(args.begin() + 1, args.end());
if (triggers.size() < 1) {
*err = "actions must have a trigger";
return false;
}
- auto action = std::make_unique<Action>(false);
+ auto action = std::make_unique<Action>(false, filename, line);
if (!action->InitTriggers(triggers, err)) {
return false;
}
@@ -419,14 +342,12 @@
return true;
}
-bool ActionParser::ParseLineSection(const std::vector<std::string>& args,
- const std::string& filename, int line,
- std::string* err) const {
- return action_ ? action_->AddCommand(args, filename, line, err) : false;
+bool ActionParser::ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) {
+ return action_ ? action_->AddCommand(std::move(args), line, err) : false;
}
void ActionParser::EndSection() {
if (action_ && action_->NumCommands() > 0) {
- ActionManager::GetInstance().AddAction(std::move(action_));
+ action_manager_->AddAction(std::move(action_));
}
}
diff --git a/init/action.h b/init/action.h
index 0bae9f0..d006c50 100644
--- a/init/action.h
+++ b/init/action.h
@@ -20,6 +20,7 @@
#include <map>
#include <queue>
#include <string>
+#include <variant>
#include <vector>
#include "builtins.h"
@@ -27,44 +28,44 @@
#include "keyword_map.h"
class Command {
-public:
- Command(BuiltinFunction f, const std::vector<std::string>& args,
- const std::string& filename, int line);
+ public:
+ Command(BuiltinFunction f, const std::vector<std::string>& args, int line);
int InvokeFunc() const;
std::string BuildCommandString() const;
- std::string BuildSourceString() const;
-private:
+ int line() const { return line_; }
+
+ private:
BuiltinFunction func_;
std::vector<std::string> args_;
- std::string filename_;
int line_;
};
-class Action {
-public:
- explicit Action(bool oneshot = false);
+using EventTrigger = std::string;
+using PropertyChange = std::pair<std::string, std::string>;
+using BuiltinAction = class Action*;
- bool AddCommand(const std::vector<std::string>& args,
- const std::string& filename, int line, std::string* err);
- void AddCommand(BuiltinFunction f,
- const std::vector<std::string>& args,
- const std::string& filename = "", int line = 0);
- void CombineAction(const Action& action);
+class Action {
+ public:
+ explicit Action(bool oneshot, const std::string& filename, int line);
+
+ bool AddCommand(const std::vector<std::string>& args, int line, std::string* err);
+ 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);
std::size_t NumCommands() const;
void ExecuteOneCommand(std::size_t command) const;
void ExecuteAllCommands() const;
- bool CheckEventTrigger(const std::string& trigger) const;
- bool CheckPropertyTrigger(const std::string& name,
- const std::string& value) const;
- bool TriggersEqual(const Action& other) const;
+ bool CheckEvent(const EventTrigger& event_trigger) const;
+ bool CheckEvent(const PropertyChange& property_change) const;
+ bool CheckEvent(const BuiltinAction& builtin_action) const;
std::string BuildTriggersString() const;
void DumpState() const;
bool oneshot() const { return oneshot_; }
+ const std::string& filename() const { return filename_; }
+ int line() const { return line_; }
static void set_function_map(const KeywordMap<BuiltinFunction>* function_map) {
function_map_ = function_map;
}
@@ -80,53 +81,48 @@
std::string event_trigger_;
std::vector<Command> commands_;
bool oneshot_;
+ std::string filename_;
+ int line_;
static const KeywordMap<BuiltinFunction>* function_map_;
};
-class Trigger {
-public:
- virtual ~Trigger() { }
- virtual bool CheckTriggers(const Action& action) const = 0;
-};
-
class ActionManager {
-public:
+ public:
static ActionManager& GetInstance();
+ // Exposed for testing
+ ActionManager();
+
void AddAction(std::unique_ptr<Action> action);
void QueueEventTrigger(const std::string& trigger);
- void QueuePropertyTrigger(const std::string& name, const std::string& value);
- void QueueAllPropertyTriggers();
+ void QueuePropertyChange(const std::string& name, const std::string& value);
+ void QueueAllPropertyActions();
void QueueBuiltinAction(BuiltinFunction func, const std::string& name);
void ExecuteOneCommand();
bool HasMoreCommands() const;
void DumpState() const;
-private:
- ActionManager();
-
+ private:
ActionManager(ActionManager const&) = delete;
void operator=(ActionManager const&) = delete;
std::vector<std::unique_ptr<Action>> actions_;
- std::queue<std::unique_ptr<Trigger>> trigger_queue_;
+ std::queue<std::variant<EventTrigger, PropertyChange, BuiltinAction>> event_queue_;
std::queue<const Action*> current_executing_actions_;
std::size_t current_command_;
};
class ActionParser : public SectionParser {
-public:
- ActionParser() : action_(nullptr) {
- }
- bool ParseSection(const std::vector<std::string>& args,
+ 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(const std::vector<std::string>& args,
- const std::string& filename, int line,
- std::string* err) const override;
+ bool ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) override;
void EndSection() override;
- void EndFile(const std::string&) override {
- }
-private:
+
+ private:
+ ActionManager* action_manager_;
std::unique_ptr<Action> action_;
};
diff --git a/init/bootchart.cpp b/init/bootchart.cpp
index beabea1..825603a 100644
--- a/init/bootchart.cpp
+++ b/init/bootchart.cpp
@@ -17,7 +17,6 @@
#include "bootchart.h"
#include <dirent.h>
-#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
@@ -31,9 +30,7 @@
#include <condition_variable>
#include <memory>
#include <mutex>
-#include <string>
#include <thread>
-#include <vector>
#include <android-base/file.h>
#include <android-base/logging.h>
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 64c00e9..bdd86ed 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -19,33 +19,28 @@
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
+#include <linux/loop.h>
+#include <linux/module.h>
#include <mntent.h>
#include <net/if.h>
-#include <signal.h>
#include <sched.h>
+#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <sys/socket.h>
#include <sys/mount.h>
#include <sys/resource.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
#include <sys/syscall.h>
+#include <sys/system_properties.h>
#include <sys/time.h>
#include <sys/types.h>
-#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
-#include <linux/loop.h>
-#include <linux/module.h>
-
-#include <string>
-#include <thread>
-
-#include <selinux/android.h>
-#include <selinux/selinux.h>
-#include <selinux/label.h>
#include <android-base/file.h>
+#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
@@ -55,14 +50,14 @@
#include <ext4_utils/ext4_crypt.h>
#include <ext4_utils/ext4_crypt_init_extensions.h>
#include <fs_mgr.h>
-#include <logwrap/logwrap.h>
+#include <selinux/android.h>
+#include <selinux/label.h>
+#include <selinux/selinux.h>
#include "action.h"
#include "bootchart.h"
-#include "devices.h"
#include "init.h"
#include "init_parser.h"
-#include "log.h"
#include "property_service.h"
#include "reboot.h"
#include "service.h"
@@ -155,7 +150,12 @@
}
static int do_domainname(const std::vector<std::string>& args) {
- return write_file("/proc/sys/kernel/domainname", args[1]) ? 0 : 1;
+ std::string err;
+ if (!WriteFile("/proc/sys/kernel/domainname", args[1], &err)) {
+ LOG(ERROR) << err;
+ return -1;
+ }
+ return 0;
}
static int do_enable(const std::vector<std::string>& args) {
@@ -179,7 +179,12 @@
}
static int do_hostname(const std::vector<std::string>& args) {
- return write_file("/proc/sys/kernel/hostname", args[1]) ? 0 : 1;
+ std::string err;
+ if (!WriteFile("/proc/sys/kernel/hostname", args[1], &err)) {
+ LOG(ERROR) << err;
+ return -1;
+ }
+ return 0;
}
static int do_ifup(const std::vector<std::string>& args) {
@@ -210,7 +215,7 @@
mode = std::strtoul(args[2].c_str(), 0, 8);
}
- ret = make_dir(args[1].c_str(), mode);
+ 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);
@@ -220,11 +225,19 @@
}
if (args.size() >= 4) {
- uid_t uid = decode_uid(args[3].c_str());
+ 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;
+ }
gid_t gid = -1;
if (args.size() == 5) {
- gid = decode_uid(args[4].c_str());
+ if (!DecodeUid(args[4], &gid, &decode_uid_err)) {
+ LOG(ERROR) << "Unable to find GID for '" << args[3] << "': " << decode_uid_err;
+ return -1;
+ }
}
if (lchown(args[1].c_str(), uid, gid) == -1) {
@@ -395,7 +408,7 @@
// Turning this on and letting the INFO logging be discarded adds 0.2s to
// Nexus 9 boot time, so it's disabled by default.
- if (false) parser.DumpState();
+ if (false) DumpState();
}
/* mount_fstab
@@ -486,6 +499,23 @@
// Although encrypted, we have device key, so we do not need to
// do anything different from the nonencrypted case.
ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
+ } else if (code == FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED) {
+ if (e4crypt_install_keyring()) {
+ return -1;
+ }
+ property_set("ro.crypto.state", "encrypted");
+ property_set("ro.crypto.type", "file");
+
+ // defaultcrypto detects file/block encryption. init flow is same for each.
+ ActionManager::GetInstance().QueueEventTrigger("defaultcrypto");
+ } else if (code == FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION) {
+ if (e4crypt_install_keyring()) {
+ return -1;
+ }
+ property_set("ro.crypto.type", "file");
+
+ // encrypt detects file/block encryption. init flow is same for each.
+ ActionManager::GetInstance().QueueEventTrigger("encrypt");
} else if (code > 0) {
PLOG(ERROR) << "fs_mgr_mount_all returned unexpected error " << code;
}
@@ -603,54 +633,6 @@
return 0;
}
-static int do_powerctl(const std::vector<std::string>& args) {
- const std::string& command = args[1];
- unsigned int cmd = 0;
- std::vector<std::string> cmd_params = android::base::Split(command, ",");
- std::string reason_string = cmd_params[0];
- std::string reboot_target = "";
- bool runFsck = false;
- bool commandInvalid = false;
-
- if (cmd_params.size() > 2) {
- commandInvalid = 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.
- runFsck = true;
- reason_string = cmd_params[1];
- }
- } else if (cmd_params[0] == "reboot") {
- cmd = ANDROID_RB_RESTART2;
- if (cmd_params.size() == 2) {
- reboot_target = cmd_params[1];
- // When rebooting to the bootloader notify the bootloader writing
- // also the BCB.
- if (reboot_target == "bootloader") {
- std::string err;
- if (!write_reboot_bootloader(&err)) {
- LOG(ERROR) << "reboot-bootloader: Error writing "
- "bootloader_message: "
- << err;
- }
- }
- }
- } else if (command == "thermal-shutdown") { // no additional parameter allowed
- cmd = ANDROID_RB_THERMOFF;
- } else {
- commandInvalid = true;
- }
- if (commandInvalid) {
- LOG(ERROR) << "powerctl: unrecognized command '" << command << "'";
- return -EINVAL;
- }
-
- DoReboot(cmd, reason_string, reboot_target, runFsck);
- return 0;
-}
-
static int do_trigger(const std::vector<std::string>& args) {
ActionManager::GetInstance().QueueEventTrigger(args[1]);
return 0;
@@ -678,11 +660,11 @@
static int do_verity_load_state(const std::vector<std::string>& args) {
int mode = -1;
- int rc = fs_mgr_load_verity_state(&mode);
- if (rc == 0 && mode != VERITY_MODE_DEFAULT) {
+ bool loaded = fs_mgr_load_verity_state(&mode);
+ if (loaded && mode != VERITY_MODE_DEFAULT) {
ActionManager::GetInstance().QueueEventTrigger("verity-logging");
}
- return rc;
+ return loaded ? 0 : 1;
}
static void verity_update_property(fstab_rec *fstab, const char *mount_point,
@@ -692,33 +674,53 @@
}
static int do_verity_update_state(const std::vector<std::string>& args) {
- return fs_mgr_update_verity_state(verity_update_property);
+ return fs_mgr_update_verity_state(verity_update_property) ? 0 : 1;
}
static int do_write(const std::vector<std::string>& args) {
- return write_file(args[1], args[2]) ? 0 : 1;
+ std::string err;
+ if (!WriteFile(args[1], args[2], &err)) {
+ LOG(ERROR) << err;
+ return -1;
+ }
+ return 0;
}
static int do_copy(const std::vector<std::string>& args) {
std::string data;
- if (read_file(args[1], &data)) {
- return write_file(args[2], data) ? 0 : 1;
+ std::string err;
+ if (!ReadFile(args[1], &data, &err)) {
+ LOG(ERROR) << err;
+ return -1;
}
- return 1;
+ if (!WriteFile(args[2], data, &err)) {
+ LOG(ERROR) << err;
+ return -1;
+ }
+ return 0;
}
static int do_chown(const std::vector<std::string>& args) {
- /* GID is optional. */
- if (args.size() == 3) {
- if (lchown(args[2].c_str(), decode_uid(args[1].c_str()), -1) == -1)
- return -errno;
- } else if (args.size() == 4) {
- if (lchown(args[3].c_str(), decode_uid(args[1].c_str()),
- decode_uid(args[2].c_str())) == -1)
- return -errno;
- } else {
+ 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;
}
+
+ // 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;
+
+ 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;
+ }
+ }
+
+ if (lchown(path.c_str(), uid, gid) == -1) return -errno;
+
return 0;
}
@@ -777,7 +779,7 @@
}
} else {
in_flags = false;
- if (restorecon(args[i].c_str(), flag) < 0) {
+ if (selinux_android_restorecon(args[i].c_str(), flag) < 0) {
ret = -errno;
}
}
@@ -862,7 +864,7 @@
* Callback to make a directory from the ext4 code
*/
static int do_installkeys_ensure_dir_exists(const char* dir) {
- if (make_dir(dir, 0700) && errno != EEXIST) {
+ if (make_dir(dir, 0700, sehandle) && errno != EEXIST) {
return -1;
}
@@ -888,10 +890,12 @@
}
static int do_init_user0(const std::vector<std::string>& args) {
- return e4crypt_do_init_user0();
+ std::vector<std::string> exec_args = {"exec", "/system/bin/vdc", "--wait", "cryptfs",
+ "init_user0"};
+ return do_exec(exec_args);
}
-BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
+const BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
// clang-format off
static const Map builtin_functions = {
@@ -920,7 +924,6 @@
{"mount_all", {1, kMax, do_mount_all}},
{"mount", {3, kMax, do_mount}},
{"umount", {1, 1, do_umount}},
- {"powerctl", {1, 1, do_powerctl}},
{"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 53f4a71..e1f0567 100644
--- a/init/builtins.h
+++ b/init/builtins.h
@@ -17,19 +17,20 @@
#ifndef _INIT_BUILTINS_H
#define _INIT_BUILTINS_H
+#include <functional>
#include <map>
#include <string>
#include <vector>
#include "keyword_map.h"
-using BuiltinFunction = int (*) (const std::vector<std::string>& args);
+using BuiltinFunction = std::function<int(const std::vector<std::string>&)>;
class BuiltinFunctionMap : public KeywordMap<BuiltinFunction> {
-public:
- BuiltinFunctionMap() {
- }
-private:
- Map& map() const override;
+ public:
+ BuiltinFunctionMap() {}
+
+ private:
+ const Map& map() const override;
};
#endif
diff --git a/init/descriptors.cpp b/init/descriptors.cpp
index 6e457cd..6f729a3 100644
--- a/init/descriptors.cpp
+++ b/init/descriptors.cpp
@@ -19,16 +19,16 @@
#include <ctype.h>
#include <fcntl.h>
#include <sys/stat.h>
-#include <sys/types.h>
#include <unistd.h>
+#include <android-base/logging.h>
#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <cutils/android_get_control_file.h>
#include <cutils/sockets.h>
#include "init.h"
-#include "log.h"
#include "util.h"
DescriptorInfo::DescriptorInfo(const std::string& name, const std::string& type, uid_t uid,
@@ -78,10 +78,12 @@
}
int SocketInfo::Create(const std::string& context) const {
- int flags = ((type() == "stream" ? SOCK_STREAM :
- (type() == "dgram" ? SOCK_DGRAM :
- SOCK_SEQPACKET)));
- return create_socket(name().c_str(), flags, perm(), uid(), gid(), context.c_str());
+ auto types = android::base::Split(type(), "+");
+ 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);
}
const std::string SocketInfo::key() const {
diff --git a/init/devices.cpp b/init/devices.cpp
index 405f92e..40f9740 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007-2014 The Android Open Source Project
+ * Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,238 +14,208 @@
* limitations under the License.
*/
-#include <dirent.h>
+#include "devices.h"
+
#include <errno.h>
-#include <fcntl.h>
#include <fnmatch.h>
-#include <libgen.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/sendfile.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-#include <sys/types.h>
-#include <sys/un.h>
-#include <sys/wait.h>
+#include <sys/sysmacros.h>
#include <unistd.h>
-#include <linux/netlink.h>
-
#include <memory>
-#include <thread>
-#include <selinux/selinux.h>
-#include <selinux/label.h>
-#include <selinux/android.h>
-#include <selinux/avc.h>
-
-#include <private/android_filesystem_config.h>
-
-#include <android-base/file.h>
+#include <android-base/logging.h>
#include <android-base/stringprintf.h>
-#include <android-base/unique_fd.h>
-#include <cutils/list.h>
-#include <cutils/uevent.h>
+#include <android-base/strings.h>
+#include <private/android_filesystem_config.h>
+#include <selinux/android.h>
+#include <selinux/selinux.h>
-#include "devices.h"
-#include "ueventd_parser.h"
+#include "ueventd.h"
#include "util.h"
-#include "log.h"
-#define SYSFS_PREFIX "/sys"
-static const char *firmware_dirs[] = { "/etc/firmware",
- "/vendor/firmware",
- "/firmware/image" };
+#ifdef _INIT_INIT_H
+#error "Do not include init.h in files used by ueventd or watchdogd; it will expose init's globals"
+#endif
-extern struct selabel_handle *sehandle;
+/* Given a path that may start with a PCI device, populate the supplied buffer
+ * with the PCI domain/bus number and the peripheral ID and return 0.
+ * If it doesn't start with a PCI device, or there is some error, return -1 */
+static bool FindPciDevicePrefix(const std::string& path, std::string* result) {
+ result->clear();
-static android::base::unique_fd device_fd;
+ if (!android::base::StartsWith(path, "/devices/pci")) return false;
-struct perms_ {
- char *name;
- char *attr;
- mode_t perm;
- unsigned int uid;
- unsigned int gid;
- unsigned short prefix;
- unsigned short wildcard;
-};
+ /* Beginning of the prefix is the initial "pci" after "/devices/" */
+ std::string::size_type start = 9;
-struct perm_node {
- struct perms_ dp;
- struct listnode plist;
-};
+ /* End of the prefix is two path '/' later, capturing the domain/bus number
+ * and the peripheral ID. Example: pci0000:00/0000:00:1f.2 */
+ auto end = path.find('/', start);
+ if (end == std::string::npos) return false;
-struct platform_node {
- char *name;
- char *path;
- int path_len;
- struct listnode list;
-};
+ end = path.find('/', end + 1);
+ if (end == std::string::npos) return false;
-static list_declare(sys_perms);
-static list_declare(dev_perms);
-static list_declare(platform_names);
-
-int add_dev_perms(const char *name, const char *attr,
- mode_t perm, unsigned int uid, unsigned int gid,
- unsigned short prefix,
- unsigned short wildcard) {
- struct perm_node *node = (perm_node*) calloc(1, sizeof(*node));
- if (!node)
- return -ENOMEM;
-
- node->dp.name = strdup(name);
- if (!node->dp.name) {
- free(node);
- return -ENOMEM;
- }
-
- if (attr) {
- node->dp.attr = strdup(attr);
- if (!node->dp.attr) {
- free(node->dp.name);
- free(node);
- return -ENOMEM;
- }
- }
-
- node->dp.perm = perm;
- node->dp.uid = uid;
- node->dp.gid = gid;
- node->dp.prefix = prefix;
- node->dp.wildcard = wildcard;
-
- if (attr)
- list_add_tail(&sys_perms, &node->plist);
- else
- list_add_tail(&dev_perms, &node->plist);
-
- return 0;
-}
-
-static bool perm_path_matches(const char *path, struct perms_ *dp)
-{
- if (dp->prefix) {
- if (strncmp(path, dp->name, strlen(dp->name)) == 0)
- return true;
- } else if (dp->wildcard) {
- if (fnmatch(dp->name, path, FNM_PATHNAME) == 0)
- return true;
- } else {
- if (strcmp(path, dp->name) == 0)
- return true;
- }
-
- return false;
-}
-
-static bool match_subsystem(perms_* dp, const char* pattern,
- const char* path, const char* subsystem) {
- if (!pattern || !subsystem || strstr(dp->name, subsystem) == NULL) {
+ auto length = end - start;
+ if (length <= 4) {
+ // The minimum string that will get to this check is 'pci/', which is malformed,
+ // so return false
return false;
}
- std::string subsys_path = android::base::StringPrintf(pattern, subsystem, basename(path));
- return perm_path_matches(subsys_path.c_str(), dp);
+ *result = path.substr(start, length);
+ return true;
}
-static void fixup_sys_perms(const char* upath, const char* subsystem) {
+/* Given a path that may start with a virtual block device, populate
+ * the supplied buffer with the virtual block device ID and return 0.
+ * If it doesn't start with a virtual block device, or there is some
+ * error, return -1 */
+static bool FindVbdDevicePrefix(const std::string& path, std::string* result) {
+ result->clear();
+
+ if (!android::base::StartsWith(path, "/devices/vbd-")) return false;
+
+ /* Beginning of the prefix is the initial "vbd-" after "/devices/" */
+ std::string::size_type start = 13;
+
+ /* End of the prefix is one path '/' later, capturing the
+ virtual block device ID. Example: 768 */
+ auto end = path.find('/', start);
+ if (end == std::string::npos) return false;
+
+ auto length = end - start;
+ if (length == 0) return false;
+
+ *result = path.substr(start, length);
+ return true;
+}
+
+Permissions::Permissions(const std::string& name, mode_t perm, uid_t uid, gid_t gid)
+ : name_(name), perm_(perm), uid_(uid), gid_(gid), prefix_(false), wildcard_(false) {
+ // Set 'prefix_' or 'wildcard_' based on the below cases:
+ //
+ // 1) No '*' in 'name' -> Neither are set and Match() checks a given path for strict
+ // equality with 'name'
+ //
+ // 2) '*' only appears as the last character in 'name' -> 'prefix'_ is set to true and
+ // Match() checks if 'name' is a prefix of a given path.
+ //
+ // 3) '*' appears elsewhere -> 'wildcard_' is set to true and Match() uses fnmatch()
+ // with FNM_PATHNAME to compare 'name' to a given path.
+
+ auto wildcard_position = name_.find('*');
+ if (wildcard_position != std::string::npos) {
+ if (wildcard_position == name_.length() - 1) {
+ prefix_ = true;
+ name_.pop_back();
+ } else {
+ wildcard_ = true;
+ }
+ }
+}
+
+bool Permissions::Match(const std::string& path) const {
+ if (prefix_) return android::base::StartsWith(path, name_.c_str());
+ if (wildcard_) return fnmatch(name_.c_str(), path.c_str(), FNM_PATHNAME) == 0;
+ return path == name_;
+}
+
+bool SysfsPermissions::MatchWithSubsystem(const std::string& path,
+ const std::string& subsystem) const {
+ std::string path_basename = android::base::Basename(path);
+ if (name().find(subsystem) != std::string::npos) {
+ if (Match("/sys/class/" + subsystem + "/" + path_basename)) return true;
+ if (Match("/sys/bus/" + subsystem + "/devices/" + path_basename)) return true;
+ }
+ return Match(path);
+}
+
+void SysfsPermissions::SetPermissions(const std::string& path) const {
+ std::string attribute_file = path + "/" + attribute_;
+ LOG(VERBOSE) << "fixup " << attribute_file << " " << uid() << " " << gid() << " " << std::oct
+ << perm();
+
+ if (access(attribute_file.c_str(), F_OK) == 0) {
+ if (chown(attribute_file.c_str(), uid(), gid()) != 0) {
+ PLOG(ERROR) << "chown(" << attribute_file << ", " << uid() << ", " << gid()
+ << ") failed";
+ }
+ if (chmod(attribute_file.c_str(), perm()) != 0) {
+ PLOG(ERROR) << "chmod(" << attribute_file << ", " << perm() << ") failed";
+ }
+ }
+}
+
+// Given a path that may start with a platform device, find the length of the
+// platform device prefix. If it doesn't start with a platform device, return false
+bool PlatformDeviceList::Find(const std::string& path, std::string* out_path) const {
+ out_path->clear();
+ // platform_devices is searched backwards, since parents are added before their children,
+ // and we want to match as deep of a child as we can.
+ for (auto it = platform_devices_.crbegin(); it != platform_devices_.crend(); ++it) {
+ auto platform_device_path_length = it->length();
+ if (platform_device_path_length < path.length() &&
+ path[platform_device_path_length] == '/' &&
+ android::base::StartsWith(path, it->c_str())) {
+ *out_path = *it;
+ return true;
+ }
+ }
+ return false;
+}
+
+void DeviceHandler::FixupSysPermissions(const std::string& upath,
+ const std::string& subsystem) const {
// upaths omit the "/sys" that paths in this list
// contain, so we prepend it...
- std::string path = std::string(SYSFS_PREFIX) + upath;
+ std::string path = "/sys" + upath;
- listnode* node;
- list_for_each(node, &sys_perms) {
- perms_* dp = &(node_to_item(node, perm_node, plist))->dp;
- if (match_subsystem(dp, SYSFS_PREFIX "/class/%s/%s", path.c_str(), subsystem)) {
- ; // matched
- } else if (match_subsystem(dp, SYSFS_PREFIX "/bus/%s/devices/%s", path.c_str(), subsystem)) {
- ; // matched
- } else if (!perm_path_matches(path.c_str(), dp)) {
- continue;
- }
-
- std::string attr_file = path + "/" + dp->attr;
- LOG(INFO) << "fixup " << attr_file
- << " " << dp->uid << " " << dp->gid << " " << std::oct << dp->perm;
- chown(attr_file.c_str(), dp->uid, dp->gid);
- chmod(attr_file.c_str(), dp->perm);
+ for (const auto& s : sysfs_permissions_) {
+ if (s.MatchWithSubsystem(path, subsystem)) s.SetPermissions(path);
}
if (access(path.c_str(), F_OK) == 0) {
LOG(VERBOSE) << "restorecon_recursive: " << path;
- restorecon(path.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE);
+ if (selinux_android_restorecon(path.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE) != 0) {
+ PLOG(ERROR) << "selinux_android_restorecon(" << path << ") failed";
+ }
}
}
-static mode_t get_device_perm(const char *path, const char **links,
- unsigned *uid, unsigned *gid)
-{
- struct listnode *node;
- struct perm_node *perm_node;
- struct perms_ *dp;
-
- /* search the perms list in reverse so that ueventd.$hardware can
- * override ueventd.rc
- */
- list_for_each_reverse(node, &dev_perms) {
- bool match = false;
-
- perm_node = node_to_item(node, struct perm_node, plist);
- dp = &perm_node->dp;
-
- if (perm_path_matches(path, dp)) {
- match = true;
- } else {
- if (links) {
- int i;
- for (i = 0; links[i]; i++) {
- if (perm_path_matches(links[i], dp)) {
- match = true;
- break;
- }
- }
- }
- }
-
- if (match) {
- *uid = dp->uid;
- *gid = dp->gid;
- return dp->perm;
+std::tuple<mode_t, uid_t, gid_t> DeviceHandler::GetDevicePermissions(
+ const std::string& path, const std::vector<std::string>& links) const {
+ // Search the perms list in reverse so that ueventd.$hardware can override ueventd.rc.
+ for (auto it = dev_permissions_.crbegin(); it != dev_permissions_.crend(); ++it) {
+ if (it->Match(path) || std::any_of(links.cbegin(), links.cend(),
+ [it](const auto& link) { return it->Match(link); })) {
+ return {it->perm(), it->uid(), it->gid()};
}
}
/* Default if nothing found. */
- *uid = 0;
- *gid = 0;
- return 0600;
+ return {0600, 0, 0};
}
-static void make_device(const char *path,
- const char */*upath*/,
- int block, int major, int minor,
- const char **links)
-{
- unsigned uid;
- unsigned gid;
- mode_t mode;
- dev_t dev;
- char *secontext = NULL;
+void DeviceHandler::MakeDevice(const std::string& path, int block, int major, int minor,
+ const std::vector<std::string>& links) const {
+ auto[mode, uid, gid] = GetDevicePermissions(path, links);
+ mode |= (block ? S_IFBLK : S_IFCHR);
- mode = get_device_perm(path, links, &uid, &gid) | (block ? S_IFBLK : S_IFCHR);
-
- if (sehandle) {
- if (selabel_lookup_best_match(sehandle, &secontext, path, links, mode)) {
+ 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);
}
- dev = makedev(major, minor);
+ dev_t dev = makedev(major, minor);
/* Temporarily change egid to avoid race condition setting the gid of the
* device node. Unforunately changing the euid would prevent creation of
* some device nodes, so the uid has to be set with chown() and is still
@@ -257,10 +227,9 @@
}
/* 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, mode, dev) && (errno == EEXIST) && secontext) {
-
+ if (mknod(path.c_str(), mode, dev) && (errno == EEXIST) && secontext) {
char* fcon = nullptr;
- int rc = lgetfilecon(path, &fcon);
+ int rc = lgetfilecon(path.c_str(), &fcon);
if (rc < 0) {
PLOG(ERROR) << "Cannot get SELinux label on '" << path << "' device";
goto out;
@@ -269,821 +238,241 @@
bool different = strcmp(fcon, secontext) != 0;
freecon(fcon);
- if (different && lsetfilecon(path, secontext)) {
- PLOG(ERROR) << "Cannot set '" << secontext << "' SELinux label on '" << path << "' device";
+ if (different && lsetfilecon(path.c_str(), secontext)) {
+ PLOG(ERROR) << "Cannot set '" << secontext << "' SELinux label on '" << path
+ << "' device";
}
}
out:
- chown(path, uid, -1);
+ chown(path.c_str(), uid, -1);
if (setegid(AID_ROOT)) {
PLOG(FATAL) << "setegid(AID_ROOT) failed";
}
if (secontext) {
freecon(secontext);
- setfscreatecon(NULL);
+ setfscreatecon(nullptr);
}
}
-static void add_platform_device(const char *path)
-{
- int path_len = strlen(path);
- struct platform_node *bus;
- const char *name = path;
+std::vector<std::string> DeviceHandler::GetCharacterDeviceSymlinks(const Uevent& uevent) const {
+ std::string parent_device;
+ if (!platform_devices_.Find(uevent.path, &parent_device)) return {};
- if (!strncmp(path, "/devices/", 9)) {
- name += 9;
- if (!strncmp(name, "platform/", 9))
- name += 9;
- }
+ // skip path to the parent driver
+ std::string path = uevent.path.substr(parent_device.length());
- LOG(VERBOSE) << "adding platform device " << name << " (" << path << ")";
+ if (!android::base::StartsWith(path, "/usb")) return {};
- bus = (platform_node*) calloc(1, sizeof(struct platform_node));
- bus->path = strdup(path);
- bus->path_len = path_len;
- bus->name = bus->path + (name - path);
- list_add_tail(&platform_names, &bus->list);
-}
+ // skip root hub name and device. use device interface
+ // skip 3 slashes, including the first / by starting the search at the 1st character, not 0th.
+ // then extract what comes between the 3rd and 4th slash
+ // e.g. "/usb/usb_device/name/tty2-1:1.0" -> "name"
-/*
- * given a path that may start with a platform device, find the length of the
- * platform device prefix. If it doesn't start with a platform device, return
- * 0.
- */
-static struct platform_node *find_platform_device(const char *path)
-{
- int path_len = strlen(path);
- struct listnode *node;
- struct platform_node *bus;
+ std::string::size_type start = 0;
+ start = path.find('/', start + 1);
+ if (start == std::string::npos) return {};
- list_for_each_reverse(node, &platform_names) {
- bus = node_to_item(node, struct platform_node, list);
- if ((bus->path_len < path_len) &&
- (path[bus->path_len] == '/') &&
- !strncmp(path, bus->path, bus->path_len))
- return bus;
- }
+ start = path.find('/', start + 1);
+ if (start == std::string::npos) return {};
- return NULL;
-}
+ auto end = path.find('/', start + 1);
+ if (end == std::string::npos) return {};
-static void remove_platform_device(const char *path)
-{
- struct listnode *node;
- struct platform_node *bus;
+ start++; // Skip the first '/'
- list_for_each_reverse(node, &platform_names) {
- bus = node_to_item(node, struct platform_node, list);
- if (!strcmp(path, bus->path)) {
- LOG(INFO) << "removing platform device " << bus->name;
- free(bus->path);
- list_remove(node);
- free(bus);
- return;
- }
- }
-}
+ auto length = end - start;
+ if (length == 0) return {};
-static void destroy_platform_devices() {
- struct listnode* node;
- struct listnode* n;
- struct platform_node* bus;
+ auto name_string = path.substr(start, length);
- list_for_each_safe(node, n, &platform_names) {
- list_remove(node);
- bus = node_to_item(node, struct platform_node, list);
- free(bus->path);
- free(bus);
- }
-}
+ std::vector<std::string> links;
+ links.emplace_back("/dev/usb/" + uevent.subsystem + name_string);
-/* Given a path that may start with a PCI device, populate the supplied buffer
- * with the PCI domain/bus number and the peripheral ID and return 0.
- * If it doesn't start with a PCI device, or there is some error, return -1 */
-static int find_pci_device_prefix(const char *path, char *buf, ssize_t buf_sz)
-{
- const char *start, *end;
-
- if (strncmp(path, "/devices/pci", 12))
- return -1;
-
- /* Beginning of the prefix is the initial "pci" after "/devices/" */
- start = path + 9;
-
- /* End of the prefix is two path '/' later, capturing the domain/bus number
- * and the peripheral ID. Example: pci0000:00/0000:00:1f.2 */
- end = strchr(start, '/');
- if (!end)
- return -1;
- end = strchr(end + 1, '/');
- if (!end)
- return -1;
-
- /* Make sure we have enough room for the string plus null terminator */
- if (end - start + 1 > buf_sz)
- return -1;
-
- strncpy(buf, start, end - start);
- buf[end - start] = '\0';
- return 0;
-}
-
-/* Given a path that may start with a virtual block device, populate
- * the supplied buffer with the virtual block device ID and return 0.
- * If it doesn't start with a virtual block device, or there is some
- * error, return -1 */
-static int find_vbd_device_prefix(const char *path, char *buf, ssize_t buf_sz)
-{
- const char *start, *end;
-
- /* Beginning of the prefix is the initial "vbd-" after "/devices/" */
- if (strncmp(path, "/devices/vbd-", 13))
- return -1;
-
- /* End of the prefix is one path '/' later, capturing the
- virtual block device ID. Example: 768 */
- start = path + 13;
- end = strchr(start, '/');
- if (!end)
- return -1;
-
- /* Make sure we have enough room for the string plus null terminator */
- if (end - start + 1 > buf_sz)
- return -1;
-
- strncpy(buf, start, end - start);
- buf[end - start] = '\0';
- return 0;
-}
-
-static void parse_event(const char *msg, struct uevent *uevent)
-{
- uevent->action = "";
- uevent->path = "";
- uevent->subsystem = "";
- uevent->firmware = "";
- uevent->major = -1;
- uevent->minor = -1;
- uevent->partition_name = NULL;
- uevent->partition_num = -1;
- uevent->device_name = NULL;
-
- /* currently ignoring SEQNUM */
- while(*msg) {
- if(!strncmp(msg, "ACTION=", 7)) {
- msg += 7;
- uevent->action = msg;
- } else if(!strncmp(msg, "DEVPATH=", 8)) {
- msg += 8;
- uevent->path = msg;
- } else if(!strncmp(msg, "SUBSYSTEM=", 10)) {
- msg += 10;
- uevent->subsystem = msg;
- } else if(!strncmp(msg, "FIRMWARE=", 9)) {
- msg += 9;
- uevent->firmware = msg;
- } else if(!strncmp(msg, "MAJOR=", 6)) {
- msg += 6;
- uevent->major = atoi(msg);
- } else if(!strncmp(msg, "MINOR=", 6)) {
- msg += 6;
- uevent->minor = atoi(msg);
- } else if(!strncmp(msg, "PARTN=", 6)) {
- msg += 6;
- uevent->partition_num = atoi(msg);
- } else if(!strncmp(msg, "PARTNAME=", 9)) {
- msg += 9;
- uevent->partition_name = msg;
- } else if(!strncmp(msg, "DEVNAME=", 8)) {
- msg += 8;
- uevent->device_name = msg;
- }
-
- /* advance to after the next \0 */
- while(*msg++)
- ;
- }
-
- if (LOG_UEVENTS) {
- LOG(INFO) << android::base::StringPrintf("event { '%s', '%s', '%s', '%s', %d, %d }",
- uevent->action, uevent->path, uevent->subsystem,
- uevent->firmware, uevent->major, uevent->minor);
- }
-}
-
-static char **get_character_device_symlinks(struct uevent *uevent)
-{
- const char *parent;
- const char *slash;
- char **links;
- int link_num = 0;
- int width;
- struct platform_node *pdev;
-
- pdev = find_platform_device(uevent->path);
- if (!pdev)
- return NULL;
-
- links = (char**) malloc(sizeof(char *) * 2);
- if (!links)
- return NULL;
- memset(links, 0, sizeof(char *) * 2);
-
- /* skip "/devices/platform/<driver>" */
- parent = strchr(uevent->path + pdev->path_len, '/');
- if (!parent)
- goto err;
-
- if (!strncmp(parent, "/usb", 4)) {
- /* skip root hub name and device. use device interface */
- while (*++parent && *parent != '/');
- if (*parent)
- while (*++parent && *parent != '/');
- if (!*parent)
- goto err;
- slash = strchr(++parent, '/');
- if (!slash)
- goto err;
- width = slash - parent;
- if (width <= 0)
- goto err;
-
- if (asprintf(&links[link_num], "/dev/usb/%s%.*s", uevent->subsystem, width, parent) > 0)
- link_num++;
- else
- links[link_num] = NULL;
- mkdir("/dev/usb", 0755);
- }
- else {
- goto err;
- }
+ mkdir("/dev/usb", 0755);
return links;
-err:
- free(links);
- return NULL;
}
-static char **get_block_device_symlinks(struct uevent *uevent)
-{
- const char *device;
- struct platform_node *pdev;
- const char *slash;
- const char *type;
- char buf[256];
- char link_path[256];
- int link_num = 0;
- char *p;
+// replaces any unacceptable characters with '_', the
+// length of the resulting string is equal to the input string
+void SanitizePartitionName(std::string* string) {
+ const char* accept =
+ "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "0123456789"
+ "_-.";
- pdev = find_platform_device(uevent->path);
- if (pdev) {
- device = pdev->name;
+ if (!string) return;
+
+ std::string::size_type pos = 0;
+ while ((pos = string->find_first_not_of(accept, pos)) != std::string::npos) {
+ (*string)[pos] = '_';
+ }
+}
+
+std::vector<std::string> DeviceHandler::GetBlockDeviceSymlinks(const Uevent& uevent) const {
+ std::string device;
+ std::string type;
+
+ if (platform_devices_.Find(uevent.path, &device)) {
+ // Skip /devices/platform or /devices/ if present
+ static const std::string devices_platform_prefix = "/devices/platform/";
+ static const std::string devices_prefix = "/devices/";
+
+ if (android::base::StartsWith(device, devices_platform_prefix.c_str())) {
+ device = device.substr(devices_platform_prefix.length());
+ } else if (android::base::StartsWith(device, devices_prefix.c_str())) {
+ device = device.substr(devices_prefix.length());
+ }
+
type = "platform";
- } else if (!find_pci_device_prefix(uevent->path, buf, sizeof(buf))) {
- device = buf;
+ } else if (FindPciDevicePrefix(uevent.path, &device)) {
type = "pci";
- } else if (!find_vbd_device_prefix(uevent->path, buf, sizeof(buf))) {
- device = buf;
+ } else if (FindVbdDevicePrefix(uevent.path, &device)) {
type = "vbd";
} else {
- return NULL;
+ return {};
}
- char **links = (char**) malloc(sizeof(char *) * 4);
- if (!links)
- return NULL;
- memset(links, 0, sizeof(char *) * 4);
+ std::vector<std::string> links;
LOG(VERBOSE) << "found " << type << " device " << device;
- snprintf(link_path, sizeof(link_path), "/dev/block/%s/%s", type, device);
+ auto link_path = "/dev/block/" + type + "/" + device;
- if (uevent->partition_name) {
- p = strdup(uevent->partition_name);
- sanitize(p);
- if (strcmp(uevent->partition_name, p)) {
- LOG(VERBOSE) << "Linking partition '" << uevent->partition_name << "' as '" << p << "'";
+ if (!uevent.partition_name.empty()) {
+ std::string partition_name_sanitized(uevent.partition_name);
+ SanitizePartitionName(&partition_name_sanitized);
+ if (partition_name_sanitized != uevent.partition_name) {
+ LOG(VERBOSE) << "Linking partition '" << uevent.partition_name << "' as '"
+ << partition_name_sanitized << "'";
}
- if (asprintf(&links[link_num], "%s/by-name/%s", link_path, p) > 0)
- link_num++;
- else
- links[link_num] = NULL;
- free(p);
+ links.emplace_back(link_path + "/by-name/" + partition_name_sanitized);
}
- if (uevent->partition_num >= 0) {
- if (asprintf(&links[link_num], "%s/by-num/p%d", link_path, uevent->partition_num) > 0)
- link_num++;
- else
- links[link_num] = NULL;
+ if (uevent.partition_num >= 0) {
+ links.emplace_back(link_path + "/by-num/p" + std::to_string(uevent.partition_num));
}
- slash = strrchr(uevent->path, '/');
- if (asprintf(&links[link_num], "%s/%s", link_path, slash + 1) > 0)
- link_num++;
- else
- links[link_num] = NULL;
+ auto last_slash = uevent.path.rfind('/');
+ links.emplace_back(link_path + "/" + uevent.path.substr(last_slash + 1));
return links;
}
-static void make_link_init(const char* oldpath, const char* newpath) {
- const char* slash = strrchr(newpath, '/');
- if (!slash) return;
+void DeviceHandler::HandleDevice(const std::string& action, const std::string& devpath, int block,
+ int major, int minor, const std::vector<std::string>& links) const {
+ if (action == "add") {
+ MakeDevice(devpath, block, major, minor, links);
+ for (const auto& link : links) {
+ if (mkdir_recursive(android::base::Dirname(link), 0755, sehandle_)) {
+ PLOG(ERROR) << "Failed to create directory " << android::base::Dirname(link);
+ }
- if (mkdir_recursive(dirname(newpath), 0755)) {
- PLOG(ERROR) << "Failed to create directory " << dirname(newpath);
- }
-
- if (symlink(oldpath, newpath) && errno != EEXIST) {
- PLOG(ERROR) << "Failed to symlink " << oldpath << " to " << newpath;
- }
-}
-
-static void remove_link(const char* oldpath, const char* newpath) {
- std::string path;
- if (android::base::Readlink(newpath, &path) && path == oldpath) unlink(newpath);
-}
-
-static void handle_device(const char *action, const char *devpath,
- const char *path, int block, int major, int minor, char **links)
-{
- if(!strcmp(action, "add")) {
- make_device(devpath, path, block, major, minor, (const char **)links);
- if (links) {
- for (int i = 0; links[i]; i++) {
- make_link_init(devpath, links[i]);
+ if (symlink(devpath.c_str(), link.c_str()) && errno != EEXIST) {
+ PLOG(ERROR) << "Failed to symlink " << devpath << " to " << link;
}
}
}
- if(!strcmp(action, "remove")) {
- if (links) {
- for (int i = 0; links[i]; i++) {
- remove_link(devpath, links[i]);
+ if (action == "remove") {
+ for (const auto& link : links) {
+ std::string link_path;
+ if (android::base::Readlink(link, &link_path) && link_path == devpath) {
+ unlink(link.c_str());
}
}
- unlink(devpath);
- }
-
- if (links) {
- for (int i = 0; links[i]; i++) {
- free(links[i]);
- }
- free(links);
+ unlink(devpath.c_str());
}
}
-static void handle_platform_device_event(struct uevent *uevent)
-{
- const char *path = uevent->path;
-
- if (!strcmp(uevent->action, "add"))
- add_platform_device(path);
- else if (!strcmp(uevent->action, "remove"))
- remove_platform_device(path);
+void DeviceHandler::HandlePlatformDeviceEvent(const Uevent& uevent) {
+ if (uevent.action == "add") {
+ platform_devices_.Add(uevent.path);
+ } else if (uevent.action == "remove") {
+ platform_devices_.Remove(uevent.path);
+ }
}
-static const char *parse_device_name(struct uevent *uevent, unsigned int len)
-{
- const char *name;
+void DeviceHandler::HandleBlockDeviceEvent(const Uevent& uevent) const {
+ // if it's not a /dev device, nothing to do
+ if (uevent.major < 0 || uevent.minor < 0) return;
- /* if it's not a /dev device, nothing else to do */
- if((uevent->major < 0) || (uevent->minor < 0))
- return NULL;
+ const char* base = "/dev/block/";
+ make_dir(base, 0755, sehandle_);
- /* do we have a name? */
- name = strrchr(uevent->path, '/');
- if(!name)
- return NULL;
- name++;
+ std::string name = android::base::Basename(uevent.path);
+ std::string devpath = base + name;
- /* too-long names would overrun our buffer */
- if(strlen(name) > len) {
- LOG(ERROR) << "DEVPATH=" << name << " exceeds " << len << "-character limit on filename; ignoring event";
- return NULL;
+ std::vector<std::string> links;
+ if (android::base::StartsWith(uevent.path, "/devices")) {
+ links = GetBlockDeviceSymlinks(uevent);
}
- return name;
+ HandleDevice(uevent.action, devpath, 1, uevent.major, uevent.minor, links);
}
-#define DEVPATH_LEN 96
-#define MAX_DEV_NAME 64
+void DeviceHandler::HandleGenericDeviceEvent(const Uevent& uevent) const {
+ // if it's not a /dev device, nothing to do
+ if (uevent.major < 0 || uevent.minor < 0) return;
-static void handle_block_device_event(struct uevent *uevent)
-{
- const char *base = "/dev/block/";
- const char *name;
- char devpath[DEVPATH_LEN];
- char **links = NULL;
+ std::string devpath;
- name = parse_device_name(uevent, MAX_DEV_NAME);
- if (!name)
- return;
-
- snprintf(devpath, sizeof(devpath), "%s%s", base, name);
- make_dir(base, 0755);
-
- if (!strncmp(uevent->path, "/devices/", 9))
- links = get_block_device_symlinks(uevent);
-
- handle_device(uevent->action, devpath, uevent->path, 1,
- uevent->major, uevent->minor, links);
-}
-
-static bool assemble_devpath(char *devpath, const char *dirname,
- const char *devname)
-{
- int s = snprintf(devpath, DEVPATH_LEN, "%s/%s", dirname, devname);
- if (s < 0) {
- PLOG(ERROR) << "failed to assemble device path; ignoring event";
- return false;
- } else if (s >= DEVPATH_LEN) {
- LOG(ERROR) << dirname << "/" << devname
- << " exceeds " << DEVPATH_LEN << "-character limit on path; ignoring event";
- return false;
- }
- return true;
-}
-
-static void mkdir_recursive_for_devpath(const char *devpath)
-{
- char dir[DEVPATH_LEN];
- char *slash;
-
- strcpy(dir, devpath);
- slash = strrchr(dir, '/');
- *slash = '\0';
- mkdir_recursive(dir, 0755);
-}
-
-static void handle_generic_device_event(struct uevent *uevent)
-{
- const char *base;
- const char *name;
- char devpath[DEVPATH_LEN] = {0};
- char **links = NULL;
-
- name = parse_device_name(uevent, MAX_DEV_NAME);
- if (!name)
- return;
-
- struct ueventd_subsystem *subsystem =
- ueventd_subsystem_find_by_name(uevent->subsystem);
-
- if (subsystem) {
- const char *devname;
-
- switch (subsystem->devname_src) {
- case DEVNAME_UEVENT_DEVNAME:
- devname = uevent->device_name;
- break;
-
- case DEVNAME_UEVENT_DEVPATH:
- devname = name;
- break;
-
- default:
- LOG(ERROR) << uevent->subsystem << " subsystem's devpath option is not set; ignoring event";
+ if (android::base::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 = android::base::StringPrintf("/dev/bus/usb/%03d/%03d", bus_id, device_id);
+ }
+ } else {
+ // ignore other USB events
return;
}
-
- if (!assemble_devpath(devpath, subsystem->dirname, devname))
- return;
- mkdir_recursive_for_devpath(devpath);
- } else if (!strncmp(uevent->subsystem, "usb", 3)) {
- if (!strcmp(uevent->subsystem, "usb")) {
- if (uevent->device_name) {
- if (!assemble_devpath(devpath, "/dev", uevent->device_name))
- return;
- mkdir_recursive_for_devpath(devpath);
- }
- 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;
- /* build directories */
- make_dir("/dev/bus", 0755);
- make_dir("/dev/bus/usb", 0755);
- snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d", bus_id);
- make_dir(devpath, 0755);
- snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d/%03d", bus_id, device_id);
- }
- } else {
- /* ignore other USB events */
- return;
- }
- } else if (!strncmp(uevent->subsystem, "graphics", 8)) {
- base = "/dev/graphics/";
- make_dir(base, 0755);
- } else if (!strncmp(uevent->subsystem, "drm", 3)) {
- base = "/dev/dri/";
- make_dir(base, 0755);
- } else if (!strncmp(uevent->subsystem, "oncrpc", 6)) {
- base = "/dev/oncrpc/";
- make_dir(base, 0755);
- } else if (!strncmp(uevent->subsystem, "adsp", 4)) {
- base = "/dev/adsp/";
- make_dir(base, 0755);
- } else if (!strncmp(uevent->subsystem, "msm_camera", 10)) {
- base = "/dev/msm_camera/";
- make_dir(base, 0755);
- } else if(!strncmp(uevent->subsystem, "input", 5)) {
- base = "/dev/input/";
- make_dir(base, 0755);
- } else if(!strncmp(uevent->subsystem, "mtd", 3)) {
- base = "/dev/mtd/";
- make_dir(base, 0755);
- } else if(!strncmp(uevent->subsystem, "sound", 5)) {
- base = "/dev/snd/";
- make_dir(base, 0755);
- } else if(!strncmp(uevent->subsystem, "misc", 4) && !strncmp(name, "log_", 4)) {
- LOG(INFO) << "kernel logger is deprecated";
- base = "/dev/log/";
- make_dir(base, 0755);
- name += 4;
- } else
- base = "/dev/";
- links = get_character_device_symlinks(uevent);
-
- if (!devpath[0])
- snprintf(devpath, sizeof(devpath), "%s%s", base, name);
-
- handle_device(uevent->action, devpath, uevent->path, 0,
- uevent->major, uevent->minor, links);
-}
-
-static void handle_device_event(struct uevent *uevent)
-{
- if (!strcmp(uevent->action,"add") || !strcmp(uevent->action, "change") || !strcmp(uevent->action, "online"))
- fixup_sys_perms(uevent->path, uevent->subsystem);
-
- if (!strncmp(uevent->subsystem, "block", 5)) {
- handle_block_device_event(uevent);
- } else if (!strncmp(uevent->subsystem, "platform", 8)) {
- handle_platform_device_event(uevent);
+ } else if (const auto subsystem =
+ std::find(subsystems_.cbegin(), subsystems_.cend(), uevent.subsystem);
+ subsystem != subsystems_.cend()) {
+ devpath = subsystem->ParseDevPath(uevent);
} else {
- handle_generic_device_event(uevent);
+ devpath = "/dev/" + android::base::Basename(uevent.path);
}
+
+ mkdir_recursive(android::base::Dirname(devpath), 0755, sehandle_);
+
+ auto links = GetCharacterDeviceSymlinks(uevent);
+
+ HandleDevice(uevent.action, devpath, 0, uevent.major, uevent.minor, links);
}
-static void load_firmware(uevent* uevent, const std::string& root,
- int fw_fd, size_t fw_size,
- int loading_fd, int data_fd) {
- // Start transfer.
- android::base::WriteFully(loading_fd, "1", 1);
-
- // Copy the firmware.
- int rc = sendfile(data_fd, fw_fd, nullptr, fw_size);
- if (rc == -1) {
- PLOG(ERROR) << "firmware: sendfile failed { '" << root << "', '" << uevent->firmware << "' }";
+void DeviceHandler::HandleDeviceEvent(const Uevent& uevent) {
+ if (uevent.action == "add" || uevent.action == "change" || uevent.action == "online") {
+ FixupSysPermissions(uevent.path, uevent.subsystem);
}
- // Tell the firmware whether to abort or commit.
- const char* response = (rc != -1) ? "0" : "-1";
- android::base::WriteFully(loading_fd, response, strlen(response));
-}
-
-static int is_booting() {
- return access("/dev/.booting", F_OK) == 0;
-}
-
-static void process_firmware_event(uevent* uevent) {
- int booting = is_booting();
-
- LOG(INFO) << "firmware: loading '" << uevent->firmware << "' for '" << uevent->path << "'";
-
- std::string root = android::base::StringPrintf("/sys%s", uevent->path);
- std::string loading = root + "/loading";
- std::string data = root + "/data";
-
- android::base::unique_fd loading_fd(open(loading.c_str(), O_WRONLY|O_CLOEXEC));
- if (loading_fd == -1) {
- PLOG(ERROR) << "couldn't open firmware loading fd for " << uevent->firmware;
- return;
- }
-
- android::base::unique_fd data_fd(open(data.c_str(), O_WRONLY|O_CLOEXEC));
- if (data_fd == -1) {
- PLOG(ERROR) << "couldn't open firmware data fd for " << uevent->firmware;
- return;
- }
-
-try_loading_again:
- for (size_t i = 0; i < arraysize(firmware_dirs); i++) {
- std::string file = android::base::StringPrintf("%s/%s", firmware_dirs[i], uevent->firmware);
- android::base::unique_fd fw_fd(open(file.c_str(), O_RDONLY|O_CLOEXEC));
- struct stat sb;
- if (fw_fd != -1 && fstat(fw_fd, &sb) != -1) {
- load_firmware(uevent, root, fw_fd, sb.st_size, loading_fd, data_fd);
- return;
- }
- }
-
- if (booting) {
- // If we're not fully booted, we may be missing
- // filesystems needed for firmware, wait and retry.
- std::this_thread::sleep_for(100ms);
- booting = is_booting();
- goto try_loading_again;
- }
-
- LOG(ERROR) << "firmware: could not find firmware for " << uevent->firmware;
-
- // Write "-1" as our response to the kernel's firmware request, since we have nothing for it.
- write(loading_fd, "-1", 2);
-}
-
-static void handle_firmware_event(uevent* uevent) {
- if (strcmp(uevent->subsystem, "firmware")) return;
- if (strcmp(uevent->action, "add")) return;
-
- // Loading the firmware in a child means we can do that in parallel...
- // (We ignore SIGCHLD rather than wait for our children.)
- pid_t pid = fork();
- if (pid == 0) {
- Timer t;
- process_firmware_event(uevent);
- LOG(INFO) << "loading " << uevent->path << " took " << t;
- _exit(EXIT_SUCCESS);
- } else if (pid == -1) {
- PLOG(ERROR) << "could not fork to process firmware event for " << uevent->firmware;
- }
-}
-
-static bool inline should_stop_coldboot(coldboot_action_t act)
-{
- return (act == COLDBOOT_STOP || act == COLDBOOT_FINISH);
-}
-
-#define UEVENT_MSG_LEN 2048
-
-static inline coldboot_action_t handle_device_fd_with(
- std::function<coldboot_action_t(uevent* uevent)> handle_uevent)
-{
- char msg[UEVENT_MSG_LEN+2];
- int n;
- while ((n = uevent_kernel_multicast_recv(device_fd, msg, UEVENT_MSG_LEN)) > 0) {
- if(n >= UEVENT_MSG_LEN) /* overflow -- discard */
- continue;
-
- msg[n] = '\0';
- msg[n+1] = '\0';
-
- struct uevent uevent;
- parse_event(msg, &uevent);
- coldboot_action_t act = handle_uevent(&uevent);
- if (should_stop_coldboot(act))
- return act;
- }
-
- return COLDBOOT_CONTINUE;
-}
-
-coldboot_action_t handle_device_fd(coldboot_callback fn)
-{
- coldboot_action_t ret = handle_device_fd_with(
- [&](uevent* uevent) -> coldboot_action_t {
- if (selinux_status_updated() > 0) {
- struct selabel_handle *sehandle2;
- sehandle2 = selinux_android_file_context_handle();
- if (sehandle2) {
- selabel_close(sehandle);
- sehandle = sehandle2;
- }
- }
-
- // default is to always create the devices
- coldboot_action_t act = COLDBOOT_CREATE;
- if (fn) {
- act = fn(uevent);
- }
-
- if (act == COLDBOOT_CREATE || act == COLDBOOT_STOP) {
- handle_device_event(uevent);
- handle_firmware_event(uevent);
- }
-
- return act;
- });
-
- return ret;
-}
-
-/* Coldboot walks parts of the /sys tree and pokes the uevent files
-** to cause the kernel to regenerate device add events that happened
-** before init's device manager was started
-**
-** We drain any pending events from the netlink socket every time
-** we poke another uevent file to make sure we don't overrun the
-** socket's buffer.
-*/
-
-static coldboot_action_t do_coldboot(DIR *d, coldboot_callback fn)
-{
- struct dirent *de;
- int dfd, fd;
- coldboot_action_t act = COLDBOOT_CONTINUE;
-
- dfd = dirfd(d);
-
- fd = openat(dfd, "uevent", O_WRONLY);
- if (fd >= 0) {
- write(fd, "add\n", 4);
- close(fd);
- act = handle_device_fd(fn);
- if (should_stop_coldboot(act))
- return act;
- }
-
- while (!should_stop_coldboot(act) && (de = readdir(d))) {
- DIR *d2;
-
- if(de->d_type != DT_DIR || de->d_name[0] == '.')
- continue;
-
- fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY);
- if(fd < 0)
- continue;
-
- d2 = fdopendir(fd);
- if(d2 == 0)
- close(fd);
- else {
- act = do_coldboot(d2, fn);
- closedir(d2);
- }
- }
-
- // default is always to continue looking for uevents
- return act;
-}
-
-static coldboot_action_t coldboot(const char *path, coldboot_callback fn)
-{
- std::unique_ptr<DIR, decltype(&closedir)> d(opendir(path), closedir);
- if (d) {
- return do_coldboot(d.get(), fn);
- }
-
- return COLDBOOT_CONTINUE;
-}
-
-void device_init(const char* path, coldboot_callback fn) {
- if (!sehandle) {
- sehandle = selinux_android_file_context_handle();
- }
- // open uevent socket and selinux status only if it hasn't been
- // done before
- if (device_fd == -1) {
- /* is 256K enough? udev uses 16MB! */
- device_fd.reset(uevent_open_socket(256 * 1024, true));
- if (device_fd == -1) {
- return;
- }
- fcntl(device_fd, F_SETFL, O_NONBLOCK);
- selinux_status_open(true);
- }
-
- if (access(COLDBOOT_DONE, F_OK) == 0) {
- LOG(VERBOSE) << "Skipping coldboot, already done!";
- return;
- }
-
- Timer t;
- coldboot_action_t act;
- if (!path) {
- act = coldboot("/sys/class", fn);
- if (!should_stop_coldboot(act)) {
- act = coldboot("/sys/block", fn);
- if (!should_stop_coldboot(act)) {
- act = coldboot("/sys/devices", fn);
- }
- }
+ if (uevent.subsystem == "block") {
+ HandleBlockDeviceEvent(uevent);
+ } else if (uevent.subsystem == "platform") {
+ HandlePlatformDeviceEvent(uevent);
} else {
- act = coldboot(path, fn);
+ HandleGenericDeviceEvent(uevent);
}
-
- // If we have a callback, then do as it says. If no, then the default is
- // to always create COLDBOOT_DONE file.
- if (!fn || (act == COLDBOOT_FINISH)) {
- close(open(COLDBOOT_DONE, O_WRONLY|O_CREAT|O_CLOEXEC, 0000));
- }
-
- LOG(INFO) << "Coldboot took " << t;
}
-void device_close() {
- destroy_platform_devices();
- device_fd.reset();
- selinux_status_close();
-}
+DeviceHandler::DeviceHandler(std::vector<Permissions> dev_permissions,
+ std::vector<SysfsPermissions> sysfs_permissions,
+ std::vector<Subsystem> subsystems)
+ : dev_permissions_(std::move(dev_permissions)),
+ sysfs_permissions_(std::move(sysfs_permissions)),
+ subsystems_(std::move(subsystems)),
+ sehandle_(selinux_android_file_context_handle()) {}
-int get_device_fd() {
- return device_fd;
-}
+DeviceHandler::DeviceHandler()
+ : DeviceHandler(std::vector<Permissions>{}, std::vector<SysfsPermissions>{},
+ std::vector<Subsystem>{}) {}
diff --git a/init/devices.h b/init/devices.h
index 26a064b..50f49fc 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -17,42 +17,130 @@
#ifndef _INIT_DEVICES_H
#define _INIT_DEVICES_H
-#include <functional>
#include <sys/stat.h>
+#include <sys/types.h>
-enum coldboot_action_t {
- // coldboot continues without creating the device for the uevent
- COLDBOOT_CONTINUE = 0,
- // coldboot continues after creating the device for the uevent
- COLDBOOT_CREATE,
- // coldboot stops after creating the device for uevent but doesn't
- // create the COLDBOOT_DONE file
- COLDBOOT_STOP,
- // same as COLDBOOT_STOP, but creates the COLDBOOT_DONE file
- COLDBOOT_FINISH
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <selinux/label.h>
+
+#include "uevent.h"
+
+class Permissions {
+ public:
+ Permissions(const std::string& name, mode_t perm, uid_t uid, gid_t gid);
+
+ bool Match(const std::string& path) const;
+
+ mode_t perm() const { return perm_; }
+ uid_t uid() const { return uid_; }
+ gid_t gid() const { return gid_; }
+
+ protected:
+ const std::string& name() const { return name_; }
+
+ private:
+ std::string name_;
+ mode_t perm_;
+ uid_t uid_;
+ gid_t gid_;
+ bool prefix_;
+ bool wildcard_;
};
-struct uevent {
- const char* action;
- const char* path;
- const char* subsystem;
- const char* firmware;
- const char* partition_name;
- const char* device_name;
- int partition_num;
- int major;
- int minor;
+class SysfsPermissions : public Permissions {
+ public:
+ SysfsPermissions(const std::string& name, const std::string& attribute, mode_t perm, uid_t uid,
+ gid_t gid)
+ : Permissions(name, perm, uid, gid), attribute_(attribute) {}
+
+ bool MatchWithSubsystem(const std::string& path, const std::string& subsystem) const;
+ void SetPermissions(const std::string& path) const;
+
+ private:
+ const std::string attribute_;
};
-typedef std::function<coldboot_action_t(struct uevent* uevent)> coldboot_callback;
-extern coldboot_action_t handle_device_fd(coldboot_callback fn = nullptr);
-extern void device_init(const char* path = nullptr, coldboot_callback fn = nullptr);
-extern void device_close();
+class Subsystem {
+ public:
+ friend class SubsystemParser;
-extern int add_dev_perms(const char *name, const char *attr,
- mode_t perm, unsigned int uid,
- unsigned int gid, unsigned short prefix,
- unsigned short wildcard);
-int get_device_fd();
+ Subsystem() {}
-#endif /* _INIT_DEVICES_H */
+ // 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
+ std::string ParseDevPath(const Uevent& uevent) const {
+ std::string devname = devname_source_ == DevnameSource::DEVNAME_UEVENT_DEVNAME
+ ? uevent.device_name
+ : android::base::Basename(uevent.path);
+
+ return dir_name_ + "/" + devname;
+ }
+
+ bool operator==(const std::string& string_name) const { return name_ == string_name; }
+
+ private:
+ enum class DevnameSource {
+ DEVNAME_UEVENT_DEVNAME,
+ DEVNAME_UEVENT_DEVPATH,
+ };
+
+ std::string name_;
+ std::string dir_name_ = "/dev";
+ DevnameSource devname_source_;
+};
+
+class PlatformDeviceList {
+ public:
+ void Add(const std::string& path) { platform_devices_.emplace_back(path); }
+ void Remove(const std::string& path) {
+ auto it = std::find(platform_devices_.begin(), platform_devices_.end(), path);
+ if (it != platform_devices_.end()) platform_devices_.erase(it);
+ }
+ bool Find(const std::string& path, std::string* out_path) const;
+ auto size() const { return platform_devices_.size(); }
+
+ private:
+ std::vector<std::string> platform_devices_;
+};
+
+class DeviceHandler {
+ public:
+ friend class DeviceHandlerTester;
+
+ DeviceHandler();
+ DeviceHandler(std::vector<Permissions> dev_permissions,
+ std::vector<SysfsPermissions> sysfs_permissions,
+ std::vector<Subsystem> subsystems);
+ ~DeviceHandler(){};
+
+ void HandleDeviceEvent(const Uevent& uevent);
+ std::vector<std::string> GetBlockDeviceSymlinks(const Uevent& uevent) const;
+
+ private:
+ void FixupSysPermissions(const std::string& upath, const std::string& subsystem) const;
+ std::tuple<mode_t, uid_t, gid_t> GetDevicePermissions(
+ const std::string& path, const std::vector<std::string>& links) const;
+ void MakeDevice(const std::string& path, int block, int major, int minor,
+ const std::vector<std::string>& links) const;
+ std::vector<std::string> GetCharacterDeviceSymlinks(const Uevent& uevent) const;
+ void HandleDevice(const std::string& action, const std::string& devpath, int block, int major,
+ int minor, const std::vector<std::string>& links) const;
+ void HandlePlatformDeviceEvent(const Uevent& uevent);
+ void HandleBlockDeviceEvent(const Uevent& uevent) const;
+ void HandleGenericDeviceEvent(const Uevent& uevent) const;
+
+ std::vector<Permissions> dev_permissions_;
+ std::vector<SysfsPermissions> sysfs_permissions_;
+ std::vector<Subsystem> subsystems_;
+ PlatformDeviceList platform_devices_;
+ selabel_handle* sehandle_;
+};
+
+// Exposed for testing
+void SanitizePartitionName(std::string* string);
+
+#endif
diff --git a/init/devices_test.cpp b/init/devices_test.cpp
new file mode 100644
index 0000000..41b101b
--- /dev/null
+++ b/init/devices_test.cpp
@@ -0,0 +1,430 @@
+/*
+ * 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 "devices.h"
+
+#include <string>
+#include <vector>
+
+#include <android-base/scopeguard.h>
+#include <gtest/gtest.h>
+
+class DeviceHandlerTester {
+ public:
+ void AddPlatformDevice(const std::string& path) {
+ Uevent uevent = {
+ .action = "add", .subsystem = "platform", .path = path,
+ };
+ device_handler_.HandlePlatformDeviceEvent(uevent);
+ }
+
+ void RemovePlatformDevice(const std::string& path) {
+ Uevent uevent = {
+ .action = "remove", .subsystem = "platform", .path = path,
+ };
+ device_handler_.HandlePlatformDeviceEvent(uevent);
+ }
+
+ void TestGetSymlinks(const std::string& platform_device_name, const Uevent& uevent,
+ const std::vector<std::string> expected_links, bool block) {
+ AddPlatformDevice(platform_device_name);
+ auto platform_device_remover = android::base::make_scope_guard(
+ [this, &platform_device_name]() { RemovePlatformDevice(platform_device_name); });
+
+ std::vector<std::string> result;
+ if (block) {
+ result = device_handler_.GetBlockDeviceSymlinks(uevent);
+ } else {
+ result = device_handler_.GetCharacterDeviceSymlinks(uevent);
+ }
+
+ auto expected_size = expected_links.size();
+ ASSERT_EQ(expected_size, result.size());
+ if (expected_size == 0) return;
+
+ // Explicitly iterate so the results are visible if a failure occurs
+ for (unsigned int i = 0; i < expected_size; ++i) {
+ EXPECT_EQ(expected_links[i], result[i]);
+ }
+ }
+
+ private:
+ DeviceHandler device_handler_;
+};
+
+TEST(device_handler, PlatformDeviceList) {
+ PlatformDeviceList platform_device_list;
+
+ platform_device_list.Add("/devices/platform/some_device_name");
+ platform_device_list.Add("/devices/platform/some_device_name/longer");
+ platform_device_list.Add("/devices/platform/other_device_name");
+ EXPECT_EQ(3U, platform_device_list.size());
+
+ std::string out_path;
+ EXPECT_FALSE(platform_device_list.Find("/devices/platform/not_found", &out_path));
+ EXPECT_EQ("", out_path);
+
+ EXPECT_FALSE(platform_device_list.Find("/devices/platform/some_device_name_with_same_prefix",
+ &out_path));
+
+ EXPECT_TRUE(platform_device_list.Find("/devices/platform/some_device_name/longer/longer_child",
+ &out_path));
+ EXPECT_EQ("/devices/platform/some_device_name/longer", out_path);
+
+ EXPECT_TRUE(
+ platform_device_list.Find("/devices/platform/some_device_name/other_child", &out_path));
+ EXPECT_EQ("/devices/platform/some_device_name", out_path);
+}
+
+TEST(device_handler, get_character_device_symlinks_success) {
+ const char* platform_device = "/devices/platform/some_device_name";
+ Uevent uevent = {
+ .path = "/devices/platform/some_device_name/usb/usb_device/name/tty2-1:1.0",
+ .subsystem = "tty",
+ };
+ std::vector<std::string> expected_result{"/dev/usb/ttyname"};
+
+ DeviceHandlerTester device_handler_tester_;
+ device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, false);
+}
+
+TEST(device_handler, get_character_device_symlinks_no_pdev_match) {
+ const char* platform_device = "/devices/platform/some_device_name";
+ Uevent uevent = {
+ .path = "/device/name/tty2-1:1.0", .subsystem = "tty",
+ };
+ std::vector<std::string> expected_result;
+
+ DeviceHandlerTester device_handler_tester_;
+ device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, false);
+}
+
+TEST(device_handler, get_character_device_symlinks_nothing_after_platform_device) {
+ const char* platform_device = "/devices/platform/some_device_name";
+ Uevent uevent = {
+ .path = "/devices/platform/some_device_name", .subsystem = "tty",
+ };
+ std::vector<std::string> expected_result;
+
+ DeviceHandlerTester device_handler_tester_;
+ device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, false);
+}
+
+TEST(device_handler, get_character_device_symlinks_no_usb_found) {
+ const char* platform_device = "/devices/platform/some_device_name";
+ Uevent uevent = {
+ .path = "/devices/platform/some_device_name/bad/bad/", .subsystem = "tty",
+ };
+ std::vector<std::string> expected_result;
+
+ DeviceHandlerTester device_handler_tester_;
+ device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, false);
+}
+
+TEST(device_handler, get_character_device_symlinks_no_roothub) {
+ const char* platform_device = "/devices/platform/some_device_name";
+ Uevent uevent = {
+ .path = "/devices/platform/some_device_name/usb/", .subsystem = "tty",
+ };
+ std::vector<std::string> expected_result;
+
+ DeviceHandlerTester device_handler_tester_;
+ device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, false);
+}
+
+TEST(device_handler, get_character_device_symlinks_no_usb_device) {
+ const char* platform_device = "/devices/platform/some_device_name";
+ Uevent uevent = {
+ .path = "/devices/platform/some_device_name/usb/usb_device/", .subsystem = "tty",
+ };
+ std::vector<std::string> expected_result;
+
+ DeviceHandlerTester device_handler_tester_;
+ device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, false);
+}
+
+TEST(device_handler, get_character_device_symlinks_no_final_slash) {
+ const char* platform_device = "/devices/platform/some_device_name";
+ Uevent uevent = {
+ .path = "/devices/platform/some_device_name/usb/usb_device/name", .subsystem = "tty",
+ };
+ std::vector<std::string> expected_result;
+
+ DeviceHandlerTester device_handler_tester_;
+ device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, false);
+}
+
+TEST(device_handler, get_character_device_symlinks_no_final_name) {
+ const char* platform_device = "/devices/platform/some_device_name";
+ Uevent uevent = {
+ .path = "/devices/platform/some_device_name/usb/usb_device//", .subsystem = "tty",
+ };
+ std::vector<std::string> expected_result;
+
+ DeviceHandlerTester device_handler_tester_;
+ device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, false);
+}
+
+TEST(device_handler, get_block_device_symlinks_success_platform) {
+ // These are actual paths from bullhead
+ const char* platform_device = "/devices/soc.0/f9824900.sdhci";
+ Uevent uevent = {
+ .path = "/devices/soc.0/f9824900.sdhci/mmc_host/mmc0/mmc0:0001/block/mmcblk0",
+ .partition_name = "",
+ .partition_num = -1,
+ };
+ std::vector<std::string> expected_result{"/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0"};
+
+ DeviceHandlerTester device_handler_tester_;
+ device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true);
+}
+
+TEST(device_handler, get_block_device_symlinks_success_platform_with_partition) {
+ // These are actual paths from bullhead
+ const char* platform_device = "/devices/soc.0/f9824900.sdhci";
+ Uevent uevent = {
+ .path = "/devices/soc.0/f9824900.sdhci/mmc_host/mmc0/mmc0:0001/block/mmcblk0p1",
+ .partition_name = "modem",
+ .partition_num = 1,
+ };
+ std::vector<std::string> expected_result{
+ "/dev/block/platform/soc.0/f9824900.sdhci/by-name/modem",
+ "/dev/block/platform/soc.0/f9824900.sdhci/by-num/p1",
+ "/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1",
+ };
+
+ DeviceHandlerTester device_handler_tester_;
+ device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true);
+}
+
+TEST(device_handler, get_block_device_symlinks_success_platform_with_partition_only_num) {
+ const char* platform_device = "/devices/soc.0/f9824900.sdhci";
+ Uevent uevent = {
+ .path = "/devices/soc.0/f9824900.sdhci/mmc_host/mmc0/mmc0:0001/block/mmcblk0p1",
+ .partition_name = "",
+ .partition_num = 1,
+ };
+ std::vector<std::string> expected_result{
+ "/dev/block/platform/soc.0/f9824900.sdhci/by-num/p1",
+ "/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1",
+ };
+
+ DeviceHandlerTester device_handler_tester_;
+ device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true);
+}
+
+TEST(device_handler, get_block_device_symlinks_success_platform_with_partition_only_name) {
+ const char* platform_device = "/devices/soc.0/f9824900.sdhci";
+ Uevent uevent = {
+ .path = "/devices/soc.0/f9824900.sdhci/mmc_host/mmc0/mmc0:0001/block/mmcblk0p1",
+ .partition_name = "modem",
+ .partition_num = -1,
+ };
+ std::vector<std::string> expected_result{
+ "/dev/block/platform/soc.0/f9824900.sdhci/by-name/modem",
+ "/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1",
+ };
+
+ DeviceHandlerTester device_handler_tester_;
+ device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true);
+}
+
+TEST(device_handler, get_block_device_symlinks_success_pci) {
+ const char* platform_device = "/devices/do/not/match";
+ Uevent uevent = {
+ .path = "/devices/pci0000:00/0000:00:1f.2/mmcblk0", .partition_name = "", .partition_num = -1,
+ };
+ std::vector<std::string> expected_result{"/dev/block/pci/pci0000:00/0000:00:1f.2/mmcblk0"};
+
+ DeviceHandlerTester device_handler_tester_;
+ device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true);
+}
+
+TEST(device_handler, get_block_device_symlinks_pci_bad_format) {
+ const char* platform_device = "/devices/do/not/match";
+ Uevent uevent = {
+ .path = "/devices/pci//mmcblk0", .partition_name = "", .partition_num = -1,
+ };
+ std::vector<std::string> expected_result{};
+
+ DeviceHandlerTester device_handler_tester_;
+ device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true);
+}
+
+TEST(device_handler, get_block_device_symlinks_success_vbd) {
+ const char* platform_device = "/devices/do/not/match";
+ Uevent uevent = {
+ .path = "/devices/vbd-1234/mmcblk0", .partition_name = "", .partition_num = -1,
+ };
+ std::vector<std::string> expected_result{"/dev/block/vbd/1234/mmcblk0"};
+
+ DeviceHandlerTester device_handler_tester_;
+ device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true);
+}
+
+TEST(device_handler, get_block_device_symlinks_vbd_bad_format) {
+ const char* platform_device = "/devices/do/not/match";
+ Uevent uevent = {
+ .path = "/devices/vbd-/mmcblk0", .partition_name = "", .partition_num = -1,
+ };
+ std::vector<std::string> expected_result{};
+
+ DeviceHandlerTester device_handler_tester_;
+ device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true);
+}
+
+TEST(device_handler, get_block_device_symlinks_no_matches) {
+ const char* platform_device = "/devices/soc.0/f9824900.sdhci";
+ Uevent uevent = {
+ .path = "/devices/soc.0/not_the_device/mmc_host/mmc0/mmc0:0001/block/mmcblk0p1",
+ .partition_name = "",
+ .partition_num = -1,
+ };
+ std::vector<std::string> expected_result;
+
+ DeviceHandlerTester device_handler_tester_;
+ device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true);
+}
+
+TEST(device_handler, sanitize_null) {
+ SanitizePartitionName(nullptr);
+}
+
+TEST(device_handler, sanitize_empty) {
+ std::string empty;
+ SanitizePartitionName(&empty);
+ EXPECT_EQ(0u, empty.size());
+}
+
+TEST(device_handler, sanitize_allgood) {
+ std::string good =
+ "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "0123456789"
+ "_-.";
+ std::string good_copy = good;
+ SanitizePartitionName(&good);
+ EXPECT_EQ(good_copy, good);
+}
+
+TEST(device_handler, sanitize_somebad) {
+ std::string string = "abc!@#$%^&*()";
+ SanitizePartitionName(&string);
+ EXPECT_EQ("abc__________", string);
+}
+
+TEST(device_handler, sanitize_allbad) {
+ std::string string = "!@#$%^&*()";
+ SanitizePartitionName(&string);
+ EXPECT_EQ("__________", string);
+}
+
+TEST(device_handler, sanitize_onebad) {
+ std::string string = ")";
+ SanitizePartitionName(&string);
+ EXPECT_EQ("_", string);
+}
+
+TEST(device_handler, DevPermissionsMatchNormal) {
+ // Basic from ueventd.rc
+ // /dev/null 0666 root root
+ Permissions permissions("/dev/null", 0666, 0, 0);
+ EXPECT_TRUE(permissions.Match("/dev/null"));
+ EXPECT_FALSE(permissions.Match("/dev/nullsuffix"));
+ EXPECT_FALSE(permissions.Match("/dev/nul"));
+ EXPECT_EQ(0666U, permissions.perm());
+ EXPECT_EQ(0U, permissions.uid());
+ EXPECT_EQ(0U, permissions.gid());
+}
+
+TEST(device_handler, DevPermissionsMatchPrefix) {
+ // Prefix from ueventd.rc
+ // /dev/dri/* 0666 root graphics
+ Permissions permissions("/dev/dri/*", 0666, 0, 1000);
+ EXPECT_TRUE(permissions.Match("/dev/dri/some_dri_device"));
+ EXPECT_TRUE(permissions.Match("/dev/dri/some_other_dri_device"));
+ EXPECT_TRUE(permissions.Match("/dev/dri/"));
+ EXPECT_FALSE(permissions.Match("/dev/dr/non_match"));
+ EXPECT_EQ(0666U, permissions.perm());
+ EXPECT_EQ(0U, permissions.uid());
+ EXPECT_EQ(1000U, permissions.gid());
+}
+
+TEST(device_handler, DevPermissionsMatchWildcard) {
+ // Wildcard example
+ // /dev/device*name 0666 root graphics
+ Permissions permissions("/dev/device*name", 0666, 0, 1000);
+ EXPECT_TRUE(permissions.Match("/dev/devicename"));
+ EXPECT_TRUE(permissions.Match("/dev/device123name"));
+ EXPECT_TRUE(permissions.Match("/dev/deviceabcname"));
+ EXPECT_FALSE(permissions.Match("/dev/device123name/subdevice"));
+ EXPECT_FALSE(permissions.Match("/dev/deviceame"));
+ EXPECT_EQ(0666U, permissions.perm());
+ EXPECT_EQ(0U, permissions.uid());
+ EXPECT_EQ(1000U, permissions.gid());
+}
+
+TEST(device_handler, DevPermissionsMatchWildcardPrefix) {
+ // Wildcard+Prefix example
+ // /dev/device*name* 0666 root graphics
+ Permissions permissions("/dev/device*name*", 0666, 0, 1000);
+ EXPECT_TRUE(permissions.Match("/dev/devicename"));
+ EXPECT_TRUE(permissions.Match("/dev/device123name"));
+ EXPECT_TRUE(permissions.Match("/dev/deviceabcname"));
+ EXPECT_TRUE(permissions.Match("/dev/device123namesomething"));
+ // FNM_PATHNAME doesn't match '/' with *
+ EXPECT_FALSE(permissions.Match("/dev/device123name/something"));
+ EXPECT_FALSE(permissions.Match("/dev/deviceame"));
+ EXPECT_EQ(0666U, permissions.perm());
+ EXPECT_EQ(0U, permissions.uid());
+ EXPECT_EQ(1000U, permissions.gid());
+}
+
+TEST(device_handler, SysfsPermissionsMatchWithSubsystemNormal) {
+ // /sys/devices/virtual/input/input* enable 0660 root input
+ SysfsPermissions permissions("/sys/devices/virtual/input/input*", "enable", 0660, 0, 1001);
+ EXPECT_TRUE(permissions.MatchWithSubsystem("/sys/devices/virtual/input/input0", "input"));
+ EXPECT_FALSE(permissions.MatchWithSubsystem("/sys/devices/virtual/input/not_input0", "input"));
+ EXPECT_EQ(0660U, permissions.perm());
+ EXPECT_EQ(0U, permissions.uid());
+ EXPECT_EQ(1001U, permissions.gid());
+}
+
+TEST(device_handler, SysfsPermissionsMatchWithSubsystemClass) {
+ // /sys/class/input/event* enable 0660 root input
+ SysfsPermissions permissions("/sys/class/input/event*", "enable", 0660, 0, 1001);
+ EXPECT_TRUE(permissions.MatchWithSubsystem(
+ "/sys/devices/soc.0/f9924000.i2c/i2c-2/2-0020/input/input0/event0", "input"));
+ EXPECT_FALSE(permissions.MatchWithSubsystem(
+ "/sys/devices/soc.0/f9924000.i2c/i2c-2/2-0020/input/input0/not_event0", "input"));
+ EXPECT_FALSE(permissions.MatchWithSubsystem(
+ "/sys/devices/soc.0/f9924000.i2c/i2c-2/2-0020/input/input0/event0", "not_input"));
+ EXPECT_EQ(0660U, permissions.perm());
+ EXPECT_EQ(0U, permissions.uid());
+ EXPECT_EQ(1001U, permissions.gid());
+}
+
+TEST(device_handler, SysfsPermissionsMatchWithSubsystemBus) {
+ // /sys/bus/i2c/devices/i2c-* enable 0660 root input
+ SysfsPermissions permissions("/sys/bus/i2c/devices/i2c-*", "enable", 0660, 0, 1001);
+ EXPECT_TRUE(permissions.MatchWithSubsystem("/sys/devices/soc.0/f9967000.i2c/i2c-5", "i2c"));
+ EXPECT_FALSE(permissions.MatchWithSubsystem("/sys/devices/soc.0/f9967000.i2c/not-i2c", "i2c"));
+ EXPECT_FALSE(
+ permissions.MatchWithSubsystem("/sys/devices/soc.0/f9967000.i2c/i2c-5", "not_i2c"));
+ EXPECT_EQ(0660U, permissions.perm());
+ EXPECT_EQ(0U, permissions.uid());
+ EXPECT_EQ(1001U, permissions.gid());
+}
diff --git a/init/firmware_handler.cpp b/init/firmware_handler.cpp
new file mode 100644
index 0000000..1471aeb
--- /dev/null
+++ b/init/firmware_handler.cpp
@@ -0,0 +1,116 @@
+/*
+ * 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 "firmware_handler.h"
+
+#include <fcntl.h>
+#include <sys/sendfile.h>
+#include <unistd.h>
+
+#include <string>
+#include <thread>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+
+#include "util.h"
+
+static void LoadFirmware(const Uevent& uevent, const std::string& root, int fw_fd, size_t fw_size,
+ int loading_fd, int data_fd) {
+ // Start transfer.
+ android::base::WriteFully(loading_fd, "1", 1);
+
+ // Copy the firmware.
+ int rc = sendfile(data_fd, fw_fd, nullptr, fw_size);
+ if (rc == -1) {
+ PLOG(ERROR) << "firmware: sendfile failed { '" << root << "', '" << uevent.firmware
+ << "' }";
+ }
+
+ // Tell the firmware whether to abort or commit.
+ const char* response = (rc != -1) ? "0" : "-1";
+ android::base::WriteFully(loading_fd, response, strlen(response));
+}
+
+static bool IsBooting() {
+ return access("/dev/.booting", F_OK) == 0;
+}
+
+static void ProcessFirmwareEvent(const Uevent& uevent) {
+ int booting = IsBooting();
+
+ LOG(INFO) << "firmware: loading '" << uevent.firmware << "' for '" << uevent.path << "'";
+
+ std::string root = "/sys" + uevent.path;
+ std::string loading = root + "/loading";
+ std::string data = root + "/data";
+
+ android::base::unique_fd loading_fd(open(loading.c_str(), O_WRONLY | O_CLOEXEC));
+ if (loading_fd == -1) {
+ PLOG(ERROR) << "couldn't open firmware loading fd for " << uevent.firmware;
+ return;
+ }
+
+ android::base::unique_fd data_fd(open(data.c_str(), O_WRONLY | O_CLOEXEC));
+ if (data_fd == -1) {
+ PLOG(ERROR) << "couldn't open firmware data fd for " << uevent.firmware;
+ return;
+ }
+
+ static const char* firmware_dirs[] = {"/etc/firmware/", "/vendor/firmware/",
+ "/firmware/image/"};
+
+try_loading_again:
+ for (size_t i = 0; i < arraysize(firmware_dirs); i++) {
+ std::string file = firmware_dirs[i] + uevent.firmware;
+ android::base::unique_fd fw_fd(open(file.c_str(), O_RDONLY | O_CLOEXEC));
+ struct stat sb;
+ if (fw_fd != -1 && fstat(fw_fd, &sb) != -1) {
+ LoadFirmware(uevent, root, fw_fd, sb.st_size, loading_fd, data_fd);
+ return;
+ }
+ }
+
+ if (booting) {
+ // If we're not fully booted, we may be missing
+ // filesystems needed for firmware, wait and retry.
+ std::this_thread::sleep_for(100ms);
+ booting = IsBooting();
+ goto try_loading_again;
+ }
+
+ LOG(ERROR) << "firmware: could not find firmware for " << uevent.firmware;
+
+ // Write "-1" as our response to the kernel's firmware request, since we have nothing for it.
+ write(loading_fd, "-1", 2);
+}
+
+void HandleFirmwareEvent(const Uevent& uevent) {
+ if (uevent.subsystem != "firmware" || uevent.action != "add") return;
+
+ // Loading the firmware in a child means we can do that in parallel...
+ // (We ignore SIGCHLD rather than wait for our children.)
+ pid_t pid = fork();
+ if (pid == 0) {
+ Timer t;
+ ProcessFirmwareEvent(uevent);
+ LOG(INFO) << "loading " << uevent.path << " took " << t;
+ _exit(EXIT_SUCCESS);
+ } else if (pid == -1) {
+ PLOG(ERROR) << "could not fork to process firmware event for " << uevent.firmware;
+ }
+}
diff --git a/include/system/window.h b/init/firmware_handler.h
similarity index 70%
copy from include/system/window.h
copy to init/firmware_handler.h
index efa10d6..be9daae 100644
--- a/include/system/window.h
+++ b/init/firmware_handler.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 The Android Open Source Project
+ * Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,9 +14,11 @@
* limitations under the License.
*/
-#ifndef SYSTEM_CORE_INCLUDE_ANDROID_WINDOW_H
-#define SYSTEM_CORE_INCLUDE_ANDROID_WINDOW_H
+#ifndef _INIT_FIRMWARE_HANDLER_H
+#define _INIT_FIRMWARE_HANDLER_H
-#include <system/window-deprecated.h>
+#include "uevent.h"
-#endif /* SYSTEM_CORE_INCLUDE_ANDROID_WINDOW_H */
+void HandleFirmwareEvent(const Uevent& uevent);
+
+#endif
diff --git a/init/import_parser.cpp b/init/import_parser.cpp
index d52247b..99275e5 100644
--- a/init/import_parser.cpp
+++ b/init/import_parser.cpp
@@ -16,16 +16,12 @@
#include "import_parser.h"
-#include "errno.h"
+#include <android-base/logging.h>
-#include <string>
-#include <vector>
-
-#include "log.h"
#include "util.h"
-bool ImportParser::ParseSection(const std::vector<std::string>& args,
- std::string* err) {
+bool ImportParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
+ int line, std::string* err) {
if (args.size() != 2) {
*err = "single argument needed for import\n";
return false;
@@ -39,16 +35,18 @@
}
LOG(INFO) << "Added '" << conf_file << "' to import list";
- imports_.emplace_back(std::move(conf_file));
+ if (filename_.empty()) filename_ = filename;
+ imports_.emplace_back(std::move(conf_file), line);
return true;
}
-void ImportParser::EndFile(const std::string& filename) {
+void ImportParser::EndFile() {
auto current_imports = std::move(imports_);
imports_.clear();
- for (const auto& s : current_imports) {
- if (!Parser::GetInstance().ParseConfig(s)) {
- PLOG(ERROR) << "could not import file '" << s << "' from '" << filename << "'";
+ for (const auto& [import, line_num] : current_imports) {
+ if (!parser_->ParseConfig(import)) {
+ PLOG(ERROR) << filename_ << ": " << line_num << ": Could not import file '" << import
+ << "'";
}
}
}
diff --git a/init/import_parser.h b/init/import_parser.h
index 0e91025..45cbfad 100644
--- a/init/import_parser.h
+++ b/init/import_parser.h
@@ -23,21 +23,18 @@
#include <vector>
class ImportParser : public SectionParser {
-public:
- ImportParser() {
- }
- bool ParseSection(const std::vector<std::string>& args,
+ public:
+ ImportParser(Parser* parser) : parser_(parser) {}
+ bool ParseSection(std::vector<std::string>&& args, const std::string& filename, int line,
std::string* err) override;
- bool ParseLineSection(const std::vector<std::string>& args,
- const std::string& filename, int line,
- std::string* err) const override {
- return true;
- }
- void EndSection() override {
- }
- void EndFile(const std::string& filename) override;
-private:
- std::vector<std::string> imports_;
+ void EndFile() override;
+
+ private:
+ Parser* parser_;
+ // Store filename for later error reporting.
+ std::string filename_;
+ // Vector of imports and their line numbers for later error reporting.
+ std::vector<std::pair<std::string, int>> imports_;
};
#endif
diff --git a/init/init.cpp b/init/init.cpp
index 641e985..ee87145 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "init.h"
+
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
@@ -36,34 +38,33 @@
#include <sys/wait.h>
#include <unistd.h>
-#include <selinux/selinux.h>
-#include <selinux/label.h>
-#include <selinux/android.h>
-
#include <android-base/chrono_utils.h>
#include <android-base/file.h>
+#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
+#include <keyutils.h>
#include <libavb/libavb.h>
#include <private/android_filesystem_config.h>
+#include <selinux/android.h>
+#include <selinux/label.h>
+#include <selinux/selinux.h>
#include <fstream>
#include <memory>
-#include <set>
#include <vector>
#include "action.h"
#include "bootchart.h"
-#include "devices.h"
-#include "fs_mgr.h"
#include "import_parser.h"
-#include "init.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 "signal_handler.h"
#include "ueventd.h"
@@ -92,6 +93,11 @@
static std::string wait_prop_name;
static std::string wait_prop_value;
+void DumpState() {
+ ServiceManager::GetInstance().DumpState();
+ ActionManager::GetInstance().DumpState();
+}
+
void register_epoll_handler(int fd, void (*fn)()) {
epoll_event ev;
ev.events = EPOLLIN;
@@ -150,10 +156,15 @@
return true;
}
-void property_changed(const char *name, const char *value)
-{
- if (property_triggers_enabled)
- ActionManager::GetInstance().QueuePropertyTrigger(name, value);
+void property_changed(const std::string& name, const std::string& value) {
+ // If the property is sys.powerctl, we bypass the event queue and immediately handle it.
+ // This is to ensure that init will always and immediately shutdown/reboot, regardless of
+ // if there are other pending events to process or if init is waiting on an exec service or
+ // waiting on a property.
+ if (name == "sys.powerctl") HandlePowerctlMessage(value);
+
+ if (property_triggers_enabled) ActionManager::GetInstance().QueuePropertyChange(name, value);
+
if (waiting_for_prop) {
if (wait_prop_name == name && wait_prop_value == value) {
wait_prop_name.clear();
@@ -480,42 +491,12 @@
}
}
-static constexpr char android_dt_dir[] = "/proc/device-tree/firmware/android";
-
-static bool is_dt_compatible() {
- std::string dt_value;
- std::string file_name = StringPrintf("%s/compatible", android_dt_dir);
-
- if (android::base::ReadFileToString(file_name, &dt_value)) {
- // trim the trailing '\0' out, otherwise the comparison
- // will produce false-negatives.
- dt_value.resize(dt_value.size() - 1);
- if (dt_value == "android,firmware") {
- return true;
- }
- }
-
- return false;
-}
-
-static bool is_dt_fstab_compatible() {
- std::string dt_value;
- std::string file_name = StringPrintf("%s/%s/compatible", android_dt_dir, "fstab");
-
- if (android::base::ReadFileToString(file_name, &dt_value)) {
- dt_value.resize(dt_value.size() - 1);
- if (dt_value == "android,fstab") {
- return true;
- }
- }
-
- return false;
-}
-
static void process_kernel_dt() {
- if (!is_dt_compatible()) return;
+ if (!is_android_dt_value_expected("compatible", "android,firmware")) {
+ return;
+ }
- std::unique_ptr<DIR, int(*)(DIR*)>dir(opendir(android_dt_dir), closedir);
+ std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(kAndroidDtDir.c_str()), closedir);
if (!dir) return;
std::string dt_file;
@@ -525,7 +506,7 @@
continue;
}
- std::string file_name = StringPrintf("%s/%s", android_dt_dir, dp->d_name);
+ std::string file_name = kAndroidDtDir + dp->d_name;
android::base::ReadFileToString(file_name, &dt_file);
std::replace(dt_file.begin(), dt_file.end(), ',', '.');
@@ -553,7 +534,7 @@
static int queue_property_triggers_action(const std::vector<std::string>& args)
{
ActionManager::GetInstance().QueueBuiltinAction(property_enable_triggers_action, "enable_property_trigger");
- ActionManager::GetInstance().QueueAllPropertyTriggers();
+ ActionManager::GetInstance().QueueAllPropertyActions();
return 0;
}
@@ -716,14 +697,18 @@
return false;
}
std::string actual_plat_id;
- if (!read_first_line("/system/etc/selinux/plat_sepolicy.cil.sha256", &actual_plat_id)) {
- PLOG(INFO) << "Failed to read /system/etc/selinux/plat_sepolicy.cil.sha256";
+ 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.sha256",
+ 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.sha256";
+ 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)) {
@@ -734,6 +719,18 @@
return true;
}
+static bool selinux_get_vendor_mapping_version(std::string* plat_vers) {
+ if (!read_first_line("/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;
+}
+
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; }
@@ -788,14 +785,20 @@
return false;
}
+ // Determine which mapping file to include
+ std::string vend_plat_vers;
+ if (!selinux_get_vendor_mapping_version(&vend_plat_vers)) {
+ return false;
+ }
+ std::string mapping_file("/system/etc/selinux/mapping/" + vend_plat_vers + ".cil");
// clang-format off
const char* compile_args[] = {
"/system/bin/secilc",
plat_policy_cil_file,
- "-M", "true",
+ "-M", "true", "-G", "-N",
// Target the highest policy language version supported by the kernel
"-c", std::to_string(max_policy_version).c_str(),
- "/vendor/etc/selinux/mapping_sepolicy.cil",
+ mapping_file.c_str(),
"/vendor/etc/selinux/nonplat_sepolicy.cil",
"-o", compiled_sepolicy,
// We don't care about file_contexts output by the compiler
@@ -866,7 +869,9 @@
}
}
- if (!write_file("/sys/fs/selinux/checkreqprot", "0")) {
+ std::string err;
+ if (!WriteFile("/sys/fs/selinux/checkreqprot", "0", &err)) {
+ LOG(ERROR) << err;
security_failure();
}
@@ -877,33 +882,41 @@
}
}
-// The files and directories that were created before initial sepolicy load
-// need to have their security context restored to the proper value.
-// This must happen before /dev is populated by ueventd.
+// 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...";
- restorecon("/dev");
- restorecon("/dev/kmsg");
- restorecon("/dev/socket");
- restorecon("/dev/random");
- restorecon("/dev/urandom");
- restorecon("/dev/__properties__");
+ 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);
- restorecon("/file_contexts.bin");
- restorecon("/plat_file_contexts");
- restorecon("/nonplat_file_contexts");
- restorecon("/plat_property_contexts");
- restorecon("/nonplat_property_contexts");
- restorecon("/plat_seapp_contexts");
- restorecon("/nonplat_seapp_contexts");
- restorecon("/plat_service_contexts");
- restorecon("/nonplat_service_contexts");
- restorecon("/sepolicy");
- restorecon("/vndservice_contexts");
+ 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);
- restorecon("/sys", SELINUX_ANDROID_RESTORECON_RECURSE);
- restorecon("/dev/block", SELINUX_ANDROID_RESTORECON_RECURSE);
- restorecon("/dev/device-mapper");
+ selinux_android_restorecon("/sys", SELINUX_ANDROID_RESTORECON_RECURSE);
+ 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.
@@ -922,193 +935,6 @@
}
}
-static bool early_mount_one(struct fstab_rec* rec) {
- if (rec && fs_mgr_is_verified(rec)) {
- // setup verity and create the dm-XX block device
- // needed to mount this partition
- int ret = fs_mgr_setup_verity(rec, false);
- if (ret == FS_MGR_SETUP_VERITY_FAIL) {
- PLOG(ERROR) << "early_mount: Failed to setup verity for '" << rec->mount_point << "'";
- return false;
- }
-
- // The exact block device name is added as a mount source by
- // fs_mgr_setup_verity() in ->blk_device as "/dev/block/dm-XX"
- // We create that device by running coldboot on /sys/block/dm-XX
- std::string dm_device(basename(rec->blk_device));
- std::string syspath = StringPrintf("/sys/block/%s", dm_device.c_str());
- device_init(syspath.c_str(), [&](uevent* uevent) -> coldboot_action_t {
- if (uevent->device_name && !strcmp(dm_device.c_str(), uevent->device_name)) {
- LOG(VERBOSE) << "early_mount: creating dm-verity device : " << dm_device;
- return COLDBOOT_STOP;
- }
- return COLDBOOT_CONTINUE;
- });
- }
-
- if (rec && fs_mgr_do_mount_one(rec)) {
- PLOG(ERROR) << "early_mount: Failed to mount '" << rec->mount_point << "'";
- return false;
- }
-
- return true;
-}
-
-// Creates devices with uevent->partition_name matching one in the in/out
-// partition_names. Note that the partition_names MUST have A/B suffix
-// when A/B is used. Found partitions will then be removed from the
-// partition_names for caller to check which devices are NOT created.
-static void early_device_init(std::set<std::string>* partition_names) {
- if (partition_names->empty()) {
- return;
- }
- device_init(nullptr, [=](uevent* uevent) -> coldboot_action_t {
- if (!strncmp(uevent->subsystem, "firmware", 8)) {
- return COLDBOOT_CONTINUE;
- }
-
- // we need platform devices to create symlinks
- if (!strncmp(uevent->subsystem, "platform", 8)) {
- return COLDBOOT_CREATE;
- }
-
- // Ignore everything that is not a block device
- if (strncmp(uevent->subsystem, "block", 5)) {
- return COLDBOOT_CONTINUE;
- }
-
- if (uevent->partition_name) {
- // match partition names to create device nodes for partitions
- // both partition_names and uevent->partition_name have A/B suffix when A/B is used
- auto iter = partition_names->find(uevent->partition_name);
- if (iter != partition_names->end()) {
- LOG(VERBOSE) << "early_mount: found partition: " << *iter;
- partition_names->erase(iter);
- if (partition_names->empty()) {
- return COLDBOOT_STOP; // found all partitions, stop coldboot
- } else {
- return COLDBOOT_CREATE; // create this device and continue to find others
- }
- }
- }
- // Not found a partition or find an unneeded partition, continue to find others
- return COLDBOOT_CONTINUE;
- });
-}
-
-static bool get_early_partitions(const std::vector<fstab_rec*>& early_fstab_recs,
- std::set<std::string>* out_partitions, bool* out_need_verity) {
- std::string meta_partition;
- out_partitions->clear();
- *out_need_verity = false;
-
- for (auto fstab_rec : early_fstab_recs) {
- // don't allow verifyatboot for early mounted partitions
- if (fs_mgr_is_verifyatboot(fstab_rec)) {
- LOG(ERROR) << "early_mount: partitions can't be verified at boot";
- return false;
- }
- // check for verified partitions
- if (fs_mgr_is_verified(fstab_rec)) {
- *out_need_verity = true;
- }
- // check if verity metadata is on a separate partition and get partition
- // name from the end of the ->verity_loc path. verity state is not partition
- // specific, so there must be only 1 additional partition that carries
- // verity state.
- if (fstab_rec->verity_loc) {
- if (!meta_partition.empty()) {
- LOG(ERROR) << "early_mount: more than one meta partition found: " << meta_partition
- << ", " << basename(fstab_rec->verity_loc);
- return false;
- } else {
- meta_partition = basename(fstab_rec->verity_loc);
- }
- }
- }
-
- // includes those early mount partitions and meta_partition (if any)
- // note that fstab_rec->blk_device has A/B suffix updated by fs_mgr when A/B is used
- for (auto fstab_rec : early_fstab_recs) {
- out_partitions->emplace(basename(fstab_rec->blk_device));
- }
-
- if (!meta_partition.empty()) {
- out_partitions->emplace(std::move(meta_partition));
- }
-
- return true;
-}
-
-/* Early mount vendor and ODM partitions. The fstab is read from device-tree. */
-static bool early_mount() {
- // skip early mount if we're in recovery mode
- if (access("/sbin/recovery", F_OK) == 0) {
- LOG(INFO) << "Early mount skipped (recovery mode)";
- return true;
- }
-
- // first check if device tree fstab entries are compatible
- if (!is_dt_fstab_compatible()) {
- LOG(INFO) << "Early mount skipped (missing/incompatible fstab in device tree)";
- return true;
- }
-
- std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> tab(
- fs_mgr_read_fstab_dt(), fs_mgr_free_fstab);
- if (!tab) {
- LOG(ERROR) << "Early mount failed to read fstab from device tree";
- return false;
- }
-
- // find out fstab records for odm, system and vendor
- std::vector<fstab_rec*> early_fstab_recs;
- for (auto mount_point : {"/odm", "/system", "/vendor"}) {
- fstab_rec* fstab_rec = fs_mgr_get_entry_for_mount_point(tab.get(), mount_point);
- if (fstab_rec != nullptr) {
- early_fstab_recs.push_back(fstab_rec);
- }
- }
-
- // nothing to early mount
- if (early_fstab_recs.empty()) return true;
-
- bool need_verity;
- std::set<std::string> partition_names;
- // partition_names MUST have A/B suffix when A/B is used
- if (!get_early_partitions(early_fstab_recs, &partition_names, &need_verity)) {
- return false;
- }
-
- bool success = false;
- // create the devices we need..
- early_device_init(&partition_names);
-
- // early_device_init will remove found partitions from partition_names
- // So if the partition_names is not empty here, means some partitions
- // are not found
- if (!partition_names.empty()) {
- LOG(ERROR) << "early_mount: partition(s) not found: "
- << android::base::Join(partition_names, ", ");
- goto done;
- }
-
- if (need_verity) {
- // create /dev/device mapper
- device_init("/sys/devices/virtual/misc/device-mapper",
- [&](uevent* uevent) -> coldboot_action_t { return COLDBOOT_STOP; });
- }
-
- for (auto fstab_rec : early_fstab_recs) {
- if (!early_mount_one(fstab_rec)) goto done;
- }
- success = true;
-
-done:
- device_close();
- return success;
-}
-
static void install_reboot_signal_handlers() {
// Instead of panic'ing the kernel as is the default behavior when init crashes,
// we prefer to reboot to bootloader on development builds, as this will prevent
@@ -1171,7 +997,13 @@
setgroups(arraysize(groups), groups);
mount("sysfs", "/sys", "sysfs", 0, NULL);
mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL);
+
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/random", S_IFCHR | 0666, makedev(1, 8));
mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9));
@@ -1181,17 +1013,19 @@
LOG(INFO) << "init first stage started!";
- if (!early_mount()) {
+ if (!DoFirstStageMount()) {
LOG(ERROR) << "Failed to mount required partitions early ...";
panic();
}
+ SetInitAvbVersionInRecovery();
+
// Set up SELinux, loading the SELinux policy.
selinux_initialize(true);
// 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 (restorecon("/init") == -1) {
+ if (selinux_android_restorecon("/init", 0) == -1) {
PLOG(ERROR) << "restorecon failed";
security_failure();
}
@@ -1216,6 +1050,11 @@
InitKernelLogging(argv);
LOG(INFO) << "init second stage started!";
+ // Set up a session keyring that all processes will have access to. It
+ // will hold things like FBE encryption keys. No process should override
+ // its session keyring.
+ keyctl_get_keyring_ID(KEY_SPEC_SESSION_KEYRING, 1);
+
// Indicate that booting is in progress to background fw loaders, etc.
close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
@@ -1235,12 +1074,14 @@
property_set("ro.boottime.init.selinux", getenv("INIT_SELINUX_TOOK"));
// Set libavb version for Framework-only OTA match in Treble build.
- property_set("ro.boot.init.avb_version", std::to_string(AVB_MAJOR_VERSION).c_str());
+ const char* avb_version = getenv("INIT_AVB_VERSION");
+ if (avb_version) property_set("ro.boot.avb_version", avb_version);
// Clean up our environment.
unsetenv("INIT_SECOND_STAGE");
unsetenv("INIT_STARTED_AT");
unsetenv("INIT_SELINUX_TOOK");
+ unsetenv("INIT_AVB_VERSION");
// Now set up SELinux for second stage.
selinux_initialize(false);
@@ -1262,10 +1103,13 @@
const BuiltinFunctionMap function_map;
Action::set_function_map(&function_map);
+ ActionManager& am = ActionManager::GetInstance();
+ ServiceManager& sm = ServiceManager::GetInstance();
Parser& parser = Parser::GetInstance();
- parser.AddSectionParser("service",std::make_unique<ServiceParser>());
- parser.AddSectionParser("on", std::make_unique<ActionParser>());
- parser.AddSectionParser("import", std::make_unique<ImportParser>());
+
+ 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");
@@ -1283,9 +1127,7 @@
// Turning this on and letting the INFO logging be discarded adds 0.2s to
// Nexus 9 boot time, so it's disabled by default.
- if (false) parser.DumpState();
-
- ActionManager& am = ActionManager::GetInstance();
+ if (false) DumpState();
am.QueueEventTrigger("early-init");
@@ -1320,10 +1162,10 @@
// By default, sleep until something happens.
int epoll_timeout_ms = -1;
- if (!(waiting_for_prop || ServiceManager::GetInstance().IsWaitingForExec())) {
+ if (!(waiting_for_prop || sm.IsWaitingForExec())) {
am.ExecuteOneCommand();
}
- if (!(waiting_for_prop || ServiceManager::GetInstance().IsWaitingForExec())) {
+ if (!(waiting_for_prop || sm.IsWaitingForExec())) {
restart_processes();
// If there's a process that needs restarting, wake up in time for that.
diff --git a/init/init.h b/init/init.h
index fe850ef..6725a70 100644
--- a/init/init.h
+++ b/init/init.h
@@ -19,6 +19,9 @@
#include <string>
+// 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;
@@ -26,7 +29,7 @@
void handle_control_message(const std::string& msg, const std::string& arg);
-void property_changed(const char *name, const char *value);
+void property_changed(const std::string& name, const std::string& value);
void register_epoll_handler(int fd, void (*fn)());
@@ -34,4 +37,6 @@
bool start_waiting_for_property(const char *name, const char *value);
+void DumpState();
+
#endif /* _INIT_INIT_H */
diff --git a/init/init_first_stage.cpp b/init/init_first_stage.cpp
new file mode 100644
index 0000000..8a7d9a2
--- /dev/null
+++ b/init/init_first_stage.cpp
@@ -0,0 +1,474 @@
+/*
+ * 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 "init_first_stage.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+
+#include "devices.h"
+#include "fs_mgr.h"
+#include "fs_mgr_avb.h"
+#include "uevent.h"
+#include "uevent_listener.h"
+#include "util.h"
+
+// Class Declarations
+// ------------------
+class FirstStageMount {
+ public:
+ FirstStageMount();
+ virtual ~FirstStageMount() = default;
+
+ // The factory method to create either FirstStageMountVBootV1 or FirstStageMountVBootV2
+ // based on device tree configurations.
+ static std::unique_ptr<FirstStageMount> Create();
+ bool DoFirstStageMount(); // Mounts fstab entries read from device tree.
+ bool InitDevices();
+
+ protected:
+ void InitRequiredDevices();
+ void InitVerityDevice(const std::string& verity_device);
+ bool MountPartitions();
+
+ virtual RegenerationAction UeventCallback(const Uevent& uevent);
+
+ // Pure virtual functions.
+ virtual bool GetRequiredDevices() = 0;
+ virtual bool SetUpDmVerity(fstab_rec* fstab_rec) = 0;
+
+ bool need_dm_verity_;
+ // Device tree fstab entries.
+ std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> device_tree_fstab_;
+ // Eligible first stage mount candidates, only allow /system, /vendor and/or /odm.
+ std::vector<fstab_rec*> mount_fstab_recs_;
+ std::set<std::string> required_devices_partition_names_;
+ DeviceHandler device_handler_;
+ UeventListener uevent_listener_;
+};
+
+class FirstStageMountVBootV1 : public FirstStageMount {
+ public:
+ FirstStageMountVBootV1() = default;
+ ~FirstStageMountVBootV1() override = default;
+
+ protected:
+ bool GetRequiredDevices() override;
+ bool SetUpDmVerity(fstab_rec* fstab_rec) override;
+};
+
+class FirstStageMountVBootV2 : public FirstStageMount {
+ public:
+ friend void SetInitAvbVersionInRecovery();
+
+ FirstStageMountVBootV2();
+ ~FirstStageMountVBootV2() override = default;
+
+ protected:
+ RegenerationAction UeventCallback(const Uevent& uevent) override;
+ bool GetRequiredDevices() override;
+ bool SetUpDmVerity(fstab_rec* fstab_rec) override;
+ bool InitAvbHandle();
+
+ std::string device_tree_vbmeta_parts_;
+ FsManagerAvbUniquePtr avb_handle_;
+ ByNameSymlinkMap by_name_symlink_map_;
+};
+
+// Static Functions
+// ----------------
+static inline bool IsDtVbmetaCompatible() {
+ return is_android_dt_value_expected("vbmeta/compatible", "android,vbmeta");
+}
+
+static bool inline IsRecoveryMode() {
+ return access("/sbin/recovery", F_OK) == 0;
+}
+
+// Class Definitions
+// -----------------
+FirstStageMount::FirstStageMount()
+ : need_dm_verity_(false), device_tree_fstab_(fs_mgr_read_fstab_dt(), fs_mgr_free_fstab) {
+ if (!device_tree_fstab_) {
+ LOG(ERROR) << "Failed to read fstab from device tree";
+ return;
+ }
+ for (auto mount_point : {"/system", "/vendor", "/odm"}) {
+ fstab_rec* fstab_rec =
+ fs_mgr_get_entry_for_mount_point(device_tree_fstab_.get(), mount_point);
+ if (fstab_rec != nullptr) {
+ mount_fstab_recs_.push_back(fstab_rec);
+ }
+ }
+}
+
+std::unique_ptr<FirstStageMount> FirstStageMount::Create() {
+ if (IsDtVbmetaCompatible()) {
+ return std::make_unique<FirstStageMountVBootV2>();
+ } else {
+ return std::make_unique<FirstStageMountVBootV1>();
+ }
+}
+
+bool FirstStageMount::DoFirstStageMount() {
+ // Nothing to mount.
+ if (mount_fstab_recs_.empty()) return true;
+
+ if (!InitDevices()) return false;
+
+ if (!MountPartitions()) return false;
+
+ return true;
+}
+
+bool FirstStageMount::InitDevices() {
+ if (!GetRequiredDevices()) return false;
+
+ InitRequiredDevices();
+
+ // InitRequiredDevices() will remove found partitions from required_devices_partition_names_.
+ // So if it isn't empty here, it means some partitions are not found.
+ if (!required_devices_partition_names_.empty()) {
+ LOG(ERROR) << __FUNCTION__ << "(): partition(s) not found: "
+ << android::base::Join(required_devices_partition_names_, ", ");
+ return false;
+ } else {
+ return true;
+ }
+}
+
+// Creates devices with uevent->partition_name matching one in the member variable
+// required_devices_partition_names_. Found partitions will then be removed from it
+// for the subsequent member function to check which devices are NOT created.
+void FirstStageMount::InitRequiredDevices() {
+ if (required_devices_partition_names_.empty()) {
+ return;
+ }
+
+ if (need_dm_verity_) {
+ const std::string dm_path = "/devices/virtual/misc/device-mapper";
+ uevent_listener_.RegenerateUeventsForPath("/sys" + dm_path,
+ [this, &dm_path](const Uevent& uevent) {
+ if (uevent.path == dm_path) {
+ device_handler_.HandleDeviceEvent(uevent);
+ return RegenerationAction::kStop;
+ }
+ return RegenerationAction::kContinue;
+ });
+ }
+
+ uevent_listener_.RegenerateUevents(
+ [this](const Uevent& uevent) { return UeventCallback(uevent); });
+}
+
+RegenerationAction FirstStageMount::UeventCallback(const Uevent& uevent) {
+ // We need platform devices to create symlinks.
+ if (uevent.subsystem == "platform") {
+ device_handler_.HandleDeviceEvent(uevent);
+ return RegenerationAction::kContinue;
+ }
+
+ // Ignores everything that is not a block device.
+ if (uevent.subsystem != "block") {
+ return RegenerationAction::kContinue;
+ }
+
+ if (!uevent.partition_name.empty()) {
+ // Matches partition name to create device nodes.
+ // Both required_devices_partition_names_ and uevent->partition_name have A/B
+ // suffix when A/B is used.
+ auto iter = required_devices_partition_names_.find(uevent.partition_name);
+ if (iter != required_devices_partition_names_.end()) {
+ LOG(VERBOSE) << __FUNCTION__ << "(): found partition: " << *iter;
+ required_devices_partition_names_.erase(iter);
+ device_handler_.HandleDeviceEvent(uevent);
+ if (required_devices_partition_names_.empty()) {
+ return RegenerationAction::kStop;
+ } else {
+ return RegenerationAction::kContinue;
+ }
+ }
+ }
+ // Not found a partition or find an unneeded partition, continue to find others.
+ return RegenerationAction::kContinue;
+}
+
+// Creates "/dev/block/dm-XX" for dm-verity by running coldboot on /sys/block/dm-XX.
+void FirstStageMount::InitVerityDevice(const std::string& verity_device) {
+ const std::string device_name(basename(verity_device.c_str()));
+ const std::string syspath = "/sys/block/" + device_name;
+
+ uevent_listener_.RegenerateUeventsForPath(
+ syspath, [&device_name, &verity_device, this](const Uevent& uevent) {
+ if (uevent.device_name == device_name) {
+ LOG(VERBOSE) << "Creating dm-verity device : " << verity_device;
+ device_handler_.HandleDeviceEvent(uevent);
+ return RegenerationAction::kStop;
+ }
+ return RegenerationAction::kContinue;
+ });
+}
+
+bool FirstStageMount::MountPartitions() {
+ for (auto fstab_rec : mount_fstab_recs_) {
+ if (!SetUpDmVerity(fstab_rec)) {
+ PLOG(ERROR) << "Failed to setup verity for '" << fstab_rec->mount_point << "'";
+ return false;
+ }
+ if (fs_mgr_do_mount_one(fstab_rec)) {
+ PLOG(ERROR) << "Failed to mount '" << fstab_rec->mount_point << "'";
+ return false;
+ }
+ }
+ return true;
+}
+
+bool FirstStageMountVBootV1::GetRequiredDevices() {
+ std::string verity_loc_device;
+ need_dm_verity_ = false;
+
+ for (auto fstab_rec : mount_fstab_recs_) {
+ // Don't allow verifyatboot in the first stage.
+ if (fs_mgr_is_verifyatboot(fstab_rec)) {
+ LOG(ERROR) << "Partitions can't be verified at boot";
+ return false;
+ }
+ // Checks for verified partitions.
+ if (fs_mgr_is_verified(fstab_rec)) {
+ need_dm_verity_ = true;
+ }
+ // Checks if verity metadata is on a separate partition. Note that it is
+ // not partition specific, so there must be only one additional partition
+ // that carries verity state.
+ if (fstab_rec->verity_loc) {
+ if (verity_loc_device.empty()) {
+ verity_loc_device = fstab_rec->verity_loc;
+ } else if (verity_loc_device != fstab_rec->verity_loc) {
+ LOG(ERROR) << "More than one verity_loc found: " << verity_loc_device << ", "
+ << fstab_rec->verity_loc;
+ return false;
+ }
+ }
+ }
+
+ // Includes the partition names of fstab records and verity_loc_device (if any).
+ // Notes that fstab_rec->blk_device has A/B suffix updated by fs_mgr when A/B is used.
+ for (auto fstab_rec : mount_fstab_recs_) {
+ required_devices_partition_names_.emplace(basename(fstab_rec->blk_device));
+ }
+
+ if (!verity_loc_device.empty()) {
+ required_devices_partition_names_.emplace(basename(verity_loc_device.c_str()));
+ }
+
+ return true;
+}
+
+bool FirstStageMountVBootV1::SetUpDmVerity(fstab_rec* fstab_rec) {
+ if (fs_mgr_is_verified(fstab_rec)) {
+ int ret = fs_mgr_setup_verity(fstab_rec, false /* wait_for_verity_dev */);
+ if (ret == FS_MGR_SETUP_VERITY_DISABLED) {
+ LOG(INFO) << "Verity disabled for '" << fstab_rec->mount_point << "'";
+ } else if (ret == FS_MGR_SETUP_VERITY_SUCCESS) {
+ // The exact block device name (fstab_rec->blk_device) is changed to "/dev/block/dm-XX".
+ // Needs to create it because ueventd isn't started in init first stage.
+ InitVerityDevice(fstab_rec->blk_device);
+ } else {
+ return false;
+ }
+ }
+ return true; // Returns true to mount the partition.
+}
+
+// FirstStageMountVBootV2 constructor.
+// Gets the vbmeta partitions from device tree.
+// /{
+// firmware {
+// android {
+// vbmeta {
+// compatible = "android,vbmeta";
+// parts = "vbmeta,boot,system,vendor"
+// };
+// };
+// };
+// }
+FirstStageMountVBootV2::FirstStageMountVBootV2() : avb_handle_(nullptr) {
+ if (!read_android_dt_file("vbmeta/parts", &device_tree_vbmeta_parts_)) {
+ PLOG(ERROR) << "Failed to read vbmeta/parts from device tree";
+ return;
+ }
+}
+
+bool FirstStageMountVBootV2::GetRequiredDevices() {
+ need_dm_verity_ = false;
+
+ // fstab_rec->blk_device has A/B suffix.
+ for (auto fstab_rec : mount_fstab_recs_) {
+ if (fs_mgr_is_avb(fstab_rec)) {
+ need_dm_verity_ = true;
+ }
+ required_devices_partition_names_.emplace(basename(fstab_rec->blk_device));
+ }
+
+ // libavb verifies AVB metadata on all verified partitions at once.
+ // e.g., The device_tree_vbmeta_parts_ will be "vbmeta,boot,system,vendor"
+ // for libavb to verify metadata, even if there is only /vendor in the
+ // above mount_fstab_recs_.
+ if (need_dm_verity_) {
+ if (device_tree_vbmeta_parts_.empty()) {
+ LOG(ERROR) << "Missing vbmeta parts in device tree";
+ return false;
+ }
+ std::vector<std::string> partitions = android::base::Split(device_tree_vbmeta_parts_, ",");
+ std::string ab_suffix = fs_mgr_get_slot_suffix();
+ for (const auto& partition : partitions) {
+ // required_devices_partition_names_ is of type std::set so it's not an issue
+ // to emplace a partition twice. e.g., /vendor might be in both places:
+ // - device_tree_vbmeta_parts_ = "vbmeta,boot,system,vendor"
+ // - mount_fstab_recs_: /vendor_a
+ required_devices_partition_names_.emplace(partition + ab_suffix);
+ }
+ }
+ return true;
+}
+
+RegenerationAction FirstStageMountVBootV2::UeventCallback(const Uevent& uevent) {
+ // Check if this uevent corresponds to one of the required partitions and store its symlinks if
+ // so, in order to create FsManagerAvbHandle later.
+ // Note that the parent callback removes partitions from the list of required partitions
+ // as it finds them, so this must happen first.
+ if (!uevent.partition_name.empty() &&
+ required_devices_partition_names_.find(uevent.partition_name) !=
+ required_devices_partition_names_.end()) {
+ // GetBlockDeviceSymlinks() will return three symlinks at most, depending on
+ // the content of uevent. by-name symlink will be at [0] if uevent->partition_name
+ // is not empty. e.g.,
+ // - /dev/block/platform/soc.0/f9824900.sdhci/by-name/modem
+ // - /dev/block/platform/soc.0/f9824900.sdhci/by-num/p1
+ // - /dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1
+ std::vector<std::string> links = device_handler_.GetBlockDeviceSymlinks(uevent);
+ if (!links.empty()) {
+ auto[it, inserted] = by_name_symlink_map_.emplace(uevent.partition_name, links[0]);
+ if (!inserted) {
+ LOG(ERROR) << "Partition '" << uevent.partition_name
+ << "' already existed in the by-name symlink map with a value of '"
+ << it->second << "', new value '" << links[0] << "' will be ignored.";
+ }
+ }
+ }
+
+ return FirstStageMount::UeventCallback(uevent);
+}
+
+bool FirstStageMountVBootV2::SetUpDmVerity(fstab_rec* fstab_rec) {
+ if (fs_mgr_is_avb(fstab_rec)) {
+ if (!InitAvbHandle()) return false;
+ if (avb_handle_->hashtree_disabled()) {
+ LOG(INFO) << "avb hashtree disabled for '" << fstab_rec->mount_point << "'";
+ } else if (avb_handle_->SetUpAvb(fstab_rec, false /* wait_for_verity_dev */)) {
+ // The exact block device name (fstab_rec->blk_device) is changed to "/dev/block/dm-XX".
+ // Needs to create it because ueventd isn't started in init first stage.
+ InitVerityDevice(fstab_rec->blk_device);
+ } else {
+ return false;
+ }
+ }
+ return true; // Returns true to mount the partition.
+}
+
+bool FirstStageMountVBootV2::InitAvbHandle() {
+ if (avb_handle_) return true; // Returns true if the handle is already initialized.
+
+ if (by_name_symlink_map_.empty()) {
+ LOG(ERROR) << "by_name_symlink_map_ is empty";
+ return false;
+ }
+
+ avb_handle_ = FsManagerAvbHandle::Open(std::move(by_name_symlink_map_));
+ by_name_symlink_map_.clear(); // Removes all elements after the above std::move().
+
+ if (!avb_handle_) {
+ PLOG(ERROR) << "Failed to open FsManagerAvbHandle";
+ return false;
+ }
+ // Sets INIT_AVB_VERSION here for init to set ro.boot.avb_version in the second stage.
+ setenv("INIT_AVB_VERSION", avb_handle_->avb_version().c_str(), 1);
+ return true;
+}
+
+// Public functions
+// ----------------
+// Mounts /system, /vendor, and/or /odm if they are present in the fstab provided by device tree.
+bool DoFirstStageMount() {
+ // Skips first stage mount if we're in recovery mode.
+ if (IsRecoveryMode()) {
+ LOG(INFO) << "First stage mount skipped (recovery mode)";
+ return true;
+ }
+
+ // Firstly checks if device tree fstab entries are compatible.
+ if (!is_android_dt_value_expected("fstab/compatible", "android,fstab")) {
+ LOG(INFO) << "First stage mount skipped (missing/incompatible fstab in device tree)";
+ return true;
+ }
+
+ std::unique_ptr<FirstStageMount> handle = FirstStageMount::Create();
+ if (!handle) {
+ LOG(ERROR) << "Failed to create FirstStageMount";
+ return false;
+ }
+ return handle->DoFirstStageMount();
+}
+
+void SetInitAvbVersionInRecovery() {
+ if (!IsRecoveryMode()) {
+ LOG(INFO) << "Skipped setting INIT_AVB_VERSION (not in recovery mode)";
+ return;
+ }
+
+ if (!IsDtVbmetaCompatible()) {
+ LOG(INFO) << "Skipped setting INIT_AVB_VERSION (not vbmeta compatible)";
+ return;
+ }
+
+ // Initializes required devices for the subsequent FsManagerAvbHandle::Open()
+ // to verify AVB metadata on all partitions in the verified chain.
+ // We only set INIT_AVB_VERSION when the AVB verification succeeds, i.e., the
+ // Open() function returns a valid handle.
+ // We don't need to mount partitions here in recovery mode.
+ FirstStageMountVBootV2 avb_first_mount;
+ if (!avb_first_mount.InitDevices()) {
+ LOG(ERROR) << "Failed to init devices for INIT_AVB_VERSION";
+ return;
+ }
+
+ FsManagerAvbUniquePtr avb_handle =
+ FsManagerAvbHandle::Open(std::move(avb_first_mount.by_name_symlink_map_));
+ if (!avb_handle) {
+ PLOG(ERROR) << "Failed to open FsManagerAvbHandle for INIT_AVB_VERSION";
+ return;
+ }
+ setenv("INIT_AVB_VERSION", avb_handle->avb_version().c_str(), 1);
+}
diff --git a/include/system/window.h b/init/init_first_stage.h
similarity index 70%
rename from include/system/window.h
rename to init/init_first_stage.h
index efa10d6..170a24c 100644
--- a/include/system/window.h
+++ b/init/init_first_stage.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 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.
@@ -14,9 +14,10 @@
* limitations under the License.
*/
-#ifndef SYSTEM_CORE_INCLUDE_ANDROID_WINDOW_H
-#define SYSTEM_CORE_INCLUDE_ANDROID_WINDOW_H
+#ifndef _INIT_FIRST_STAGE_H
+#define _INIT_FIRST_STAGE_H
-#include <system/window-deprecated.h>
+bool DoFirstStageMount();
+void SetInitAvbVersionInRecovery();
-#endif /* SYSTEM_CORE_INCLUDE_ANDROID_WINDOW_H */
+#endif
diff --git a/init/init_parser.cpp b/init/init_parser.cpp
index a192862..1b31cf2 100644
--- a/init/init_parser.cpp
+++ b/init/init_parser.cpp
@@ -14,18 +14,16 @@
* limitations under the License.
*/
-#include <dirent.h>
-#include <errno.h>
-#include <fcntl.h>
-
-#include "action.h"
#include "init_parser.h"
-#include "log.h"
-#include "parser.h"
-#include "service.h"
-#include "util.h"
+#include <dirent.h>
+
+#include <android-base/logging.h>
#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+#include "parser.h"
+#include "util.h"
Parser::Parser() {
}
@@ -40,13 +38,16 @@
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.filename = filename.c_str();
state.line = 0;
state.ptr = &data_copy[0];
state.nexttoken = 0;
@@ -66,21 +67,34 @@
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(args, &ret_err)) {
- parse_error(&state, "%s\n", ret_err.c_str());
+ 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(args, state.filename,
- state.line, &ret_err)) {
- parse_error(&state, "%s\n", ret_err.c_str());
+ if (!section_parser->ParseLineSection(std::move(args), state.line, &ret_err)) {
+ LOG(ERROR) << filename << ": " << state.line << ": " << ret_err;
}
}
args.clear();
@@ -96,14 +110,16 @@
LOG(INFO) << "Parsing file " << path << "...";
Timer t;
std::string data;
- if (!read_file(path, &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& sp : section_parsers_) {
- sp.second->EndFile(path);
+ for (const auto& [section_name, section_parser] : section_parsers_) {
+ section_parser->EndFile();
}
LOG(VERBOSE) << "(Parsing " << path << " took " << t << ".)";
@@ -143,8 +159,3 @@
}
return ParseConfigFile(path);
}
-
-void Parser::DumpState() const {
- ServiceManager::GetInstance().DumpState();
- ActionManager::GetInstance().DumpState();
-}
diff --git a/init/init_parser.h b/init/init_parser.h
index f66ba52..722ebb2 100644
--- a/init/init_parser.h
+++ b/init/init_parser.h
@@ -18,50 +18,76 @@
#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.
+
class SectionParser {
-public:
- virtual ~SectionParser() {
- }
- virtual bool ParseSection(const std::vector<std::string>& args,
- std::string* err) = 0;
- virtual bool ParseLineSection(const std::vector<std::string>& args,
- const std::string& filename, int line,
- std::string* err) const = 0;
- virtual void EndSection() = 0;
- virtual void EndFile(const std::string& filename) = 0;
+ 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:
+ 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();
- void DumpState() const;
+
+ Parser();
+
bool ParseConfig(const std::string& path);
- void AddSectionParser(const std::string& name,
- std::unique_ptr<SectionParser> parser);
- 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;
- }
+ 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:
- Parser();
-
+ 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;
diff --git a/init/init_parser_test.cpp b/init/init_parser_test.cpp
index 52aaa37..86d60d0 100644
--- a/init/init_parser_test.cpp
+++ b/init/init_parser_test.cpp
@@ -18,9 +18,7 @@
#include "init.h"
#include "service.h"
-#include "util.h"
-#include <errno.h>
#include <gtest/gtest.h>
#include <string>
@@ -88,19 +86,29 @@
ASSERT_EQ("", svc->seclabel());
}
if (uid) {
- ASSERT_EQ(decode_uid("log"), svc->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) {
- ASSERT_EQ(decode_uid("shell"), svc->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());
- ASSERT_EQ(decode_uid("system"), svc->supp_gids()[0]);
- ASSERT_EQ(decode_uid("adb"), svc->supp_gids()[1]);
+ 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());
}
diff --git a/init/init_test.cpp b/init/init_test.cpp
new file mode 100644
index 0000000..7093ba9
--- /dev/null
+++ b/init/init_test.cpp
@@ -0,0 +1,187 @@
+/*
+ * 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 <functional>
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+
+#include "action.h"
+#include "builtins.h"
+#include "import_parser.h"
+#include "init_parser.h"
+#include "keyword_map.h"
+#include "util.h"
+
+class TestFunctionMap : public KeywordMap<BuiltinFunction> {
+ public:
+ // Helper for argument-less functions
+ using BuiltinFunctionNoArgs = std::function<void(void)>;
+ void Add(const std::string& name, const BuiltinFunctionNoArgs function) {
+ Add(name, 0, 0, [function](const std::vector<std::string>&) {
+ function();
+ return 0;
+ });
+ }
+
+ void Add(const std::string& name, std::size_t min_parameters, std::size_t max_parameters,
+ const BuiltinFunction function) {
+ builtin_functions_[name] = make_tuple(min_parameters, max_parameters, function);
+ }
+
+ private:
+ Map builtin_functions_ = {};
+
+ const Map& map() const override { return builtin_functions_; }
+};
+
+using ActionManagerCommand = std::function<void(ActionManager&)>;
+
+void TestInit(const std::string& init_script_file, const TestFunctionMap& test_function_map,
+ const std::vector<ActionManagerCommand>& commands) {
+ ActionManager am;
+
+ Action::set_function_map(&test_function_map);
+
+ Parser parser;
+ parser.AddSectionParser("on", std::make_unique<ActionParser>(&am));
+ parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
+
+ ASSERT_TRUE(parser.ParseConfig(init_script_file));
+
+ for (const auto& command : commands) {
+ command(am);
+ }
+
+ while (am.HasMoreCommands()) {
+ am.ExecuteOneCommand();
+ }
+}
+
+void TestInitText(const std::string& init_script, const TestFunctionMap& test_function_map,
+ const std::vector<ActionManagerCommand>& commands) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ ASSERT_TRUE(android::base::WriteStringToFd(init_script, tf.fd));
+ TestInit(tf.path, test_function_map, commands);
+}
+
+TEST(init, SimpleEventTrigger) {
+ bool expect_true = false;
+ std::string init_script =
+ R"init(
+on boot
+pass_test
+)init";
+
+ TestFunctionMap test_function_map;
+ test_function_map.Add("pass_test", [&expect_true]() { expect_true = true; });
+
+ ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
+ std::vector<ActionManagerCommand> commands{trigger_boot};
+
+ TestInitText(init_script, test_function_map, commands);
+
+ EXPECT_TRUE(expect_true);
+}
+
+TEST(init, EventTriggerOrder) {
+ std::string init_script =
+ R"init(
+on boot
+execute_first
+
+on boot && property:ro.hardware=*
+execute_second
+
+on boot
+execute_third
+
+)init";
+
+ int num_executed = 0;
+ TestFunctionMap test_function_map;
+ test_function_map.Add("execute_first", [&num_executed]() { EXPECT_EQ(0, num_executed++); });
+ test_function_map.Add("execute_second", [&num_executed]() { EXPECT_EQ(1, num_executed++); });
+ test_function_map.Add("execute_third", [&num_executed]() { EXPECT_EQ(2, num_executed++); });
+
+ ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
+ std::vector<ActionManagerCommand> commands{trigger_boot};
+
+ TestInitText(init_script, test_function_map, commands);
+}
+
+TEST(init, EventTriggerOrderMultipleFiles) {
+ // 6 total files, which should have their triggers executed in the following order:
+ // 1: start - original script parsed
+ // 2: first_import - immediately imported by first_script
+ // 3: dir_a - file named 'a.rc' in dir; dir is imported after first_import
+ // 4: a_import - file imported by dir_a
+ // 5: dir_b - file named 'b.rc' in dir
+ // 6: last_import - imported after dir is imported
+
+ TemporaryFile first_import;
+ ASSERT_TRUE(first_import.fd != -1);
+ ASSERT_TRUE(android::base::WriteStringToFd("on boot\nexecute 2", first_import.fd));
+
+ TemporaryFile dir_a_import;
+ ASSERT_TRUE(dir_a_import.fd != -1);
+ ASSERT_TRUE(android::base::WriteStringToFd("on boot\nexecute 4", dir_a_import.fd));
+
+ TemporaryFile last_import;
+ ASSERT_TRUE(last_import.fd != -1);
+ ASSERT_TRUE(android::base::WriteStringToFd("on boot\nexecute 6", last_import.fd));
+
+ TemporaryDir dir;
+ // clang-format off
+ std::string dir_a_script = "import " + std::string(dir_a_import.path) + "\n"
+ "on boot\n"
+ "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) + "/b.rc", "on boot\nexecute 5", &err));
+
+ // clang-format off
+ std::string start_script = "import " + std::string(first_import.path) + "\n"
+ "import " + std::string(dir.path) + "\n"
+ "import " + std::string(last_import.path) + "\n"
+ "on boot\n"
+ "execute 1";
+ // clang-format on
+ TemporaryFile start;
+ ASSERT_TRUE(android::base::WriteStringToFd(start_script, start.fd));
+
+ int num_executed = 0;
+ 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;
+ };
+
+ TestFunctionMap test_function_map;
+ test_function_map.Add("execute", 1, 1, execute_command);
+
+ ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
+ std::vector<ActionManagerCommand> commands{trigger_boot};
+
+ TestInit(start.path, test_function_map, commands);
+
+ EXPECT_EQ(6, num_executed);
+}
diff --git a/init/keychords.cpp b/init/keychords.cpp
index 5801ea8..c572cee 100644
--- a/init/keychords.cpp
+++ b/init/keychords.cpp
@@ -14,19 +14,17 @@
* limitations under the License.
*/
-#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
-#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <linux/keychord.h>
#include <unistd.h>
+#include <android-base/logging.h>
#include <android-base/properties.h>
#include "init.h"
-#include "log.h"
#include "service.h"
static struct input_keychord *keychords = 0;
diff --git a/init/keyword_map.h b/init/keyword_map.h
index 693d82a..88bad01 100644
--- a/init/keyword_map.h
+++ b/init/keyword_map.h
@@ -24,18 +24,23 @@
template <typename Function>
class KeywordMap {
-public:
+ public:
using FunctionInfo = std::tuple<std::size_t, std::size_t, Function>;
- using Map = const std::map<std::string, FunctionInfo>;
+ using Map = std::map<std::string, FunctionInfo>;
virtual ~KeywordMap() {
}
- const Function FindFunction(const std::string& keyword,
- size_t num_args,
- std::string* err) const {
+ const Function FindFunction(const std::vector<std::string>& args, std::string* err) const {
using android::base::StringPrintf;
+ if (args.empty()) {
+ *err = "keyword needed, but not provided";
+ return nullptr;
+ }
+ 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());
@@ -68,10 +73,10 @@
return std::get<Function>(function_info);
}
-private:
-//Map of keyword ->
-//(minimum number of arguments, maximum number of arguments, function pointer)
- virtual Map& map() const = 0;
+ private:
+ // Map of keyword ->
+ // (minimum number of arguments, maximum number of arguments, function pointer)
+ virtual const Map& map() const = 0;
};
#endif
diff --git a/init/log.cpp b/init/log.cpp
index 6b32526..0615730 100644
--- a/init/log.cpp
+++ b/init/log.cpp
@@ -17,10 +17,10 @@
#include "log.h"
#include <fcntl.h>
+#include <linux/audit.h>
#include <string.h>
-#include <linux/audit.h>
-#include <netlink/netlink.h>
+#include <android-base/logging.h>
#include <selinux/selinux.h>
void InitKernelLogging(char* argv[]) {
@@ -40,24 +40,6 @@
android::base::InitLogging(argv, &android::base::KernelLogger);
}
-static void selinux_avc_log(char* buf, size_t buf_len) {
- size_t str_len = strnlen(buf, buf_len);
-
- // trim newline at end of string
- buf[str_len - 1] = '\0';
-
- struct nl_sock* sk = nl_socket_alloc();
- if (sk == NULL) {
- return;
- }
- nl_connect(sk, NETLINK_AUDIT);
- int result;
- do {
- result = nl_send_simple(sk, AUDIT_USER_AVC, 0, buf, str_len);
- } while (result == -NLE_INTR);
- nl_socket_free(sk);
-}
-
int selinux_klog_callback(int type, const char *fmt, ...) {
android::base::LogSeverity severity = android::base::ERROR;
if (type == SELINUX_WARNING) {
@@ -68,15 +50,8 @@
char buf[1024];
va_list ap;
va_start(ap, fmt);
- int res = vsnprintf(buf, sizeof(buf), fmt, ap);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
- if (res <= 0) {
- return 0;
- }
- if (type == SELINUX_AVC) {
- selinux_avc_log(buf, sizeof(buf));
- } else {
- android::base::KernelLogger(android::base::MAIN, severity, "selinux", nullptr, 0, buf);
- }
+ android::base::KernelLogger(android::base::MAIN, severity, "selinux", nullptr, 0, buf);
return 0;
}
diff --git a/init/log.h b/init/log.h
index 8fa6d74..29a27af 100644
--- a/init/log.h
+++ b/init/log.h
@@ -17,7 +17,7 @@
#ifndef _INIT_LOG_H_
#define _INIT_LOG_H_
-#include <android-base/logging.h>
+#include <sys/cdefs.h>
void InitKernelLogging(char* argv[]);
diff --git a/init/parser.cpp b/init/parser.cpp
index 45862b7..0d13cfe 100644
--- a/init/parser.cpp
+++ b/init/parser.cpp
@@ -1,28 +1,5 @@
#include "parser.h"
-#include <stdarg.h>
-#include <stdio.h>
-#include <string.h>
-
-#include "log.h"
-
-void parse_error(struct parse_state *state, const char *fmt, ...)
-{
- va_list ap;
- char buf[128];
- int off;
-
- snprintf(buf, sizeof(buf), "%s: %d: ", state->filename, state->line);
- buf[127] = 0;
- off = strlen(buf);
-
- va_start(ap, fmt);
- vsnprintf(buf + off, 128 - off, fmt, ap);
- va_end(ap);
- buf[127] = 0;
- LOG(ERROR) << buf;
-}
-
int next_token(struct parse_state *state)
{
char *x = state->ptr;
diff --git a/init/parser.h b/init/parser.h
index 95e1164..3dcc566 100644
--- a/init/parser.h
+++ b/init/parser.h
@@ -27,14 +27,8 @@
char *text;
int line;
int nexttoken;
- void *context;
- void (*parse_line)(struct parse_state *state, int nargs, char **args);
- const char *filename;
- void *priv;
};
-void dump_parser_state(void);
int next_token(struct parse_state *state);
-void parse_error(struct parse_state *state, const char *fmt, ...);
#endif /* PARSER_H_ */
diff --git a/init/property_service.cpp b/init/property_service.cpp
index a4d8b5f..3490544 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -14,46 +14,46 @@
* limitations under the License.
*/
+#include "property_service.h"
+
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
#include <inttypes.h>
+#include <limits.h>
+#include <netinet/in.h>
+#include <stdarg.h>
+#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
-#include <unistd.h>
#include <string.h>
-#include <ctype.h>
-#include <fcntl.h>
-#include <stdarg.h>
-#include <dirent.h>
-#include <limits.h>
-#include <errno.h>
+#include <sys/mman.h>
#include <sys/poll.h>
-
-#include <memory>
-#include <vector>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <unistd.h>
#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
#include <sys/_system_properties.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <sys/select.h>
-#include <sys/types.h>
-#include <netinet/in.h>
-#include <sys/mman.h>
+#include <memory>
+#include <vector>
-#include <selinux/android.h>
-#include <selinux/selinux.h>
-#include <selinux/label.h>
-
-#include <fs_mgr.h>
#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
-#include "bootimg.h"
+#include <bootimg.h>
+#include <fs_mgr.h>
+#include <selinux/android.h>
+#include <selinux/label.h>
+#include <selinux/selinux.h>
-#include "property_service.h"
#include "init.h"
#include "util.h"
-#include "log.h"
using android::base::StringPrintf;
@@ -144,7 +144,7 @@
if (name[0] == '.') return false;
if (name[namelen - 1] == '.') return false;
- /* Only allow alphanumeric, plus '.', '-', '@', or '_' */
+ /* Only allow alphanumeric, plus '.', '-', '@', ':', or '_' */
/* Don't allow ".." to appear in a property name */
for (size_t i = 0; i < namelen; i++) {
if (name[i] == '.') {
@@ -152,7 +152,7 @@
if (name[i-1] == '.') return false;
continue;
}
- if (name[i] == '_' || name[i] == '-' || name[i] == '@') continue;
+ if (name[i] == '_' || name[i] == '-' || name[i] == '@' || name[i] == ':') continue;
if (name[i] >= 'a' && name[i] <= 'z') continue;
if (name[i] >= 'A' && name[i] <= 'Z') continue;
if (name[i] >= '0' && name[i] <= '9') continue;
@@ -177,7 +177,7 @@
}
if (name == "selinux.restorecon_recursive" && valuelen > 0) {
- if (restorecon(value.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE) != 0) {
+ if (selinux_android_restorecon(value.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE) != 0) {
LOG(ERROR) << "Failed to restorecon_recursive " << value;
}
}
@@ -206,7 +206,7 @@
if (persistent_properties_loaded && android::base::StartsWith(name, "persist.")) {
write_persistent_property(name.c_str(), value.c_str());
}
- property_changed(name.c_str(), value.c_str());
+ property_changed(name, value);
return PROP_SUCCESS;
}
@@ -510,8 +510,9 @@
static void load_properties_from_file(const char* filename, const char* filter) {
Timer t;
std::string data;
- if (!read_file(filename, &data)) {
- PLOG(WARNING) << "Couldn't load properties from " << filename;
+ std::string err;
+ if (!ReadFile(filename, &data, &err)) {
+ PLOG(WARNING) << "Couldn't load property file: " << err;
return;
}
data.push_back('\n');
@@ -574,10 +575,28 @@
}
}
+// persist.sys.usb.config values can't be combined on build-time when property
+// files are split into each partition.
+// So we need to apply the same rule of build/make/tools/post_process_props.py
+// on runtime.
+static void update_sys_usb_config() {
+ bool is_debuggable = android::base::GetBoolProperty("ro.debuggable", false);
+ std::string config = android::base::GetProperty("persist.sys.usb.config", "");
+ if (config.empty()) {
+ property_set("persist.sys.usb.config", is_debuggable ? "adb" : "none");
+ } else if (is_debuggable && config.find("adb") == std::string::npos &&
+ config.length() + 4 < PROP_VALUE_MAX) {
+ config.append(",adb");
+ property_set("persist.sys.usb.config", config);
+ }
+}
+
void property_load_boot_defaults() {
load_properties_from_file("/default.prop", NULL);
load_properties_from_file("/odm/default.prop", NULL);
load_properties_from_file("/vendor/default.prop", NULL);
+
+ update_sys_usb_config();
}
static void load_override_properties() {
@@ -640,8 +659,8 @@
void start_property_service() {
property_set("ro.property_service.version", "2");
- property_set_fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
- 0666, 0, 0, NULL);
+ property_set_fd = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
+ false, 0666, 0, 0, nullptr, sehandle);
if (property_set_fd == -1) {
PLOG(ERROR) << "start_property_service socket creation failed";
exit(1);
diff --git a/init/property_service.h b/init/property_service.h
index 994da63..9a5b6f6 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -17,9 +17,8 @@
#ifndef _INIT_PROPERTY_H
#define _INIT_PROPERTY_H
-#include <stddef.h>
#include <sys/socket.h>
-#include <sys/system_properties.h>
+
#include <string>
struct property_audit_data {
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 62e5c85..cdfc698 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -13,12 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
+#include "reboot.h"
+
#include <dirent.h>
#include <fcntl.h>
+#include <linux/fs.h>
#include <mntent.h>
+#include <selinux/selinux.h>
#include <sys/cdefs.h>
+#include <sys/ioctl.h>
#include <sys/mount.h>
-#include <sys/quota.h>
#include <sys/reboot.h>
#include <sys/stat.h>
#include <sys/syscall.h>
@@ -27,25 +32,24 @@
#include <memory>
#include <set>
-#include <string>
#include <thread>
#include <vector>
#include <android-base/file.h>
+#include <android-base/logging.h>
#include <android-base/macros.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 <fs_mgr.h>
#include <logwrap/logwrap.h>
+#include <private/android_filesystem_config.h>
-#include "log.h"
#include "property_service.h"
-#include "reboot.h"
#include "service.h"
-#include "util.h"
using android::base::StringPrintf;
@@ -67,39 +71,58 @@
// Utility for struct mntent
class MountEntry {
public:
- explicit MountEntry(const mntent& entry, bool isMounted = true)
+ explicit MountEntry(const mntent& entry)
: mnt_fsname_(entry.mnt_fsname),
mnt_dir_(entry.mnt_dir),
mnt_type_(entry.mnt_type),
- is_mounted_(isMounted) {}
+ mnt_opts_(entry.mnt_opts) {}
- bool IsF2Fs() const { return mnt_type_ == "f2fs"; }
+ bool Umount() {
+ int r = umount2(mnt_dir_.c_str(), 0);
+ if (r == 0) {
+ LOG(INFO) << "umounted " << mnt_fsname_ << ":" << mnt_dir_ << " opts " << mnt_opts_;
+ return true;
+ } else {
+ PLOG(WARNING) << "cannot umount " << mnt_fsname_ << ":" << mnt_dir_ << " opts "
+ << mnt_opts_;
+ return false;
+ }
+ }
- bool IsExt4() const { return mnt_type_ == "ext4"; }
-
- bool is_mounted() const { return is_mounted_; }
-
- void set_is_mounted() { is_mounted_ = false; }
-
- const std::string& mnt_fsname() const { return mnt_fsname_; }
-
- const std::string& mnt_dir() const { return mnt_dir_; }
+ void DoFsck() {
+ int st;
+ if (IsF2Fs()) {
+ const char* f2fs_argv[] = {
+ "/system/bin/fsck.f2fs", "-f", mnt_fsname_.c_str(),
+ };
+ android_fork_execvp_ext(arraysize(f2fs_argv), (char**)f2fs_argv, &st, true, LOG_KLOG,
+ true, nullptr, nullptr, 0);
+ } else if (IsExt4()) {
+ const char* ext4_argv[] = {
+ "/system/bin/e2fsck", "-f", "-y", mnt_fsname_.c_str(),
+ };
+ android_fork_execvp_ext(arraysize(ext4_argv), (char**)ext4_argv, &st, true, LOG_KLOG,
+ true, nullptr, nullptr, 0);
+ }
+ }
static bool IsBlockDevice(const struct mntent& mntent) {
return android::base::StartsWith(mntent.mnt_fsname, "/dev/block");
}
static bool IsEmulatedDevice(const struct mntent& mntent) {
- static const std::string SDCARDFS_NAME = "sdcardfs";
- return android::base::StartsWith(mntent.mnt_fsname, "/data/") &&
- SDCARDFS_NAME == mntent.mnt_type;
+ return android::base::StartsWith(mntent.mnt_fsname, "/data/");
}
private:
+ bool IsF2Fs() const { return mnt_type_ == "f2fs"; }
+
+ bool IsExt4() const { return mnt_type_ == "ext4"; }
+
std::string mnt_fsname_;
std::string mnt_dir_;
std::string mnt_type_;
- bool is_mounted_;
+ std::string mnt_opts_;
};
// Turn off backlight while we are performing power down cleanup activities.
@@ -125,50 +148,6 @@
}
}
-static void DoFsck(const MountEntry& entry) {
- static constexpr int UNMOUNT_CHECK_TIMES = 10;
-
- if (!entry.IsF2Fs() && !entry.IsExt4()) return;
-
- int count = 0;
- while (count++ < UNMOUNT_CHECK_TIMES) {
- int fd = TEMP_FAILURE_RETRY(open(entry.mnt_fsname().c_str(), O_RDONLY | O_EXCL));
- if (fd >= 0) {
- /* |entry->mnt_dir| has sucessfully been unmounted. */
- close(fd);
- break;
- } else if (errno == EBUSY) {
- // Some processes using |entry->mnt_dir| are still alive. Wait for a
- // while then retry.
- std::this_thread::sleep_for(5000ms / UNMOUNT_CHECK_TIMES);
- continue;
- } else {
- /* Cannot open the device. Give up. */
- return;
- }
- }
-
- // NB: With watchdog still running, there is no cap on the time it takes
- // to complete the fsck, from the users perspective the device graphics
- // and responses are locked-up and they may choose to hold the power
- // button in frustration if it drags out.
-
- int st;
- if (entry.IsF2Fs()) {
- const char* f2fs_argv[] = {
- "/system/bin/fsck.f2fs", "-f", entry.mnt_fsname().c_str(),
- };
- android_fork_execvp_ext(arraysize(f2fs_argv), (char**)f2fs_argv, &st, true, LOG_KLOG, true,
- nullptr, nullptr, 0);
- } else if (entry.IsExt4()) {
- const char* ext4_argv[] = {
- "/system/bin/e2fsck", "-f", "-y", entry.mnt_fsname().c_str(),
- };
- android_fork_execvp_ext(arraysize(ext4_argv), (char**)ext4_argv, &st, true, LOG_KLOG, true,
- nullptr, nullptr, 0);
- }
-}
-
static void ShutdownVold() {
const char* vdc_argv[] = {"/system/bin/vdc", "volume", "shutdown"};
int status;
@@ -202,21 +181,11 @@
abort();
}
-static void DoSync() {
- // quota sync is not done by sync call, so should be done separately.
- // quota sync is in VFS level, so do it before sync, which goes down to fs level.
- int r = quotactl(QCMD(Q_SYNC, 0), nullptr, 0 /* do not care */, 0 /* do not care */);
- if (r < 0) {
- PLOG(ERROR) << "quotactl failed";
- }
- sync();
-}
-
/* Find all read+write block devices and emulated devices in /proc/mounts
* and add them to correpsponding list.
*/
static bool FindPartitionsToUmount(std::vector<MountEntry>* blockDevPartitions,
- std::vector<MountEntry>* emulatedPartitions) {
+ std::vector<MountEntry>* emulatedPartitions, bool dump) {
std::unique_ptr<std::FILE, int (*)(std::FILE*)> fp(setmntent("/proc/mounts", "r"), endmntent);
if (fp == nullptr) {
PLOG(ERROR) << "Failed to open /proc/mounts";
@@ -224,44 +193,67 @@
}
mntent* mentry;
while ((mentry = getmntent(fp.get())) != nullptr) {
- if (MountEntry::IsBlockDevice(*mentry) && hasmntopt(mentry, "rw")) {
- blockDevPartitions->emplace_back(*mentry);
+ if (dump) {
+ LOG(INFO) << "mount entry " << mentry->mnt_fsname << ":" << mentry->mnt_dir << " opts "
+ << mentry->mnt_opts << " type " << mentry->mnt_type;
+ } else if (MountEntry::IsBlockDevice(*mentry) && hasmntopt(mentry, "rw")) {
+ blockDevPartitions->emplace(blockDevPartitions->begin(), *mentry);
} else if (MountEntry::IsEmulatedDevice(*mentry)) {
- emulatedPartitions->emplace_back(*mentry);
+ emulatedPartitions->emplace(emulatedPartitions->begin(), *mentry);
}
}
return true;
}
-static bool UmountPartitions(std::vector<MountEntry>* partitions, int maxRetry, int flags) {
- static constexpr int SLEEP_AFTER_RETRY_US = 100000;
-
- bool umountDone;
- int retryCounter = 0;
-
- while (true) {
- umountDone = true;
- for (auto& entry : *partitions) {
- if (entry.is_mounted()) {
- int r = umount2(entry.mnt_dir().c_str(), flags);
- if (r == 0) {
- entry.set_is_mounted();
- LOG(INFO) << StringPrintf("umounted %s, flags:0x%x", entry.mnt_fsname().c_str(),
- flags);
- } else {
- umountDone = false;
- PLOG(WARNING) << StringPrintf("cannot umount %s, mnt_dir %s, flags:0x%x",
- entry.mnt_fsname().c_str(),
- entry.mnt_dir().c_str(), flags);
- }
- }
- }
- if (umountDone) break;
- retryCounter++;
- if (retryCounter >= maxRetry) break;
- usleep(SLEEP_AFTER_RETRY_US);
+static void DumpUmountDebuggingInfo(bool dump_all) {
+ int status;
+ if (!security_getenforce()) {
+ LOG(INFO) << "Run lsof";
+ const char* lsof_argv[] = {"/system/bin/lsof"};
+ android_fork_execvp_ext(arraysize(lsof_argv), (char**)lsof_argv, &status, true, LOG_KLOG,
+ true, nullptr, nullptr, 0);
}
- return umountDone;
+ FindPartitionsToUmount(nullptr, nullptr, true);
+ if (dump_all) {
+ // dump current tasks, this log can be lengthy, so only dump with dump_all
+ android::base::WriteStringToFile("t", "/proc/sysrq-trigger");
+ }
+}
+
+static UmountStat UmountPartitions(int timeoutMs) {
+ 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.
+ */
+ while (true) {
+ std::vector<MountEntry> block_devices;
+ std::vector<MountEntry> emulated_devices;
+ if (!FindPartitionsToUmount(&block_devices, &emulated_devices, false)) {
+ return UMOUNT_STAT_ERROR;
+ }
+ if (block_devices.size() == 0) {
+ stat = UMOUNT_STAT_SUCCESS;
+ break;
+ }
+ if ((timeoutMs < t.duration_ms()) && retry > 0) { // try umount at least once
+ stat = UMOUNT_STAT_TIMEOUT;
+ break;
+ }
+ if (emulated_devices.size() > 0 &&
+ std::all_of(emulated_devices.begin(), emulated_devices.end(),
+ [](auto& entry) { return entry.Umount(); })) {
+ sync();
+ }
+ for (auto& entry : block_devices) {
+ entry.Umount();
+ }
+ retry++;
+ std::this_thread::sleep_for(100ms);
+ }
+ return stat;
}
static void KillAllProcesses() { android::base::WriteStringToFile("i", "/proc/sysrq-trigger"); }
@@ -277,56 +269,38 @@
*/
static UmountStat TryUmountAndFsck(bool runFsck, int timeoutMs) {
Timer t;
- std::vector<MountEntry> emulatedPartitions;
- std::vector<MountEntry> blockDevRwPartitions;
+ std::vector<MountEntry> block_devices;
+ std::vector<MountEntry> emulated_devices;
TurnOffBacklight(); // this part can take time. save power.
- if (!FindPartitionsToUmount(&blockDevRwPartitions, &emulatedPartitions)) {
+ if (runFsck && !FindPartitionsToUmount(&block_devices, &emulated_devices, false)) {
return UMOUNT_STAT_ERROR;
}
- if (emulatedPartitions.size() > 0) {
- LOG(WARNING) << "emulated partitions still exist, will umount";
- /* Pending writes in emulated partitions can fail umount. After a few trials, detach
- * it so that it can be umounted when all writes are done.
- */
- if (!UmountPartitions(&emulatedPartitions, 1, 0)) {
- UmountPartitions(&emulatedPartitions, 1, MNT_DETACH);
- }
- }
- DoSync(); // emulated partition change can lead to update
- UmountStat stat = UMOUNT_STAT_SUCCESS;
- /* data partition needs all pending writes to be completed and all emulated partitions
- * umounted. If umount failed in the above step, it DETACH is requested, so umount can
- * still happen while waiting for /data. If the current waiting is not good enough, give
- * up and leave it to e2fsck after reboot to fix it.
- */
- int remainingTimeMs = timeoutMs - t.duration_ms();
- // each retry takes 100ms, and run at least once.
- int retry = std::max(remainingTimeMs / 100, 1);
- if (!UmountPartitions(&blockDevRwPartitions, retry, 0)) {
- /* Last resort, kill all and try again */
- LOG(WARNING) << "umount still failing, trying kill all";
+
+ UmountStat stat = UmountPartitions(timeoutMs - t.duration_ms());
+ if (stat != UMOUNT_STAT_SUCCESS) {
+ LOG(INFO) << "umount timeout, last resort, kill all and try";
+ if (DUMP_ON_UMOUNT_FAILURE) DumpUmountDebuggingInfo(false);
KillAllProcesses();
- DoSync();
- if (!UmountPartitions(&blockDevRwPartitions, 1, 0)) {
- stat = UMOUNT_STAT_TIMEOUT;
- }
- }
- // fsck part is excluded from timeout check. It only runs for user initiated shutdown
- // and should not affect reboot time.
- if (stat == UMOUNT_STAT_SUCCESS && runFsck) {
- for (auto& entry : blockDevRwPartitions) {
- DoFsck(entry);
- }
+ // even if it succeeds, still it is timeout and do not run fsck with all processes killed
+ UmountPartitions(0);
+ if (DUMP_ON_UMOUNT_FAILURE) DumpUmountDebuggingInfo(true);
}
+ if (stat == UMOUNT_STAT_SUCCESS && runFsck) {
+ // fsck part is excluded from timeout check. It only runs for user initiated shutdown
+ // and should not affect reboot time.
+ for (auto& entry : block_devices) {
+ entry.DoFsck();
+ }
+ }
return stat;
}
static void __attribute__((noreturn)) DoThermalOff() {
LOG(WARNING) << "Thermal system shutdown";
- DoSync();
+ sync();
RebootSystem(ANDROID_RB_THERMOFF, "");
abort();
}
@@ -336,15 +310,15 @@
Timer t;
LOG(INFO) << "Reboot start, reason: " << reason << ", rebootTarget: " << rebootTarget;
- android::base::WriteStringToFile(StringPrintf("%s\n", reason.c_str()), LAST_REBOOT_REASON_FILE);
+ android::base::WriteStringToFile(StringPrintf("%s\n", reason.c_str()), LAST_REBOOT_REASON_FILE,
+ S_IRUSR | S_IWUSR, AID_SYSTEM, AID_SYSTEM);
if (cmd == ANDROID_RB_THERMOFF) { // do not wait if it is thermal
DoThermalOff();
abort();
}
- /* TODO update default waiting time based on usage data */
- constexpr unsigned int shutdownTimeoutDefault = 10;
+ constexpr unsigned int shutdownTimeoutDefault = 6;
unsigned int shutdownTimeout = shutdownTimeoutDefault;
if (SHUTDOWN_ZERO_TIMEOUT) { // eng build
shutdownTimeout = 0;
@@ -357,7 +331,7 @@
// 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", "vold"};
+ const std::set<std::string> to_starts{"watchdogd", "vold", "ueventd"};
ServiceManager::GetInstance().ForEachService([&kill_after_apps, &to_starts](Service* s) {
if (kill_after_apps.count(s->name())) {
s->SetShutdownCritical();
@@ -370,18 +344,9 @@
Service* bootAnim = ServiceManager::GetInstance().FindServiceByName("bootanim");
Service* surfaceFlinger = ServiceManager::GetInstance().FindServiceByName("surfaceflinger");
if (bootAnim != nullptr && surfaceFlinger != nullptr && surfaceFlinger->IsRunning()) {
- property_set("service.bootanim.exit", "0");
- // Could be in the middle of animation. Stop and start so that it can pick
- // up the right mode.
- bootAnim->Stop();
- // start all animation classes if stopped.
ServiceManager::GetInstance().ForEachServiceInClass("animation", [](Service* s) {
- s->Start();
s->SetShutdownCritical(); // will not check animation class separately
});
- bootAnim->Start();
- surfaceFlinger->SetShutdownCritical();
- bootAnim->SetShutdownCritical();
}
// optional shutdown step
@@ -426,8 +391,8 @@
// minimum safety steps before restarting
// 2. kill all services except ones that are necessary for the shutdown sequence.
- ServiceManager::GetInstance().ForEachService([&kill_after_apps](Service* s) {
- if (!s->IsShutdownCritical() || kill_after_apps.count(s->name())) s->Stop();
+ ServiceManager::GetInstance().ForEachService([](Service* s) {
+ if (!s->IsShutdownCritical()) s->Stop();
});
ServiceManager::GetInstance().ReapAnyOutstandingChildren();
@@ -435,14 +400,71 @@
Service* voldService = ServiceManager::GetInstance().FindServiceByName("vold");
if (voldService != nullptr && voldService->IsRunning()) {
ShutdownVold();
+ voldService->Stop();
} else {
LOG(INFO) << "vold not running, skipping vold shutdown";
}
+ // logcat stopped here
+ ServiceManager::GetInstance().ForEachService([&kill_after_apps](Service* s) {
+ if (kill_after_apps.count(s->name())) s->Stop();
+ });
// 4. sync, try umount, and optionally run fsck for user shutdown
- DoSync();
+ sync();
UmountStat stat = TryUmountAndFsck(runFsck, shutdownTimeout * 1000 - t.duration_ms());
+ // Follow what linux shutdown is doing: one more sync with little bit delay
+ sync();
+ 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);
abort();
}
+
+bool HandlePowerctlMessage(const std::string& command) {
+ unsigned int cmd = 0;
+ std::vector<std::string> cmd_params = android::base::Split(command, ",");
+ std::string reboot_target = "";
+ bool run_fsck = false;
+ bool command_invalid = false;
+
+ if (cmd_params.size() > 3) {
+ 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;
+ }
+ } else if (cmd_params[0] == "reboot") {
+ cmd = ANDROID_RB_RESTART2;
+ if (cmd_params.size() >= 2) {
+ reboot_target = cmd_params[1];
+ // When rebooting to the bootloader notify the bootloader writing
+ // also the BCB.
+ if (reboot_target == "bootloader") {
+ std::string err;
+ if (!write_reboot_bootloader(&err)) {
+ LOG(ERROR) << "reboot-bootloader: Error writing "
+ "bootloader_message: "
+ << err;
+ }
+ }
+ // If there is an additional bootloader parameter, pass it along
+ if (cmd_params.size() == 3) {
+ reboot_target += "," + cmd_params[2];
+ }
+ }
+ } else if (command == "thermal-shutdown") { // no additional parameter allowed
+ cmd = ANDROID_RB_THERMOFF;
+ } else {
+ command_invalid = true;
+ }
+ if (command_invalid) {
+ LOG(ERROR) << "powerctl: unrecognized command '" << command << "'";
+ return false;
+ }
+
+ DoReboot(cmd, command, reboot_target, run_fsck);
+ return true;
+}
diff --git a/init/reboot.h b/init/reboot.h
index 3956249..b304b3c 100644
--- a/init/reboot.h
+++ b/init/reboot.h
@@ -17,6 +17,8 @@
#ifndef _INIT_REBOOT_H
#define _INIT_REBOOT_H
+#include <string>
+
/* Reboot / shutdown the system.
* cmd ANDROID_RB_* as defined in android_reboot.h
* reason Reason string like "reboot", "userrequested"
@@ -27,4 +29,7 @@
void DoReboot(unsigned int cmd, const std::string& reason, const std::string& rebootTarget,
bool runFsck) __attribute__((__noreturn__));
+// Parses and handles a setprop sys.powerctl message.
+bool HandlePowerctlMessage(const std::string& command);
+
#endif
diff --git a/init/service.cpp b/init/service.cpp
index e89de9a..7c931da 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -24,28 +24,23 @@
#include <sys/prctl.h>
#include <sys/resource.h>
#include <sys/stat.h>
+#include <sys/system_properties.h>
#include <sys/time.h>
-#include <sys/types.h>
#include <sys/wait.h>
#include <termios.h>
#include <unistd.h>
-#include <selinux/selinux.h>
-
-#include <android-base/chrono_utils.h>
#include <android-base/file.h>
+#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 <processgroup/processgroup.h>
+#include <selinux/selinux.h>
#include <system/thread_defs.h>
-#include <processgroup/processgroup.h>
-
-#include "action.h"
#include "init.h"
-#include "init_parser.h"
-#include "log.h"
#include "property_service.h"
#include "util.h"
@@ -162,6 +157,8 @@
gid_(0),
namespace_flags_(0),
seclabel_(""),
+ onrestart_(false, "<Service '" + name + "' onrestart>", 0),
+ keychord_id_(0),
ioprio_class_(IoSchedClass_NONE),
ioprio_pri_(0),
priority_(0),
@@ -185,6 +182,8 @@
capabilities_(capabilities),
namespace_flags_(namespace_flags),
seclabel_(seclabel),
+ onrestart_(false, "<Service '" + name + "' onrestart>", 0),
+ keychord_id_(0),
ioprio_class_(IoSchedClass_NONE),
ioprio_pri_(0),
priority_(0),
@@ -210,21 +209,33 @@
}
void Service::KillProcessGroup(int signal) {
- LOG(INFO) << "Sending signal " << signal
- << " to service '" << name_
- << "' (pid " << pid_ << ") process group...";
- int r;
- if (signal == SIGTERM) {
- r = killProcessGroupOnce(uid_, pid_, signal);
- } else {
- r = killProcessGroup(uid_, pid_, signal);
- }
- if (r == -1) {
- PLOG(ERROR) << "killProcessGroup(" << uid_ << ", " << pid_ << ", " << signal << ") failed";
- }
- if (kill(-pid_, signal) == -1) {
+ // We ignore reporting errors of ESRCH as this commonly happens in the below case,
+ // 1) Terminate() is called, which sends SIGTERM to the process
+ // 2) The process successfully exits
+ // 3) ReapOneProcess() is called, which calls waitpid(-1, ...) which removes the pid entry.
+ // 4) Reap() is called, which sends SIGKILL, but the pid no longer exists.
+ // TODO: sigaction for SIGCHLD reports the pid of the exiting process,
+ // we should do this kill with that pid first before calling waitpid().
+ if (kill(-pid_, signal) == -1 && errno != ESRCH) {
PLOG(ERROR) << "kill(" << pid_ << ", " << signal << ") failed";
}
+
+ // If we've already seen a successful result from killProcessGroup*(), then we have removed
+ // the cgroup already and calling these functions a second time will simply result in an error.
+ // This is true regardless of which signal was sent.
+ // These functions handle their own logging, so no additional logging is needed.
+ if (!process_cgroup_empty_) {
+ LOG(INFO) << "Sending signal " << signal << " to service '" << name_ << "' (pid " << pid_
+ << ") process group...";
+ int r;
+ if (signal == SIGTERM) {
+ r = killProcessGroupOnce(uid_, pid_, signal);
+ } else {
+ r = killProcessGroup(uid_, pid_, signal);
+ }
+
+ if (r == 0) process_cgroup_empty_ = true;
+ }
}
void Service::SetProcessAttributes() {
@@ -277,10 +288,6 @@
std::for_each(descriptors_.begin(), descriptors_.end(),
std::bind(&DescriptorInfo::Clean, std::placeholders::_1));
- if (flags_ & SVC_EXEC) {
- LOG(INFO) << "SVC_EXEC pid " << pid_ << " finished...";
- }
-
if (flags_ & SVC_TEMPORARY) {
return;
}
@@ -385,9 +392,18 @@
}
bool Service::ParseGroup(const std::vector<std::string>& args, std::string* err) {
- gid_ = decode_uid(args[1].c_str());
+ 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;
+ }
for (std::size_t n = 2; n < args.size(); n++) {
- supp_gids_.emplace_back(decode_uid(args[n].c_str()));
+ gid_t gid;
+ if (!DecodeUid(args[n], &gid, &decode_uid_err)) {
+ *err = "Unable to find GID for '" + args[n] + "': " + decode_uid_err;
+ return false;
+ }
+ supp_gids_.emplace_back(gid);
}
return true;
}
@@ -443,7 +459,8 @@
bool Service::ParseOnrestart(const std::vector<std::string>& args, std::string* err) {
std::vector<std::string> str_args(args.begin() + 1, args.end());
- onrestart_.AddCommand(str_args, "", 0, err);
+ int line = onrestart_.NumCommands() + 1;
+ onrestart_.AddCommand(str_args, line, err);
return true;
}
@@ -484,10 +501,25 @@
template <typename T>
bool Service::AddDescriptor(const std::vector<std::string>& args, std::string* err) {
int perm = args.size() > 3 ? std::strtoul(args[3].c_str(), 0, 8) : -1;
- uid_t uid = args.size() > 4 ? decode_uid(args[4].c_str()) : 0;
- gid_t gid = args.size() > 5 ? decode_uid(args[5].c_str()) : 0;
+ uid_t uid = 0;
+ 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;
+ }
+ }
+
+ 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;
+ }
+ }
+
auto descriptor = std::make_unique<T>(args[1], args[2], uid, gid, perm, context);
auto old =
@@ -505,7 +537,9 @@
// name type perm [ uid gid context ]
bool Service::ParseSocket(const std::vector<std::string>& args, std::string* err) {
- if (args[2] != "dgram" && args[2] != "stream" && args[2] != "seqpacket") {
+ if (!android::base::StartsWith(args[2], "dgram") &&
+ !android::base::StartsWith(args[2], "stream") &&
+ !android::base::StartsWith(args[2], "seqpacket")) {
*err = "socket type must be 'dgram', 'stream' or 'seqpacket'";
return false;
}
@@ -526,7 +560,11 @@
}
bool Service::ParseUser(const std::vector<std::string>& args, std::string* err) {
- uid_ = decode_uid(args[1].c_str());
+ 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;
+ }
return true;
}
@@ -536,14 +574,14 @@
}
class Service::OptionParserMap : public KeywordMap<OptionParser> {
-public:
- OptionParserMap() {
- }
-private:
- Map& map() const override;
+ public:
+ OptionParserMap() {}
+
+ private:
+ const Map& map() const override;
};
-Service::OptionParserMap::Map& Service::OptionParserMap::map() const {
+const Service::OptionParserMap::Map& Service::OptionParserMap::map() const {
constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
// clang-format off
static const Map option_parsers = {
@@ -574,13 +612,8 @@
}
bool Service::ParseLine(const std::vector<std::string>& args, std::string* err) {
- if (args.empty()) {
- *err = "option needed, but not provided";
- return false;
- }
-
static const OptionParserMap parser_map;
- auto parser = parser_map.FindFunction(args[0], args.size() - 1, err);
+ auto parser = parser_map.FindFunction(args, err);
if (!parser) {
return false;
@@ -641,7 +674,6 @@
if (!seclabel_.empty()) {
scon = seclabel_;
} else {
- LOG(INFO) << "computing context for service '" << name_ << "'";
scon = ComputeContextFromExecutable(name_, args_[0]);
if (scon == "") {
return false;
@@ -746,6 +778,7 @@
time_started_ = boot_clock::now();
pid_ = pid;
flags_ |= SVC_RUNNING;
+ process_cgroup_empty_ = false;
errno = -createProcessGroup(uid_, pid_);
if (errno != 0) {
@@ -879,11 +912,6 @@
}
void ServiceManager::AddService(std::unique_ptr<Service> service) {
- Service* old_service = FindServiceByName(service->name());
- if (old_service) {
- LOG(ERROR) << "ignored duplicate definition of service '" << service->name() << "'";
- return;
- }
services_.emplace_back(std::move(service));
}
@@ -938,7 +966,9 @@
std::vector<std::string> str_args(args.begin() + command_arg, args.end());
exec_count_++;
- std::string name = StringPrintf("exec %d (%s)", exec_count_, str_args[0].c_str());
+ std::string name =
+ "exec " + std::to_string(exec_count_) + " (" + android::base::Join(str_args, " ") + ")";
+
unsigned flags = SVC_EXEC | SVC_ONESHOT | SVC_TEMPORARY;
CapSet no_capabilities;
unsigned namespace_flags = 0;
@@ -949,15 +979,28 @@
}
uid_t uid = 0;
if (command_arg > 3) {
- uid = decode_uid(args[2].c_str());
+ 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;
+ return nullptr;
+ }
}
gid_t gid = 0;
std::vector<gid_t> supp_gids;
if (command_arg > 4) {
- gid = decode_uid(args[3].c_str());
+ 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;
+ 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) {
- supp_gids.push_back(decode_uid(args[4 + i].c_str()));
+ 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;
+ return nullptr;
+ }
+ supp_gids.push_back(supp_gid);
}
}
@@ -1058,21 +1101,26 @@
Service* svc = FindServiceByPid(pid);
std::string name;
+ std::string wait_string;
if (svc) {
name = android::base::StringPrintf("Service '%s' (pid %d)",
svc->name().c_str(), pid);
+ if (svc->flags() & SVC_EXEC) {
+ wait_string =
+ android::base::StringPrintf(" waiting took %f seconds", exec_waiter_->duration_s());
+ }
} else {
name = android::base::StringPrintf("Untracked pid %d", pid);
}
if (WIFEXITED(status)) {
- LOG(INFO) << name << " exited with status " << WEXITSTATUS(status);
+ LOG(INFO) << name << " exited with status " << WEXITSTATUS(status) << wait_string;
} else if (WIFSIGNALED(status)) {
- LOG(INFO) << name << " killed by signal " << WTERMSIG(status);
+ LOG(INFO) << name << " killed by signal " << WTERMSIG(status) << wait_string;
} else if (WIFSTOPPED(status)) {
- LOG(INFO) << name << " stopped by signal " << WSTOPSIG(status);
+ LOG(INFO) << name << " stopped by signal " << WSTOPSIG(status) << wait_string;
} else {
- LOG(INFO) << name << " state changed";
+ LOG(INFO) << name << " state changed" << wait_string;
}
if (!svc) {
@@ -1082,7 +1130,6 @@
svc->Reap();
if (svc->flags() & SVC_EXEC) {
- LOG(INFO) << "Wait for exec took " << *exec_waiter_;
exec_waiter_.reset();
}
if (svc->flags() & SVC_TEMPORARY) {
@@ -1097,8 +1144,8 @@
}
}
-bool ServiceParser::ParseSection(const std::vector<std::string>& args,
- std::string* err) {
+bool ServiceParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
+ int line, std::string* err) {
if (args.size() < 3) {
*err = "services must have a name and a program";
return false;
@@ -1110,20 +1157,24 @@
return false;
}
+ Service* old_service = service_manager_->FindServiceByName(name);
+ if (old_service) {
+ *err = "ignored duplicate definition of service '" + name + "'";
+ return false;
+ }
+
std::vector<std::string> str_args(args.begin() + 2, args.end());
service_ = std::make_unique<Service>(name, str_args);
return true;
}
-bool ServiceParser::ParseLineSection(const std::vector<std::string>& args,
- const std::string& filename, int line,
- std::string* err) const {
- return service_ ? service_->ParseLine(args, err) : false;
+bool ServiceParser::ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) {
+ return service_ ? service_->ParseLine(std::move(args), err) : false;
}
void ServiceParser::EndSection() {
if (service_) {
- ServiceManager::GetInstance().AddService(std::move(service_));
+ service_manager_->AddService(std::move(service_));
}
}
diff --git a/init/service.h b/init/service.h
index d84ce02..b9c270a 100644
--- a/init/service.h
+++ b/init/service.h
@@ -19,14 +19,13 @@
#include <sys/types.h>
-#include <cutils/iosched_policy.h>
-
#include <memory>
#include <set>
#include <string>
#include <vector>
#include <android-base/chrono_utils.h>
+#include <cutils/iosched_policy.h>
#include "action.h"
#include "capabilities.h"
@@ -95,14 +94,20 @@
const std::set<std::string>& classnames() const { return classnames_; }
unsigned flags() const { return flags_; }
pid_t pid() const { return pid_; }
+ int crash_count() const { return crash_count_; }
uid_t uid() const { return uid_; }
gid_t gid() const { return gid_; }
- int priority() const { return priority_; }
+ unsigned namespace_flags() const { return namespace_flags_; }
const std::vector<gid_t>& supp_gids() const { return supp_gids_; }
const std::string& seclabel() const { return seclabel_; }
const std::vector<int>& keycodes() const { return keycodes_; }
int keychord_id() const { return keychord_id_; }
void set_keychord_id(int keychord_id) { keychord_id_ = keychord_id; }
+ IoSchedClass ioprio_class() const { return ioprio_class_; }
+ int ioprio_pri() const { return ioprio_pri_; }
+ int priority() const { return priority_; }
+ int oom_score_adjust() const { return oom_score_adjust_; }
+ bool process_cgroup_empty() const { return process_cgroup_empty_; }
const std::vector<std::string>& args() const { return args_; }
private:
@@ -175,6 +180,8 @@
int oom_score_adjust_;
+ bool process_cgroup_empty_ = false;
+
std::vector<std::string> args_;
};
@@ -182,6 +189,9 @@
public:
static ServiceManager& GetInstance();
+ // Exposed for testing
+ ServiceManager();
+
void AddService(std::unique_ptr<Service> service);
Service* MakeExecOneshotService(const std::vector<std::string>& args);
bool Exec(const std::vector<std::string>& args);
@@ -200,8 +210,6 @@
void DumpState() const;
private:
- ServiceManager();
-
// Cleans up a child process that exited.
// Returns true iff a children was cleaned up.
bool ReapOneProcess();
@@ -213,20 +221,18 @@
};
class ServiceParser : public SectionParser {
-public:
- ServiceParser() : service_(nullptr) {
- }
- bool ParseSection(const std::vector<std::string>& args,
+ 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(const std::vector<std::string>& args,
- const std::string& filename, int line,
- std::string* err) const override;
+ bool ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) override;
void EndSection() override;
- void EndFile(const std::string&) override {
- }
-private:
+
+ private:
bool IsValidName(const std::string& name) const;
+ ServiceManager* service_manager_;
std::unique_ptr<Service> service_;
};
diff --git a/init/service_test.cpp b/init/service_test.cpp
new file mode 100644
index 0000000..b9c4627
--- /dev/null
+++ b/init/service_test.cpp
@@ -0,0 +1,69 @@
+/*
+ * 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 "service.h"
+
+#include <algorithm>
+#include <memory>
+#include <type_traits>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+TEST(service, pod_initialized) {
+ constexpr auto memory_size = sizeof(Service);
+ alignas(alignof(Service)) char old_memory[memory_size];
+
+ for (std::size_t i = 0; i < memory_size; ++i) {
+ old_memory[i] = 0xFF;
+ }
+
+ std::vector<std::string> dummy_args{"/bin/test"};
+ Service* service_in_old_memory = new (old_memory) Service("test_old_memory", dummy_args);
+
+ EXPECT_EQ(0U, service_in_old_memory->flags());
+ EXPECT_EQ(0, service_in_old_memory->pid());
+ EXPECT_EQ(0, service_in_old_memory->crash_count());
+ EXPECT_EQ(0U, service_in_old_memory->uid());
+ EXPECT_EQ(0U, service_in_old_memory->gid());
+ EXPECT_EQ(0U, service_in_old_memory->namespace_flags());
+ EXPECT_EQ(0, service_in_old_memory->keychord_id());
+ EXPECT_EQ(IoSchedClass_NONE, service_in_old_memory->ioprio_class());
+ EXPECT_EQ(0, service_in_old_memory->ioprio_pri());
+ EXPECT_EQ(0, service_in_old_memory->priority());
+ EXPECT_EQ(-1000, service_in_old_memory->oom_score_adjust());
+ EXPECT_FALSE(service_in_old_memory->process_cgroup_empty());
+
+ for (std::size_t i = 0; i < memory_size; ++i) {
+ old_memory[i] = 0xFF;
+ }
+
+ Service* service_in_old_memory2 = new (old_memory)
+ Service("test_old_memory", 0U, 0U, 0U, std::vector<gid_t>(), CapSet(), 0U, "", dummy_args);
+
+ EXPECT_EQ(0U, service_in_old_memory2->flags());
+ EXPECT_EQ(0, service_in_old_memory2->pid());
+ EXPECT_EQ(0, service_in_old_memory2->crash_count());
+ EXPECT_EQ(0U, service_in_old_memory2->uid());
+ EXPECT_EQ(0U, service_in_old_memory2->gid());
+ EXPECT_EQ(0U, service_in_old_memory2->namespace_flags());
+ EXPECT_EQ(0, service_in_old_memory2->keychord_id());
+ EXPECT_EQ(IoSchedClass_NONE, service_in_old_memory2->ioprio_class());
+ EXPECT_EQ(0, service_in_old_memory2->ioprio_pri());
+ EXPECT_EQ(0, service_in_old_memory2->priority());
+ EXPECT_EQ(-1000, service_in_old_memory2->oom_score_adjust());
+ EXPECT_FALSE(service_in_old_memory->process_cgroup_empty());
+}
diff --git a/init/signal_handler.cpp b/init/signal_handler.cpp
index 5e3acac..4d56d84 100644
--- a/init/signal_handler.cpp
+++ b/init/signal_handler.cpp
@@ -14,22 +14,17 @@
* limitations under the License.
*/
-#include <errno.h>
-#include <fcntl.h>
#include <signal.h>
-#include <stdio.h>
+#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
-#include <sys/wait.h>
#include <unistd.h>
+#include <android-base/logging.h>
#include <android-base/stringprintf.h>
-#include "action.h"
#include "init.h"
-#include "log.h"
#include "service.h"
-#include "util.h"
static int signal_write_fd = -1;
static int signal_read_fd = -1;
diff --git a/init/test_service/Android.bp b/init/test_service/Android.bp
new file mode 100644
index 0000000..9bd6f27
--- /dev/null
+++ b/init/test_service/Android.bp
@@ -0,0 +1,22 @@
+//
+// 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_binary {
+ name: "test_service",
+ srcs: ["test_service.cpp"],
+ shared_libs: ["libbase"],
+ init_rc: ["test_service.rc"],
+}
diff --git a/init/test_service/Android.mk b/init/test_service/Android.mk
deleted file mode 100644
index 30c9e9d..0000000
--- a/init/test_service/Android.mk
+++ /dev/null
@@ -1,27 +0,0 @@
-# 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.
-
-LOCAL_PATH := $(call my-dir)
-
-# Sample service for testing.
-# =========================================================
-include $(CLEAR_VARS)
-LOCAL_MODULE := test_service
-LOCAL_SRC_FILES := test_service.cpp
-
-LOCAL_SHARED_LIBRARIES += libbase
-
-LOCAL_INIT_RC := test_service.rc
-
-include $(BUILD_EXECUTABLE)
diff --git a/include/system/window.h b/init/uevent.h
similarity index 60%
copy from include/system/window.h
copy to init/uevent.h
index efa10d6..1095665 100644
--- a/include/system/window.h
+++ b/init/uevent.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 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.
@@ -14,9 +14,21 @@
* limitations under the License.
*/
-#ifndef SYSTEM_CORE_INCLUDE_ANDROID_WINDOW_H
-#define SYSTEM_CORE_INCLUDE_ANDROID_WINDOW_H
+#ifndef _INIT_UEVENT_H
+#define _INIT_UEVENT_H
-#include <system/window-deprecated.h>
+#include <string>
-#endif /* SYSTEM_CORE_INCLUDE_ANDROID_WINDOW_H */
+struct Uevent {
+ std::string action;
+ std::string path;
+ std::string subsystem;
+ std::string firmware;
+ std::string partition_name;
+ std::string device_name;
+ int partition_num;
+ int major;
+ int minor;
+};
+
+#endif
diff --git a/init/uevent_listener.cpp b/init/uevent_listener.cpp
new file mode 100644
index 0000000..27c5d23
--- /dev/null
+++ b/init/uevent_listener.cpp
@@ -0,0 +1,196 @@
+/*
+ * 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 "uevent_listener.h"
+
+#include <fcntl.h>
+#include <poll.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <memory>
+
+#include <android-base/logging.h>
+#include <cutils/uevent.h>
+
+static void ParseEvent(const char* msg, Uevent* uevent) {
+ uevent->partition_num = -1;
+ uevent->major = -1;
+ uevent->minor = -1;
+ uevent->action.clear();
+ uevent->path.clear();
+ uevent->subsystem.clear();
+ uevent->firmware.clear();
+ uevent->partition_name.clear();
+ uevent->device_name.clear();
+ // currently ignoring SEQNUM
+ while (*msg) {
+ if (!strncmp(msg, "ACTION=", 7)) {
+ msg += 7;
+ uevent->action = msg;
+ } else if (!strncmp(msg, "DEVPATH=", 8)) {
+ msg += 8;
+ uevent->path = msg;
+ } else if (!strncmp(msg, "SUBSYSTEM=", 10)) {
+ msg += 10;
+ uevent->subsystem = msg;
+ } else if (!strncmp(msg, "FIRMWARE=", 9)) {
+ msg += 9;
+ uevent->firmware = msg;
+ } else if (!strncmp(msg, "MAJOR=", 6)) {
+ msg += 6;
+ uevent->major = atoi(msg);
+ } else if (!strncmp(msg, "MINOR=", 6)) {
+ msg += 6;
+ uevent->minor = atoi(msg);
+ } else if (!strncmp(msg, "PARTN=", 6)) {
+ msg += 6;
+ uevent->partition_num = atoi(msg);
+ } else if (!strncmp(msg, "PARTNAME=", 9)) {
+ msg += 9;
+ uevent->partition_name = msg;
+ } else if (!strncmp(msg, "DEVNAME=", 8)) {
+ msg += 8;
+ uevent->device_name = msg;
+ }
+
+ // advance to after the next \0
+ while (*msg++)
+ ;
+ }
+
+ if (LOG_UEVENTS) {
+ LOG(INFO) << "event { '" << uevent->action << "', '" << uevent->path << "', '"
+ << uevent->subsystem << "', '" << uevent->firmware << "', " << uevent->major
+ << ", " << uevent->minor << " }";
+ }
+}
+
+UeventListener::UeventListener() {
+ // is 256K enough? udev uses 16MB!
+ device_fd_.reset(uevent_open_socket(256 * 1024, true));
+ if (device_fd_ == -1) {
+ LOG(FATAL) << "Could not open uevent socket";
+ }
+
+ fcntl(device_fd_, F_SETFL, O_NONBLOCK);
+}
+
+bool UeventListener::ReadUevent(Uevent* uevent) const {
+ char msg[UEVENT_MSG_LEN + 2];
+ int n = uevent_kernel_multicast_recv(device_fd_, msg, UEVENT_MSG_LEN);
+ if (n <= 0) {
+ if (errno != EAGAIN && errno != EWOULDBLOCK) {
+ LOG(ERROR) << "Error reading from Uevent Fd";
+ }
+ return false;
+ }
+ if (n >= UEVENT_MSG_LEN) {
+ LOG(ERROR) << "Uevent overflowed buffer, discarding";
+ // Return true here even if we discard as we may have more uevents pending and we
+ // want to keep processing them.
+ return true;
+ }
+
+ msg[n] = '\0';
+ msg[n + 1] = '\0';
+
+ ParseEvent(msg, uevent);
+
+ return true;
+}
+
+// RegenerateUevents*() walks parts of the /sys tree and pokes the uevent files to cause the kernel
+// to regenerate device add uevents that have already happened. This is particularly useful when
+// starting ueventd, to regenerate all of the uevents that it had previously missed.
+//
+// We drain any pending events from the netlink socket every time we poke another uevent file to
+// make sure we don't overrun the socket's buffer.
+//
+
+RegenerationAction UeventListener::RegenerateUeventsForDir(DIR* d,
+ RegenerateCallback callback) const {
+ int dfd = dirfd(d);
+
+ int fd = openat(dfd, "uevent", O_WRONLY);
+ if (fd >= 0) {
+ write(fd, "add\n", 4);
+ close(fd);
+
+ Uevent uevent;
+ while (ReadUevent(&uevent)) {
+ if (callback(uevent) == RegenerationAction::kStop) return RegenerationAction::kStop;
+ }
+ }
+
+ dirent* de;
+ while ((de = readdir(d)) != nullptr) {
+ if (de->d_type != DT_DIR || de->d_name[0] == '.') continue;
+
+ fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY);
+ if (fd < 0) continue;
+
+ std::unique_ptr<DIR, decltype(&closedir)> d2(fdopendir(fd), closedir);
+ if (d2 == 0) {
+ close(fd);
+ } else {
+ if (RegenerateUeventsForDir(d2.get(), callback) == RegenerationAction::kStop) {
+ return RegenerationAction::kStop;
+ }
+ }
+ }
+
+ // default is always to continue looking for uevents
+ return RegenerationAction::kContinue;
+}
+
+RegenerationAction UeventListener::RegenerateUeventsForPath(const std::string& path,
+ RegenerateCallback callback) const {
+ std::unique_ptr<DIR, decltype(&closedir)> d(opendir(path.c_str()), closedir);
+ if (!d) return RegenerationAction::kContinue;
+
+ return RegenerateUeventsForDir(d.get(), callback);
+}
+
+static const char* kRegenerationPaths[] = {"/sys/class", "/sys/block", "/sys/devices"};
+
+void UeventListener::RegenerateUevents(RegenerateCallback callback) const {
+ for (const auto path : kRegenerationPaths) {
+ if (RegenerateUeventsForPath(path, callback) == RegenerationAction::kStop) return;
+ }
+}
+
+void UeventListener::DoPolling(PollCallback callback) const {
+ pollfd ufd;
+ ufd.events = POLLIN;
+ ufd.fd = device_fd_;
+
+ while (true) {
+ ufd.revents = 0;
+ int nr = poll(&ufd, 1, -1);
+ if (nr <= 0) {
+ continue;
+ }
+ if (ufd.revents & POLLIN) {
+ // We're non-blocking, so if we receive a poll event keep processing until there
+ // we have exhausted all uevent messages.
+ Uevent uevent;
+ while (ReadUevent(&uevent)) {
+ callback(uevent);
+ }
+ }
+ }
+}
diff --git a/init/uevent_listener.h b/init/uevent_listener.h
new file mode 100644
index 0000000..ba31aaa
--- /dev/null
+++ b/init/uevent_listener.h
@@ -0,0 +1,54 @@
+/*
+ * 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_UEVENT_LISTENER_H
+#define _INIT_UEVENT_LISTENER_H
+
+#include <dirent.h>
+
+#include <functional>
+
+#include <android-base/unique_fd.h>
+
+#include "uevent.h"
+
+#define UEVENT_MSG_LEN 2048
+
+enum class RegenerationAction {
+ kStop = 0, // Stop regenerating uevents as we've handled the one(s) we're interested in.
+ kContinue, // Continue regenerating uevents as we haven't seen the one(s) we're interested in.
+};
+
+using RegenerateCallback = std::function<RegenerationAction(const Uevent&)>;
+using PollCallback = std::function<void(const Uevent&)>;
+
+class UeventListener {
+ public:
+ UeventListener();
+
+ void RegenerateUevents(RegenerateCallback callback) const;
+ RegenerationAction RegenerateUeventsForPath(const std::string& path,
+ RegenerateCallback callback) const;
+ void DoPolling(PollCallback callback) const;
+
+ private:
+ bool ReadUevent(Uevent* uevent) const;
+ RegenerationAction RegenerateUeventsForDir(DIR* d, RegenerateCallback callback) const;
+
+ android::base::unique_fd device_fd_;
+};
+
+#endif
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index ba53e47..bd21a3e 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -14,27 +14,58 @@
* limitations under the License.
*/
+#include "ueventd.h"
+
#include <ctype.h>
#include <fcntl.h>
-#include <grp.h>
-#include <poll.h>
-#include <pwd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <sys/types.h>
-
+#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <selinux/selinux.h>
-#include "ueventd.h"
-#include "log.h"
-#include "util.h"
#include "devices.h"
+#include "firmware_handler.h"
+#include "log.h"
+#include "uevent_listener.h"
#include "ueventd_parser.h"
+#include "util.h"
+
+DeviceHandler CreateDeviceHandler() {
+ Parser parser;
+
+ std::vector<Subsystem> subsystems;
+ parser.AddSectionParser("subsystem", std::make_unique<SubsystemParser>(&subsystems));
+
+ 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("/dev/",
+ std::bind(ParsePermissionsLine, _1, _2, nullptr, &dev_permissions));
+
+ parser.ParseConfig("/ueventd.rc");
+ parser.ParseConfig("/vendor/ueventd.rc");
+ parser.ParseConfig("/odm/ueventd.rc");
+
+ /*
+ * keep the current product name base configuration so
+ * we remain backwards compatible and allow it to override
+ * everything
+ * TODO: cleanup platform ueventd.rc to remove vendor specific
+ * device node entries (b/34968103)
+ */
+ std::string hardware = android::base::GetProperty("ro.hardware", "");
+ parser.ParseConfig("/ueventd." + hardware + ".rc");
+
+ return DeviceHandler(std::move(dev_permissions), std::move(sysfs_permissions),
+ std::move(subsystems));
+}
int ueventd_main(int argc, char **argv)
{
@@ -60,107 +91,26 @@
cb.func_log = selinux_klog_callback;
selinux_set_callback(SELINUX_CB_LOG, cb);
- ueventd_parse_config_file("/ueventd.rc");
- ueventd_parse_config_file("/vendor/ueventd.rc");
- ueventd_parse_config_file("/odm/ueventd.rc");
+ DeviceHandler device_handler = CreateDeviceHandler();
+ UeventListener uevent_listener;
- /*
- * keep the current product name base configuration so
- * we remain backwards compatible and allow it to override
- * everything
- * TODO: cleanup platform ueventd.rc to remove vendor specific
- * device node entries (b/34968103)
- */
- std::string hardware = android::base::GetProperty("ro.hardware", "");
- ueventd_parse_config_file(android::base::StringPrintf("/ueventd.%s.rc", hardware.c_str()).c_str());
+ if (access(COLDBOOT_DONE, F_OK) != 0) {
+ Timer t;
- device_init();
+ uevent_listener.RegenerateUevents([&device_handler](const Uevent& uevent) {
+ HandleFirmwareEvent(uevent);
+ device_handler.HandleDeviceEvent(uevent);
+ return RegenerationAction::kContinue;
+ });
- pollfd ufd;
- ufd.events = POLLIN;
- ufd.fd = get_device_fd();
-
- while (true) {
- ufd.revents = 0;
- int nr = poll(&ufd, 1, -1);
- if (nr <= 0) {
- continue;
- }
- if (ufd.revents & POLLIN) {
- handle_device_fd();
- }
+ close(open(COLDBOOT_DONE, O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
+ LOG(INFO) << "Coldboot took " << t;
}
+ uevent_listener.DoPolling([&device_handler](const Uevent& uevent) {
+ HandleFirmwareEvent(uevent);
+ device_handler.HandleDeviceEvent(uevent);
+ });
+
return 0;
}
-
-void set_device_permission(const char* fn, int line, int nargs, char **args)
-{
- char *name;
- char *attr = 0;
- mode_t perm;
- uid_t uid;
- gid_t gid;
- int prefix = 0;
- int wildcard = 0;
- char *endptr;
-
- if (nargs == 0)
- return;
-
- if (args[0][0] == '#')
- return;
-
- name = args[0];
-
- if (!strncmp(name,"/sys/", 5) && (nargs == 5)) {
- LOG(INFO) << "/sys/ rule " << args[0] << " " << args[1];
- attr = args[1];
- args++;
- nargs--;
- }
-
- if (nargs != 4) {
- LOG(ERROR) << "invalid line (" << fn << ":" << line << ") line for '" << args[0] << "'";
- return;
- }
-
- int len = strlen(name);
- char *wildcard_chr = strchr(name, '*');
- if ((name[len - 1] == '*') && (wildcard_chr == (name + len - 1))) {
- prefix = 1;
- name[len - 1] = '\0';
- } else if (wildcard_chr) {
- wildcard = 1;
- }
-
- perm = strtol(args[1], &endptr, 8);
- if (!endptr || *endptr != '\0') {
- LOG(ERROR) << "invalid mode (" << fn << ":" << line << ") '" << args[1] << "'";
- return;
- }
-
- struct passwd* pwd = getpwnam(args[2]);
- if (!pwd) {
- LOG(ERROR) << "invalid uid (" << fn << ":" << line << ") '" << args[2] << "'";
- return;
- }
- uid = pwd->pw_uid;
-
- struct group* grp = getgrnam(args[3]);
- if (!grp) {
- LOG(ERROR) << "invalid gid (" << fn << ":" << line << ") '" << args[3] << "'";
- return;
- }
- gid = grp->gr_gid;
-
- if (add_dev_perms(name, attr, perm, uid, gid, prefix, wildcard) != 0) {
- PLOG(ERROR) << "add_dev_perms(name=" << name <<
- ", attr=" << attr <<
- ", perm=" << std::oct << perm << std::dec <<
- ", uid=" << uid << ", gid=" << gid <<
- ", prefix=" << prefix << ", wildcard=" << wildcard <<
- ")";
- return;
- }
-}
diff --git a/init/ueventd.h b/init/ueventd.h
index d12d7fe..1f424d3 100644
--- a/init/ueventd.h
+++ b/init/ueventd.h
@@ -17,23 +17,6 @@
#ifndef _INIT_UEVENTD_H_
#define _INIT_UEVENTD_H_
-#include <cutils/list.h>
-#include <sys/types.h>
-
-enum devname_src_t {
- DEVNAME_UNKNOWN = 0,
- DEVNAME_UEVENT_DEVNAME,
- DEVNAME_UEVENT_DEVPATH,
-};
-
-struct ueventd_subsystem {
- struct listnode slist;
-
- const char *name;
- const char *dirname;
- devname_src_t devname_src;
-};
-
-int ueventd_main(int argc, char **argv);
+int ueventd_main(int argc, char** argv);
#endif
diff --git a/init/ueventd_keywords.h b/init/ueventd_keywords.h
deleted file mode 100644
index 88e8f01..0000000
--- a/init/ueventd_keywords.h
+++ /dev/null
@@ -1,15 +0,0 @@
-#ifndef KEYWORD
-#define __MAKE_KEYWORD_ENUM__
-#define KEYWORD(symbol, flags, nargs) K_##symbol,
-enum {
- K_UNKNOWN,
-#endif
- KEYWORD(subsystem, SECTION, 1)
- KEYWORD(devname, OPTION, 1)
- KEYWORD(dirname, OPTION, 1)
-#ifdef __MAKE_KEYWORD_ENUM__
- KEYWORD_COUNT,
-};
-#undef __MAKE_KEYWORD_ENUM__
-#undef KEYWORD
-#endif
diff --git a/init/ueventd_parser.cpp b/init/ueventd_parser.cpp
index 554c1e3..7156e76 100644
--- a/init/ueventd_parser.cpp
+++ b/init/ueventd_parser.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 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.
@@ -14,228 +14,132 @@
* limitations under the License.
*/
-#include <ctype.h>
-#include <errno.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <stdarg.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "ueventd.h"
#include "ueventd_parser.h"
-#include "parser.h"
-#include "log.h"
-#include "util.h"
-static list_declare(subsystem_list);
+#include <grp.h>
+#include <pwd.h>
-static void parse_line_device(struct parse_state *state, int nargs, char **args);
+#include "keyword_map.h"
-#define SECTION 0x01
-#define OPTION 0x02
-
-#include "ueventd_keywords.h"
-
-#define KEYWORD(symbol, flags, nargs) \
- [ K_##symbol ] = { #symbol, (nargs) + 1, flags, },
-
-static struct {
- const char *name;
- unsigned char nargs;
- unsigned char flags;
-} keyword_info[KEYWORD_COUNT] = {
- [ K_UNKNOWN ] = { "unknown", 0, 0 },
-#include "ueventd_keywords.h"
-};
-#undef KEYWORD
-
-#define kw_is(kw, type) (keyword_info[kw].flags & (type))
-#define kw_nargs(kw) (keyword_info[kw].nargs)
-
-static int lookup_keyword(const char *s)
-{
- switch (*s++) {
- case 'd':
- if (!strcmp(s, "evname")) return K_devname;
- if (!strcmp(s, "irname")) return K_dirname;
- break;
- case 's':
- if (!strcmp(s, "ubsystem")) return K_subsystem;
- break;
- }
- return K_UNKNOWN;
-}
-
-static void parse_line_no_op(struct parse_state*, int, char**) {
-}
-
-static int valid_name(const char *name)
-{
- while (*name) {
- if (!isalnum(*name) && (*name != '_') && (*name != '-')) {
- return 0;
- }
- name++;
- }
- return 1;
-}
-
-struct ueventd_subsystem *ueventd_subsystem_find_by_name(const char *name)
-{
- struct listnode *node;
- struct ueventd_subsystem *s;
-
- list_for_each(node, &subsystem_list) {
- s = node_to_item(node, struct ueventd_subsystem, slist);
- if (!strcmp(s->name, name)) {
- return s;
- }
- }
- return 0;
-}
-
-static void *parse_subsystem(parse_state* state, int /*nargs*/, char** args) {
- if (!valid_name(args[1])) {
- parse_error(state, "invalid subsystem name '%s'\n", args[1]);
- return 0;
+bool ParsePermissionsLine(std::vector<std::string>&& args, std::string* err,
+ 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;
}
- ueventd_subsystem* s = ueventd_subsystem_find_by_name(args[1]);
- if (s) {
- parse_error(state, "ignored duplicate definition of subsystem '%s'\n",
- args[1]);
- return 0;
+ if (!is_sysfs && args.size() != 4) {
+ *err = "/dev/ lines must have 4 entries";
+ return false;
}
- s = (ueventd_subsystem*) calloc(1, sizeof(*s));
- if (!s) {
- parse_error(state, "out of memory\n");
- return 0;
- }
- s->name = args[1];
- s->dirname = "/dev";
- list_add_tail(&subsystem_list, &s->slist);
- return s;
-}
+ auto it = args.begin();
+ const std::string& name = *it++;
-static void parse_line_subsystem(struct parse_state *state, int nargs,
- char **args)
-{
- struct ueventd_subsystem *s = (ueventd_subsystem*) state->context;
- int kw;
+ std::string sysfs_attribute;
+ if (is_sysfs) sysfs_attribute = *it++;
- if (nargs == 0) {
- return;
+ // args is now common to both sys and dev entries and contains: <perm> <uid> <gid>
+ std::string& perm_string = *it++;
+ 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;
}
- kw = lookup_keyword(args[0]);
- switch (kw) {
- case K_devname:
- if (!strcmp(args[1], "uevent_devname"))
- s->devname_src = DEVNAME_UEVENT_DEVNAME;
- else if (!strcmp(args[1], "uevent_devpath"))
- s->devname_src = DEVNAME_UEVENT_DEVPATH;
- else
- parse_error(state, "invalid devname '%s'\n", args[1]);
- break;
-
- case K_dirname:
- if (args[1][0] == '/')
- s->dirname = args[1];
- else
- parse_error(state, "dirname '%s' does not start with '/'\n",
- args[1]);
- break;
-
- default:
- parse_error(state, "invalid option '%s'\n", args[0]);
+ std::string& uid_string = *it++;
+ passwd* pwd = getpwnam(uid_string.c_str());
+ if (!pwd) {
+ *err = "invalid uid '" + uid_string + "'";
+ return false;
}
-}
+ uid_t uid = pwd->pw_uid;
-static void parse_new_section(struct parse_state *state, int kw,
- int nargs, char **args)
-{
- printf("[ %s %s ]\n", args[0],
- nargs > 1 ? args[1] : "");
-
- switch(kw) {
- case K_subsystem:
- state->context = parse_subsystem(state, nargs, args);
- if (state->context) {
- state->parse_line = parse_line_subsystem;
- return;
- }
- break;
+ std::string& gid_string = *it++;
+ struct group* grp = getgrnam(gid_string.c_str());
+ if (!grp) {
+ *err = "invalid gid '" + gid_string + "'";
+ return false;
}
- state->parse_line = parse_line_no_op;
-}
+ gid_t gid = grp->gr_gid;
-static void parse_line(struct parse_state *state, char **args, int nargs)
-{
- int kw = lookup_keyword(args[0]);
- int kw_nargs = kw_nargs(kw);
-
- if (nargs < kw_nargs) {
- parse_error(state, "%s requires %d %s\n", args[0], kw_nargs - 1,
- kw_nargs > 2 ? "arguments" : "argument");
- return;
- }
-
- if (kw_is(kw, SECTION)) {
- parse_new_section(state, kw, nargs, args);
- } else if (kw_is(kw, OPTION)) {
- state->parse_line(state, nargs, args);
+ if (is_sysfs) {
+ out_sysfs_permissions->emplace_back(name, sysfs_attribute, perm, uid, gid);
} else {
- parse_line_device(state, nargs, args);
+ out_dev_permissions->emplace_back(name, perm, uid, gid);
}
+ return true;
}
-static void parse_config(const char *fn, const std::string& data)
-{
- char *args[UEVENTD_PARSER_MAXARGS];
+bool SubsystemParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
+ int line, std::string* err) {
+ if (args.size() != 2) {
+ *err = "subsystems must have exactly one name";
+ return false;
+ }
- int nargs = 0;
- parse_state state;
- state.filename = fn;
- state.line = 1;
- state.ptr = strdup(data.c_str()); // TODO: fix this code!
- state.nexttoken = 0;
- state.parse_line = parse_line_no_op;
- for (;;) {
- int token = next_token(&state);
- switch (token) {
- case T_EOF:
- parse_line(&state, args, nargs);
- return;
- case T_NEWLINE:
- if (nargs) {
- parse_line(&state, args, nargs);
- nargs = 0;
- }
- state.line++;
- break;
- case T_TEXT:
- if (nargs < UEVENTD_PARSER_MAXARGS) {
- args[nargs++] = state.text;
- }
- break;
+ if (std::find(subsystems_->begin(), subsystems_->end(), args[1]) != subsystems_->end()) {
+ *err = "ignoring duplicate subsystem entry";
+ return false;
+ }
+
+ subsystem_.name_ = args[1];
+
+ return true;
+}
+
+bool SubsystemParser::ParseDevName(std::vector<std::string>&& args, std::string* err) {
+ if (args[1] == "uevent_devname") {
+ subsystem_.devname_source_ = Subsystem::DevnameSource::DEVNAME_UEVENT_DEVNAME;
+ return true;
+ }
+ if (args[1] == "uevent_devpath") {
+ subsystem_.devname_source_ = Subsystem::DevnameSource::DEVNAME_UEVENT_DEVPATH;
+ return true;
+ }
+
+ *err = "invalid devname '" + args[1] + "'";
+ return false;
+}
+
+bool SubsystemParser::ParseDirName(std::vector<std::string>&& args, std::string* err) {
+ if (args[1].front() != '/') {
+ *err = "dirname '" + args[1] + " ' does not start with '/'";
+ return false;
+ }
+
+ subsystem_.dir_name_ = args[1];
+ return true;
+}
+
+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);
+ static class OptionParserMap : public KeywordMap<OptionParser> {
+ private:
+ const Map& map() const override {
+ // clang-format off
+ static const Map option_parsers = {
+ {"devname", {1, 1, &SubsystemParser::ParseDevName}},
+ {"dirname", {1, 1, &SubsystemParser::ParseDirName}},
+ };
+ // clang-format on
+ return option_parsers;
}
- }
-}
+ } parser_map;
-int ueventd_parse_config_file(const char *fn)
-{
- std::string data;
- if (!read_file(fn, &data)) {
- return -1;
+ auto parser = parser_map.FindFunction(args, err);
+
+ if (!parser) {
+ return false;
}
- data.push_back('\n'); // TODO: fix parse_config.
- parse_config(fn, data);
- return 0;
+ return (this->*parser)(std::move(args), err);
}
-static void parse_line_device(parse_state* state, int nargs, char** args) {
- set_device_permission(state->filename, state->line, nargs, args);
+void SubsystemParser::EndSection() {
+ subsystems_->emplace_back(std::move(subsystem_));
}
diff --git a/init/ueventd_parser.h b/init/ueventd_parser.h
index 4d69897..c1ce976 100644
--- a/init/ueventd_parser.h
+++ b/init/ueventd_parser.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,15 +14,33 @@
* limitations under the License.
*/
-#ifndef _INIT_UEVENTD_PARSER_H_
-#define _INIT_UEVENTD_PARSER_H_
+#ifndef _INIT_UEVENTD_PARSER_H
+#define _INIT_UEVENTD_PARSER_H
-#include "ueventd.h"
+#include <string>
+#include <vector>
-#define UEVENTD_PARSER_MAXARGS 5
+#include "devices.h"
+#include "init_parser.h"
-int ueventd_parse_config_file(const char *fn);
-void set_device_permission(const char* fn, int line, int nargs, char **args);
-struct ueventd_subsystem *ueventd_subsystem_find_by_name(const char *name);
+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;
+ void EndSection() override;
+
+ private:
+ bool ParseDevName(std::vector<std::string>&& args, std::string* err);
+ bool ParseDirName(std::vector<std::string>&& args, std::string* err);
+
+ 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);
#endif
diff --git a/init/util.cpp b/init/util.cpp
index bf4109c..75f81b9 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -14,26 +14,21 @@
* limitations under the License.
*/
+#include "util.h"
+
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
-#include <ftw.h>
#include <pwd.h>
#include <stdarg.h>
-#include <stdlib.h>
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
+#include <sys/socket.h>
+#include <sys/un.h>
#include <time.h>
#include <unistd.h>
-#include <selinux/android.h>
-#include <selinux/label.h>
-
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/un.h>
-
#include <thread>
#include <android-base/file.h>
@@ -42,61 +37,54 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
-
#include <cutils/android_reboot.h>
-/* for ANDROID_SOCKET_* */
#include <cutils/sockets.h>
+#include <selinux/android.h>
-#include "init.h"
-#include "log.h"
#include "reboot.h"
-#include "util.h"
+
+#ifdef _INIT_INIT_H
+#error "Do not include init.h in files used by ueventd or watchdogd; it will expose init's globals"
+#endif
using android::base::boot_clock;
+using namespace std::literals::string_literals;
-static unsigned int do_decode_uid(const char *s)
-{
- unsigned int v;
+// 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 = "";
- if (!s || *s == '\0')
- return UINT_MAX;
-
- if (isalpha(s[0])) {
- struct passwd* pwd = getpwnam(s);
- if (!pwd)
- return UINT_MAX;
- return pwd->pw_uid;
+ 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;
}
errno = 0;
- v = (unsigned int) strtoul(s, 0, 0);
- if (errno)
- return UINT_MAX;
- return v;
-}
-
-/*
- * decode_uid - 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.
- */
-unsigned int decode_uid(const char *s) {
- unsigned int v = do_decode_uid(s);
- if (v == UINT_MAX) {
- LOG(ERROR) << "decode_uid: Unable to find UID for '" << s << "'; returning UINT_MAX";
+ uid_t result = static_cast<uid_t>(strtoul(name.c_str(), 0, 0));
+ if (errno) {
+ *err = "strtoul failed: "s + strerror(errno);
+ return false;
}
- return v;
+ *uid = result;
+ return true;
}
/*
- * create_socket - creates a Unix domain socket in ANDROID_SOCKET_DIR
+ * CreateSocket - creates a Unix domain socket in ANDROID_SOCKET_DIR
* ("/dev/socket") as dictated in init.rc. This socket is inherited by the
* daemon. We communicate the file descriptor's value via the environment
* variable ANDROID_SOCKET_ENV_PREFIX<name> ("ANDROID_SOCKET_foo").
*/
-int create_socket(const char *name, int type, mode_t perm, uid_t uid,
- gid_t gid, const char *socketcon)
-{
+int CreateSocket(const char* name, int type, bool passcred, mode_t perm, uid_t uid, gid_t gid,
+ const char* socketcon, selabel_handle* sehandle) {
if (socketcon) {
if (setsockcreatecon(socketcon) == -1) {
PLOG(ERROR) << "setsockcreatecon(\"" << socketcon << "\") failed";
@@ -130,6 +118,14 @@
}
}
+ if (passcred) {
+ int on = 1;
+ if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on))) {
+ PLOG(ERROR) << "Failed to set SO_PASSCRED '" << name << "'";
+ return -1;
+ }
+ }
+
int ret = bind(fd, (struct sockaddr *) &addr, sizeof (addr));
int savederrno = errno;
@@ -163,12 +159,14 @@
return -1;
}
-bool read_file(const std::string& path, std::string* content) {
+bool ReadFile(const std::string& path, std::string* content, std::string* err) {
content->clear();
+ *err = "";
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;
}
@@ -176,86 +174,52 @@
// or group-writable files.
struct stat sb;
if (fstat(fd, &sb) == -1) {
- PLOG(ERROR) << "fstat failed for '" << path << "'";
+ *err = "fstat failed for '" + path + "': " + strerror(errno);
return false;
}
if ((sb.st_mode & (S_IWGRP | S_IWOTH)) != 0) {
- LOG(ERROR) << "skipping insecure file '" << path << "'";
+ *err = "Skipping insecure file '" + path + "'";
return false;
}
- return android::base::ReadFdToString(fd, content);
+ if (!android::base::ReadFdToString(fd, content)) {
+ *err = "Unable to read '" + path + "': " + strerror(errno);
+ return false;
+ }
+ return true;
}
-bool write_file(const std::string& path, const std::string& content) {
+bool WriteFile(const std::string& path, const std::string& content, std::string* err) {
+ *err = "";
+
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) {
- PLOG(ERROR) << "write_file: Unable to open '" << path << "'";
+ *err = "Unable to open '" + path + "': " + strerror(errno);
return false;
}
- bool success = android::base::WriteStringToFd(content, fd);
- if (!success) {
- PLOG(ERROR) << "write_file: Unable to write to '" << path << "'";
+ if (!android::base::WriteStringToFd(content, fd)) {
+ *err = "Unable to write to '" + path + "': " + strerror(errno);
+ return false;
}
- return success;
+ return true;
}
-int mkdir_recursive(const char *pathname, mode_t mode)
-{
- char buf[128];
- const char *slash;
- const char *p = pathname;
- int width;
- int ret;
- struct stat info;
-
- while ((slash = strchr(p, '/')) != NULL) {
- width = slash - pathname;
- p = slash + 1;
- if (width < 0)
- break;
- if (width == 0)
- continue;
- if ((unsigned int)width > sizeof(buf) - 1) {
- LOG(ERROR) << "path too long for mkdir_recursive";
- return -1;
- }
- memcpy(buf, pathname, width);
- buf[width] = 0;
- if (stat(buf, &info) != 0) {
- ret = make_dir(buf, mode);
- if (ret && errno != EEXIST)
- return ret;
+int mkdir_recursive(const std::string& path, mode_t mode, selabel_handle* sehandle) {
+ 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;
}
}
- ret = make_dir(pathname, mode);
- if (ret && errno != EEXIST)
- return ret;
+ auto ret = make_dir(path.c_str(), mode, sehandle);
+ if (ret && errno != EEXIST) return ret;
return 0;
}
-/*
- * replaces any unacceptable characters with '_', the
- * length of the resulting string is equal to the input string
- */
-void sanitize(char *s)
-{
- const char* accept =
- "abcdefghijklmnopqrstuvwxyz"
- "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
- "0123456789"
- "_-.";
-
- if (!s)
- return;
-
- while (*s) {
- s += strspn(s, accept);
- if (*s) *s++ = '_';
- }
-}
-
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) {
@@ -280,8 +244,7 @@
}
}
-int make_dir(const char *path, mode_t mode)
-{
+int make_dir(const char* path, mode_t mode, selabel_handle* sehandle) {
int rc;
char *secontext = NULL;
@@ -303,11 +266,6 @@
return rc;
}
-int restorecon(const char* pathname, int flags)
-{
- return selinux_android_restorecon(pathname, flags);
-}
-
/*
* Writes hex_len hex characters (1/2 byte) to hex from bytes.
*/
@@ -416,3 +374,26 @@
os << t.duration_s() << " seconds";
return os;
}
+
+// Reads the content of device tree file under kAndroidDtDir 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;
+ if (android::base::ReadFileToString(file_name, dt_content)) {
+ if (!dt_content->empty()) {
+ dt_content->pop_back(); // Trims the trailing '\0' out.
+ return true;
+ }
+ }
+ return false;
+}
+
+bool is_android_dt_value_expected(const std::string& sub_path, const std::string& expected_content) {
+ std::string dt_content;
+ if (read_android_dt_file(sub_path, &dt_content)) {
+ if (dt_content == expected_content) {
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/init/util.h b/init/util.h
index 1034c9b..1ad6b77 100644
--- a/init/util.h
+++ b/init/util.h
@@ -26,17 +26,20 @@
#include <string>
#include <android-base/chrono_utils.h>
+#include <selinux/label.h>
#define COLDBOOT_DONE "/dev/.coldboot_done"
+const std::string kAndroidDtDir("/proc/device-tree/firmware/android/");
+
using android::base::boot_clock;
using namespace std::chrono_literals;
-int create_socket(const char *name, int type, mode_t perm,
- uid_t uid, gid_t gid, const char *socketcon);
+int CreateSocket(const char* name, int type, bool passcred, mode_t perm, uid_t uid, gid_t gid,
+ const char* socketcon, selabel_handle* sehandle);
-bool read_file(const std::string& path, std::string* content);
-bool write_file(const std::string& path, const std::string& content);
+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);
class Timer {
public:
@@ -58,19 +61,21 @@
std::ostream& operator<<(std::ostream& os, const Timer& t);
-unsigned int decode_uid(const char *s);
+bool DecodeUid(const std::string& name, uid_t* uid, std::string* err);
-int mkdir_recursive(const char *pathname, mode_t mode);
-void sanitize(char *p);
+int mkdir_recursive(const std::string& pathname, mode_t mode, selabel_handle* sehandle);
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);
-int restorecon(const char *pathname, int flags = 0);
+int make_dir(const char* path, mode_t mode, selabel_handle* sehandle);
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.
+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);
+
#endif
diff --git a/init/util_test.cpp b/init/util_test.cpp
index 0c0350a..4bb8a83 100644
--- a/init/util_test.cpp
+++ b/init/util_test.cpp
@@ -24,53 +24,67 @@
#include <android-base/test_utils.h>
#include <gtest/gtest.h>
-TEST(util, read_file_ENOENT) {
- std::string s("hello");
- errno = 0;
- EXPECT_FALSE(read_file("/proc/does-not-exist", &s));
- EXPECT_EQ(ENOENT, errno);
- EXPECT_EQ("", s); // s was cleared.
+using namespace std::literals::string_literals;
+
+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);
+ EXPECT_EQ(ENOENT, errno);
+ EXPECT_EQ("", s); // s was cleared.
}
-TEST(util, read_file_group_writeable) {
+TEST(util, ReadFileGroupWriteable) {
std::string s("hello");
TemporaryFile tf;
+ std::string err;
ASSERT_TRUE(tf.fd != -1);
- EXPECT_TRUE(write_file(tf.path, s)) << strerror(errno);
+ EXPECT_TRUE(WriteFile(tf.path, s, &err)) << strerror(errno);
+ EXPECT_EQ("", err);
EXPECT_NE(-1, fchmodat(AT_FDCWD, tf.path, 0620, AT_SYMLINK_NOFOLLOW)) << strerror(errno);
- EXPECT_FALSE(read_file(tf.path, &s)) << 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.
}
-TEST(util, read_file_world_writeable) {
+TEST(util, ReadFileWorldWiteable) {
std::string s("hello");
TemporaryFile tf;
+ std::string err;
ASSERT_TRUE(tf.fd != -1);
- EXPECT_TRUE(write_file(tf.path, s.c_str())) << strerror(errno);
+ EXPECT_TRUE(WriteFile(tf.path, s, &err)) << strerror(errno);
+ EXPECT_EQ("", err);
EXPECT_NE(-1, fchmodat(AT_FDCWD, tf.path, 0602, AT_SYMLINK_NOFOLLOW)) << strerror(errno);
- EXPECT_FALSE(read_file(tf.path, &s)) << 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.
}
-TEST(util, read_file_symbolic_link) {
+TEST(util, ReadFileSymbolicLink) {
std::string s("hello");
errno = 0;
// lrwxrwxrwx 1 root root 13 1970-01-01 00:00 charger -> /sbin/healthd
- EXPECT_FALSE(read_file("/charger", &s));
+ std::string err;
+ EXPECT_FALSE(ReadFile("/charger", &s, &err));
+ EXPECT_EQ("Unable to open '/charger': Too many symbolic links encountered", err);
EXPECT_EQ(ELOOP, errno);
EXPECT_EQ("", s); // s was cleared.
}
-TEST(util, read_file_success) {
- std::string s("hello");
- EXPECT_TRUE(read_file("/proc/version", &s));
- EXPECT_GT(s.length(), 6U);
- EXPECT_EQ('\n', s[s.length() - 1]);
- s[5] = 0;
- EXPECT_STREQ("Linux", s.c_str());
+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());
}
-TEST(util, write_file_binary) {
+TEST(util, WriteFileBinary) {
std::string contents("abcd");
contents.push_back('\0');
contents.push_back('\0');
@@ -78,22 +92,28 @@
ASSERT_EQ(10u, contents.size());
TemporaryFile tf;
+ std::string err;
ASSERT_TRUE(tf.fd != -1);
- EXPECT_TRUE(write_file(tf.path, contents)) << strerror(errno);
+ EXPECT_TRUE(WriteFile(tf.path, contents, &err)) << strerror(errno);
+ EXPECT_EQ("", err);
std::string read_back_contents;
- EXPECT_TRUE(read_file(tf.path, &read_back_contents)) << strerror(errno);
+ 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());
}
-TEST(util, write_file_not_exist) {
+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);
- EXPECT_TRUE(write_file(path, s));
- EXPECT_TRUE(read_file(path, &s2));
+ 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);
struct stat sb;
int fd = open(path.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
@@ -103,20 +123,67 @@
EXPECT_EQ(0, unlink(path.c_str()));
}
-TEST(util, write_file_exist) {
+TEST(util, WriteFileExist) {
std::string s2("");
TemporaryFile tf;
ASSERT_TRUE(tf.fd != -1);
- EXPECT_TRUE(write_file(tf.path, "1hello1")) << strerror(errno);
- EXPECT_TRUE(read_file(tf.path, &s2));
+ 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(write_file(tf.path, "2ll2"));
- EXPECT_TRUE(read_file(tf.path, &s2));
+ 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());
}
-TEST(util, decode_uid) {
- EXPECT_EQ(0U, decode_uid("root"));
- EXPECT_EQ(UINT_MAX, decode_uid("toot"));
- EXPECT_EQ(123U, decode_uid("123"));
+TEST(util, DecodeUid) {
+ uid_t decoded_uid;
+ std::string err;
+
+ EXPECT_TRUE(DecodeUid("root", &decoded_uid, &err));
+ EXPECT_EQ("", err);
+ EXPECT_EQ(0U, decoded_uid);
+
+ 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);
+}
+
+TEST(util, is_dir) {
+ TemporaryDir test_dir;
+ EXPECT_TRUE(is_dir(test_dir.path));
+ TemporaryFile tf;
+ EXPECT_FALSE(is_dir(tf.path));
+}
+
+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));
+ 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);
+ EXPECT_TRUE(is_dir(path1.c_str()));
+ std::string path3 = android::base::StringPrintf("%s/three/directories/deep", test_dir.path);
+ EXPECT_TRUE(is_dir(path1.c_str()));
+}
+
+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));
+ 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);
+ EXPECT_TRUE(is_dir(path1.c_str()));
+ std::string path3 = android::base::StringPrintf("%s/three/directories/deep", test_dir.path);
+ EXPECT_TRUE(is_dir(path1.c_str()));
}
diff --git a/init/watchdogd.cpp b/init/watchdogd.cpp
index b196147..7baa487 100644
--- a/init/watchdogd.cpp
+++ b/init/watchdogd.cpp
@@ -16,14 +16,18 @@
#include <errno.h>
#include <fcntl.h>
+#include <linux/watchdog.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
-#include <linux/watchdog.h>
+#include <android-base/logging.h>
#include "log.h"
-#include "util.h"
+
+#ifdef _INIT_INIT_H
+#error "Do not include init.h in files used by ueventd or watchdogd; it will expose init's globals"
+#endif
#define DEV_NAME "/dev/watchdog"
diff --git a/libappfuse/FuseBuffer.cc b/libappfuse/FuseBuffer.cc
index b42a049..653e96b 100644
--- a/libappfuse/FuseBuffer.cc
+++ b/libappfuse/FuseBuffer.cc
@@ -119,7 +119,12 @@
return ResultOrAgain::kFailure;
}
}
- CHECK(static_cast<uint32_t>(result) == header.len);
+
+ 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;
}
}
@@ -141,8 +146,8 @@
}
constexpr int kMaxMessageSize = sizeof(FuseBuffer);
- if (setsockopt(fds[0], SOL_SOCKET, SO_SNDBUF, &kMaxMessageSize, sizeof(int)) != 0 ||
- setsockopt(fds[1], SOL_SOCKET, SO_SNDBUF, &kMaxMessageSize, sizeof(int)) != 0) {
+ 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;
}
diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp
index 8f74a1a..7dd9227 100644
--- a/libbacktrace/Android.bp
+++ b/libbacktrace/Android.bp
@@ -55,8 +55,15 @@
"UnwindPtrace.cpp",
]
+cc_library_headers {
+ name: "libbacktrace_headers",
+ vendor_available: true,
+ export_include_dirs: ["include"],
+}
+
cc_library {
name: "libbacktrace",
+ vendor_available: true,
defaults: ["libbacktrace_common"],
host_supported: true,
@@ -64,6 +71,8 @@
"BacktraceMap.cpp",
],
+ export_include_dirs: ["include"],
+
target: {
darwin: {
enabled: true,
@@ -101,7 +110,7 @@
"libunwind",
],
- static_libs: ["libcutils"],
+ static_libs: ["libasync_safe", "libcutils"],
},
},
}
diff --git a/libbacktrace/BacktraceAsyncSafeLog.h b/libbacktrace/BacktraceAsyncSafeLog.h
new file mode 100644
index 0000000..14f51be
--- /dev/null
+++ b/libbacktrace/BacktraceAsyncSafeLog.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBBACKTRACE_BACKTRACE_ASYNC_SAFE_LOG_H
+#define _LIBBACKTRACE_BACKTRACE_ASYNC_SAFE_LOG_H
+
+#if defined(__ANDROID__)
+
+#include <async_safe/log.h>
+
+// Logging macros for use in signal handler, only available on target.
+#define BACK_ASYNC_SAFE_LOGW(format, ...) \
+ async_safe_format_log(ANDROID_LOG_WARN, "libbacktrace", "%s: " format, __PRETTY_FUNCTION__, \
+ ##__VA_ARGS__)
+
+#define BACK_ASYNC_SAFE_LOGE(format, ...) \
+ async_safe_format_log(ANDROID_LOG_ERROR, "libbacktrace", "%s: " format, __PRETTY_FUNCTION__, \
+ ##__VA_ARGS__)
+
+#else
+
+#define BACK_ASYNC_SAFE_LOGW(format, ...)
+
+#define BACK_ASYNC_SAFE_LOGE(format, ...)
+
+#endif
+
+#endif // _LIBBACKTRACE_BACKTRACE_ASYNC_SAFE_LOG_H
diff --git a/libbacktrace/BacktraceCurrent.cpp b/libbacktrace/BacktraceCurrent.cpp
index d7a3b01..fb76b85 100644
--- a/libbacktrace/BacktraceCurrent.cpp
+++ b/libbacktrace/BacktraceCurrent.cpp
@@ -31,8 +31,8 @@
#include <backtrace/Backtrace.h>
#include <backtrace/BacktraceMap.h>
+#include "BacktraceAsyncSafeLog.h"
#include "BacktraceCurrent.h"
-#include "BacktraceLog.h"
#include "ThreadEntry.h"
#include "thread_utils.h"
@@ -47,7 +47,7 @@
*out_value = *reinterpret_cast<word_t*>(ptr);
return true;
} else {
- BACK_LOGW("pointer %p not in a readable map", reinterpret_cast<void*>(ptr));
+ BACK_ASYNC_SAFE_LOGW("pointer %p not in a readable map", reinterpret_cast<void*>(ptr));
*out_value = static_cast<word_t>(-1);
return false;
}
@@ -114,7 +114,8 @@
static void SignalLogOnly(int, siginfo_t*, void*) {
ErrnoRestorer restore;
- BACK_LOGE("pid %d, tid %d: Received a spurious signal %d\n", getpid(), gettid(), THREAD_SIGNAL);
+ BACK_ASYNC_SAFE_LOGE("pid %d, tid %d: Received a spurious signal %d\n", getpid(), gettid(),
+ THREAD_SIGNAL);
}
static void SignalHandler(int, siginfo_t*, void* sigcontext) {
@@ -122,7 +123,7 @@
ThreadEntry* entry = ThreadEntry::Get(getpid(), gettid(), false);
if (!entry) {
- BACK_LOGE("pid %d, tid %d entry not found", getpid(), gettid());
+ BACK_ASYNC_SAFE_LOGE("pid %d, tid %d entry not found", getpid(), gettid());
return;
}
@@ -141,7 +142,7 @@
entry->Wake();
} else {
// At this point, it is possible that entry has been freed, so just exit.
- BACK_LOGE("Timed out waiting for unwind thread to indicate it completed.");
+ BACK_ASYNC_SAFE_LOGE("Timed out waiting for unwind thread to indicate it completed.");
}
}
@@ -159,7 +160,7 @@
act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
sigemptyset(&act.sa_mask);
if (sigaction(THREAD_SIGNAL, &act, &oldact) != 0) {
- BACK_LOGE("sigaction failed: %s", strerror(errno));
+ BACK_ASYNC_SAFE_LOGE("sigaction failed: %s", strerror(errno));
ThreadEntry::Remove(entry);
pthread_mutex_unlock(&g_sigaction_mutex);
error_ = BACKTRACE_UNWIND_ERROR_INTERNAL;
@@ -212,7 +213,7 @@
// Wait for the thread to indicate it is done with the ThreadEntry.
if (!entry->Wait(3)) {
// Send a warning, but do not mark as a failure to unwind.
- BACK_LOGW("Timed out waiting for signal handler to indicate it finished.");
+ BACK_ASYNC_SAFE_LOGW("Timed out waiting for signal handler to indicate it finished.");
}
} else {
// Check to see if the thread has disappeared.
@@ -220,7 +221,7 @@
error_ = BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST;
} else {
error_ = BACKTRACE_UNWIND_ERROR_THREAD_TIMEOUT;
- BACK_LOGE("Timed out waiting for signal handler to get ucontext data.");
+ BACK_ASYNC_SAFE_LOGE("Timed out waiting for signal handler to get ucontext data.");
}
}
diff --git a/libbacktrace/ThreadEntry.cpp b/libbacktrace/ThreadEntry.cpp
index 084c1aa..9bd59e4 100644
--- a/libbacktrace/ThreadEntry.cpp
+++ b/libbacktrace/ThreadEntry.cpp
@@ -21,7 +21,7 @@
#include <time.h>
#include <ucontext.h>
-#include "BacktraceLog.h"
+#include "BacktraceAsyncSafeLog.h"
#include "ThreadEntry.h"
// Initialize static member variables.
@@ -106,7 +106,7 @@
while (wait_value_ != value) {
int ret = pthread_cond_timedwait(&wait_cond_, &wait_mutex_, &ts);
if (ret != 0) {
- BACK_LOGW("pthread_cond_timedwait for value %d failed: %s", value, strerror(ret));
+ BACK_ASYNC_SAFE_LOGW("pthread_cond_timedwait for value %d failed: %s", value, strerror(ret));
wait_completed = false;
break;
}
diff --git a/include/backtrace/Backtrace.h b/libbacktrace/include/backtrace/Backtrace.h
similarity index 100%
rename from include/backtrace/Backtrace.h
rename to libbacktrace/include/backtrace/Backtrace.h
diff --git a/include/backtrace/BacktraceMap.h b/libbacktrace/include/backtrace/BacktraceMap.h
similarity index 100%
rename from include/backtrace/BacktraceMap.h
rename to libbacktrace/include/backtrace/BacktraceMap.h
diff --git a/include/backtrace/backtrace_constants.h b/libbacktrace/include/backtrace/backtrace_constants.h
similarity index 100%
rename from include/backtrace/backtrace_constants.h
rename to libbacktrace/include/backtrace/backtrace_constants.h
diff --git a/libbinderwrapper/Android.bp b/libbinderwrapper/Android.bp
new file mode 100644
index 0000000..6fac0d8
--- /dev/null
+++ b/libbinderwrapper/Android.bp
@@ -0,0 +1,61 @@
+//
+// 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.
+//
+
+cc_defaults {
+ name: "libbinderwrapper_defaults",
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-unused-parameter",
+
+ // for libchrome
+ "-Wno-sign-promo",
+ ],
+ export_include_dirs: ["include"],
+ shared_libs: [
+ "libbinder",
+ "libchrome",
+ "libutils",
+ ],
+}
+
+// libbinderwrapper shared library
+// ========================================================
+cc_library_shared {
+ name: "libbinderwrapper",
+ defaults: ["libbinderwrapper_defaults"],
+
+ srcs: [
+ "binder_wrapper.cc",
+ "real_binder_wrapper.cc",
+ ],
+}
+
+// libbinderwrapper_test_support static library
+// ========================================================
+cc_library_static {
+ name: "libbinderwrapper_test_support",
+ defaults: ["libbinderwrapper_defaults"],
+
+ static_libs: ["libgtest"],
+ shared_libs: ["libbinderwrapper"],
+
+ srcs: [
+ "binder_test_base.cc",
+ "stub_binder_wrapper.cc",
+ ],
+}
diff --git a/libbinderwrapper/Android.mk b/libbinderwrapper/Android.mk
deleted file mode 100644
index c768373..0000000
--- a/libbinderwrapper/Android.mk
+++ /dev/null
@@ -1,62 +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)
-
-binderwrapperCommonCFlags := -Wall -Werror -Wno-unused-parameter
-binderwrapperCommonCFlags += -Wno-sign-promo # for libchrome
-binderwrapperCommonExportCIncludeDirs := $(LOCAL_PATH)/include
-binderwrapperCommonCIncludes := $(LOCAL_PATH)/include
-binderwrapperCommonSharedLibraries := \
- libbinder \
- libchrome \
- libutils \
-
-# libbinderwrapper shared library
-# ========================================================
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libbinderwrapper
-LOCAL_CPP_EXTENSION := .cc
-LOCAL_CFLAGS := $(binderwrapperCommonCFlags)
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(binderwrapperCommonExportCIncludeDirs)
-LOCAL_C_INCLUDES := $(binderwrapperCommonCIncludes)
-LOCAL_SHARED_LIBRARIES := $(binderwrapperCommonSharedLibraries)
-LOCAL_SRC_FILES := \
- binder_wrapper.cc \
- real_binder_wrapper.cc \
-
-include $(BUILD_SHARED_LIBRARY)
-
-# libbinderwrapper_test_support static library
-# ========================================================
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libbinderwrapper_test_support
-LOCAL_CPP_EXTENSION := .cc
-LOCAL_CFLAGS := $(binderwrapperCommonCFlags)
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(binderwrapperCommonExportCIncludeDirs)
-LOCAL_C_INCLUDES := $(binderwrapperCommonCIncludes)
-LOCAL_STATIC_LIBRARIES := libgtest
-LOCAL_SHARED_LIBRARIES := \
- $(binderwrapperCommonSharedLibraries) \
- libbinderwrapper \
-
-LOCAL_SRC_FILES := \
- binder_test_base.cc \
- stub_binder_wrapper.cc \
-
-include $(BUILD_STATIC_LIBRARY)
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 f668f18..245deb1 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -24,23 +24,20 @@
"socket_inaddr_any_server_unix.c",
"socket_local_client_unix.c",
"socket_local_server_unix.c",
- "socket_loopback_server_unix.c",
"socket_network_client_unix.c",
"sockets_unix.cpp",
"str_parms.c",
]
cc_library_headers {
- name: "libcutils_vndk_headers",
- host_supported: true,
- export_include_dirs: ["include_vndk"],
-}
-
-cc_library_headers {
name: "libcutils_headers",
+ vendor_available: true,
host_supported: true,
export_include_dirs: ["include"],
target: {
+ vendor: {
+ export_include_dirs: ["include_vndk"],
+ },
linux_bionic: {
enabled: true,
},
@@ -52,10 +49,11 @@
cc_library {
name: "libcutils",
+ vendor_available: true,
host_supported: true,
srcs: [
"config_utils.c",
- "fs_config.c",
+ "fs_config.cpp",
"canned_fs_config.c",
"hashmap.c",
"iosched_policy.c",
@@ -96,6 +94,9 @@
shared: {
enabled: false,
},
+ cflags: [
+ "-D_GNU_SOURCE",
+ ],
},
android: {
@@ -144,7 +145,10 @@
},
shared_libs: ["liblog"],
- header_libs: ["libcutils_headers"],
+ header_libs: [
+ "libcutils_headers",
+ "libutils_headers",
+ ],
export_header_lib_headers: ["libcutils_headers"],
cflags: [
diff --git a/libcutils/ashmem-dev.c b/libcutils/ashmem-dev.c
index 92717c0..b4abb79 100644
--- a/libcutils/ashmem-dev.c
+++ b/libcutils/ashmem-dev.c
@@ -28,6 +28,7 @@
#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
+#include <sys/sysmacros.h>
#include <sys/types.h>
#include <unistd.h>
diff --git a/libcutils/fs_config.c b/libcutils/fs_config.cpp
similarity index 75%
rename from libcutils/fs_config.c
rename to libcutils/fs_config.cpp
index a2f5816..221dea2 100644
--- a/libcutils/fs_config.c
+++ b/libcutils/fs_config.cpp
@@ -14,15 +14,12 @@
* 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 used to define the properties of the filesystem
+// images generated by build tools (mkbootfs and mkyaffs2image) and
+// by the device side of adb.
#define LOG_TAG "fs_config"
-#define _GNU_SOURCE
-
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
@@ -42,8 +39,10 @@
#define O_BINARY 0
#endif
-/* My kingdom for <endian.h> */
-static inline uint16_t get2LE(const uint8_t* src) { return src[0] | (src[1] << 8); }
+// My kingdom for <endian.h>
+static inline uint16_t get2LE(const uint8_t* src) {
+ return src[0] | (src[1] << 8);
+}
static inline uint64_t get8LE(const uint8_t* src) {
uint32_t low, high;
@@ -55,14 +54,13 @@
#define ALIGN(x, alignment) (((x) + ((alignment)-1)) & ~((alignment)-1))
-/* Rules for directories.
-** These rules are applied based on "first match", so they
-** should start with the most specific path and work their
-** way up to the root.
-*/
+// Rules for directories.
+// These rules are applied based on "first match", so they
+// should start with the most specific path and work their
+// way up to the root.
static const struct fs_path_config android_dirs[] = {
- /* clang-format off */
+ // clang-format off
{ 00770, AID_SYSTEM, AID_CACHE, 0, "cache" },
{ 00500, AID_ROOT, AID_ROOT, 0, "config" },
{ 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/app" },
@@ -92,25 +90,26 @@
{ 00755, AID_ROOT, AID_SHELL, 0, "system/xbin" },
{ 00755, AID_ROOT, AID_SHELL, 0, "vendor" },
{ 00755, AID_ROOT, AID_ROOT, 0, 0 },
- /* clang-format on */
+ // clang-format on
};
+#ifndef __ANDROID_VNDK__
+auto __for_testing_only__android_dirs = android_dirs;
+#endif
-/* Rules for files.
-** These rules are applied based on "first match", so they
-** should start with the most specific path and work their
-** way up to the root. Prefixes ending in * denotes wildcard
-** and will allow partial matches.
-*/
+// Rules for files.
+// These rules are applied based on "first match", so they
+// should start with the most specific path and work their
+// way up to the root. Prefixes ending in * denotes wildcard
+// and will allow partial matches.
static const char sys_conf_dir[] = "/system/etc/fs_config_dirs";
static const char sys_conf_file[] = "/system/etc/fs_config_files";
-/* No restrictions are placed on the vendor and oem file-system config files,
- * although the developer is advised to restrict the scope to the /vendor or
- * oem/ file-system since the intent is to provide support for customized
- * portions of a separate vendor.img or oem.img. Has to remain open so that
- * customization can also land on /system/vendor, /system/oem or /system/odm.
- * We expect build-time checking or filtering when constructing the associated
- * fs_config_* files (see build/tools/fs_config/fs_config_generate.c)
- */
+// No restrictions are placed on the vendor and oem file-system config files,
+// although the developer is advised to restrict the scope to the /vendor or
+// oem/ file-system since the intent is to provide support for customized
+// portions of a separate vendor.img or oem.img. Has to remain open so that
+// customization can also land on /system/vendor, /system/oem or /system/odm.
+// We expect build-time checking or filtering when constructing the associated
+// fs_config_* files (see build/tools/fs_config/fs_config_generate.c)
static const char ven_conf_dir[] = "/vendor/etc/fs_config_dirs";
static const char ven_conf_file[] = "/vendor/etc/fs_config_files";
static const char oem_conf_dir[] = "/oem/etc/fs_config_dirs";
@@ -125,7 +124,7 @@
};
static const struct fs_path_config android_files[] = {
- /* clang-format off */
+ // clang-format off
{ 00644, AID_SYSTEM, AID_SYSTEM, 0, "data/app/*" },
{ 00644, AID_SYSTEM, AID_SYSTEM, 0, "data/app-ephemeral/*" },
{ 00644, AID_SYSTEM, AID_SYSTEM, 0, "data/app-private/*" },
@@ -158,19 +157,18 @@
{ 00555, AID_ROOT, AID_ROOT, 0, "system/etc/ppp/*" },
{ 00555, AID_ROOT, AID_ROOT, 0, "system/etc/rc.*" },
{ 00440, AID_ROOT, AID_ROOT, 0, "system/etc/recovery.img" },
- { 00440, AID_RADIO, AID_ROOT, 0, "system/etc/xtables.lock" },
{ 00600, AID_ROOT, AID_ROOT, 0, "vendor/build.prop" },
{ 00600, AID_ROOT, AID_ROOT, 0, "vendor/default.prop" },
{ 00444, AID_ROOT, AID_ROOT, 0, ven_conf_dir + 1 },
{ 00444, AID_ROOT, AID_ROOT, 0, ven_conf_file + 1 },
- /* the following two files are INTENTIONALLY set-uid, but they
- * are NOT included on user builds. */
+ // the following two files are INTENTIONALLY set-uid, but they
+ // are NOT included on user builds.
{ 06755, AID_ROOT, AID_ROOT, 0, "system/xbin/procmem" },
{ 04750, AID_ROOT, AID_SHELL, 0, "system/xbin/su" },
- /* the following files have enhanced capabilities and ARE included
- * in user builds. */
+ // the following files have enhanced capabilities and ARE included
+ // in user builds.
{ 00700, AID_SYSTEM, AID_SHELL, CAP_MASK_LONG(CAP_BLOCK_SUSPEND),
"system/bin/inputflinger" },
{ 00550, AID_LOGD, AID_LOGD, CAP_MASK_LONG(CAP_SYSLOG) |
@@ -181,26 +179,27 @@
CAP_MASK_LONG(CAP_SETGID),
"system/bin/run-as" },
- /* Support FIFO scheduling mode in SurfaceFlinger. */
+ // Support FIFO scheduling mode in SurfaceFlinger.
{ 00755, AID_SYSTEM, AID_GRAPHICS, CAP_MASK_LONG(CAP_SYS_NICE),
"system/bin/surfaceflinger" },
- /* Support hostapd administering a network interface. */
+ // Support hostapd administering a network interface.
{ 00755, AID_WIFI, AID_WIFI, CAP_MASK_LONG(CAP_NET_ADMIN) |
CAP_MASK_LONG(CAP_NET_RAW),
"vendor/bin/hostapd" },
- /* Support Bluetooth legacy hal accessing /sys/class/rfkill */
- { 00700, AID_BLUETOOTH, AID_BLUETOOTH, CAP_MASK_LONG(CAP_NET_ADMIN),
+ // Support Bluetooth legacy hal accessing /sys/class/rfkill
+ // Support RT scheduling in Bluetooth
+ { 00700, AID_BLUETOOTH, AID_BLUETOOTH, CAP_MASK_LONG(CAP_NET_ADMIN) |
+ CAP_MASK_LONG(CAP_SYS_NICE),
"vendor/bin/hw/android.hardware.bluetooth@1.0-service" },
- /* Support wifi_hal_legacy administering a network interface. */
+ // Support wifi_hal_legacy administering a network interface.
{ 00755, AID_WIFI, AID_WIFI, CAP_MASK_LONG(CAP_NET_ADMIN) |
CAP_MASK_LONG(CAP_NET_RAW),
"vendor/bin/hw/android.hardware.wifi@1.0-service" },
- /* A non-privileged zygote that spawns
- * isolated processes for web rendering. */
+ // A non-privileged zygote that spawns isolated processes for web rendering.
{ 0750, AID_ROOT, AID_ROOT, CAP_MASK_LONG(CAP_SETUID) |
CAP_MASK_LONG(CAP_SETGID) |
CAP_MASK_LONG(CAP_SETPCAP),
@@ -210,7 +209,7 @@
CAP_MASK_LONG(CAP_SETPCAP),
"system/bin/webview_zygote64" },
- /* generic defaults */
+ // generic defaults
{ 00755, AID_ROOT, AID_ROOT, 0, "bin/*" },
{ 00640, AID_ROOT, AID_SHELL, 0, "fstab.*" },
{ 00750, AID_ROOT, AID_SHELL, 0, "init*" },
@@ -218,30 +217,34 @@
{ 00755, AID_ROOT, AID_SHELL, 0, "system/bin/*" },
{ 00755, AID_ROOT, AID_ROOT, 0, "system/lib/valgrind/*" },
{ 00755, AID_ROOT, AID_ROOT, 0, "system/lib64/valgrind/*" },
- { 00755, AID_ROOT, AID_SHELL, 0, "system/vendor/bin/*" },
- { 00755, AID_ROOT, AID_SHELL, 0, "system/vendor/xbin/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/xbin/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "vendor/bin/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "vendor/xbin/*" },
{ 00644, AID_ROOT, AID_ROOT, 0, 0 },
- /* clang-format on */
+ // clang-format on
};
+#ifndef __ANDROID_VNDK__
+auto __for_testing_only__android_files = android_files;
+#endif
+
+static size_t strip(const char* path, size_t len, const char suffix[]) {
+ if (len < strlen(suffix)) return len;
+ if (strncmp(path + len - strlen(suffix), suffix, strlen(suffix))) return len;
+ return len - strlen(suffix);
+}
static int fs_config_open(int dir, int which, const char* target_out_path) {
int fd = -1;
if (target_out_path && *target_out_path) {
- /* target_out_path is the path to the directory holding content of
- * system partition but as we cannot guaranty it ends with '/system'
- * we need this below skip_len logic */
+ // target_out_path is the path to the directory holding content of
+ // system partition but as we cannot guarantee it ends with '/system'
+ // or with or without a trailing slash, need to strip them carefully.
char* name = NULL;
- int target_out_path_len = strlen(target_out_path);
- int skip_len = strlen("/system");
-
- if (target_out_path[target_out_path_len] == '/') {
- skip_len++;
- }
- if (asprintf(&name, "%s%s", target_out_path, conf[which][dir] + skip_len) != -1) {
+ size_t len = strlen(target_out_path);
+ len = strip(target_out_path, len, "/");
+ len = strip(target_out_path, len, "/system");
+ if (asprintf(&name, "%.*s%s", (int)len, target_out_path, conf[which][dir]) != -1) {
fd = TEMP_FAILURE_RETRY(open(name, O_RDONLY | O_BINARY));
free(name);
}
@@ -252,21 +255,51 @@
return fd;
}
+// if path is "vendor/<stuff>", "oem/<stuff>" or "odm/<stuff>"
+static bool is_partition(const char* path, size_t len) {
+ static const char* partitions[] = {"vendor/", "oem/", "odm/"};
+ for (size_t i = 0; i < (sizeof(partitions) / sizeof(partitions[0])); ++i) {
+ size_t plen = strlen(partitions[i]);
+ if (len <= plen) continue;
+ if (!strncmp(path, partitions[i], plen)) return true;
+ }
+ return false;
+}
+
+// alias prefixes of "<partition>/<stuff>" to "system/<partition>/<stuff>" or
+// "system/<partition>/<stuff>" to "<partition>/<stuff>"
+static bool prefix_cmp(const char* prefix, const char* path, size_t len) {
+ if (!strncmp(prefix, path, len)) return true;
+
+ static const char system[] = "system/";
+ if (!strncmp(path, system, strlen(system))) {
+ path += strlen(system);
+ } else if (len <= strlen(system)) {
+ return false;
+ } else if (strncmp(prefix, system, strlen(system))) {
+ return false;
+ } else {
+ prefix += strlen(system);
+ len -= strlen(system);
+ }
+ return is_partition(prefix, len) && !strncmp(prefix, path, len);
+}
+
static bool fs_config_cmp(bool dir, const char* prefix, size_t len, const char* path, size_t plen) {
if (dir) {
if (plen < len) {
return false;
}
} else {
- /* If name ends in * then allow partial matches. */
+ // If name ends in * then allow partial matches.
if (prefix[len - 1] == '*') {
- return !strncmp(prefix, path, len - 1);
+ return prefix_cmp(prefix, path, len - 1);
}
if (plen != len) {
return false;
}
}
- return !strncmp(prefix, path, len);
+ return prefix_cmp(prefix, path, len);
}
void fs_config(const char* path, int dir, const char* target_out_path, unsigned* uid, unsigned* gid,
@@ -294,7 +327,7 @@
ALOGE("%s len is corrupted", conf[which][dir]);
break;
}
- prefix = calloc(1, remainder);
+ prefix = static_cast<char*>(calloc(1, remainder));
if (!prefix) {
ALOGE("%s out of memory", conf[which][dir]);
break;
@@ -305,7 +338,7 @@
break;
}
len = strnlen(prefix, remainder);
- if (len >= remainder) { /* missing a terminating null */
+ if (len >= remainder) { // missing a terminating null
free(prefix);
ALOGE("%s is corrupted", conf[which][dir]);
break;
diff --git a/libcutils/include/cutils/bitops.h b/libcutils/include/cutils/bitops.h
index 045830d..38d2840 100644
--- a/libcutils/include/cutils/bitops.h
+++ b/libcutils/include/cutils/bitops.h
@@ -24,94 +24,15 @@
__BEGIN_DECLS
-/*
- * Bitmask Operations
- *
- * Note this doesn't provide any locking/exclusion, and isn't atomic.
- * Additionally no bounds checking is done on the bitmask array.
- *
- * Example:
- *
- * int num_resources;
- * unsigned int resource_bits[BITS_TO_WORDS(num_resources)];
- * bitmask_init(resource_bits, num_resources);
- * ...
- * int bit = bitmask_ffz(resource_bits, num_resources);
- * bitmask_set(resource_bits, bit);
- * ...
- * if (bitmask_test(resource_bits, bit)) { ... }
- * ...
- * bitmask_clear(resource_bits, bit);
- *
- */
-
-#define BITS_PER_WORD (sizeof(unsigned int) * 8)
-#define BITS_TO_WORDS(x) (((x) + BITS_PER_WORD - 1) / BITS_PER_WORD)
-#define BIT_IN_WORD(x) ((x) % BITS_PER_WORD)
-#define BIT_WORD(x) ((x) / BITS_PER_WORD)
-#define BIT_MASK(x) (1 << BIT_IN_WORD(x))
-
-static inline void bitmask_init(unsigned int *bitmask, int num_bits)
-{
- memset(bitmask, 0, BITS_TO_WORDS(num_bits)*sizeof(unsigned int));
-}
-
-static inline int bitmask_ffz(unsigned int *bitmask, int num_bits)
-{
- int bit, result;
- size_t i;
-
- for (i = 0; i < BITS_TO_WORDS(num_bits); i++) {
- bit = ffs(~bitmask[i]);
- if (bit) {
- // ffs is 1-indexed, return 0-indexed result
- bit--;
- result = BITS_PER_WORD * i + bit;
- if (result >= num_bits)
- return -1;
- return result;
- }
- }
- return -1;
-}
-
-static inline int bitmask_weight(unsigned int *bitmask, int num_bits)
-{
- size_t i;
- int weight = 0;
-
- for (i = 0; i < BITS_TO_WORDS(num_bits); i++)
- weight += __builtin_popcount(bitmask[i]);
- return weight;
-}
-
-static inline void bitmask_set(unsigned int *bitmask, int bit)
-{
- bitmask[BIT_WORD(bit)] |= BIT_MASK(bit);
-}
-
-static inline void bitmask_clear(unsigned int *bitmask, int bit)
-{
- bitmask[BIT_WORD(bit)] &= ~BIT_MASK(bit);
-}
-
-static inline bool bitmask_test(unsigned int *bitmask, int bit)
-{
- return bitmask[BIT_WORD(bit)] & BIT_MASK(bit);
-}
-
-static inline int popcount(unsigned int x)
-{
+static inline int popcount(unsigned int x) {
return __builtin_popcount(x);
}
-static inline int popcountl(unsigned long x)
-{
+static inline int popcountl(unsigned long x) {
return __builtin_popcountl(x);
}
-static inline int popcountll(unsigned long long x)
-{
+static inline int popcountll(unsigned long long x) {
return __builtin_popcountll(x);
}
diff --git a/libcutils/include/cutils/multiuser.h b/libcutils/include/cutils/multiuser.h
index 5bd9c7b..9a2305c 100644
--- a/libcutils/include/cutils/multiuser.h
+++ b/libcutils/include/cutils/multiuser.h
@@ -33,6 +33,7 @@
extern gid_t multiuser_get_cache_gid(userid_t user_id, appid_t app_id);
extern gid_t multiuser_get_ext_gid(userid_t user_id, appid_t app_id);
+extern gid_t multiuser_get_ext_cache_gid(userid_t user_id, appid_t app_id);
extern gid_t multiuser_get_shared_gid(userid_t user_id, appid_t app_id);
/* TODO: switch callers over to multiuser_get_shared_gid() */
diff --git a/libcutils/include/cutils/native_handle.h b/libcutils/include/cutils/native_handle.h
index 7d6a988..abe6dd6 100644
--- a/libcutils/include/cutils/native_handle.h
+++ b/libcutils/include/cutils/native_handle.h
@@ -43,6 +43,8 @@
#endif
} native_handle_t;
+typedef const native_handle_t* buffer_handle_t;
+
/*
* native_handle_close
*
diff --git a/libcutils/include/cutils/sched_policy.h b/libcutils/include/cutils/sched_policy.h
index 15391d9..9683f91 100644
--- a/libcutils/include/cutils/sched_policy.h
+++ b/libcutils/include/cutils/sched_policy.h
@@ -47,6 +47,7 @@
SP_AUDIO_APP = 3,
SP_AUDIO_SYS = 4,
SP_TOP_APP = 5,
+ SP_RT_APP = 6,
SP_CNT,
SP_MAX = SP_CNT - 1,
SP_SYSTEM_DEFAULT = SP_FOREGROUND,
diff --git a/libcutils/include/cutils/sockets.h b/libcutils/include/cutils/sockets.h
index d724dd6..b24468b 100644
--- a/libcutils/include/cutils/sockets.h
+++ b/libcutils/include/cutils/sockets.h
@@ -88,8 +88,6 @@
cutils_socket_t socket_network_client(const char* host, int port, int type);
int socket_network_client_timeout(const char* host, int port, int type,
int timeout, int* getaddrinfo_error);
-int socket_loopback_server(int port, int type);
-int socket_loopback_server6(int port, int type);
int socket_local_server(const char* name, int namespaceId, int type);
int socket_local_server_bind(int s, const char* name, int namespaceId);
int socket_local_client_connect(int fd, const char *name, int namespaceId,
diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index 0037f15..02141d6 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -53,7 +53,7 @@
#include <sys/cdefs.h>
#include <sys/types.h>
-#if defined(__ANDROID__)
+#if defined(__BIONIC__)
#include <linux/capability.h>
#else
#include "android_filesystem_capability.h"
@@ -130,6 +130,7 @@
#define AID_MEDIA_OBB 1059 /* GID for OBB files on internal media storage */
#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 */
/* Changes to this file must be made in AOSP, *not* in internal branches. */
#define AID_SHELL 2000 /* adb and debug shell user */
@@ -152,6 +153,7 @@
#define AID_NET_BW_ACCT 3007 /* change bandwidth statistics accounting */
#define AID_READPROC 3009 /* Allow /proc read access */
#define AID_WAKELOCK 3010 /* Allow system wakelock read/write access */
+#define AID_UHID 3011 /* Allow read/write to /dev/uhid node */
/* The range 5000-5999 is also reserved for OEM, and must never be used here. */
#define AID_OEM_RESERVED_2_START 5000
@@ -171,6 +173,9 @@
#define AID_EXT_GID_START 30000 /* start of gids for apps to mark external data */
#define AID_EXT_GID_END 39999 /* end of gids for apps to mark external data */
+#define AID_EXT_CACHE_GID_START 40000 /* start of gids for apps to mark external cached data */
+#define AID_EXT_CACHE_GID_END 49999 /* end of gids for apps to mark external cached data */
+
#define AID_SHARED_GID_START 50000 /* start of gids for apps in each user to share */
#define AID_SHARED_GID_END 59999 /* end of gids for apps in each user to share */
diff --git a/libcutils/include_vndk/cutils/log.h b/libcutils/include_vndk/cutils/log.h
index ae74024..21dc11e 100644
--- a/libcutils/include_vndk/cutils/log.h
+++ b/libcutils/include_vndk/cutils/log.h
@@ -16,6 +16,32 @@
*/
#ifndef _LIBS_CUTIL_LOG_H
#define _LIBS_CUTIL_LOG_H
-#warning "Deprecated: don't include cutils/log.h, use either android/log.h or log/log.h"
+
+/* We do not know if developer wanted log/log.h or subset android/log.h */
#include <log/log.h>
+
+#if defined(__GNUC__)
+#if defined( __clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic warning "-W#warnings"
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpedantic"
+#elif (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR > 9))
+#pragma GCC diagnostic push
+#pragma GCC diagnostic warning "-W#warnings"
+#else
+#pragma GCC diagnostic push
+#pragma GCC diagnostic warning "-Wcpp"
+#endif
+#endif
+
+#warning "Deprecated: don't include cutils/log.h, use either android/log.h or log/log.h"
+
+#if defined(__GNUC__)
+#if defined( __clang__)
+#pragma clang diagnostic pop
+#endif
+#pragma GCC diagnostic pop
+#endif
+
#endif /* _LIBS_CUTIL_LOG_H */
diff --git a/libcutils/multiuser.c b/libcutils/multiuser.c
index 08d4d6c..61403f4 100644
--- a/libcutils/multiuser.c
+++ b/libcutils/multiuser.c
@@ -45,6 +45,14 @@
}
}
+gid_t multiuser_get_ext_cache_gid(userid_t user_id, appid_t app_id) {
+ if (app_id >= AID_APP_START && app_id <= AID_APP_END) {
+ return multiuser_get_uid(user_id, (app_id - AID_APP_START) + AID_EXT_CACHE_GID_START);
+ } else {
+ return -1;
+ }
+}
+
gid_t multiuser_get_shared_gid(userid_t user_id, appid_t app_id) {
if (app_id >= AID_APP_START && app_id <= AID_APP_END) {
return multiuser_get_uid(user_id, (app_id - AID_APP_START) + AID_SHARED_GID_START);
diff --git a/libcutils/native_handle.c b/libcutils/native_handle.c
index 9f4840a..95bbc41 100644
--- a/libcutils/native_handle.c
+++ b/libcutils/native_handle.c
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define LOG_TAG "NativeHandle"
+#include <cutils/native_handle.h>
#include <errno.h>
#include <stdint.h>
@@ -22,15 +22,12 @@
#include <string.h>
#include <unistd.h>
-#include <android/log.h>
-#include <cutils/native_handle.h>
-
static const int kMaxNativeFds = 1024;
static const int kMaxNativeInts = 1024;
-native_handle_t* native_handle_init(char* storage, int numFds, int numInts)
-{
+native_handle_t* native_handle_init(char* storage, int numFds, int numInts) {
if ((uintptr_t) storage % alignof(native_handle_t)) {
+ errno = EINVAL;
return NULL;
}
@@ -38,13 +35,12 @@
handle->version = sizeof(native_handle_t);
handle->numFds = numFds;
handle->numInts = numInts;
-
return handle;
}
-native_handle_t* native_handle_create(int numFds, int numInts)
-{
+native_handle_t* native_handle_create(int numFds, int numInts) {
if (numFds < 0 || numInts < 0 || numFds > kMaxNativeFds || numInts > kMaxNativeInts) {
+ errno = EINVAL;
return NULL;
}
@@ -58,14 +54,13 @@
return h;
}
-native_handle_t* native_handle_clone(const native_handle_t* handle)
-{
+native_handle_t* native_handle_clone(const native_handle_t* handle) {
native_handle_t* clone = native_handle_create(handle->numFds, handle->numInts);
- int i;
+ if (clone == NULL) return NULL;
- for (i = 0; i < handle->numFds; i++) {
+ for (int i = 0; i < handle->numFds; i++) {
clone->data[i] = dup(handle->data[i]);
- if (clone->data[i] < 0) {
+ if (clone->data[i] == -1) {
clone->numFds = i;
native_handle_close(clone);
native_handle_delete(clone);
@@ -74,30 +69,27 @@
}
memcpy(&clone->data[handle->numFds], &handle->data[handle->numFds],
- sizeof(int) * handle->numInts);
+ sizeof(int) * handle->numInts);
return clone;
}
-int native_handle_delete(native_handle_t* h)
-{
+int native_handle_delete(native_handle_t* h) {
if (h) {
- if (h->version != sizeof(native_handle_t))
- return -EINVAL;
+ if (h->version != sizeof(native_handle_t)) return -EINVAL;
free(h);
}
return 0;
}
-int native_handle_close(const native_handle_t* h)
-{
- if (h->version != sizeof(native_handle_t))
- return -EINVAL;
+int native_handle_close(const native_handle_t* h) {
+ if (h->version != sizeof(native_handle_t)) return -EINVAL;
+ int saved_errno = errno;
const int numFds = h->numFds;
- int i;
- for (i=0 ; i<numFds ; i++) {
+ for (int i = 0; i < numFds; ++i) {
close(h->data[i]);
}
+ errno = saved_errno;
return 0;
}
diff --git a/libcutils/sched_policy.cpp b/libcutils/sched_policy.cpp
index 73ca518..7170b48 100644
--- a/libcutils/sched_policy.cpp
+++ b/libcutils/sched_policy.cpp
@@ -28,6 +28,13 @@
#define UNUSED __attribute__((__unused__))
+#ifndef SLOGE
+#define SLOGE ALOGE
+#endif
+#ifndef SLOGW
+#define SLOGW ALOGW
+#endif
+
/* Re-map SP_DEFAULT to the system default policy, and leave other values unchanged.
* Call this any place a SchedPolicy is used as an input parameter.
* Returns the possibly re-mapped policy.
@@ -63,6 +70,7 @@
static int bg_schedboost_fd = -1;
static int fg_schedboost_fd = -1;
static int ta_schedboost_fd = -1;
+static int rt_schedboost_fd = -1;
/* Add tid to the scheduling group defined by the policy */
static int add_tid_to_cgroup(int tid, int fd)
@@ -166,6 +174,8 @@
fg_schedboost_fd = open(filename, O_WRONLY | O_CLOEXEC);
filename = "/dev/stune/background/tasks";
bg_schedboost_fd = open(filename, O_WRONLY | O_CLOEXEC);
+ filename = "/dev/stune/rt/tasks";
+ rt_schedboost_fd = open(filename, O_WRONLY | O_CLOEXEC);
}
}
}
@@ -256,24 +266,26 @@
char grpBuf[32];
- if (cpusets_enabled()) {
+ grpBuf[0] = '\0';
+ if (schedboost_enabled()) {
+ if (getCGroupSubsys(tid, "schedtune", grpBuf, sizeof(grpBuf)) < 0) return -1;
+ }
+ if ((grpBuf[0] == '\0') && cpusets_enabled()) {
if (getCGroupSubsys(tid, "cpuset", grpBuf, sizeof(grpBuf)) < 0) return -1;
- if (grpBuf[0] == '\0') {
- *policy = SP_FOREGROUND;
- } else if (!strcmp(grpBuf, "foreground")) {
- *policy = SP_FOREGROUND;
- } else if (!strcmp(grpBuf, "background")) {
- *policy = SP_BACKGROUND;
- } else if (!strcmp(grpBuf, "top-app")) {
- *policy = SP_TOP_APP;
- } else {
- errno = ERANGE;
- return -1;
- }
- } else {
- // In b/34193533, we removed bg_non_interactive cgroup, so now
- // all threads are in FOREGROUND cgroup
+ }
+ if (grpBuf[0] == '\0') {
*policy = SP_FOREGROUND;
+ } else if (!strcmp(grpBuf, "foreground")) {
+ *policy = SP_FOREGROUND;
+ } else if (!strcmp(grpBuf, "system-background")) {
+ *policy = SP_SYSTEM;
+ } else if (!strcmp(grpBuf, "background")) {
+ *policy = SP_BACKGROUND;
+ } else if (!strcmp(grpBuf, "top-app")) {
+ *policy = SP_TOP_APP;
+ } else {
+ errno = ERANGE;
+ return -1;
}
return 0;
}
@@ -334,16 +346,25 @@
static void set_timerslack_ns(int tid, unsigned long long slack) {
// v4.6+ kernels support the /proc/<tid>/timerslack_ns interface.
// TODO: once we've backported this, log if the open(2) fails.
- char buf[64];
- snprintf(buf, sizeof(buf), "/proc/%d/timerslack_ns", tid);
- int fd = open(buf, O_WRONLY | O_CLOEXEC);
- if (fd != -1) {
- int len = snprintf(buf, sizeof(buf), "%llu", slack);
- if (write(fd, buf, len) != len) {
- SLOGE("set_timerslack_ns write failed: %s\n", strerror(errno));
+ if (__sys_supports_timerslack) {
+ char buf[64];
+ snprintf(buf, sizeof(buf), "/proc/%d/timerslack_ns", tid);
+ int fd = open(buf, O_WRONLY | O_CLOEXEC);
+ if (fd != -1) {
+ int len = snprintf(buf, sizeof(buf), "%llu", slack);
+ if (write(fd, buf, len) != len) {
+ SLOGE("set_timerslack_ns write failed: %s\n", strerror(errno));
+ }
+ close(fd);
+ return;
}
- close(fd);
- return;
+ }
+
+ // TODO: Remove when /proc/<tid>/timerslack_ns interface is backported.
+ if ((tid == 0) || (tid == gettid())) {
+ if (prctl(PR_SET_TIMERSLACK, slack) == -1) {
+ SLOGE("set_timerslack_ns prctl failed: %s\n", strerror(errno));
+ }
}
}
@@ -390,6 +411,9 @@
case SP_SYSTEM:
SLOGD("/// tid %d (%s)", tid, thread_name);
break;
+ case SP_RT_APP:
+ SLOGD("RT tid %d (%s)", tid, thread_name);
+ break;
default:
SLOGD("??? tid %d (%s)", tid, thread_name);
break;
@@ -410,6 +434,9 @@
case SP_TOP_APP:
boost_fd = ta_schedboost_fd;
break;
+ case SP_RT_APP:
+ boost_fd = rt_schedboost_fd;
+ break;
default:
boost_fd = -1;
break;
@@ -422,10 +449,7 @@
}
- if (__sys_supports_timerslack) {
- set_timerslack_ns(tid, policy == SP_BACKGROUND ?
- TIMER_SLACK_BG : TIMER_SLACK_FG);
- }
+ set_timerslack_ns(tid, policy == SP_BACKGROUND ? TIMER_SLACK_BG : TIMER_SLACK_FG);
return 0;
}
@@ -457,6 +481,7 @@
[SP_AUDIO_APP] = "aa",
[SP_AUDIO_SYS] = "as",
[SP_TOP_APP] = "ta",
+ [SP_RT_APP] = "rt",
};
if ((policy < SP_CNT) && (strings[policy] != NULL))
return strings[policy];
diff --git a/libcutils/socket_loopback_server_unix.c b/libcutils/socket_loopback_server_unix.c
deleted file mode 100644
index 7b92fd6..0000000
--- a/libcutils/socket_loopback_server_unix.c
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
-** Copyright 2006, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-#include <errno.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#define LISTEN_BACKLOG 4
-
-#if !defined(_WIN32)
-#include <sys/socket.h>
-#include <sys/select.h>
-#include <sys/types.h>
-#include <netinet/in.h>
-#endif
-
-#include <cutils/sockets.h>
-
-static int _socket_loopback_server(int family, int type, struct sockaddr * addr, size_t size)
-{
- int s, n;
-
- s = socket(family, type, 0);
- if(s < 0)
- return -1;
-
- n = 1;
- setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *) &n, sizeof(n));
-
- if(bind(s, addr, size) < 0) {
- close(s);
- return -1;
- }
-
- if (type == SOCK_STREAM) {
- int ret;
-
- ret = listen(s, LISTEN_BACKLOG);
-
- if (ret < 0) {
- close(s);
- return -1;
- }
- }
-
- return s;
-}
-
-/* open listen() port on loopback IPv6 interface */
-int socket_loopback_server6(int port, int type)
-{
- struct sockaddr_in6 addr;
-
- memset(&addr, 0, sizeof(addr));
- addr.sin6_family = AF_INET6;
- addr.sin6_port = htons(port);
- addr.sin6_addr = in6addr_loopback;
-
- return _socket_loopback_server(AF_INET6, type, (struct sockaddr *) &addr, sizeof(addr));
-}
-
-/* open listen() port on loopback interface */
-int socket_loopback_server(int port, int type)
-{
- struct sockaddr_in addr;
-
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_port = htons(port);
- addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
-
- return _socket_loopback_server(AF_INET, type, (struct sockaddr *) &addr, sizeof(addr));
-}
diff --git a/libcutils/tests/Android.bp b/libcutils/tests/Android.bp
index a0b1d7b..7884190 100644
--- a/libcutils/tests/Android.bp
+++ b/libcutils/tests/Android.bp
@@ -27,7 +27,8 @@
"test_str_parms.cpp",
"android_get_control_socket_test.cpp",
"android_get_control_file_test.cpp",
- "multiuser_test.cpp"
+ "multiuser_test.cpp",
+ "fs_config.cpp",
],
},
diff --git a/libcutils/tests/AshmemTest.cpp b/libcutils/tests/AshmemTest.cpp
index 51c679f..a87e23e 100644
--- a/libcutils/tests/AshmemTest.cpp
+++ b/libcutils/tests/AshmemTest.cpp
@@ -14,10 +14,11 @@
* limitations under the License.
*/
-#include <sys/mman.h>
+#include <android-base/unique_fd.h>
#include <cutils/ashmem.h>
#include <gtest/gtest.h>
-#include <android-base/unique_fd.h>
+#include <linux/fs.h>
+#include <sys/mman.h>
using android::base::unique_fd;
@@ -29,8 +30,8 @@
ASSERT_EQ(0, ashmem_set_prot_region(fd, prot));
}
-void TestMmap(const unique_fd &fd, size_t size, int prot, void **region) {
- *region = mmap(nullptr, size, prot, MAP_SHARED, fd, 0);
+void TestMmap(const unique_fd& fd, size_t size, int prot, void** region, off_t off = 0) {
+ *region = mmap(nullptr, size, prot, MAP_SHARED, fd, off);
ASSERT_NE(MAP_FAILED, *region);
}
@@ -38,6 +39,10 @@
EXPECT_EQ(MAP_FAILED, mmap(nullptr, size, prot, MAP_SHARED, fd, 0));
}
+void TestProtIs(const unique_fd& fd, int prot) {
+ EXPECT_EQ(prot, ioctl(fd, ASHMEM_GET_PROT_MASK));
+}
+
void FillData(uint8_t* data, size_t dataLen) {
for (size_t i = 0; i < dataLen; i++) {
data[i] = i & 0xFF;
@@ -101,6 +106,63 @@
EXPECT_EQ(0, munmap(region2, size));
}
+TEST(AshmemTest, FileOperationsTest) {
+ unique_fd fd;
+ void* region;
+
+ // Allocate a 4-page buffer, but leave page-sized holes on either side
+ constexpr size_t size = PAGE_SIZE * 4;
+ constexpr size_t dataSize = PAGE_SIZE * 2;
+ constexpr size_t holeSize = PAGE_SIZE;
+ ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_READ | PROT_WRITE));
+ ASSERT_NO_FATAL_FAILURE(TestMmap(fd, dataSize, PROT_READ | PROT_WRITE, ®ion, holeSize));
+
+ uint8_t data[dataSize];
+ FillData(data, dataSize);
+ memcpy(region, data, dataSize);
+
+ constexpr off_t dataStart = holeSize;
+ constexpr off_t dataEnd = dataStart + dataSize;
+
+ // The sequence of seeks below looks something like this:
+ //
+ // [ ][data][data][ ]
+ // --^ lseek(99, SEEK_SET)
+ // ------^ lseek(dataStart, SEEK_CUR)
+ // ------^ lseek(0, SEEK_DATA)
+ // ------------^ lseek(dataStart, SEEK_HOLE)
+ // ^-- lseek(-99, SEEK_END)
+ // ^------ lseek(-dataStart, SEEK_CUR)
+ const struct {
+ // lseek() parameters
+ off_t offset;
+ int whence;
+ // Expected lseek() return value
+ off_t ret;
+ } seeks[] = {
+ {99, SEEK_SET, 99}, {dataStart, SEEK_CUR, dataStart + 99},
+ {0, SEEK_DATA, dataStart}, {dataStart, SEEK_HOLE, dataEnd},
+ {-99, SEEK_END, size - 99}, {-dataStart, SEEK_CUR, dataEnd - 99},
+ };
+ for (const auto& cfg : seeks) {
+ errno = 0;
+ auto off = lseek(fd, cfg.offset, cfg.whence);
+ ASSERT_EQ(cfg.ret, off) << "lseek(" << cfg.offset << ", " << cfg.whence << ") failed"
+ << (errno ? ": " : "") << (errno ? strerror(errno) : "");
+
+ if (off >= dataStart && off < dataEnd) {
+ off_t dataOff = off - dataStart;
+ ssize_t readSize = dataSize - dataOff;
+ uint8_t buf[readSize];
+
+ ASSERT_EQ(readSize, TEMP_FAILURE_RETRY(read(fd, buf, readSize)));
+ EXPECT_EQ(0, memcmp(buf, data + dataOff, readSize));
+ }
+ }
+
+ EXPECT_EQ(0, munmap(region, dataSize));
+}
+
TEST(AshmemTest, ProtTest) {
unique_fd fd;
constexpr size_t size = PAGE_SIZE;
@@ -108,13 +170,25 @@
ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_READ));
TestProtDenied(fd, size, PROT_WRITE);
+ TestProtIs(fd, PROT_READ);
ASSERT_NO_FATAL_FAILURE(TestMmap(fd, size, PROT_READ, ®ion));
EXPECT_EQ(0, munmap(region, size));
ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_WRITE));
TestProtDenied(fd, size, PROT_READ);
+ TestProtIs(fd, PROT_WRITE);
ASSERT_NO_FATAL_FAILURE(TestMmap(fd, size, PROT_WRITE, ®ion));
EXPECT_EQ(0, munmap(region, size));
+
+ ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_READ | PROT_WRITE));
+ TestProtIs(fd, PROT_READ | PROT_WRITE);
+ ASSERT_EQ(0, ashmem_set_prot_region(fd, PROT_READ));
+ errno = 0;
+ ASSERT_EQ(-1, ashmem_set_prot_region(fd, PROT_READ | PROT_WRITE))
+ << "kernel shouldn't allow adding protection bits";
+ EXPECT_EQ(EINVAL, errno);
+ TestProtIs(fd, PROT_READ);
+ TestProtDenied(fd, size, PROT_WRITE);
}
TEST(AshmemTest, ForkProtTest) {
diff --git a/libcutils/tests/fs_config.cpp b/libcutils/tests/fs_config.cpp
new file mode 100644
index 0000000..a62cd51
--- /dev/null
+++ b/libcutils/tests/fs_config.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 <inttypes.h>
+
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+#include <private/android_filesystem_config.h>
+#include <private/fs_config.h>
+
+extern const fs_path_config* __for_testing_only__android_dirs;
+extern const fs_path_config* __for_testing_only__android_files;
+
+// Maximum entries in system/core/libcutils/fs_config.cpp:android_* before we
+// hit a nullptr termination, before we declare the list is just too big or
+// could be missing the nullptr.
+static constexpr size_t max_idx = 4096;
+
+static bool check_unique(std::vector<const char*>& paths, const std::string& config_name,
+ const std::string& prefix) {
+ bool retval = false;
+
+ std::string alternate = "system/" + prefix;
+
+ for (size_t idx = 0; idx < paths.size(); ++idx) {
+ size_t second;
+ std::string path(paths[idx]);
+ // check if there are multiple identical paths
+ for (second = idx + 1; second < paths.size(); ++second) {
+ if (path == paths[second]) {
+ GTEST_LOG_(ERROR) << "duplicate paths in " << config_name << ": " << paths[idx];
+ retval = true;
+ break;
+ }
+ }
+
+ // check if path is <partition>/
+ if (android::base::StartsWith(path, prefix.c_str())) {
+ // rebuild path to be system/<partition>/... to check for alias
+ path = alternate + path.substr(prefix.size());
+ for (second = 0; second < paths.size(); ++second) {
+ if (path == paths[second]) {
+ GTEST_LOG_(ERROR) << "duplicate alias paths in " << config_name << ": "
+ << paths[idx] << " and " << paths[second]
+ << " (remove latter)";
+ retval = true;
+ break;
+ }
+ }
+ continue;
+ }
+
+ // check if path is system/<partition>/
+ if (android::base::StartsWith(path, alternate.c_str())) {
+ // rebuild path to be <partition>/... to check for alias
+ path = prefix + path.substr(alternate.size());
+ for (second = 0; second < paths.size(); ++second) {
+ if (path == paths[second]) break;
+ }
+ if (second >= paths.size()) {
+ GTEST_LOG_(ERROR) << "replace path in " << config_name << ": " << paths[idx]
+ << " with " << path;
+ retval = true;
+ }
+ }
+ }
+ return retval;
+}
+
+static bool check_unique(const fs_path_config* paths, const char* type_name,
+ const std::string& prefix) {
+ std::string config("system/core/libcutils/fs_config.cpp:android_");
+ config += type_name;
+ config += "[]";
+
+ bool retval = false;
+ std::vector<const char*> paths_tmp;
+ for (size_t idx = 0; paths[idx].prefix; ++idx) {
+ if (idx > max_idx) {
+ GTEST_LOG_(WARNING) << config << ": has no end (missing null prefix)";
+ retval = true;
+ break;
+ }
+ paths_tmp.push_back(paths[idx].prefix);
+ }
+
+ return check_unique(paths_tmp, config, prefix) || retval;
+}
+
+#define endof(pointer, field) (offsetof(typeof(*(pointer)), field) + sizeof((pointer)->field))
+
+static bool check_unique(const std::string& config, const std::string& prefix) {
+ int retval = false;
+
+ std::string data;
+ if (!android::base::ReadFileToString(config, &data)) return retval;
+
+ const fs_path_config_from_file* pc =
+ reinterpret_cast<const fs_path_config_from_file*>(data.c_str());
+ size_t len = data.size();
+
+ std::vector<const char*> paths_tmp;
+ size_t entry_number = 0;
+ while (len > 0) {
+ uint16_t host_len = (len >= endof(pc, len)) ? pc->len : INT16_MAX;
+ if (host_len > len) {
+ GTEST_LOG_(WARNING) << config << ": truncated at entry " << entry_number << " ("
+ << host_len << " > " << len << ")";
+ const std::string unknown("?");
+ GTEST_LOG_(WARNING)
+ << config << ": entry[" << entry_number << "]={ "
+ << "len=" << ((len >= endof(pc, len))
+ ? android::base::StringPrintf("%" PRIu16, pc->len)
+ : unknown)
+ << ", mode=" << ((len >= endof(pc, mode))
+ ? android::base::StringPrintf("0%" PRIo16, pc->mode)
+ : unknown)
+ << ", uid=" << ((len >= endof(pc, uid))
+ ? android::base::StringPrintf("%" PRIu16, pc->uid)
+ : unknown)
+ << ", gid=" << ((len >= endof(pc, gid))
+ ? android::base::StringPrintf("%" PRIu16, pc->gid)
+ : unknown)
+ << ", capabilities="
+ << ((len >= endof(pc, capabilities))
+ ? android::base::StringPrintf("0x%" PRIx64, pc->capabilities)
+ : unknown)
+ << ", prefix="
+ << ((len >= offsetof(fs_path_config_from_file, prefix))
+ ? android::base::StringPrintf(
+ "\"%.*s...", (int)(len - offsetof(fs_path_config_from_file, prefix)),
+ pc->prefix)
+ : unknown)
+ << " }";
+ retval = true;
+ break;
+ }
+ paths_tmp.push_back(pc->prefix);
+
+ pc = reinterpret_cast<const fs_path_config_from_file*>(reinterpret_cast<const char*>(pc) +
+ host_len);
+ len -= host_len;
+ ++entry_number;
+ }
+
+ return check_unique(paths_tmp, config, prefix) || retval;
+}
+
+void check_two(const fs_path_config* paths, const char* type_name, const char* prefix) {
+ ASSERT_FALSE(paths == nullptr);
+ ASSERT_FALSE(type_name == nullptr);
+ ASSERT_FALSE(prefix == nullptr);
+ bool check_internal = check_unique(paths, type_name, prefix);
+ EXPECT_FALSE(check_internal);
+ bool check_overrides =
+ check_unique(std::string("/") + prefix + "etc/fs_config_" + type_name, prefix);
+ EXPECT_FALSE(check_overrides);
+}
+
+TEST(fs_config, vendor_dirs_alias) {
+ check_two(__for_testing_only__android_dirs, "dirs", "vendor/");
+}
+
+TEST(fs_config, vendor_files_alias) {
+ check_two(__for_testing_only__android_files, "files", "vendor/");
+}
+
+TEST(fs_config, oem_dirs_alias) {
+ check_two(__for_testing_only__android_dirs, "dirs", "oem/");
+}
+
+TEST(fs_config, oem_files_alias) {
+ check_two(__for_testing_only__android_files, "files", "oem/");
+}
+
+TEST(fs_config, odm_dirs_alias) {
+ check_two(__for_testing_only__android_dirs, "dirs", "odm/");
+}
+
+TEST(fs_config, odm_files_alias) {
+ check_two(__for_testing_only__android_files, "files", "odm/");
+}
diff --git a/libcutils/tests/multiuser_test.cpp b/libcutils/tests/multiuser_test.cpp
index ae5c416..2f9d854 100644
--- a/libcutils/tests/multiuser_test.cpp
+++ b/libcutils/tests/multiuser_test.cpp
@@ -68,6 +68,14 @@
EXPECT_EQ(1030000U, multiuser_get_ext_gid(10, 10000));
}
+TEST(MultiuserTest, TestExtCache) {
+ EXPECT_EQ(ERR_GID, multiuser_get_ext_cache_gid(0, 0));
+ EXPECT_EQ(ERR_GID, multiuser_get_ext_cache_gid(0, 1000));
+ EXPECT_EQ(40000U, multiuser_get_ext_cache_gid(0, 10000));
+ EXPECT_EQ(ERR_GID, multiuser_get_ext_cache_gid(0, 50000));
+ EXPECT_EQ(1040000U, multiuser_get_ext_cache_gid(10, 10000));
+}
+
TEST(MultiuserTest, TestShared) {
EXPECT_EQ(ERR_GID, multiuser_get_shared_gid(0, 0));
EXPECT_EQ(ERR_GID, multiuser_get_shared_gid(0, 1000));
diff --git a/libcutils/trace-dev.c b/libcutils/trace-dev.c
index 113f423..d45e5a9 100644
--- a/libcutils/trace-dev.c
+++ b/libcutils/trace-dev.c
@@ -29,7 +29,8 @@
#include <cutils/compiler.h>
#include <cutils/properties.h>
#include <cutils/trace.h>
-#include <private/android_logger.h>
+#include <log/log.h>
+#include <log/log_properties.h>
/**
* Maximum size of a message that can be logged to the trace buffer.
diff --git a/libdiskconfig/Android.bp b/libdiskconfig/Android.bp
index 041fd63..088981a 100644
--- a/libdiskconfig/Android.bp
+++ b/libdiskconfig/Android.bp
@@ -1,5 +1,6 @@
cc_library {
name: "libdiskconfig",
+ vendor_available: true,
srcs: [
"diskconfig.c",
"diskutils.c",
diff --git a/libgrallocusage/Android.bp b/libgrallocusage/Android.bp
index 54bfee5..cf03868 100644
--- a/libgrallocusage/Android.bp
+++ b/libgrallocusage/Android.bp
@@ -14,6 +14,7 @@
cc_library_static {
name: "libgrallocusage",
+ vendor_available: true,
cppflags: [
"-Weverything",
"-Werror",
@@ -26,4 +27,5 @@
srcs: ["GrallocUsageConversion.cpp"],
export_include_dirs: ["include"],
shared_libs: ["android.hardware.graphics.allocator@2.0"],
+ header_libs: ["libhardware_headers"],
}
diff --git a/libgrallocusage/GrallocUsageConversion.cpp b/libgrallocusage/GrallocUsageConversion.cpp
index 8164beb..05c8ec4 100644
--- a/libgrallocusage/GrallocUsageConversion.cpp
+++ b/libgrallocusage/GrallocUsageConversion.cpp
@@ -16,49 +16,51 @@
#include <grallocusage/GrallocUsageConversion.h>
-#include <android/hardware/graphics/allocator/2.0/types.h>
#include <hardware/gralloc.h>
-
-using android::hardware::graphics::allocator::V2_0::ProducerUsage;
-using android::hardware::graphics::allocator::V2_0::ConsumerUsage;
+#include <hardware/gralloc1.h>
void android_convertGralloc0To1Usage(int32_t usage, uint64_t* producerUsage,
uint64_t* consumerUsage) {
- constexpr uint64_t PRODUCER_MASK = ProducerUsage::CPU_READ |
- /* ProducerUsage::CPU_READ_OFTEN | */
- ProducerUsage::CPU_WRITE |
- /* ProducerUsage::CPU_WRITE_OFTEN | */
- ProducerUsage::GPU_RENDER_TARGET | ProducerUsage::PROTECTED |
- ProducerUsage::CAMERA | ProducerUsage::VIDEO_DECODER |
- ProducerUsage::SENSOR_DIRECT_DATA;
- constexpr uint64_t CONSUMER_MASK = ConsumerUsage::CPU_READ |
- /* ConsumerUsage::CPU_READ_OFTEN | */
- ConsumerUsage::GPU_TEXTURE | ConsumerUsage::HWCOMPOSER |
- ConsumerUsage::CLIENT_TARGET | ConsumerUsage::CURSOR |
- ConsumerUsage::VIDEO_ENCODER | ConsumerUsage::CAMERA |
- ConsumerUsage::RENDERSCRIPT | ConsumerUsage::GPU_DATA_BUFFER;
+ 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 |= ProducerUsage::CPU_READ_OFTEN;
- *consumerUsage |= ConsumerUsage::CPU_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 |= ProducerUsage::CPU_WRITE_OFTEN;
+ *producerUsage |= GRALLOC1_PRODUCER_USAGE_CPU_WRITE_OFTEN;
}
}
int32_t android_convertGralloc1To0Usage(uint64_t producerUsage, uint64_t consumerUsage) {
- static_assert(uint64_t(ConsumerUsage::CPU_READ_OFTEN) == uint64_t(ProducerUsage::CPU_READ_OFTEN),
+ 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 & (ConsumerUsage::CPU_READ_OFTEN)) != 0) {
- merged &= ~uint64_t(ConsumerUsage::CPU_READ_OFTEN);
+ 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 & (ProducerUsage::CPU_WRITE_OFTEN)) != 0) {
- merged &= ~uint64_t(ProducerUsage::CPU_WRITE_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/libion/Android.bp b/libion/Android.bp
index da98111..6f267e4 100644
--- a/libion/Android.bp
+++ b/libion/Android.bp
@@ -1,6 +1,7 @@
cc_library {
name: "libion",
+ vendor_available: true,
srcs: ["ion.c"],
shared_libs: ["liblog"],
local_include_dirs: [
diff --git a/libkeyutils/.clang-format b/libkeyutils/.clang-format
new file mode 120000
index 0000000..fd0645f
--- /dev/null
+++ b/libkeyutils/.clang-format
@@ -0,0 +1 @@
+../.clang-format-2
\ No newline at end of file
diff --git a/libkeyutils/Android.bp b/libkeyutils/Android.bp
new file mode 100644
index 0000000..0285259
--- /dev/null
+++ b/libkeyutils/Android.bp
@@ -0,0 +1,16 @@
+cc_library {
+ name: "libkeyutils",
+ cflags: ["-Werror"],
+ defaults: ["linux_bionic_supported"],
+ export_include_dirs: ["include/"],
+ local_include_dirs: ["include/"],
+ srcs: ["keyutils.cpp"],
+ stl: "none",
+}
+
+cc_test {
+ name: "libkeyutils-tests",
+ cflags: ["-Werror"],
+ shared_libs: ["libkeyutils"],
+ srcs: ["keyutils_test.cpp"],
+}
diff --git a/libkeyutils/include/keyutils.h b/libkeyutils/include/keyutils.h
new file mode 100644
index 0000000..585767d
--- /dev/null
+++ b/libkeyutils/include/keyutils.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _KEYUTILS_H_
+#define _KEYUTILS_H_
+
+#include <linux/keyctl.h>
+#include <stdint.h>
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+typedef int32_t key_serial_t;
+
+key_serial_t add_key(const char* type, const char* description, const void* payload,
+ size_t payload_length, key_serial_t ring_id);
+
+key_serial_t keyctl_get_keyring_ID(key_serial_t id, int create);
+
+long keyctl_revoke(key_serial_t id); /* TODO: remove this */
+
+long keyctl_search(key_serial_t ring_id, const char* type, const char* description,
+ key_serial_t dest_ring_id);
+
+long keyctl_setperm(key_serial_t id, int permissions);
+
+long keyctl_unlink(key_serial_t key, key_serial_t keyring);
+
+__END_DECLS
+
+#endif
diff --git a/libkeyutils/keyutils.cpp b/libkeyutils/keyutils.cpp
new file mode 100644
index 0000000..58a2a17
--- /dev/null
+++ b/libkeyutils/keyutils.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <keyutils.h>
+
+#include <stdarg.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+// Deliberately not exposed. Callers should use the typed APIs instead.
+static long keyctl(int cmd, ...) {
+ va_list va;
+ va_start(va, cmd);
+ unsigned long arg2 = va_arg(va, unsigned long);
+ unsigned long arg3 = va_arg(va, unsigned long);
+ unsigned long arg4 = va_arg(va, unsigned long);
+ unsigned long arg5 = va_arg(va, unsigned long);
+ va_end(va);
+ return syscall(__NR_keyctl, cmd, arg2, arg3, arg4, arg5);
+}
+
+key_serial_t add_key(const char* type, const char* description, const void* payload,
+ size_t payload_length, key_serial_t ring_id) {
+ return syscall(__NR_add_key, type, description, payload, payload_length, ring_id);
+}
+
+key_serial_t keyctl_get_keyring_ID(key_serial_t id, int create) {
+ return keyctl(KEYCTL_GET_KEYRING_ID, id, create);
+}
+
+long keyctl_revoke(key_serial_t id) {
+ return keyctl(KEYCTL_REVOKE, id);
+}
+
+long keyctl_search(key_serial_t ring_id, const char* type, const char* description,
+ key_serial_t dest_ring_id) {
+ return keyctl(KEYCTL_SEARCH, ring_id, type, description, dest_ring_id);
+}
+
+long keyctl_setperm(key_serial_t id, int permissions) {
+ return keyctl(KEYCTL_SETPERM, id, permissions);
+}
+
+long keyctl_unlink(key_serial_t key, key_serial_t keyring) {
+ return keyctl(KEYCTL_UNLINK, key, keyring);
+}
diff --git a/libkeyutils/keyutils_test.cpp b/libkeyutils/keyutils_test.cpp
new file mode 100644
index 0000000..d41c91b
--- /dev/null
+++ b/libkeyutils/keyutils_test.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <keyutils.h>
+
+#include <dlfcn.h>
+
+#include <gtest/gtest.h>
+
+TEST(keyutils, smoke) {
+ // Check that the exported type is sane.
+ ASSERT_EQ(4U, sizeof(key_serial_t));
+
+ // Check that all the functions actually exist.
+ ASSERT_TRUE(dlsym(nullptr, "add_key") != nullptr);
+ ASSERT_TRUE(dlsym(nullptr, "keyctl_get_keyring_ID") != nullptr);
+ ASSERT_TRUE(dlsym(nullptr, "keyctl_revoke") != nullptr);
+ ASSERT_TRUE(dlsym(nullptr, "keyctl_search") != nullptr);
+ ASSERT_TRUE(dlsym(nullptr, "keyctl_setperm") != nullptr);
+ ASSERT_TRUE(dlsym(nullptr, "keyctl_unlink") != nullptr);
+}
diff --git a/liblog/Android.bp b/liblog/Android.bp
index bb8c3af..e74aa82 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -107,14 +107,16 @@
license: "NOTICE",
}
-cc_library_headers {
- name: "liblog_vndk_headers",
- export_include_dirs: ["include_vndk"],
-}
-
ndk_library {
- name: "liblog.ndk",
+ name: "liblog",
symbol_file: "liblog.map.txt",
first_version: "9",
unversioned_until: "current",
}
+
+llndk_library {
+ name: "liblog",
+ symbol_file: "liblog.map.txt",
+ unversioned: true,
+ export_include_dirs: ["include_vndk"],
+}
diff --git a/liblog/event.logtags b/liblog/event.logtags
index 301e885..0a3b650 100644
--- a/liblog/event.logtags
+++ b/liblog/event.logtags
@@ -29,6 +29,7 @@
# 4: Number of allocations
# 5: Id
# 6: Percent
+# s: Number of seconds (monotonic time)
# Default value for data of type int/long is 2 (bytes).
#
# TODO: generate ".java" and ".h" files with integer constants from this file.
diff --git a/liblog/event_tag_map.cpp b/liblog/event_tag_map.cpp
index bdad2c2..83064fd 100644
--- a/liblog/event_tag_map.cpp
+++ b/liblog/event_tag_map.cpp
@@ -31,6 +31,7 @@
#include <unordered_map>
#include <log/event_tag_map.h>
+#include <log/log_properties.h>
#include <private/android_logger.h>
#include <utils/FastStrcmp.h>
#include <utils/RWLock.h>
@@ -229,9 +230,16 @@
return it->second;
}
+// The position after the end of a valid section of the tag string,
+// caller makes sure delimited appropriately.
+static const char* endOfTag(const char* cp) {
+ while (*cp && (isalnum(*cp) || strchr("_.-@,", *cp))) ++cp;
+ return cp;
+}
+
// Scan one tag line.
//
-// "*pData" should be pointing to the first digit in the tag number. On
+// "pData" should be pointing to the first digit in the tag number. On
// successful return, it will be pointing to the last character in the
// tag line (i.e. the character before the start of the next line).
//
@@ -241,10 +249,11 @@
// data and it will outlive the call.
//
// Returns 0 on success, nonzero on failure.
-static int scanTagLine(EventTagMap* map, char** pData, int lineNum) {
- char* cp;
- unsigned long val = strtoul(*pData, &cp, 10);
- if (cp == *pData) {
+static int scanTagLine(EventTagMap* map, const char*& pData, int lineNum) {
+ char* ep;
+ unsigned long val = strtoul(pData, &ep, 10);
+ const char* cp = ep;
+ if (cp == pData) {
if (lineNum) {
fprintf(stderr, OUT_TAG ": malformed tag number on line %d\n", lineNum);
}
@@ -273,14 +282,13 @@
}
const char* tag = cp;
- // Determine whether "c" is a valid tag char.
- while (isalnum(*++cp) || (*cp == '_')) {
- }
+ cp = endOfTag(cp);
size_t tagLen = cp - tag;
if (!isspace(*cp)) {
if (lineNum) {
- fprintf(stderr, OUT_TAG ": invalid tag chars on line %d\n", lineNum);
+ fprintf(stderr, OUT_TAG ": invalid tag char %c on line %d\n", *cp,
+ lineNum);
}
errno = EINVAL;
return -1;
@@ -289,9 +297,9 @@
while (isspace(*cp) && (*cp != '\n')) ++cp;
const char* fmt = NULL;
size_t fmtLen = 0;
- if (*cp != '#') {
+ if (*cp && (*cp != '#')) {
fmt = cp;
- while ((*cp != '\n') && (*cp != '#')) ++cp;
+ while (*cp && (*cp != '\n') && (*cp != '#')) ++cp;
while ((cp > fmt) && isspace(*(cp - 1))) --cp;
fmtLen = cp - fmt;
}
@@ -301,7 +309,7 @@
// recorded for the same uid, but recording that
// unused detail in our database is too burdensome.
bool verbose = true;
- while ((*cp != '#') && (*cp != '\n')) ++cp;
+ while (*cp && (*cp != '#') && (*cp != '\n')) ++cp;
if (*cp == '#') {
do {
++cp;
@@ -309,11 +317,11 @@
verbose = !!fastcmp<strncmp>(cp, "uid=", strlen("uid="));
}
- while (*cp != '\n') ++cp;
+ while (*cp && (*cp != '\n')) ++cp;
#ifdef DEBUG
- fprintf(stderr, "%d: %p: %.*s\n", lineNum, tag, (int)(cp - *pData), *pData);
+ fprintf(stderr, "%d: %p: %.*s\n", lineNum, tag, (int)(cp - pData), pData);
#endif
- *pData = cp;
+ pData = cp;
if (lineNum) {
if (map->emplaceUnique(tagIndex,
@@ -341,9 +349,9 @@
// Parse the tags out of the file.
static int parseMapLines(EventTagMap* map, size_t which) {
- char* cp = static_cast<char*>(map->mapAddr[which]);
+ const char* cp = static_cast<char*>(map->mapAddr[which]);
size_t len = map->mapLen[which];
- char* endp = cp + len;
+ const char* endp = cp + len;
// insist on EOL at EOF; simplifies parsing and null-termination
if (!len || (*(endp - 1) != '\n')) {
@@ -370,7 +378,7 @@
lineStart = false;
} else if (isdigit(*cp)) {
// looks like a tag; scan it out
- if (scanTagLine(map, &cp, lineNum) != 0) {
+ if (scanTagLine(map, cp, lineNum) != 0) {
if (!which || (errno != EMLINK)) {
return -1;
}
@@ -445,7 +453,7 @@
mmap(NULL, end[which], which ? PROT_READ : PROT_READ | PROT_WRITE,
which ? MAP_SHARED : MAP_PRIVATE, fd[which], 0);
save_errno = errno;
- close(fd[which]);
+ close(fd[which]); /* fd DONE */
fd[which] = -1;
if ((newTagMap->mapAddr[which] != MAP_FAILED) &&
(newTagMap->mapAddr[which] != NULL)) {
@@ -465,6 +473,7 @@
delete newTagMap;
return NULL;
}
+ /* See 'fd DONE' comments above and below, no need to clean up here */
}
return newTagMap;
@@ -473,7 +482,7 @@
save_errno = EINVAL;
delete newTagMap;
fail_close:
- for (which = 0; which < NUM_MAPS; ++which) close(fd[which]);
+ for (which = 0; which < NUM_MAPS; ++which) close(fd[which]); /* fd DONE */
fail_errno:
errno = save_errno;
return NULL;
@@ -494,14 +503,13 @@
int ret = asprintf(&buf, command_template, tag);
if (ret > 0) {
// Add some buffer margin for an estimate of the full return content.
- char* cp;
size_t size =
ret - strlen(command_template) +
strlen("65535\n4294967295\t?\t\t\t?\t# uid=32767\n\n\f?success?");
if (size > (size_t)ret) {
- cp = static_cast<char*>(realloc(buf, size));
- if (cp) {
- buf = cp;
+ char* np = static_cast<char*>(realloc(buf, size));
+ if (np) {
+ buf = np;
} else {
size = ret;
}
@@ -511,10 +519,12 @@
// Ask event log tag service for an existing entry
if (__send_log_msg(buf, size) >= 0) {
buf[size - 1] = '\0';
- unsigned long val = strtoul(buf, &cp, 10); // return size
+ char* ep;
+ unsigned long val = strtoul(buf, &ep, 10); // return size
+ const char* cp = ep;
if ((buf != cp) && (val > 0) && (*cp == '\n')) { // truncation OK
++cp;
- if (!scanTagLine(map, &cp, 0)) {
+ if (!scanTagLine(map, cp, 0)) {
free(buf);
return map->find(tag);
}
@@ -572,8 +582,9 @@
LIBLOG_ABI_PUBLIC int android_lookupEventTagNum(EventTagMap* map,
const char* tagname,
const char* format, int prio) {
- size_t len = strlen(tagname);
- if (!len) {
+ const char* ep = endOfTag(tagname);
+ size_t len = ep - tagname;
+ if (!len || *ep) {
errno = EINVAL;
return -1;
}
diff --git a/liblog/fake_log_device.c b/liblog/fake_log_device.c
index ae7a334..1483c24 100644
--- a/liblog/fake_log_device.c
+++ b/liblog/fake_log_device.c
@@ -15,9 +15,7 @@
*/
/*
* Intercepts log messages intended for the Android log device.
- * When running in the context of the simulator, the messages are
- * passed on to the underlying (fake) log device. When not in the
- * simulator, messages are printed to stderr.
+ * Messages are printed to stderr.
*/
#include <ctype.h>
#include <errno.h>
@@ -561,7 +559,8 @@
* tag (N bytes -- null-terminated ASCII string)
* message (N bytes -- null-terminated ASCII string)
*/
-static ssize_t logWritev(int fd, const struct iovec* vector, int count) {
+LIBLOG_HIDDEN ssize_t fakeLogWritev(int fd, const struct iovec* vector,
+ int count) {
LogState* state;
/* Make sure that no-one frees the LogState while we're using it.
@@ -626,8 +625,18 @@
/*
* Free up our state and close the fake descriptor.
+ *
+ * The logger API has no means or need to 'stop' or 'close' using the logs,
+ * and as such, there is no way for that 'stop' or 'close' to translate into
+ * a close operation to the fake log handler. fakeLogClose is provided for
+ * completeness only.
+ *
+ * We have no intention of adding a log close operation as it would complicate
+ * every user of the logging API with no gain since the only valid place to
+ * call is in the exit handler. Logging can continue in the exit handler to
+ * help debug HOST tools ...
*/
-static int logClose(int fd) {
+LIBLOG_HIDDEN int fakeLogClose(int fd) {
deleteFakeFd(fd);
return 0;
}
@@ -635,7 +644,7 @@
/*
* Open a log output device and return a fake fd.
*/
-static int logOpen(const char* pathName, int flags __unused) {
+LIBLOG_HIDDEN int fakeLogOpen(const char* pathName) {
LogState* logState;
int fd = -1;
@@ -654,66 +663,6 @@
return fd;
}
-/*
- * Runtime redirection. If this binary is running in the simulator,
- * just pass log messages to the emulated device. If it's running
- * outside of the simulator, write the log messages to stderr.
- */
-
-static int (*redirectOpen)(const char* pathName, int flags) = NULL;
-static int (*redirectClose)(int fd) = NULL;
-static ssize_t (*redirectWritev)(int fd, const struct iovec* vector,
- int count) = NULL;
-
-static void setRedirects() {
- const char* ws;
-
- /* Wrapsim sets this environment variable on children that it's
- * created using its LD_PRELOAD wrapper.
- */
- ws = getenv("ANDROID_WRAPSIM");
- if (ws != NULL && strcmp(ws, "1") == 0) {
- /* We're running inside wrapsim, so we can just write to the device. */
- redirectOpen = (int (*)(const char* pathName, int flags))open;
- redirectClose = close;
- redirectWritev = writev;
- } else {
- /* There's no device to delegate to; handle the logging ourselves. */
- redirectOpen = logOpen;
- redirectClose = logClose;
- redirectWritev = logWritev;
- }
-}
-
-LIBLOG_HIDDEN int fakeLogOpen(const char* pathName, int flags) {
- if (redirectOpen == NULL) {
- setRedirects();
- }
- return redirectOpen(pathName, flags);
-}
-
-/*
- * The logger API has no means or need to 'stop' or 'close' using the logs,
- * and as such, there is no way for that 'stop' or 'close' to translate into
- * a close operation to the fake log handler. fakeLogClose is provided for
- * completeness only.
- *
- * We have no intention of adding a log close operation as it would complicate
- * every user of the logging API with no gain since the only valid place to
- * call is in the exit handler. Logging can continue in the exit handler to
- * help debug HOST tools ...
- */
-LIBLOG_HIDDEN int fakeLogClose(int fd) {
- /* Assume that open() was called first. */
- return redirectClose(fd);
-}
-
-LIBLOG_HIDDEN ssize_t fakeLogWritev(int fd, const struct iovec* vector,
- int count) {
- /* Assume that open() was called first. */
- return redirectWritev(fd, vector, count);
-}
-
LIBLOG_HIDDEN ssize_t __send_log_msg(char* buf __unused,
size_t buf_size __unused) {
return -ENODEV;
diff --git a/liblog/fake_log_device.h b/liblog/fake_log_device.h
index 462d026..7b0e745 100644
--- a/liblog/fake_log_device.h
+++ b/liblog/fake_log_device.h
@@ -23,7 +23,7 @@
struct iovec;
-LIBLOG_HIDDEN int fakeLogOpen(const char* pathName, int flags);
+LIBLOG_HIDDEN int fakeLogOpen(const char* pathName);
LIBLOG_HIDDEN int fakeLogClose(int fd);
LIBLOG_HIDDEN ssize_t fakeLogWritev(int fd, const struct iovec* vector,
int count);
diff --git a/liblog/fake_writer.c b/liblog/fake_writer.c
index 969661a..403dc72 100644
--- a/liblog/fake_writer.c
+++ b/liblog/fake_writer.c
@@ -55,9 +55,9 @@
continue;
}
snprintf(buf, sizeof(buf), "/dev/log_%s", android_log_id_to_name(i));
- logFds[i] = fakeLogOpen(buf, O_WRONLY);
+ logFds[i] = fakeLogOpen(buf);
if (logFds[i] < 0) {
- fprintf(stderr, "fakeLogOpen(%s, O_WRONLY) failed\n", buf);
+ fprintf(stderr, "fakeLogOpen(%s) failed\n", buf);
}
}
return 0;
diff --git a/liblog/include/log/log.h b/liblog/include/log/log.h
index 6758c84..3a215e9 100644
--- a/liblog/include/log/log.h
+++ b/liblog/include/log/log.h
@@ -32,6 +32,7 @@
#include <log/log_main.h>
#include <log/log_radio.h>
#include <log/log_read.h>
+#include <log/log_safetynet.h>
#include <log/log_system.h>
#include <log/log_time.h>
#include <log/uio.h> /* helper to define iovec for portability */
@@ -167,31 +168,6 @@
/* --------------------------------------------------------------------- */
-#ifndef _ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE
-#ifndef __ANDROID_API__
-#define __ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE 1
-#elif __ANDROID_API__ > 22 /* > Lollipop */
-#define __ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE 1
-#else
-#define __ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE 0
-#endif
-#endif
-
-#if __ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE
-
-#define android_errorWriteLog(tag, subTag) \
- __android_log_error_write(tag, subTag, -1, NULL, 0)
-
-#define android_errorWriteWithInfoLog(tag, subTag, uid, data, dataLen) \
- __android_log_error_write(tag, subTag, uid, data, dataLen)
-
-int __android_log_error_write(int tag, const char* subTag, int32_t uid,
- const char* data, uint32_t dataLen);
-
-#endif /* __ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE */
-
-/* --------------------------------------------------------------------- */
-
#ifndef __ANDROID_USE_LIBLOG_CLOSE_INTERFACE
#ifndef __ANDROID_API__
#define __ANDROID_USE_LIBLOG_CLOSE_INTERFACE 1
diff --git a/liblog/include/log/log_event_list.h b/liblog/include/log/log_event_list.h
index 55953fc..057be5d 100644
--- a/liblog/include/log/log_event_list.h
+++ b/liblog/include/log/log_event_list.h
@@ -17,6 +17,7 @@
#ifndef _LIBS_LOG_EVENT_LIST_H
#define _LIBS_LOG_EVENT_LIST_H
+#include <errno.h>
#include <stdint.h>
#if (defined(__cplusplus) && defined(_USING_LIBCXX))
@@ -148,6 +149,7 @@
return ctx;
}
+ /* return errors or transmit status */
int status() const {
return ret;
}
@@ -209,14 +211,16 @@
}
int write(log_id_t id = LOG_ID_EVENTS) {
+ /* facilitate -EBUSY retry */
+ if ((ret == -EBUSY) || (ret > 0)) ret = 0;
int retval = android_log_write_list(ctx, id);
- if (retval < 0) ret = retval;
+ /* existing errors trump transmission errors */
+ if (!ret) ret = retval;
return ret;
}
int operator<<(log_id_t id) {
- int retval = android_log_write_list(ctx, id);
- if (retval < 0) ret = retval;
+ write(id);
android_log_destroy(&ctx);
return ret;
}
diff --git a/liblog/include/log/log_properties.h b/liblog/include/log/log_properties.h
new file mode 100644
index 0000000..7d398a6
--- /dev/null
+++ b/liblog/include/log/log_properties.h
@@ -0,0 +1,35 @@
+/*
+**
+** Copyright 2017, The Android Open Source Project
+**
+** This file is dual licensed. It may be redistributed and/or modified
+** under the terms of the Apache 2.0 License OR version 2 of the GNU
+** General Public License.
+*/
+
+#ifndef _LIBS_LOG_PROPERTIES_H
+#define _LIBS_LOG_PROPERTIES_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef __ANDROID_USE_LIBLOG_IS_DEBUGGABLE_INTERFACE
+#ifndef __ANDROID_API__
+#define __ANDROID_USE_LIBLOG_IS_DEBUGGABLE_INTERFACE 1
+#elif __ANDROID_API__ > 24 /* > Nougat */
+#define __ANDROID_USE_LIBLOG_IS_DEBUGGABLE_INTERFACE 1
+#else
+#define __ANDROID_USE_LIBLOG_IS_DEBUGGABLE_INTERFACE 0
+#endif
+#endif
+
+#if __ANDROID_USE_LIBLOG_IS_DEBUGGABLE_INTERFACE
+int __android_log_is_debuggable();
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBS_LOG_PROPERTIES_H */
diff --git a/liblog/include/log/log_safetynet.h b/liblog/include/log/log_safetynet.h
new file mode 100644
index 0000000..b8ca475
--- /dev/null
+++ b/liblog/include/log/log_safetynet.h
@@ -0,0 +1,44 @@
+/*
+**
+** Copyright 2017, The Android Open Source Project
+**
+** This file is dual licensed. It may be redistributed and/or modified
+** under the terms of the Apache 2.0 License OR version 2 of the GNU
+** General Public License.
+*/
+
+#ifndef _LIBS_LOG_SAFETYNET_H
+#define _LIBS_LOG_SAFETYNET_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE
+#ifndef __ANDROID_API__
+#define __ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE 1
+#elif __ANDROID_API__ > 22 /* > Lollipop */
+#define __ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE 1
+#else
+#define __ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE 0
+#endif
+#endif
+
+#if __ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE
+
+#define android_errorWriteLog(tag, subTag) \
+ __android_log_error_write(tag, subTag, -1, NULL, 0)
+
+#define android_errorWriteWithInfoLog(tag, subTag, uid, data, dataLen) \
+ __android_log_error_write(tag, subTag, uid, data, dataLen)
+
+int __android_log_error_write(int tag, const char* subTag, int32_t uid,
+ const char* data, uint32_t dataLen);
+
+#endif /* __ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBS_LOG_SAFETYNET_H */
diff --git a/liblog/include/log/log_time.h b/liblog/include/log/log_time.h
index 5f70f7d..3764faf 100644
--- a/liblog/include/log/log_time.h
+++ b/liblog/include/log/log_time.h
@@ -22,6 +22,8 @@
/* struct log_time is a wire-format variant of struct timespec */
#define NS_PER_SEC 1000000000ULL
+#define US_PER_SEC 1000000ULL
+#define MS_PER_SEC 1000ULL
#ifndef __struct_log_time_defined
#define __struct_log_time_defined
@@ -41,13 +43,12 @@
static const uint32_t tv_sec_max = 0xFFFFFFFFUL;
static const uint32_t tv_nsec_max = 999999999UL;
- log_time(const timespec& T) {
- tv_sec = static_cast<uint32_t>(T.tv_sec);
- tv_nsec = static_cast<uint32_t>(T.tv_nsec);
+ log_time(const timespec& T)
+ : tv_sec(static_cast<uint32_t>(T.tv_sec)),
+ tv_nsec(static_cast<uint32_t>(T.tv_nsec)) {
}
- log_time(uint32_t sec, uint32_t nsec) {
- tv_sec = sec;
- tv_nsec = nsec;
+ explicit log_time(uint32_t sec, uint32_t nsec = 0)
+ : tv_sec(sec), tv_nsec(nsec) {
}
#ifdef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_
#define __struct_log_time_private_defined
@@ -56,14 +57,14 @@
log_time() {
}
#ifdef __linux__
- log_time(clockid_t id) {
+ explicit log_time(clockid_t id) {
timespec T;
clock_gettime(id, &T);
tv_sec = static_cast<uint32_t>(T.tv_sec);
tv_nsec = static_cast<uint32_t>(T.tv_nsec);
}
#endif
- log_time(const char* T) {
+ explicit log_time(const char* T) {
const uint8_t* c = reinterpret_cast<const uint8_t*>(T);
tv_sec = c[0] | (static_cast<uint32_t>(c[1]) << 8) |
(static_cast<uint32_t>(c[2]) << 16) |
@@ -149,6 +150,14 @@
uint64_t nsec() const {
return static_cast<uint64_t>(tv_sec) * NS_PER_SEC + tv_nsec;
}
+ uint64_t usec() const {
+ return static_cast<uint64_t>(tv_sec) * US_PER_SEC +
+ tv_nsec / (NS_PER_SEC / US_PER_SEC);
+ }
+ uint64_t msec() const {
+ return static_cast<uint64_t>(tv_sec) * MS_PER_SEC +
+ tv_nsec / (NS_PER_SEC / MS_PER_SEC);
+ }
#ifdef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_
static const char default_format[];
diff --git a/liblog/include/private/android_logger.h b/liblog/include/private/android_logger.h
index e3ccfcf..965de37 100644
--- a/liblog/include/private/android_logger.h
+++ b/liblog/include/private/android_logger.h
@@ -135,8 +135,6 @@
int __android_log_security_bswrite(int32_t tag, const char* payload);
int __android_log_security(); /* Device Owner is present */
-int __android_log_is_debuggable();
-
#define BOOL_DEFAULT_FLAG_TRUE_FALSE 0x1
#define BOOL_DEFAULT_FALSE 0x0 /* false if property not present */
#define BOOL_DEFAULT_TRUE 0x1 /* true if property not present */
diff --git a/liblog/include_vndk/log/log.h b/liblog/include_vndk/log/log.h
index f93b377..01623df 100644
--- a/liblog/include_vndk/log/log.h
+++ b/liblog/include_vndk/log/log.h
@@ -8,6 +8,7 @@
#include <log/log_main.h>
#include <log/log_radio.h>
#include <log/log_read.h>
+#include <log/log_safetynet.h>
#include <log/log_time.h>
/*
diff --git a/liblog/include_vndk/log/log_properties.h b/liblog/include_vndk/log/log_properties.h
new file mode 120000
index 0000000..bbec426
--- /dev/null
+++ b/liblog/include_vndk/log/log_properties.h
@@ -0,0 +1 @@
+../../include/log/log_properties.h
\ No newline at end of file
diff --git a/liblog/include_vndk/log/log_safetynet.h b/liblog/include_vndk/log/log_safetynet.h
new file mode 120000
index 0000000..a4614e7
--- /dev/null
+++ b/liblog/include_vndk/log/log_safetynet.h
@@ -0,0 +1 @@
+../../include/log/log_safetynet.h
\ No newline at end of file
diff --git a/liblog/liblog.map.txt b/liblog/liblog.map.txt
index 599dc90..3c4c1f1 100644
--- a/liblog/liblog.map.txt
+++ b/liblog/liblog.map.txt
@@ -1,6 +1,10 @@
LIBLOG {
global:
+ android_name_to_log_id; # vndk
+ android_log_id_to_name; # vndk
__android_log_assert;
+ __android_log_buf_print; # vndk
+ __android_log_buf_write; # vndk
__android_log_print;
__android_log_vprint;
__android_log_write;
@@ -8,12 +12,33 @@
*;
};
+LIBLOG_L {
+ global:
+ android_logger_clear; # vndk
+ android_logger_get_id; # vndk
+ android_logger_get_log_readable_size; # vndk
+ android_logger_get_log_version; # vndk
+ android_logger_get_log_size; # vndk
+ android_logger_list_alloc; # vndk
+ android_logger_list_alloc_time; # vndk
+ android_logger_list_free; # vndk
+ android_logger_list_open; # vndk
+ android_logger_list_read; # vndk
+ android_logger_open; # vndk
+ android_logger_set_log_size; # vndk
+};
+
LIBLOG_M {
global:
+ android_logger_get_prune_list; # vndk
+ android_logger_set_prune_list; # vndk
+ android_logger_get_statistics; # vndk
+ __android_log_error_write; # vndk
__android_log_is_loggable;
};
LIBLOG_O {
global:
__android_log_is_loggable_len;
+ __android_log_is_debuggable; # vndk
};
diff --git a/liblog/logprint.c b/liblog/logprint.c
index 2ade7b0..b62f8b4 100644
--- a/liblog/logprint.c
+++ b/liblog/logprint.c
@@ -326,6 +326,7 @@
else if (!strcmp(formatString, "uid")) format = FORMAT_MODIFIER_UID;
else if (!strcmp(formatString, "descriptive")) format = FORMAT_MODIFIER_DESCRIPT;
/* clang-format on */
+
#ifndef __MINGW32__
else {
extern char* tzname[2];
@@ -637,7 +638,8 @@
TYPE_MILLISECONDS = '3',
TYPE_ALLOCATIONS = '4',
TYPE_ID = '5',
- TYPE_PERCENT = '6'
+ TYPE_PERCENT = '6',
+ TYPE_MONOTONIC = 's'
};
static int android_log_printBinaryEvent(const unsigned char** pEventData,
@@ -651,7 +653,7 @@
size_t outBufLen = *pOutBufLen;
size_t outBufLenSave = outBufLen;
unsigned char type;
- size_t outCount;
+ size_t outCount = 0;
int result = 0;
const char* cp;
size_t len;
@@ -690,6 +692,7 @@
* 4: Number of allocations
* 5: Id
* 6: Percent
+ * s: Number of seconds (monotonic time)
* Default value for data of type int/long is 2 (bytes).
*/
if (!cp || !findChar(&cp, &len, '(')) {
@@ -921,6 +924,42 @@
outCount = snprintf(outBuf, outBufLen, "ms");
}
break;
+ case TYPE_MONOTONIC: {
+ static const uint64_t minute = 60;
+ static const uint64_t hour = 60 * minute;
+ static const uint64_t day = 24 * hour;
+
+ /* Repaint as unsigned seconds, minutes, hours ... */
+ outBuf -= outCount;
+ outBufLen += outCount;
+ uint64_t val = lval;
+ if (val >= day) {
+ outCount = snprintf(outBuf, outBufLen, "%" PRIu64 "d ", val / day);
+ if (outCount >= outBufLen) break;
+ outBuf += outCount;
+ outBufLen -= outCount;
+ val = (val % day) + day;
+ }
+ if (val >= minute) {
+ if (val >= hour) {
+ outCount = snprintf(outBuf, outBufLen, "%" PRIu64 ":",
+ (val / hour) % (day / hour));
+ if (outCount >= outBufLen) break;
+ outBuf += outCount;
+ outBufLen -= outCount;
+ }
+ outCount =
+ snprintf(outBuf, outBufLen,
+ (val >= hour) ? "%02" PRIu64 ":" : "%" PRIu64 ":",
+ (val / minute) % (hour / minute));
+ if (outCount >= outBufLen) break;
+ outBuf += outCount;
+ outBufLen -= outCount;
+ }
+ outCount = snprintf(outBuf, outBufLen,
+ (val >= minute) ? "%02" PRIu64 : "%" PRIu64 "s",
+ val % minute);
+ } break;
case TYPE_ALLOCATIONS:
outCount = 0;
/* outCount = snprintf(outBuf, outBufLen, " allocations"); */
diff --git a/liblog/pmsg_writer.c b/liblog/pmsg_writer.c
index e71c176..dc42856 100644
--- a/liblog/pmsg_writer.c
+++ b/liblog/pmsg_writer.c
@@ -26,6 +26,7 @@
#include <sys/types.h>
#include <time.h>
+#include <log/log_properties.h>
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
diff --git a/liblog/properties.c b/liblog/properties.c
index 0b0ef52..11be827 100644
--- a/liblog/properties.c
+++ b/liblog/properties.c
@@ -95,7 +95,7 @@
/* calculate the size of our key temporary buffer */
const size_t taglen = tag ? len : 0;
/* sizeof(log_namespace) = strlen(log_namespace) + 1 */
- char key[sizeof(log_namespace) + taglen]; /* may be > PROP_NAME_MAX */
+ char key[sizeof(log_namespace) + taglen];
char* kp;
size_t i;
char c = 0;
@@ -108,7 +108,8 @@
* Where the missing tag matches all tags and becomes the
* system global default. We do not support ro.log.tag* .
*/
- static char last_tag[PROP_NAME_MAX];
+ static char* last_tag;
+ static size_t last_tag_len;
static uint32_t global_serial;
/* some compilers erroneously see uninitialized use. !not_locked */
uint32_t current_global_serial = 0;
@@ -147,25 +148,29 @@
if (taglen) {
int local_change_detected = change_detected;
if (!not_locked) {
- if (!last_tag[0] || (last_tag[0] != tag[0]) ||
- strncmp(
- last_tag + 1, tag + 1,
- (len < sizeof(last_tag)) ? (len - 1) : (sizeof(last_tag) - 1)) ||
- ((len < sizeof(last_tag)) && last_tag[len])) {
+ if (!last_tag || !last_tag[0] || (last_tag[0] != tag[0]) ||
+ strncmp(last_tag + 1, tag + 1, last_tag_len - 1)) {
/* invalidate log.tag.<tag> cache */
for (i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) {
tag_cache[i].cache.pinfo = NULL;
tag_cache[i].c = '\0';
}
- last_tag[0] = '\0';
+ if (last_tag) last_tag[0] = '\0';
local_change_detected = 1;
}
- if (!last_tag[0]) {
- if (len < sizeof(last_tag)) {
+ if (!last_tag || !last_tag[0]) {
+ if (!last_tag) {
+ last_tag = calloc(1, len + 1);
+ last_tag_len = 0;
+ if (last_tag) last_tag_len = len + 1;
+ } else if (len >= last_tag_len) {
+ last_tag = realloc(last_tag, len + 1);
+ last_tag_len = 0;
+ if (last_tag) last_tag_len = len + 1;
+ }
+ if (last_tag) {
strncpy(last_tag, tag, len);
last_tag[len] = '\0';
- } else {
- strncpy(last_tag, tag, sizeof(last_tag));
}
}
}
@@ -272,7 +277,7 @@
return logLevel >= 0 && prio >= logLevel;
}
-LIBLOG_ABI_PRIVATE int __android_log_is_debuggable() {
+LIBLOG_ABI_PUBLIC int __android_log_is_debuggable() {
static uint32_t serial;
static struct cache_char tag_cache;
static const char key[] = "ro.debuggable";
@@ -435,7 +440,7 @@
int flag) {
struct cache_property property = { { NULL, -1 }, { 0 } };
if (flag & BOOL_DEFAULT_FLAG_PERSIST) {
- char newkey[PROP_NAME_MAX];
+ char newkey[strlen("persist.") + strlen(key) + 1];
snprintf(newkey, sizeof(newkey), "ro.%s", key);
refresh_cache_property(&property, newkey);
property.cache.pinfo = NULL;
@@ -454,6 +459,9 @@
if (check_flag(property.property, "false")) {
return false;
}
+ if (property.property[0]) {
+ flag &= ~(BOOL_DEFAULT_FLAG_ENG | BOOL_DEFAULT_FLAG_SVELTE);
+ }
if (check_flag(property.property, "eng")) {
flag |= BOOL_DEFAULT_FLAG_ENG;
}
@@ -600,8 +608,8 @@
evaluate_property_get_size
/* clang-format on */
};
- char key_persist[PROP_NAME_MAX];
- char key_ro[PROP_NAME_MAX];
+ char key_persist[strlen(global_tunable) + strlen(".security") + 1];
+ char key_ro[strlen(global_default) + strlen(".security") + 1];
struct cache2_property_size local = {
/* clang-format off */
PTHREAD_MUTEX_INITIALIZER, 0,
diff --git a/liblog/tests/Android.mk b/liblog/tests/Android.mk
index 0e6432c..ab96429 100644
--- a/liblog/tests/Android.mk
+++ b/liblog/tests/Android.mk
@@ -55,6 +55,7 @@
-fno-builtin \
test_src_files := \
+ libc_test.cpp \
liblog_test_default.cpp \
liblog_test_local.cpp \
liblog_test_stderr.cpp \
@@ -65,14 +66,6 @@
log_system_test.cpp \
log_time_test.cpp
-# to prevent breaking the build if bionic not relatively visible to us
-ifneq ($(wildcard $(LOCAL_PATH)/../../../../bionic/libc/bionic/libc_logging.cpp),)
-
-test_src_files += \
- libc_test.cpp
-
-endif
-
# Build tests for the device (with .so). Run with:
# adb shell /data/nativetest/liblog-unit-tests/liblog-unit-tests
include $(CLEAR_VARS)
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 70b8a28..46ec5ef 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -36,6 +36,7 @@
#endif
#include <gtest/gtest.h>
#include <log/log_event_list.h>
+#include <log/log_properties.h>
#include <log/log_transport.h>
#include <log/logprint.h>
#include <private/android_filesystem_config.h>
@@ -1719,6 +1720,7 @@
// Kills logd and toss all collected data, equivalent to logcat -b all -c,
// except we also return errors to the logging callers.
#ifdef USING_LOGGER_DEFAULT
+#ifdef __ANDROID__
#ifdef TEST_PREFIX
// helper to liblog.enoent to count end-to-end matching logging messages.
static int count_matching_ts(log_time ts) {
@@ -1786,6 +1788,12 @@
stderr,
"WARNING: test conditions request being run as root and not AID=%d\n",
getuid());
+ if (!__android_log_is_debuggable()) {
+ fprintf(
+ stderr,
+ "WARNING: can not run test on a \"user\" build, bypassing test\n");
+ return;
+ }
}
system((getuid() == AID_ROOT) ? "stop logd" : "su 0 stop logd");
@@ -1825,7 +1833,8 @@
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
}
-#endif // USING_LOCAL_LOGD
+#endif // __ANDROID__
+#endif // USING_LOGGER_DEFAULT
// Below this point we run risks of setuid(AID_SYSTEM) which may affect others.
@@ -1839,6 +1848,7 @@
// that it can be determined the property is not set.
static const char nothing_val[] = "_NOTHING_TO_SEE_HERE_";
char persist[PROP_VALUE_MAX];
+ char persist_hold[PROP_VALUE_MAX];
char readonly[PROP_VALUE_MAX];
// First part of this test requires the test itself to have the appropriate
@@ -1846,14 +1856,16 @@
// bail rather than give a failing grade.
property_get(persist_key, persist, "");
fprintf(stderr, "INFO: getprop %s -> %s\n", persist_key, persist);
+ strncpy(persist_hold, persist, PROP_VALUE_MAX);
property_get(readonly_key, readonly, nothing_val);
fprintf(stderr, "INFO: getprop %s -> %s\n", readonly_key, readonly);
if (!strcmp(readonly, nothing_val)) {
+ // Lets check if we can set the value (we should not be allowed to do so)
EXPECT_FALSE(__android_log_security());
fprintf(stderr, "WARNING: setting ro.device_owner to a domain\n");
static const char domain[] = "com.google.android.SecOps.DeviceOwner";
- property_set(readonly_key, domain);
+ EXPECT_NE(0, property_set(readonly_key, domain));
useconds_t total_time = 0;
static const useconds_t seconds = 1000000;
static const useconds_t max_time = 5 * seconds; // not going to happen
@@ -1870,9 +1882,12 @@
break;
}
}
- EXPECT_STREQ(readonly, domain);
- } else if (!strcasecmp(readonly, "false") || !readonly[0]) {
- // not enough permissions to run
+ EXPECT_STRNE(domain, readonly);
+ }
+
+ if (!strcasecmp(readonly, "false") || !readonly[0] ||
+ !strcmp(readonly, nothing_val)) {
+ // not enough permissions to run tests surrounding persist.logd.security
EXPECT_FALSE(__android_log_security());
return;
}
@@ -1883,16 +1898,51 @@
EXPECT_FALSE(__android_log_security());
}
property_set(persist_key, "TRUE");
- EXPECT_TRUE(__android_log_security());
+ property_get(persist_key, persist, "");
+ uid_t uid = getuid();
+ gid_t gid = getgid();
+ bool perm = (gid == AID_ROOT) || (uid == AID_ROOT);
+ EXPECT_STREQ(perm ? "TRUE" : persist_hold, persist);
+ if (!strcasecmp(persist, "true")) {
+ EXPECT_TRUE(__android_log_security());
+ } else {
+ EXPECT_FALSE(__android_log_security());
+ }
property_set(persist_key, "FALSE");
- EXPECT_FALSE(__android_log_security());
+ property_get(persist_key, persist, "");
+ EXPECT_STREQ(perm ? "FALSE" : persist_hold, persist);
+ if (!strcasecmp(persist, "true")) {
+ EXPECT_TRUE(__android_log_security());
+ } else {
+ EXPECT_FALSE(__android_log_security());
+ }
property_set(persist_key, "true");
- EXPECT_TRUE(__android_log_security());
+ property_get(persist_key, persist, "");
+ EXPECT_STREQ(perm ? "true" : persist_hold, persist);
+ if (!strcasecmp(persist, "true")) {
+ EXPECT_TRUE(__android_log_security());
+ } else {
+ EXPECT_FALSE(__android_log_security());
+ }
property_set(persist_key, "false");
- EXPECT_FALSE(__android_log_security());
+ property_get(persist_key, persist, "");
+ EXPECT_STREQ(perm ? "false" : persist_hold, persist);
+ if (!strcasecmp(persist, "true")) {
+ EXPECT_TRUE(__android_log_security());
+ } else {
+ EXPECT_FALSE(__android_log_security());
+ }
property_set(persist_key, "");
- EXPECT_FALSE(__android_log_security());
- property_set(persist_key, persist);
+ property_get(persist_key, persist, "");
+ EXPECT_STREQ(perm ? "" : persist_hold, persist);
+ if (!strcasecmp(persist, "true")) {
+ EXPECT_TRUE(__android_log_security());
+ } else {
+ EXPECT_FALSE(__android_log_security());
+ }
+ property_set(persist_key, persist_hold);
+ property_get(persist_key, persist, "");
+ EXPECT_STREQ(persist_hold, persist);
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
diff --git a/liblog/tests/log_read_test.cpp b/liblog/tests/log_read_test.cpp
index a63ab8e..6ed568a 100644
--- a/liblog/tests/log_read_test.cpp
+++ b/liblog/tests/log_read_test.cpp
@@ -23,6 +23,7 @@
#include <android-base/stringprintf.h>
#include <android/log.h> // minimal logging API
#include <gtest/gtest.h>
+#include <log/log_properties.h>
// Test the APIs in this standalone include file
#include <log/log_read.h>
// Do not use anything in log/log_time.h despite side effects of the above.
@@ -97,9 +98,12 @@
/* security buffer is allowed to be denied */
if (strcmp("security", name)) {
EXPECT_LT(0, get_log_size);
- /* crash buffer is allowed to be empty, that is actually healthy! */
- EXPECT_LE((strcmp("crash", name)) != 0,
- android_logger_get_log_readable_size(logger));
+ // crash buffer is allowed to be empty, that is actually healthy!
+ // kernel buffer is allowed to be empty on "user" builds
+ EXPECT_LE( // boolean 1 or 0 depending on expected content or empty
+ !!((strcmp("crash", name) != 0) &&
+ ((strcmp("kernel", name) != 0) || __android_log_is_debuggable())),
+ android_logger_get_log_readable_size(logger));
} else {
EXPECT_NE(0, get_log_size);
if (get_log_size < 0) {
diff --git a/libmemunreachable/.clang-format b/libmemunreachable/.clang-format
new file mode 120000
index 0000000..fd0645f
--- /dev/null
+++ b/libmemunreachable/.clang-format
@@ -0,0 +1 @@
+../.clang-format-2
\ No newline at end of file
diff --git a/libmemunreachable/Android.bp b/libmemunreachable/Android.bp
index 4662368..4269eaa 100644
--- a/libmemunreachable/Android.bp
+++ b/libmemunreachable/Android.bp
@@ -9,12 +9,21 @@
clang: true,
shared_libs: [
"libbase",
- "liblog",
],
+
+ target: {
+ android: {
+ static_libs: ["libasync_safe"],
+ },
+ host: {
+ shared_libs: ["liblog"],
+ }
+ },
}
cc_library_shared {
name: "libmemunreachable",
+ vendor_available: true,
defaults: ["libmemunreachable_defaults"],
srcs: [
"Allocator.cpp",
@@ -30,7 +39,6 @@
static_libs: [
"libc_malloc_debug_backtrace",
- "libc_logging",
],
// Only need this for arm since libc++ uses its own unwind code that
// doesn't mix with the other default unwind code.
diff --git a/libmemunreachable/HeapWalker.cpp b/libmemunreachable/HeapWalker.cpp
index 62366f2..c365ae5 100644
--- a/libmemunreachable/HeapWalker.cpp
+++ b/libmemunreachable/HeapWalker.cpp
@@ -42,11 +42,9 @@
} else {
Range overlap = inserted.first->first;
if (overlap != range) {
- ALOGE("range %p-%p overlaps with existing range %p-%p",
- reinterpret_cast<void*>(begin),
- reinterpret_cast<void*>(end),
- reinterpret_cast<void*>(overlap.begin),
- reinterpret_cast<void*>(overlap.end));
+ MEM_ALOGE("range %p-%p overlaps with existing range %p-%p", reinterpret_cast<void*>(begin),
+ reinterpret_cast<void*>(end), reinterpret_cast<void*>(overlap.begin),
+ reinterpret_cast<void*>(overlap.end));
}
return false;
}
@@ -154,7 +152,7 @@
void* ret = mmap(page, page_size, PROT_READ, MAP_ANONYMOUS|MAP_PRIVATE|MAP_FIXED, -1, 0);
if (ret == MAP_FAILED) {
- ALOGE("failed to map page at %p: %s", page, strerror(errno));
+ MEM_ALOGE("failed to map page at %p: %s", page, strerror(errno));
return false;
}
@@ -167,7 +165,7 @@
handler.reset();
return;
}
- ALOGW("failed to read page at %p, signal %d", si->si_addr, signal);
+ MEM_ALOGW("failed to read page at %p, signal %d", si->si_addr, signal);
if (!MapOverPage(si->si_addr)) {
handler.reset();
}
diff --git a/libmemunreachable/LeakPipe.cpp b/libmemunreachable/LeakPipe.cpp
index 080f8a7..78117e2 100644
--- a/libmemunreachable/LeakPipe.cpp
+++ b/libmemunreachable/LeakPipe.cpp
@@ -44,11 +44,11 @@
int ret = sendmsg(sock, &hdr, 0);
if (ret < 0) {
- ALOGE("failed to send fd: %s", strerror(errno));
+ MEM_ALOGE("failed to send fd: %s", strerror(errno));
return false;
}
if (ret == 0) {
- ALOGE("eof when sending fd");
+ MEM_ALOGE("eof when sending fd");
return false;
}
@@ -71,17 +71,17 @@
int ret = recvmsg(sock, &hdr, 0);
if (ret < 0) {
- ALOGE("failed to receive fd: %s", strerror(errno));
+ MEM_ALOGE("failed to receive fd: %s", strerror(errno));
return -1;
}
if (ret == 0) {
- ALOGE("eof when receiving fd");
+ MEM_ALOGE("eof when receiving fd");
return -1;
}
struct cmsghdr* cmsg = CMSG_FIRSTHDR(&hdr);
if (cmsg == NULL || cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) {
- ALOGE("missing fd while receiving fd");
+ MEM_ALOGE("missing fd while receiving fd");
return -1;
}
diff --git a/libmemunreachable/LeakPipe.h b/libmemunreachable/LeakPipe.h
index 3f4e0b7..3ea2d8f 100644
--- a/libmemunreachable/LeakPipe.h
+++ b/libmemunreachable/LeakPipe.h
@@ -36,7 +36,7 @@
LeakPipe() {
int ret = socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0, sv_);
if (ret < 0) {
- LOG_ALWAYS_FATAL("failed to create socketpair: %s", strerror(errno));
+ MEM_LOG_ALWAYS_FATAL("failed to create socketpair: %s", strerror(errno));
}
}
@@ -105,10 +105,10 @@
bool Send(const T& value) {
ssize_t ret = TEMP_FAILURE_RETRY(write(fd_, &value, sizeof(T)));
if (ret < 0) {
- ALOGE("failed to send value: %s", strerror(errno));
+ MEM_ALOGE("failed to send value: %s", strerror(errno));
return false;
} else if (static_cast<size_t>(ret) != sizeof(T)) {
- ALOGE("eof while writing value");
+ MEM_ALOGE("eof while writing value");
return false;
}
@@ -124,10 +124,10 @@
ssize_t ret = TEMP_FAILURE_RETRY(write(fd_, vector.data(), size));
if (ret < 0) {
- ALOGE("failed to send vector: %s", strerror(errno));
+ MEM_ALOGE("failed to send vector: %s", strerror(errno));
return false;
} else if (static_cast<size_t>(ret) != size) {
- ALOGE("eof while writing vector");
+ MEM_ALOGE("eof while writing vector");
return false;
}
@@ -143,10 +143,10 @@
bool Receive(T* value) {
ssize_t ret = TEMP_FAILURE_RETRY(read(fd_, reinterpret_cast<void*>(value), sizeof(T)));
if (ret < 0) {
- ALOGE("failed to receive value: %s", strerror(errno));
+ MEM_ALOGE("failed to receive value: %s", strerror(errno));
return false;
} else if (static_cast<size_t>(ret) != sizeof(T)) {
- ALOGE("eof while receiving value");
+ MEM_ALOGE("eof while receiving value");
return false;
}
@@ -166,10 +166,10 @@
while (size > 0) {
ssize_t ret = TEMP_FAILURE_RETRY(read(fd_, ptr, size));
if (ret < 0) {
- ALOGE("failed to send vector: %s", strerror(errno));
+ MEM_ALOGE("failed to send vector: %s", strerror(errno));
return false;
} else if (ret == 0) {
- ALOGE("eof while reading vector");
+ MEM_ALOGE("eof while reading vector");
return false;
}
size -= ret;
diff --git a/libmemunreachable/MemUnreachable.cpp b/libmemunreachable/MemUnreachable.cpp
index ac19a66..e7c0beb 100644
--- a/libmemunreachable/MemUnreachable.cpp
+++ b/libmemunreachable/MemUnreachable.cpp
@@ -78,51 +78,49 @@
bool MemUnreachable::CollectAllocations(const allocator::vector<ThreadInfo>& threads,
const allocator::vector<Mapping>& mappings) {
- ALOGI("searching process %d for allocations", pid_);
+ MEM_ALOGI("searching process %d for allocations", pid_);
allocator::vector<Mapping> heap_mappings{mappings};
allocator::vector<Mapping> anon_mappings{mappings};
allocator::vector<Mapping> globals_mappings{mappings};
allocator::vector<Mapping> stack_mappings{mappings};
- if (!ClassifyMappings(mappings, heap_mappings, anon_mappings,
- globals_mappings, stack_mappings)) {
+ if (!ClassifyMappings(mappings, heap_mappings, anon_mappings, globals_mappings, stack_mappings)) {
return false;
}
for (auto it = heap_mappings.begin(); it != heap_mappings.end(); it++) {
- ALOGV("Heap mapping %" PRIxPTR "-%" PRIxPTR " %s", it->begin, it->end, it->name);
- HeapIterate(*it, [&](uintptr_t base, size_t size) {
- heap_walker_.Allocation(base, base + size);
- });
+ MEM_ALOGV("Heap mapping %" PRIxPTR "-%" PRIxPTR " %s", it->begin, it->end, it->name);
+ HeapIterate(*it,
+ [&](uintptr_t base, size_t size) { heap_walker_.Allocation(base, base + size); });
}
for (auto it = anon_mappings.begin(); it != anon_mappings.end(); it++) {
- ALOGV("Anon mapping %" PRIxPTR "-%" PRIxPTR " %s", it->begin, it->end, it->name);
+ MEM_ALOGV("Anon mapping %" PRIxPTR "-%" PRIxPTR " %s", it->begin, it->end, it->name);
heap_walker_.Allocation(it->begin, it->end);
}
for (auto it = globals_mappings.begin(); it != globals_mappings.end(); it++) {
- ALOGV("Globals mapping %" PRIxPTR "-%" PRIxPTR " %s", it->begin, it->end, it->name);
+ MEM_ALOGV("Globals mapping %" PRIxPTR "-%" PRIxPTR " %s", it->begin, it->end, it->name);
heap_walker_.Root(it->begin, it->end);
}
for (auto thread_it = threads.begin(); thread_it != threads.end(); thread_it++) {
for (auto it = stack_mappings.begin(); it != stack_mappings.end(); it++) {
if (thread_it->stack.first >= it->begin && thread_it->stack.first <= it->end) {
- ALOGV("Stack %" PRIxPTR "-%" PRIxPTR " %s", thread_it->stack.first, it->end, it->name);
+ MEM_ALOGV("Stack %" PRIxPTR "-%" PRIxPTR " %s", thread_it->stack.first, it->end, it->name);
heap_walker_.Root(thread_it->stack.first, it->end);
}
}
heap_walker_.Root(thread_it->regs);
}
- ALOGI("searching done");
+ MEM_ALOGI("searching done");
return true;
}
bool MemUnreachable::GetUnreachableMemory(allocator::vector<Leak>& leaks,
size_t limit, size_t* num_leaks, size_t* leak_bytes) {
- ALOGI("sweeping process %d for unreachable memory", pid_);
+ MEM_ALOGI("sweeping process %d for unreachable memory", pid_);
leaks.clear();
if (!heap_walker_.DetectLeaks()) {
@@ -133,9 +131,9 @@
allocator::vector<Range> leaked1{allocator_};
heap_walker_.Leaked(leaked1, 0, num_leaks, leak_bytes);
- ALOGI("sweeping done");
+ MEM_ALOGI("sweeping done");
- ALOGI("folding related leaks");
+ MEM_ALOGI("folding related leaks");
LeakFolding folding(allocator_, heap_walker_);
if (!folding.FoldLeaks()) {
@@ -188,7 +186,7 @@
std::min(leak->size, Leak::contents_length));
}
- ALOGI("folding done");
+ MEM_ALOGI("folding done");
std::sort(leaks.begin(), leaks.end(), [](const Leak& a, const Leak& b) {
return a.total_size > b.total_size;
@@ -276,7 +274,7 @@
/////////////////////////////////////////////
// Collection thread
/////////////////////////////////////////////
- ALOGI("collecting thread info for process %d...", parent_pid);
+ MEM_ALOGI("collecting thread info for process %d...", parent_pid);
ThreadCapture thread_capture(parent_pid, heap);
allocator::vector<ThreadInfo> thread_info(heap);
@@ -351,7 +349,7 @@
} else {
// Nothing left to do in the collection thread, return immediately,
// releasing all the captured threads.
- ALOGI("collection thread done");
+ MEM_ALOGI("collection thread done");
return 0;
}
}};
@@ -397,10 +395,10 @@
return false;
}
- ALOGI("unreachable memory detection done");
- ALOGE("%zu bytes in %zu allocation%s unreachable out of %zu bytes in %zu allocation%s",
- info.leak_bytes, info.num_leaks, plural(info.num_leaks),
- info.allocation_bytes, info.num_allocations, plural(info.num_allocations));
+ MEM_ALOGI("unreachable memory detection done");
+ MEM_ALOGE("%zu bytes in %zu allocation%s unreachable out of %zu bytes in %zu allocation%s",
+ info.leak_bytes, info.num_leaks, plural(info.num_leaks), info.allocation_bytes,
+ info.num_allocations, plural(info.num_allocations));
return true;
}
@@ -517,7 +515,7 @@
}
for (auto it = info.leaks.begin(); it != info.leaks.end(); it++) {
- ALOGE("%s", it->ToString(log_contents).c_str());
+ MEM_ALOGE("%s", it->ToString(log_contents).c_str());
}
return true;
}
diff --git a/libmemunreachable/PtracerThread.cpp b/libmemunreachable/PtracerThread.cpp
index 4e3c41e..73b0493 100644
--- a/libmemunreachable/PtracerThread.cpp
+++ b/libmemunreachable/PtracerThread.cpp
@@ -70,7 +70,7 @@
child_pid_(0) {
stack_ = std::make_unique<Stack>(PTHREAD_STACK_MIN);
if (stack_->top() == nullptr) {
- LOG_ALWAYS_FATAL("failed to mmap child stack: %s", strerror(errno));
+ MEM_LOG_ALWAYS_FATAL("failed to mmap child stack: %s", strerror(errno));
}
func_ = std::function<int()>{[&, func]() -> int {
@@ -102,7 +102,7 @@
CLONE_VM|CLONE_FS|CLONE_FILES/*|CLONE_UNTRACED*/,
reinterpret_cast<void*>(&func_));
if (child_pid_ < 0) {
- ALOGE("failed to clone child: %s", strerror(errno));
+ MEM_ALOGE("failed to clone child: %s", strerror(errno));
return false;
}
@@ -120,7 +120,7 @@
int status;
int ret = TEMP_FAILURE_RETRY(waitpid(child_pid_, &status, __WALL));
if (ret < 0) {
- ALOGE("waitpid %d failed: %s", child_pid_, strerror(errno));
+ MEM_ALOGE("waitpid %d failed: %s", child_pid_, strerror(errno));
return -1;
}
@@ -131,7 +131,7 @@
} else if (WIFSIGNALED(status)) {
return -WTERMSIG(status);
} else {
- ALOGE("unexpected status %x", status);
+ MEM_ALOGE("unexpected status %x", status);
return -1;
}
}
diff --git a/libmemunreachable/ScopedPipe.h b/libmemunreachable/ScopedPipe.h
index 9beef9a..7f44953 100644
--- a/libmemunreachable/ScopedPipe.h
+++ b/libmemunreachable/ScopedPipe.h
@@ -26,7 +26,7 @@
ScopedPipe() : pipefd_{-1, -1} {
int ret = pipe2(pipefd_, O_CLOEXEC);
if (ret < 0) {
- LOG_ALWAYS_FATAL("failed to open pipe");
+ MEM_LOG_ALWAYS_FATAL("failed to open pipe");
}
}
~ScopedPipe() {
diff --git a/libmemunreachable/ScopedSignalHandler.h b/libmemunreachable/ScopedSignalHandler.h
index 1fd9d4d..ada2ae4 100644
--- a/libmemunreachable/ScopedSignalHandler.h
+++ b/libmemunreachable/ScopedSignalHandler.h
@@ -37,22 +37,18 @@
template <class F>
void install(int signal, F&& f) {
- LOG_ALWAYS_FATAL_IF(signal_ != -1, "ScopedSignalHandler already installed");
+ MEM_LOG_ALWAYS_FATAL_IF(signal_ != -1, "ScopedSignalHandler already installed");
handler_ = SignalFn(std::allocator_arg, allocator_,
- [=](int signal, siginfo_t* si, void* uctx) {
- f(*this, signal, si, uctx);
- });
+ [=](int signal, siginfo_t* si, void* uctx) { f(*this, signal, si, uctx); });
- struct sigaction act{};
- act.sa_sigaction = [](int signal, siginfo_t* si, void* uctx) {
- handler_(signal, si, uctx);
- };
+ struct sigaction act {};
+ act.sa_sigaction = [](int signal, siginfo_t* si, void* uctx) { handler_(signal, si, uctx); };
act.sa_flags = SA_SIGINFO;
int ret = sigaction(signal, &act, &old_act_);
if (ret < 0) {
- LOG_ALWAYS_FATAL("failed to install segfault handler: %s", strerror(errno));
+ MEM_LOG_ALWAYS_FATAL("failed to install segfault handler: %s", strerror(errno));
}
signal_ = signal;
@@ -62,7 +58,7 @@
if (signal_ != -1) {
int ret = sigaction(signal_, &old_act_, NULL);
if (ret < 0) {
- ALOGE("failed to uninstall segfault handler");
+ MEM_ALOGE("failed to uninstall segfault handler");
}
handler_ = SignalFn{};
signal_ = -1;
diff --git a/libmemunreachable/ThreadCapture.cpp b/libmemunreachable/ThreadCapture.cpp
index 9155c29..3891f2d 100644
--- a/libmemunreachable/ThreadCapture.cpp
+++ b/libmemunreachable/ThreadCapture.cpp
@@ -110,7 +110,7 @@
android::base::unique_fd fd(open(path, O_CLOEXEC | O_DIRECTORY | O_RDONLY));
if (fd == -1) {
- ALOGE("failed to open %s: %s", path, strerror(errno));
+ MEM_ALOGE("failed to open %s: %s", path, strerror(errno));
return false;
}
@@ -126,7 +126,7 @@
do {
nread = syscall(SYS_getdents64, fd.get(), dirent_buf, sizeof(dirent_buf));
if (nread < 0) {
- ALOGE("failed to get directory entries from %s: %s", path, strerror(errno));
+ MEM_ALOGE("failed to get directory entries from %s: %s", path, strerror(errno));
return false;
} else if (nread > 0) {
ssize_t off = 0;
@@ -177,8 +177,7 @@
void ThreadCaptureImpl::PtraceDetach(pid_t tid, unsigned int signal) {
void* sig_ptr = reinterpret_cast<void*>(static_cast<uintptr_t>(signal));
if (ptrace(PTRACE_DETACH, tid, NULL, sig_ptr) < 0 && errno != ESRCH) {
- ALOGE("failed to detach from thread %d of process %d: %s", tid, pid_,
- strerror(errno));
+ MEM_ALOGE("failed to detach from thread %d of process %d: %s", tid, pid_, strerror(errno));
}
}
@@ -187,8 +186,7 @@
int ThreadCaptureImpl::PtraceAttach(pid_t tid) {
int ret = ptrace(PTRACE_SEIZE, tid, NULL, NULL);
if (ret < 0) {
- ALOGE("failed to attach to thread %d of process %d: %s", tid, pid_,
- strerror(errno));
+ MEM_ALOGE("failed to attach to thread %d of process %d: %s", tid, pid_, strerror(errno));
return -1;
}
@@ -200,8 +198,7 @@
if (errno == ESRCH) {
return 0;
} else {
- ALOGE("failed to interrupt thread %d of process %d: %s", tid, pid_,
- strerror(errno));
+ MEM_ALOGE("failed to interrupt thread %d of process %d: %s", tid, pid_, strerror(errno));
PtraceDetach(tid, 0);
return -1;
}
@@ -219,8 +216,7 @@
iovec.iov_len = sizeof(regs);
if (ptrace(PTRACE_GETREGSET, tid, reinterpret_cast<void*>(NT_PRSTATUS), &iovec)) {
- ALOGE("ptrace getregset for thread %d of process %d failed: %s",
- tid, pid_, strerror(errno));
+ MEM_ALOGE("ptrace getregset for thread %d of process %d failed: %s", tid, pid_, strerror(errno));
return false;
}
@@ -258,15 +254,13 @@
int status = 0;
if (TEMP_FAILURE_RETRY(waitpid(tid, &status, __WALL)) < 0) {
- ALOGE("failed to wait for pause of thread %d of process %d: %s", tid, pid_,
- strerror(errno));
+ MEM_ALOGE("failed to wait for pause of thread %d of process %d: %s", tid, pid_, strerror(errno));
PtraceDetach(tid, 0);
return -1;
}
if (!WIFSTOPPED(status)) {
- ALOGE("thread %d of process %d was not paused after waitpid, killed?",
- tid, pid_);
+ MEM_ALOGE("thread %d of process %d was not paused after waitpid, killed?", tid, pid_);
return 0;
}
@@ -285,8 +279,8 @@
// normal ptrace interrupt stop
break;
default:
- ALOGE("unexpected signal %d with PTRACE_EVENT_STOP for thread %d of process %d",
- signal, tid, pid_);
+ MEM_ALOGE("unexpected signal %d with PTRACE_EVENT_STOP for thread %d of process %d", signal,
+ tid, pid_);
return -1;
}
} else {
diff --git a/libmemunreachable/log.h b/libmemunreachable/log.h
index cdfbfd9..1725c53 100644
--- a/libmemunreachable/log.h
+++ b/libmemunreachable/log.h
@@ -19,6 +19,32 @@
#define LOG_TAG "libmemunreachable"
+#if defined(__ANDROID__)
+
+#include <async_safe/log.h>
+
+#define MEM_ALOGE(...) async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, ##__VA_ARGS__)
+#define MEM_ALOGW(...) async_safe_format_log(ANDROID_LOG_WARN, LOG_TAG, ##__VA_ARGS__)
+#define MEM_ALOGI(...) async_safe_format_log(ANDROID_LOG_INFO, LOG_TAG, ##__VA_ARGS__)
+#define MEM_ALOGV(...) async_safe_format_log(ANDROID_LOG_VERBOSE, LOG_TAG, ##__VA_ARGS__)
+
+#define MEM_LOG_ALWAYS_FATAL(...) async_safe_fatal(__VA_ARGS__)
+
+#define MEM_LOG_ALWAYS_FATAL_IF(cond, ...) \
+ ((__predict_false(cond)) ? async_safe_fatal(__VA_ARGS__) : (void)0)
+
+#else
+
#include <log/log.h>
+#define MEM_ALOGW ALOGW
+#define MEM_ALOGE ALOGE
+#define MEM_ALOGV ALOGV
+#define MEM_ALOGI ALOGI
+
+#define MEM_LOG_ALWAYS_FATAL LOG_ALWAYS_FATAL
+#define MEM_LOG_ALWAYS_FATAL_IF LOG_ALWAYS_FATAL_IF
+
+#endif
+
#endif // LIBMEMUNREACHABLE_LOG_H_
diff --git a/libmemunreachable/tests/DisableMalloc_test.cpp b/libmemunreachable/tests/DisableMalloc_test.cpp
index ea5c22c..4e6155b 100644
--- a/libmemunreachable/tests/DisableMalloc_test.cpp
+++ b/libmemunreachable/tests/DisableMalloc_test.cpp
@@ -74,7 +74,7 @@
{
alarm(100ms);
ScopedDisableMalloc disable_malloc;
- char* ptr = new(char);
+ char* ptr = new (std::nothrow)(char);
ASSERT_NE(ptr, nullptr);
delete(ptr);
}
@@ -89,6 +89,8 @@
alarm(250ms);
ScopedDisableMalloc disable_malloc;
delete(ptr);
+ // Force ptr usage or this code gets optimized away by the arm64 compiler.
+ ASSERT_NE(ptr, nullptr);
}
}, "");
}
diff --git a/libmemunreachable/tests/MemUnreachable_test.cpp b/libmemunreachable/tests/MemUnreachable_test.cpp
index 2ae3db8..71da365 100644
--- a/libmemunreachable/tests/MemUnreachable_test.cpp
+++ b/libmemunreachable/tests/MemUnreachable_test.cpp
@@ -23,8 +23,6 @@
#include <memunreachable/memunreachable.h>
-void* ptr;
-
class HiddenPointer {
public:
explicit HiddenPointer(size_t size = 256) {
@@ -92,10 +90,12 @@
}
}
+void* g_ptr;
+
TEST(MemunreachableTest, global) {
HiddenPointer hidden_ptr;
- ptr = hidden_ptr.Get();
+ g_ptr = hidden_ptr.Get();
{
UnreachableMemoryInfo info;
@@ -104,7 +104,7 @@
ASSERT_EQ(0U, info.leaks.size());
}
- ptr = NULL;
+ g_ptr = nullptr;
{
UnreachableMemoryInfo info;
@@ -126,7 +126,7 @@
TEST(MemunreachableTest, tls) {
HiddenPointer hidden_ptr;
pthread_key_t key;
- pthread_key_create(&key, NULL);
+ pthread_key_create(&key, nullptr);
pthread_setspecific(key, hidden_ptr.Get());
diff --git a/libmemunreachable/tests/ThreadCapture_test.cpp b/libmemunreachable/tests/ThreadCapture_test.cpp
index 41ed84e..44aabd7 100644
--- a/libmemunreachable/tests/ThreadCapture_test.cpp
+++ b/libmemunreachable/tests/ThreadCapture_test.cpp
@@ -55,7 +55,7 @@
threads_.reserve(threads);
tids_.reserve(threads);
for (unsigned int i = 0; i < threads; i++) {
- threads_.emplace_back([&, i, threads, this]() {
+ threads_.emplace_back([&, threads, this]() {
{
std::lock_guard<std::mutex> lk(m_);
tids_.push_back(gettid());
diff --git a/libmetricslogger/Android.bp b/libmetricslogger/Android.bp
index da8afe1..38859d1 100644
--- a/libmetricslogger/Android.bp
+++ b/libmetricslogger/Android.bp
@@ -23,6 +23,7 @@
// 524291 corresponds to sysui_histogram, from
// frameworks/base/core/java/com/android/internal/logging/EventLogTags.logtags
"-DHISTOGRAM_LOG_TAG=524292",
+ "-DCOUNT_LOG_TAG=524290",
],
}
@@ -30,6 +31,7 @@
// -----------------------------------------------------------------------------
cc_library_shared {
name: "libmetricslogger",
+ vendor_available: true,
srcs: metricslogger_lib_src_files,
defaults: ["metricslogger_defaults"],
}
diff --git a/libmetricslogger/include/metricslogger/metrics_logger.h b/libmetricslogger/include/metricslogger/metrics_logger.h
index 26aa189..36e124d 100644
--- a/libmetricslogger/include/metricslogger/metrics_logger.h
+++ b/libmetricslogger/include/metricslogger/metrics_logger.h
@@ -24,13 +24,18 @@
// buffer.
void LogHistogram(const std::string& event, int32_t data);
+// Logs a Tron counter metric named |name| containing |val| count to the Tron
+// log buffer.
+void LogCounter(const std::string& name, int32_t val);
+
// TODO: replace these with the metric_logger.proto definitions
enum {
- LOGBUILDER_CATEGORY = 757,
- LOGBUILDER_NAME = 799,
- LOGBUILDER_BUCKET = 801,
- LOGBUILDER_VALUE = 802,
- LOGBUILDER_HISTOGRAM = 804,
+ LOGBUILDER_CATEGORY = 757,
+ LOGBUILDER_NAME = 799,
+ LOGBUILDER_BUCKET = 801,
+ LOGBUILDER_VALUE = 802,
+ LOGBUILDER_COUNTER = 803,
+ LOGBUILDER_HISTOGRAM = 804,
};
} // namespace metricslogger
diff --git a/libmetricslogger/metrics_logger.cpp b/libmetricslogger/metrics_logger.cpp
index 0d08f5c..6f65e10 100644
--- a/libmetricslogger/metrics_logger.cpp
+++ b/libmetricslogger/metrics_logger.cpp
@@ -25,12 +25,16 @@
// Mirror com.android.internal.logging.MetricsLogger#histogram().
void LogHistogram(const std::string& event, int32_t data) {
- android_log_event_list log(HISTOGRAM_LOG_TAG);
- log << LOGBUILDER_CATEGORY << LOGBUILDER_HISTOGRAM
- << LOGBUILDER_NAME << event
- << LOGBUILDER_BUCKET << data
- << LOGBUILDER_VALUE << 1
- << LOG_ID_EVENTS;
+ android_log_event_list log(HISTOGRAM_LOG_TAG);
+ log << LOGBUILDER_CATEGORY << LOGBUILDER_HISTOGRAM << LOGBUILDER_NAME << event
+ << LOGBUILDER_BUCKET << data << LOGBUILDER_VALUE << 1 << LOG_ID_EVENTS;
+}
+
+// Mirror com.android.internal.logging.MetricsLogger#count().
+void LogCounter(const std::string& name, int32_t val) {
+ android_log_event_list log(COUNT_LOG_TAG);
+ log << LOGBUILDER_CATEGORY << LOGBUILDER_COUNTER << LOGBUILDER_NAME << name << LOGBUILDER_VALUE
+ << val << LOG_ID_EVENTS;
}
} // namespace metricslogger
diff --git a/libmetricslogger/metrics_logger_test.cpp b/libmetricslogger/metrics_logger_test.cpp
index 5a30ad7..440645c 100644
--- a/libmetricslogger/metrics_logger_test.cpp
+++ b/libmetricslogger/metrics_logger_test.cpp
@@ -19,6 +19,10 @@
#include <gtest/gtest.h>
TEST(MetricsLoggerTest, AddSingleBootEvent) {
- android::metricslogger::LogHistogram("test_event", 42);
- // TODO(jhawkins): Verify the EventLog is updated.
+ android::metricslogger::LogHistogram("test_event", 42);
+ // TODO(jhawkins): Verify the EventLog is updated.
+}
+
+TEST(MetricsLoggerTest, AddCounterVal) {
+ android::metricslogger::LogCounter("test_count", 10);
}
diff --git a/libnativebridge/Android.bp b/libnativebridge/Android.bp
index 5fb56f2..377b7dd 100644
--- a/libnativebridge/Android.bp
+++ b/libnativebridge/Android.bp
@@ -22,3 +22,5 @@
},
},
}
+
+subdirs = ["tests"]
\ No newline at end of file
diff --git a/libnativebridge/native_bridge.cc b/libnativebridge/native_bridge.cc
index 83f35b1..02b4fe7 100644
--- a/libnativebridge/native_bridge.cc
+++ b/libnativebridge/native_bridge.cc
@@ -85,12 +85,14 @@
// Nativebridge implementation.
// Used by isCompatibleWith() which is introduced in v2.
enum NativeBridgeImplementationVersion {
- // first version, not used.
- DEFAULT_VERSION = 1,
- // The version which signal semantic is introduced.
- SIGNAL_VERSION = 2,
- // The version which namespace semantic is introduced.
- NAMESPACE_VERSION = 3,
+ // first version, not used.
+ DEFAULT_VERSION = 1,
+ // The version which signal semantic is introduced.
+ SIGNAL_VERSION = 2,
+ // The version which namespace semantic is introduced.
+ NAMESPACE_VERSION = 3,
+ // The version with vendor namespaces
+ VENDOR_NAMESPACE_VERSION = 4,
};
// Whether we had an error at some point.
@@ -573,11 +575,11 @@
return false;
}
-bool NativeBridgeInitNamespace(const char* public_ns_sonames,
- const char* anon_ns_library_path) {
+bool NativeBridgeInitAnonymousNamespace(const char* public_ns_sonames,
+ const char* anon_ns_library_path) {
if (NativeBridgeInitialized()) {
if (isCompatibleWith(NAMESPACE_VERSION)) {
- return callbacks->initNamespace(public_ns_sonames, anon_ns_library_path);
+ return callbacks->initAnonymousNamespace(public_ns_sonames, anon_ns_library_path);
} else {
ALOGE("not compatible with version %d, cannot init namespace", NAMESPACE_VERSION);
}
@@ -608,6 +610,27 @@
return nullptr;
}
+bool NativeBridgeLinkNamespaces(native_bridge_namespace_t* from, native_bridge_namespace_t* to,
+ const char* shared_libs_sonames) {
+ if (NativeBridgeInitialized()) {
+ if (isCompatibleWith(NAMESPACE_VERSION)) {
+ return callbacks->linkNamespaces(from, to, shared_libs_sonames);
+ } else {
+ ALOGE("not compatible with version %d, cannot init namespace", NAMESPACE_VERSION);
+ }
+ }
+
+ return false;
+}
+
+native_bridge_namespace_t* NativeBridgeGetVendorNamespace() {
+ if (!NativeBridgeInitialized() || !isCompatibleWith(VENDOR_NAMESPACE_VERSION)) {
+ return nullptr;
+ }
+
+ return callbacks->getVendorNamespace();
+}
+
void* NativeBridgeLoadLibraryExt(const char* libpath, int flag, native_bridge_namespace_t* ns) {
if (NativeBridgeInitialized()) {
if (isCompatibleWith(NAMESPACE_VERSION)) {
diff --git a/libnativebridge/tests/Android.bp b/libnativebridge/tests/Android.bp
new file mode 100644
index 0000000..efd3978
--- /dev/null
+++ b/libnativebridge/tests/Android.bp
@@ -0,0 +1,53 @@
+//
+// 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_defaults {
+ name: "libnativebridge-dummy-defaults",
+
+ host_supported: true,
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+ cppflags: ["-fvisibility=protected"],
+ target: {
+ android: {
+ shared_libs: ["libdl"],
+ },
+ host: {
+ host_ldlibs: ["-ldl"],
+ },
+ },
+}
+
+cc_library_shared {
+ name: "libnativebridge-dummy",
+ srcs: ["DummyNativeBridge.cpp"],
+ defaults: ["libnativebridge-dummy-defaults"],
+}
+
+cc_library_shared {
+ name: "libnativebridge2-dummy",
+ srcs: ["DummyNativeBridge2.cpp"],
+ defaults: ["libnativebridge-dummy-defaults"],
+}
+
+cc_library_shared {
+ name: "libnativebridge3-dummy",
+ srcs: ["DummyNativeBridge3.cpp"],
+ defaults: ["libnativebridge-dummy-defaults"],
+}
diff --git a/libnativebridge/tests/Android.mk b/libnativebridge/tests/Android.mk
index 4c3e862..70b3fcc 100644
--- a/libnativebridge/tests/Android.mk
+++ b/libnativebridge/tests/Android.mk
@@ -1,8 +1,6 @@
# Build the unit tests.
LOCAL_PATH := $(call my-dir)
-include $(LOCAL_PATH)/Android.nativebridge-dummy.mk
-
include $(CLEAR_VARS)
# Build the unit tests.
@@ -24,7 +22,7 @@
NativeBridge3UnloadLibrary_test.cpp \
NativeBridge3GetError_test.cpp \
NativeBridge3IsPathSupported_test.cpp \
- NativeBridge3InitNamespace_test.cpp \
+ NativeBridge3InitAnonymousNamespace_test.cpp \
NativeBridge3CreateNamespace_test.cpp \
NativeBridge3LoadLibraryExt_test.cpp
diff --git a/libnativebridge/tests/Android.nativebridge-dummy.mk b/libnativebridge/tests/Android.nativebridge-dummy.mk
deleted file mode 100644
index 2d78be0..0000000
--- a/libnativebridge/tests/Android.nativebridge-dummy.mk
+++ /dev/null
@@ -1,108 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-NATIVE_BRIDGE_COMMON_SRC_FILES := \
- DummyNativeBridge.cpp
-
-# Shared library for target
-# ========================================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE:= libnativebridge-dummy
-
-LOCAL_SRC_FILES:= $(NATIVE_BRIDGE_COMMON_SRC_FILES)
-LOCAL_CLANG := true
-LOCAL_CFLAGS += -Werror -Wall
-LOCAL_CPPFLAGS := -fvisibility=protected
-LOCAL_SHARED_LIBRARIES := libdl
-LOCAL_MULTILIB := both
-
-include $(BUILD_SHARED_LIBRARY)
-
-# Shared library for host
-# ========================================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE:= libnativebridge-dummy
-
-LOCAL_SRC_FILES:= $(NATIVE_BRIDGE_COMMON_SRC_FILES)
-LOCAL_CLANG := true
-LOCAL_CFLAGS += -Werror -Wall
-LOCAL_CPPFLAGS := -fvisibility=protected
-LOCAL_LDFLAGS := -ldl
-LOCAL_MULTILIB := both
-
-include $(BUILD_HOST_SHARED_LIBRARY)
-
-
-# v2.
-
-NATIVE_BRIDGE2_COMMON_SRC_FILES := \
- DummyNativeBridge2.cpp
-
-# Shared library for target
-# ========================================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE:= libnativebridge2-dummy
-
-LOCAL_SRC_FILES:= $(NATIVE_BRIDGE2_COMMON_SRC_FILES)
-LOCAL_CLANG := true
-LOCAL_CFLAGS += -Werror -Wall
-LOCAL_CPPFLAGS := -fvisibility=protected
-LOCAL_SHARED_LIBRARIES := libdl
-LOCAL_MULTILIB := both
-
-include $(BUILD_SHARED_LIBRARY)
-
-# Shared library for host
-# ========================================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE:= libnativebridge2-dummy
-
-LOCAL_SRC_FILES:= $(NATIVE_BRIDGE2_COMMON_SRC_FILES)
-LOCAL_CLANG := true
-LOCAL_CFLAGS += -Werror -Wall
-LOCAL_CPPFLAGS := -fvisibility=protected
-LOCAL_LDFLAGS := -ldl
-LOCAL_MULTILIB := both
-
-include $(BUILD_HOST_SHARED_LIBRARY)
-
-
-# v3.
-
-NATIVE_BRIDGE3_COMMON_SRC_FILES := \
- DummyNativeBridge3.cpp
-
-# Shared library for target
-# ========================================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE:= libnativebridge3-dummy
-
-LOCAL_SRC_FILES:= $(NATIVE_BRIDGE3_COMMON_SRC_FILES)
-LOCAL_CLANG := true
-LOCAL_CFLAGS += -Werror -Wall
-LOCAL_CPPFLAGS := -std=gnu++11 -fvisibility=protected
-LOCAL_LDFLAGS := -ldl
-LOCAL_MULTILIB := both
-
-include $(BUILD_SHARED_LIBRARY)
-
-# Shared library for host
-# ========================================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE:= libnativebridge3-dummy
-
-LOCAL_SRC_FILES:= $(NATIVE_BRIDGE3_COMMON_SRC_FILES)
-LOCAL_CLANG := true
-LOCAL_CFLAGS += -Werror -Wall
-LOCAL_CPPFLAGS := -std=gnu++11 -fvisibility=protected
-LOCAL_LDFLAGS := -ldl
-LOCAL_MULTILIB := both
-
-include $(BUILD_HOST_SHARED_LIBRARY)
-
-
diff --git a/libnativebridge/tests/DummyNativeBridge3.cpp b/libnativebridge/tests/DummyNativeBridge3.cpp
index 13fce85..4ef1c82 100644
--- a/libnativebridge/tests/DummyNativeBridge3.cpp
+++ b/libnativebridge/tests/DummyNativeBridge3.cpp
@@ -76,8 +76,8 @@
return true;
}
-extern "C" bool native_bridge3_initNamespace(const char* /* public_ns_sonames */,
- const char* /* anon_ns_library_path */) {
+extern "C" bool native_bridge3_initAnonymousNamespace(const char* /* public_ns_sonames */,
+ const char* /* anon_ns_library_path */) {
return true;
}
@@ -91,30 +91,34 @@
return nullptr;
}
+extern "C" bool native_bridge3_linkNamespaces(android::native_bridge_namespace_t* /* from */,
+ android::native_bridge_namespace_t* /* to */,
+ const char* /* shared_libs_soname */) {
+ return true;
+}
+
extern "C" void* native_bridge3_loadLibraryExt(const char* /* libpath */,
int /* flag */,
android::native_bridge_namespace_t* /* ns */) {
return nullptr;
}
-
-android::NativeBridgeCallbacks NativeBridgeItf {
- // v1
- .version = 3,
- .initialize = &native_bridge3_initialize,
- .loadLibrary = &native_bridge3_loadLibrary,
- .getTrampoline = &native_bridge3_getTrampoline,
- .isSupported = &native_bridge3_isSupported,
- .getAppEnv = &native_bridge3_getAppEnv,
- // v2
- .isCompatibleWith = &native_bridge3_isCompatibleWith,
- .getSignalHandler = &native_bridge3_getSignalHandler,
- // v3
- .unloadLibrary = &native_bridge3_unloadLibrary,
- .getError = &native_bridge3_getError,
- .isPathSupported = &native_bridge3_isPathSupported,
- .initNamespace = &native_bridge3_initNamespace,
- .createNamespace = &native_bridge3_createNamespace,
- .loadLibraryExt = &native_bridge3_loadLibraryExt
-};
-
+android::NativeBridgeCallbacks NativeBridgeItf{
+ // v1
+ .version = 3,
+ .initialize = &native_bridge3_initialize,
+ .loadLibrary = &native_bridge3_loadLibrary,
+ .getTrampoline = &native_bridge3_getTrampoline,
+ .isSupported = &native_bridge3_isSupported,
+ .getAppEnv = &native_bridge3_getAppEnv,
+ // v2
+ .isCompatibleWith = &native_bridge3_isCompatibleWith,
+ .getSignalHandler = &native_bridge3_getSignalHandler,
+ // v3
+ .unloadLibrary = &native_bridge3_unloadLibrary,
+ .getError = &native_bridge3_getError,
+ .isPathSupported = &native_bridge3_isPathSupported,
+ .initAnonymousNamespace = &native_bridge3_initAnonymousNamespace,
+ .createNamespace = &native_bridge3_createNamespace,
+ .linkNamespaces = &native_bridge3_linkNamespaces,
+ .loadLibraryExt = &native_bridge3_loadLibraryExt};
diff --git a/libnativebridge/tests/NativeBridge3InitAnonymousNamespace_test.cpp b/libnativebridge/tests/NativeBridge3InitAnonymousNamespace_test.cpp
new file mode 100644
index 0000000..b0d6b09
--- /dev/null
+++ b/libnativebridge/tests/NativeBridge3InitAnonymousNamespace_test.cpp
@@ -0,0 +1,39 @@
+/*
+ * 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 "NativeBridgeTest.h"
+
+namespace android {
+
+constexpr const char* kNativeBridgeLibrary3 = "libnativebridge3-dummy.so";
+
+TEST_F(NativeBridgeTest, V3_InitAnonymousNamespace) {
+ // Init
+ ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary3, nullptr));
+ ASSERT_TRUE(NativeBridgeAvailable());
+ ASSERT_TRUE(PreInitializeNativeBridge(".", "isa"));
+ ASSERT_TRUE(NativeBridgeAvailable());
+ ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr));
+ ASSERT_TRUE(NativeBridgeAvailable());
+
+ ASSERT_EQ(3U, NativeBridgeGetVersion());
+ ASSERT_EQ(true, NativeBridgeInitAnonymousNamespace(nullptr, nullptr));
+
+ // Clean-up code_cache
+ ASSERT_EQ(0, rmdir(kCodeCache));
+}
+
+} // namespace android
diff --git a/libnativebridge/tests/NativeBridge3InitNamespace_test.cpp b/libnativebridge/tests/NativeBridge3InitNamespace_test.cpp
deleted file mode 100644
index ae0fd2b..0000000
--- a/libnativebridge/tests/NativeBridge3InitNamespace_test.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * 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 "NativeBridgeTest.h"
-
-namespace android {
-
-constexpr const char* kNativeBridgeLibrary3 = "libnativebridge3-dummy.so";
-
-TEST_F(NativeBridgeTest, V3_InitNamespace) {
- // Init
- ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary3, nullptr));
- ASSERT_TRUE(NativeBridgeAvailable());
- ASSERT_TRUE(PreInitializeNativeBridge(".", "isa"));
- ASSERT_TRUE(NativeBridgeAvailable());
- ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr));
- ASSERT_TRUE(NativeBridgeAvailable());
-
- ASSERT_EQ(3U, NativeBridgeGetVersion());
- ASSERT_EQ(true, NativeBridgeInitNamespace(nullptr, nullptr));
-
- // Clean-up code_cache
- ASSERT_EQ(0, rmdir(kCodeCache));
-}
-
-} // namespace android
diff --git a/libnativeloader/include/nativeloader/dlext_namespaces.h b/libnativeloader/include/nativeloader/dlext_namespaces.h
index ac64f71..43c9329 100644
--- a/libnativeloader/include/nativeloader/dlext_namespaces.h
+++ b/libnativeloader/include/nativeloader/dlext_namespaces.h
@@ -55,6 +55,12 @@
* permitted_path from the caller's namespace.
*/
ANDROID_NAMESPACE_TYPE_SHARED = 2,
+
+ /* This flag instructs linker to enable grey-list workaround for the namespace.
+ * See http://b/26394120 for details.
+ */
+ ANDROID_NAMESPACE_TYPE_GREYLIST_ENABLED = 0x08000000,
+
ANDROID_NAMESPACE_TYPE_SHARED_ISOLATED = ANDROID_NAMESPACE_TYPE_SHARED |
ANDROID_NAMESPACE_TYPE_ISOLATED,
};
@@ -118,6 +124,8 @@
*/
extern void android_get_LD_LIBRARY_PATH(char* buffer, size_t buffer_size);
+extern android_namespace_t* android_get_exported_namespace(const char* name);
+
__END_DECLS
#endif /* __ANDROID_DLEXT_NAMESPACES_H__ */
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index 74f2f1d..36a2e44 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -83,6 +83,12 @@
static constexpr const char* kPublicNativeLibrariesVendorConfig =
"/vendor/etc/public.libraries.txt";
+// The device may be configured to have the vendor libraries loaded to a separate namespace.
+// For historical reasons this namespace was named sphal but effectively it is intended
+// to use to load vendor libraries to separate namespace with controlled interface between
+// vendor and system namespaces.
+static constexpr const char* kVendorNamespaceName = "sphal";
+
// (http://b/27588281) This is a workaround for apps using custom classloaders and calling
// System.load() with an absolute path which is outside of the classloader library search path.
// This list includes all directories app is allowed to access this way.
@@ -99,6 +105,7 @@
LibraryNamespaces() : initialized_(false) { }
bool Create(JNIEnv* env,
+ uint32_t target_sdk_version,
jobject class_loader,
bool is_shared,
jstring java_library_path,
@@ -141,6 +148,10 @@
namespace_type |= ANDROID_NAMESPACE_TYPE_SHARED;
}
+ if (target_sdk_version < 24) {
+ namespace_type |= ANDROID_NAMESPACE_TYPE_GREYLIST_ENABLED;
+ }
+
NativeLoaderNamespace parent_ns;
bool found_parent_namespace = FindParentNamespaceByClassLoader(env, class_loader, &parent_ns);
@@ -165,11 +176,23 @@
return false;
}
- if (!android_link_namespaces(ns, nullptr, public_libraries_.c_str())) {
+ // Note that when vendor_ns is not configured this function will return nullptr
+ // and it will result in linking vendor_public_libraries_ to the default namespace
+ // which is expected behavior in this case.
+ android_namespace_t* vendor_ns = android_get_exported_namespace(kVendorNamespaceName);
+
+ if (!android_link_namespaces(ns, nullptr, system_public_libraries_.c_str())) {
*error_msg = dlerror();
return false;
}
+ if (!vendor_public_libraries_.empty()) {
+ if (!android_link_namespaces(ns, vendor_ns, vendor_public_libraries_.c_str())) {
+ *error_msg = dlerror();
+ return false;
+ }
+ }
+
native_loader_ns = NativeLoaderNamespace(ns);
} else {
native_bridge_namespace_t* ns = NativeBridgeCreateNamespace("classloader-namespace",
@@ -178,11 +201,26 @@
namespace_type,
permitted_path.c_str(),
parent_ns.get_native_bridge_ns());
+
if (ns == nullptr) {
*error_msg = NativeBridgeGetError();
return false;
}
+ native_bridge_namespace_t* vendor_ns = NativeBridgeGetVendorNamespace();
+
+ if (!NativeBridgeLinkNamespaces(ns, nullptr, system_public_libraries_.c_str())) {
+ *error_msg = NativeBridgeGetError();
+ return false;
+ }
+
+ if (!vendor_public_libraries_.empty()) {
+ if (!NativeBridgeLinkNamespaces(ns, vendor_ns, vendor_public_libraries_.c_str())) {
+ *error_msg = NativeBridgeGetError();
+ return false;
+ }
+ }
+
native_loader_ns = NativeLoaderNamespace(ns);
}
@@ -239,9 +277,6 @@
}
}
- // This file is optional, quietly ignore if the file does not exist.
- ReadConfig(kPublicNativeLibrariesVendorConfig, &sonames);
-
// android_init_namespaces() expects all the public libraries
// to be loaded so that they can be found by soname alone.
//
@@ -256,7 +291,13 @@
soname.c_str(), dlerror());
}
- public_libraries_ = base::Join(sonames, ':');
+ system_public_libraries_ = base::Join(sonames, ':');
+
+ sonames.clear();
+ // This file is optional, quietly ignore if the file does not exist.
+ ReadConfig(kPublicNativeLibrariesVendorConfig, &sonames);
+
+ vendor_public_libraries_ = base::Join(sonames, ':');
}
void Reset() {
@@ -315,7 +356,7 @@
// code is one example) unknown to linker in which case linker uses anonymous
// namespace. The second argument specifies the search path for the anonymous
// namespace which is the library_path of the classloader.
- initialized_ = android_init_anonymous_namespace(public_libraries_.c_str(),
+ initialized_ = android_init_anonymous_namespace(system_public_libraries_.c_str(),
is_native_bridge ? nullptr : library_path);
if (!initialized_) {
*error_msg = dlerror();
@@ -324,8 +365,8 @@
// and now initialize native bridge namespaces if necessary.
if (NativeBridgeInitialized()) {
- initialized_ = NativeBridgeInitNamespace(public_libraries_.c_str(),
- is_native_bridge ? library_path : nullptr);
+ initialized_ = NativeBridgeInitAnonymousNamespace(system_public_libraries_.c_str(),
+ is_native_bridge ? library_path : nullptr);
if (!initialized_) {
*error_msg = NativeBridgeGetError();
}
@@ -361,8 +402,8 @@
bool initialized_;
std::vector<std::pair<jweak, NativeLoaderNamespace>> namespaces_;
- std::string public_libraries_;
-
+ std::string system_public_libraries_;
+ std::string vendor_public_libraries_;
DISALLOW_COPY_AND_ASSIGN(LibraryNamespaces);
};
@@ -392,12 +433,12 @@
jstring library_path,
jstring permitted_path) {
#if defined(__ANDROID__)
- UNUSED(target_sdk_version);
std::lock_guard<std::mutex> guard(g_namespaces_mutex);
std::string error_msg;
NativeLoaderNamespace ns;
bool success = g_namespaces->Create(env,
+ target_sdk_version,
class_loader,
is_shared,
library_path,
@@ -434,7 +475,14 @@
if (!g_namespaces->FindNamespaceByClassLoader(env, class_loader, &ns)) {
// This is the case where the classloader was not created by ApplicationLoaders
// In this case we create an isolated not-shared namespace for it.
- if (!g_namespaces->Create(env, class_loader, false, library_path, nullptr, &ns, error_msg)) {
+ if (!g_namespaces->Create(env,
+ target_sdk_version,
+ class_loader,
+ false,
+ library_path,
+ nullptr,
+ &ns,
+ error_msg)) {
return nullptr;
}
}
diff --git a/libnetutils/Android.bp b/libnetutils/Android.bp
new file mode 100644
index 0000000..9967ef8
--- /dev/null
+++ b/libnetutils/Android.bp
@@ -0,0 +1,34 @@
+cc_library_shared {
+ name: "libnetutils",
+ vendor_available: true,
+
+ srcs: [
+ "dhcpclient.c",
+ "dhcpmsg.c",
+ "ifc_utils.c",
+ "packet.c",
+ ],
+
+ shared_libs: [
+ "libcutils",
+ "liblog",
+ ],
+
+ cflags: ["-Werror"],
+
+ export_include_dirs: ["include"],
+}
+
+cc_binary {
+ name: "dhcpdbg",
+
+ srcs: [
+ "dhcptool.c",
+ ],
+
+ shared_libs: [
+ "libnetutils",
+ ],
+
+ cflags: ["-Werror"],
+}
diff --git a/libnetutils/Android.mk b/libnetutils/Android.mk
deleted file mode 100644
index 2150279..0000000
--- a/libnetutils/Android.mk
+++ /dev/null
@@ -1,21 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
- dhcpclient.c \
- dhcpmsg.c \
- ifc_utils.c \
- packet.c
-
-LOCAL_SHARED_LIBRARIES := \
- libcutils \
- liblog
-
-LOCAL_MODULE := libnetutils
-
-LOCAL_CFLAGS := -Werror
-
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/libnetutils/dhcptool.c b/libnetutils/dhcptool.c
new file mode 100644
index 0000000..280b977
--- /dev/null
+++ b/libnetutils/dhcptool.c
@@ -0,0 +1,47 @@
+/*
+ * 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 <err.h>
+#include <errno.h>
+#include <error.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include <netutils/ifc.h>
+
+extern int do_dhcp(char*);
+
+int main(int argc, char* argv[]) {
+ if (argc != 2) {
+ error(EXIT_FAILURE, 0, "usage: %s INTERFACE", argv[0]);
+ }
+
+ char* interface = argv[1];
+ if (ifc_init()) {
+ err(errno, "dhcptool %s: ifc_init failed", interface);
+ ifc_close();
+ return EXIT_FAILURE;
+ }
+
+ int rc = do_dhcp(interface);
+ if (rc) {
+ err(errno, "dhcptool %s: do_dhcp failed", interface);
+ }
+ warn("IP assignment is for debug purposes ONLY");
+ ifc_close();
+
+ return rc ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/libprocessgroup/Android.bp b/libprocessgroup/Android.bp
new file mode 100644
index 0000000..1974f2c
--- /dev/null
+++ b/libprocessgroup/Android.bp
@@ -0,0 +1,11 @@
+cc_library {
+ srcs: ["processgroup.cpp"],
+ name: "libprocessgroup",
+ defaults: ["linux_bionic_supported"],
+ shared_libs: ["libbase"],
+ export_include_dirs: ["include"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+}
diff --git a/libprocessgroup/Android.mk b/libprocessgroup/Android.mk
deleted file mode 100644
index 0bfc391..0000000
--- a/libprocessgroup/Android.mk
+++ /dev/null
@@ -1,19 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := processgroup.cpp
-LOCAL_MODULE := libprocessgroup
-LOCAL_STATIC_LIBRARIES := libbase
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_CFLAGS := -Wall -Werror
-include $(BUILD_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := processgroup.cpp
-LOCAL_MODULE := libprocessgroup
-LOCAL_SHARED_LIBRARIES := libbase
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_CFLAGS := -Wall -Werror
-include $(BUILD_SHARED_LIBRARY)
diff --git a/libprocessgroup/include/processgroup/processgroup.h b/libprocessgroup/include/processgroup/processgroup.h
index 47f6ff3..f0c3795 100644
--- a/libprocessgroup/include/processgroup/processgroup.h
+++ b/libprocessgroup/include/processgroup/processgroup.h
@@ -22,8 +22,13 @@
__BEGIN_DECLS
+// Return 0 and removes the cgroup if there are no longer any processes in it.
+// Returns -1 in the case of an error occurring or if there are processes still running
+// even after retrying for up to 200ms.
int killProcessGroup(uid_t uid, int initialPid, int signal);
+// Returns the same as killProcessGroup(), however it does not retry, which means
+// that it only returns 0 in the case that the cgroup exists and it contains no processes.
int killProcessGroupOnce(uid_t uid, int initialPid, int signal);
int createProcessGroup(uid_t uid, int initialPid);
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 1572cb3..27b4065 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -34,6 +34,7 @@
#include <thread>
#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
#include <private/android_filesystem_config.h>
#include <processgroup/processgroup.h>
@@ -64,12 +65,27 @@
std::once_flag init_path_flag;
-struct ctx {
- bool initialized;
- int fd;
- char buf[128];
- char *buf_ptr;
- size_t buf_len;
+class ProcessGroup {
+ public:
+ ProcessGroup() : buf_ptr_(buf_), buf_len_(0) {}
+
+ bool Open(uid_t uid, int pid);
+
+ // Return positive number and sets *pid = next pid in process cgroup on success
+ // Returns 0 if there are no pids left in the process cgroup
+ // Returns -errno if an error was encountered
+ int GetOneAppProcess(pid_t* pid);
+
+ private:
+ // Returns positive number of bytes filled on success
+ // Returns 0 if there was nothing to read
+ // Returns -errno if an error was encountered
+ int RefillBuffer();
+
+ android::base::unique_fd fd_;
+ char buf_[128];
+ char* buf_ptr_;
+ size_t buf_len_;
};
static const char* getCgroupRootPath() {
@@ -105,87 +121,67 @@
pid);
}
-static int initCtx(uid_t uid, int pid, struct ctx *ctx)
-{
- int ret;
+bool ProcessGroup::Open(uid_t uid, int pid) {
char path[PROCESSGROUP_MAX_PATH_LEN] = {0};
convertUidPidToPath(path, sizeof(path), uid, pid);
strlcat(path, PROCESSGROUP_CGROUP_PROCS_FILE, sizeof(path));
int fd = open(path, O_RDONLY);
- if (fd < 0) {
- ret = -errno;
- PLOG(WARNING) << "failed to open " << path;
- return ret;
- }
+ if (fd < 0) return false;
- ctx->fd = fd;
- ctx->buf_ptr = ctx->buf;
- ctx->buf_len = 0;
- ctx->initialized = true;
+ fd_.reset(fd);
LOG(VERBOSE) << "Initialized context for " << path;
- return 0;
+ return true;
}
-static int refillBuffer(struct ctx *ctx)
-{
- memmove(ctx->buf, ctx->buf_ptr, ctx->buf_len);
- ctx->buf_ptr = ctx->buf;
+int ProcessGroup::RefillBuffer() {
+ memmove(buf_, buf_ptr_, buf_len_);
+ buf_ptr_ = buf_;
- ssize_t ret = read(ctx->fd, ctx->buf_ptr + ctx->buf_len,
- sizeof(ctx->buf) - ctx->buf_len - 1);
+ ssize_t ret = read(fd_, buf_ptr_ + buf_len_, sizeof(buf_) - buf_len_ - 1);
if (ret < 0) {
return -errno;
} else if (ret == 0) {
return 0;
}
- ctx->buf_len += ret;
- ctx->buf[ctx->buf_len] = 0;
- LOG(VERBOSE) << "Read " << ret << " to buffer: " << ctx->buf;
+ buf_len_ += ret;
+ buf_[buf_len_] = 0;
+ LOG(VERBOSE) << "Read " << ret << " to buffer: " << buf_;
- assert(ctx->buf_len <= sizeof(ctx->buf));
+ assert(buf_len_ <= sizeof(buf_));
return ret;
}
-static pid_t getOneAppProcess(uid_t uid, int appProcessPid, struct ctx *ctx)
-{
- if (!ctx->initialized) {
- int ret = initCtx(uid, appProcessPid, ctx);
- if (ret < 0) {
- return ret;
- }
- }
+int ProcessGroup::GetOneAppProcess(pid_t* out_pid) {
+ *out_pid = 0;
- char *eptr;
- while ((eptr = (char *)memchr(ctx->buf_ptr, '\n', ctx->buf_len)) == NULL) {
- int ret = refillBuffer(ctx);
- if (ret == 0) {
- return -ERANGE;
- }
- if (ret < 0) {
- return ret;
- }
+ char* eptr;
+ while ((eptr = static_cast<char*>(memchr(buf_ptr_, '\n', buf_len_))) == nullptr) {
+ int ret = RefillBuffer();
+ if (ret <= 0) return ret;
}
*eptr = '\0';
- char *pid_eptr = NULL;
+ char* pid_eptr = nullptr;
errno = 0;
- long pid = strtol(ctx->buf_ptr, &pid_eptr, 10);
+ long pid = strtol(buf_ptr_, &pid_eptr, 10);
if (errno != 0) {
return -errno;
}
if (pid_eptr != eptr) {
- return -EINVAL;
+ errno = EINVAL;
+ return -errno;
}
- ctx->buf_len -= (eptr - ctx->buf_ptr) + 1;
- ctx->buf_ptr = eptr + 1;
+ buf_len_ -= (eptr - buf_ptr_) + 1;
+ buf_ptr_ = eptr + 1;
- return (pid_t)pid;
+ *out_pid = static_cast<pid_t>(pid);
+ return 1;
}
static int removeProcessGroup(uid_t uid, int pid)
@@ -219,8 +215,8 @@
}
snprintf(path, sizeof(path), "%s/%s", uid_path, dir->d_name);
- LOG(VERBOSE) << "removing " << path;
- if (rmdir(path) == -1) PLOG(WARNING) << "failed to remove " << path;
+ LOG(VERBOSE) << "Removing " << path;
+ if (rmdir(path) == -1) PLOG(WARNING) << "Failed to remove " << path;
}
}
}
@@ -231,7 +227,7 @@
const char* cgroup_root_path = getCgroupRootPath();
std::unique_ptr<DIR, decltype(&closedir)> root(opendir(cgroup_root_path), closedir);
if (root == NULL) {
- PLOG(ERROR) << "failed to open " << cgroup_root_path;
+ PLOG(ERROR) << "Failed to open " << cgroup_root_path;
} else {
dirent* dir;
while ((dir = readdir(root.get())) != nullptr) {
@@ -246,20 +242,26 @@
snprintf(path, sizeof(path), "%s/%s", cgroup_root_path, dir->d_name);
removeUidProcessGroups(path);
- LOG(VERBOSE) << "removing " << path;
- if (rmdir(path) == -1) PLOG(WARNING) << "failed to remove " << path;
+ LOG(VERBOSE) << "Removing " << path;
+ if (rmdir(path) == -1) PLOG(WARNING) << "Failed to remove " << path;
}
}
}
+// Returns number of processes killed on success
+// Returns 0 if there are no processes in the process cgroup left to kill
+// Returns -errno on error
static int doKillProcessGroupOnce(uid_t uid, int initialPid, int signal) {
- int processes = 0;
- struct ctx ctx;
+ ProcessGroup process_group;
+ if (!process_group.Open(uid, initialPid)) {
+ PLOG(WARNING) << "Failed to open process cgroup uid " << uid << " pid " << initialPid;
+ return -errno;
+ }
+
+ int ret;
pid_t pid;
-
- ctx.initialized = false;
-
- while ((pid = getOneAppProcess(uid, initialPid, &ctx)) >= 0) {
+ int processes = 0;
+ while ((ret = process_group.GetOneAppProcess(&pid)) > 0 && pid >= 0) {
processes++;
if (pid == 0) {
// Should never happen... but if it does, trying to kill this
@@ -267,55 +269,68 @@
LOG(WARNING) << "Yikes, we've been told to kill pid 0! How about we don't do that?";
continue;
}
- LOG(VERBOSE) << "Killing pid " << pid << " in uid " << uid
- << " as part of process group " << initialPid;
+ LOG(VERBOSE) << "Killing pid " << pid << " in uid " << uid << " as part of process cgroup "
+ << initialPid;
if (kill(pid, signal) == -1) {
PLOG(WARNING) << "kill(" << pid << ", " << signal << ") failed";
}
}
- if (ctx.initialized) {
- close(ctx.fd);
- }
-
- return processes;
+ return ret >= 0 ? processes : ret;
}
-static int killProcessGroup(uid_t uid, int initialPid, int signal, int retry) {
+static int killProcessGroup(uid_t uid, int initialPid, int signal, int retries) {
std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
+ int retry = retries;
int processes;
while ((processes = doKillProcessGroupOnce(uid, initialPid, signal)) > 0) {
- LOG(VERBOSE) << "killed " << processes << " processes for processgroup " << initialPid;
+ LOG(VERBOSE) << "Killed " << processes << " processes for processgroup " << initialPid;
if (retry > 0) {
std::this_thread::sleep_for(5ms);
--retry;
} else {
- LOG(ERROR) << "failed to kill " << processes << " processes for processgroup "
- << initialPid;
break;
}
}
- std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
+ if (processes < 0) {
+ PLOG(ERROR) << "Error encountered killing process cgroup uid " << uid << " pid "
+ << initialPid;
+ return -1;
+ }
+ std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
- LOG(VERBOSE) << "Killed process group uid " << uid << " pid " << initialPid << " in "
- << static_cast<int>(ms) << "ms, " << processes << " procs remain";
+
+ // We only calculate the number of 'processes' when killing the processes.
+ // In the retries == 0 case, we only kill the processes once and therefore
+ // will not have waited then recalculated how many processes are remaining
+ // after the first signals have been sent.
+ // Logging anything regarding the number of 'processes' here does not make sense.
if (processes == 0) {
+ if (retries > 0) {
+ LOG(INFO) << "Successfully killed process cgroup uid " << uid << " pid " << initialPid
+ << " in " << static_cast<int>(ms) << "ms";
+ }
return removeProcessGroup(uid, initialPid);
} else {
+ if (retries > 0) {
+ LOG(ERROR) << "Failed to kill process cgroup uid " << uid << " pid " << initialPid
+ << " in " << static_cast<int>(ms) << "ms, " << processes
+ << " processes remain";
+ }
return -1;
}
}
int killProcessGroup(uid_t uid, int initialPid, int signal) {
- return killProcessGroup(uid, initialPid, signal, 40 /*maxRetry*/);
+ return killProcessGroup(uid, initialPid, signal, 40 /*retries*/);
}
int killProcessGroupOnce(uid_t uid, int initialPid, int signal) {
- return killProcessGroup(uid, initialPid, signal, 0 /*maxRetry*/);
+ return killProcessGroup(uid, initialPid, signal, 0 /*retries*/);
}
static bool mkdirAndChown(const char *path, mode_t mode, uid_t uid, gid_t gid)
@@ -341,14 +356,14 @@
convertUidToPath(path, sizeof(path), uid);
if (!mkdirAndChown(path, 0750, AID_SYSTEM, AID_SYSTEM)) {
- PLOG(ERROR) << "failed to make and chown " << path;
+ PLOG(ERROR) << "Failed to make and chown " << path;
return -errno;
}
convertUidPidToPath(path, sizeof(path), uid, initialPid);
if (!mkdirAndChown(path, 0750, AID_SYSTEM, AID_SYSTEM)) {
- PLOG(ERROR) << "failed to make and chown " << path;
+ PLOG(ERROR) << "Failed to make and chown " << path;
return -errno;
}
@@ -357,7 +372,7 @@
int fd = open(path, O_WRONLY);
if (fd == -1) {
int ret = -errno;
- PLOG(ERROR) << "failed to open " << path;
+ PLOG(ERROR) << "Failed to open " << path;
return ret;
}
@@ -367,7 +382,7 @@
int ret = 0;
if (write(fd, pid, len) < 0) {
ret = -errno;
- PLOG(ERROR) << "failed to write '" << pid << "' to " << path;
+ PLOG(ERROR) << "Failed to write '" << pid << "' to " << path;
}
close(fd);
diff --git a/libprocinfo/Android.bp b/libprocinfo/Android.bp
index c13ffe9..aedaa38 100644
--- a/libprocinfo/Android.bp
+++ b/libprocinfo/Android.bp
@@ -22,6 +22,7 @@
cc_library {
name: "libprocinfo",
+ vendor_available: true,
host_supported: true,
srcs: [
"process.cpp",
diff --git a/libsparse/Android.bp b/libsparse/Android.bp
index dd8b5fd..6ec0991 100644
--- a/libsparse/Android.bp
+++ b/libsparse/Android.bp
@@ -10,17 +10,23 @@
"sparse.c",
"sparse_crc32.c",
"sparse_err.c",
- "sparse_read.c",
+ "sparse_read.cpp",
],
cflags: ["-Werror"],
local_include_dirs: ["include"],
export_include_dirs: ["include"],
target: {
host: {
- shared_libs: ["libz-host"],
+ shared_libs: [
+ "libz-host",
+ "libbase",
+ ],
},
android: {
- shared_libs: ["libz"],
+ shared_libs: [
+ "libz",
+ "libbase",
+ ],
},
windows: {
enabled: true,
@@ -38,6 +44,7 @@
static_libs: [
"libsparse",
"libz",
+ "libbase",
],
cflags: ["-Werror"],
@@ -50,6 +57,7 @@
static_libs: [
"libsparse",
"libz",
+ "libbase",
],
cflags: ["-Werror"],
@@ -61,6 +69,7 @@
static_libs: [
"libsparse",
"libz",
+ "libbase",
],
cflags: ["-Werror"],
diff --git a/libsparse/output_file.c b/libsparse/output_file.c
index 2115998..51e60ef 100644
--- a/libsparse/output_file.c
+++ b/libsparse/output_file.c
@@ -584,7 +584,7 @@
.file_hdr_sz = SPARSE_HEADER_LEN,
.chunk_hdr_sz = CHUNK_HEADER_LEN,
.blk_sz = out->block_size,
- .total_blks = out->len / out->block_size,
+ .total_blks = DIV_ROUND_UP(out->len, out->block_size),
.total_chunks = chunks,
.image_checksum = 0
};
diff --git a/libsparse/output_file.h b/libsparse/output_file.h
index 474c1fc..b67e94e 100644
--- a/libsparse/output_file.h
+++ b/libsparse/output_file.h
@@ -17,6 +17,10 @@
#ifndef _OUTPUT_FILE_H_
#define _OUTPUT_FILE_H_
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#include <sparse/sparse.h>
struct output_file;
@@ -38,4 +42,8 @@
int read_all(int fd, void *buf, size_t len);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/libsparse/sparse_file.h b/libsparse/sparse_file.h
index 91a12e6..763f43f 100644
--- a/libsparse/sparse_file.h
+++ b/libsparse/sparse_file.h
@@ -17,6 +17,10 @@
#ifndef _LIBSPARSE_SPARSE_FILE_H_
#define _LIBSPARSE_SPARSE_FILE_H_
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#include <sparse/sparse.h>
struct sparse_file {
@@ -28,5 +32,8 @@
struct output_file *out;
};
+#ifdef __cplusplus
+}
+#endif
#endif /* _LIBSPARSE_SPARSE_FILE_H_ */
diff --git a/libsparse/sparse_format.h b/libsparse/sparse_format.h
index c41f12a..779e038 100644
--- a/libsparse/sparse_format.h
+++ b/libsparse/sparse_format.h
@@ -18,6 +18,10 @@
#define _LIBSPARSE_SPARSE_FORMAT_H_
#include "sparse_defs.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
typedef struct sparse_header {
__le32 magic; /* 0xed26ff3a */
__le16 major_version; /* (0x1) - reject images with higher major versions */
@@ -52,4 +56,8 @@
* For a CRC32 chunk, it's 4 bytes of CRC32
*/
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/libsparse/sparse_read.c b/libsparse/sparse_read.cpp
similarity index 88%
rename from libsparse/sparse_read.c
rename to libsparse/sparse_read.cpp
index a188202..bd66873 100644
--- a/libsparse/sparse_read.c
+++ b/libsparse/sparse_read.cpp
@@ -14,10 +14,10 @@
* limitations under the License.
*/
-#define _GNU_SOURCE
#define _FILE_OFFSET_BITS 64
#define _LARGEFILE64_SOURCE 1
+#include <algorithm>
#include <inttypes.h>
#include <fcntl.h>
#include <stdarg.h>
@@ -25,17 +25,19 @@
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
-#include <string.h>
+#include <string>
#include <unistd.h>
#include <sparse/sparse.h>
+#include "android-base/stringprintf.h"
#include "defs.h"
#include "output_file.h"
#include "sparse_crc32.h"
#include "sparse_file.h"
#include "sparse_format.h"
+
#if defined(__APPLE__) && defined(__MACH__)
#define lseek64 lseek
#define off64_t off_t
@@ -45,57 +47,30 @@
#define SPARSE_HEADER_LEN (sizeof(sparse_header_t))
#define CHUNK_HEADER_LEN (sizeof(chunk_header_t))
-#define COPY_BUF_SIZE (1024U*1024U)
+static constexpr int64_t COPY_BUF_SIZE = 1024 * 1024;
static char *copybuf;
-#define min(a, b) \
- ({ typeof(a) _a = (a); typeof(b) _b = (b); (_a < _b) ? _a : _b; })
+static std::string ErrorString(int err)
+{
+ if (err == -EOVERFLOW) return "EOF while reading file";
+ if (err == -EINVAL) return "Invalid sparse file format";
+ if (err == -ENOMEM) return "Failed allocation while reading file";
+ return android::base::StringPrintf("Unknown error %d", err);
+}
static void verbose_error(bool verbose, int err, const char *fmt, ...)
{
- char *s = "";
- char *at = "";
+ if (!verbose) return;
+
+ std::string msg = ErrorString(err);
if (fmt) {
+ msg += " at ";
va_list argp;
- int size;
-
va_start(argp, fmt);
- size = vsnprintf(NULL, 0, fmt, argp);
+ android::base::StringAppendV(&msg, fmt, argp);
va_end(argp);
-
- if (size < 0) {
- return;
- }
-
- at = malloc(size + 1);
- if (at == NULL) {
- return;
- }
-
- va_start(argp, fmt);
- vsnprintf(at, size, fmt, argp);
- va_end(argp);
- at[size] = 0;
- s = " at ";
}
- if (verbose) {
-#ifndef _WIN32
- if (err == -EOVERFLOW) {
- sparse_print_verbose("EOF while reading file%s%s\n", s, at);
- } else
-#endif
- if (err == -EINVAL) {
- sparse_print_verbose("Invalid sparse file format%s%s\n", s, at);
- } else if (err == -ENOMEM) {
- sparse_print_verbose("Failed allocation while reading file%s%s\n",
- s, at);
- } else {
- sparse_print_verbose("Unknown error %d%s%s\n", err, s, at);
- }
- }
- if (fmt) {
- free(at);
- }
+ sparse_print_verbose("%s\n", msg.c_str());
}
static int process_raw_chunk(struct sparse_file *s, unsigned int chunk_size,
@@ -104,7 +79,7 @@
{
int ret;
int chunk;
- unsigned int len = blocks * s->block_size;
+ int64_t len = blocks * s->block_size;
if (chunk_size % s->block_size != 0) {
return -EINVAL;
@@ -121,7 +96,7 @@
if (crc32) {
while (len) {
- chunk = min(len, COPY_BUF_SIZE);
+ chunk = std::min(len, COPY_BUF_SIZE);
ret = read_all(fd, copybuf, chunk);
if (ret < 0) {
return ret;
@@ -168,7 +143,7 @@
}
while (len) {
- chunk = min(len, COPY_BUF_SIZE);
+ chunk = std::min(len, COPY_BUF_SIZE);
*crc32 = sparse_crc32(*crc32, copybuf, chunk);
len -= chunk;
}
@@ -190,7 +165,7 @@
memset(copybuf, 0, COPY_BUF_SIZE);
while (len) {
- int chunk = min(len, COPY_BUF_SIZE);
+ int chunk = std::min(len, COPY_BUF_SIZE);
*crc32 = sparse_crc32(*crc32, copybuf, chunk);
len -= chunk;
}
@@ -284,7 +259,7 @@
off64_t offset;
if (!copybuf) {
- copybuf = malloc(COPY_BUF_SIZE);
+ copybuf = (char *)malloc(COPY_BUF_SIZE);
}
if (!copybuf) {
@@ -357,7 +332,7 @@
static int sparse_file_read_normal(struct sparse_file *s, int fd)
{
int ret;
- uint32_t *buf = malloc(s->block_size);
+ uint32_t *buf = (uint32_t *)malloc(s->block_size);
unsigned int block = 0;
int64_t remain = s->len;
int64_t offset = 0;
@@ -370,7 +345,7 @@
}
while (remain > 0) {
- to_read = min(remain, s->block_size);
+ to_read = std::min(remain, (int64_t)(s->block_size));
ret = read_all(fd, buf, to_read);
if (ret < 0) {
error("failed to read sparse file");
diff --git a/libsuspend/Android.bp b/libsuspend/Android.bp
index d442c94..130800e 100644
--- a/libsuspend/Android.bp
+++ b/libsuspend/Android.bp
@@ -2,6 +2,8 @@
cc_library {
name: "libsuspend",
+ vendor_available: true,
+
srcs: [
"autosuspend.c",
"autosuspend_wakeup_count.c",
diff --git a/libsuspend/autosuspend_wakeup_count.c b/libsuspend/autosuspend_wakeup_count.c
index 4dedf7f..2da204a 100644
--- a/libsuspend/autosuspend_wakeup_count.c
+++ b/libsuspend/autosuspend_wakeup_count.c
@@ -24,6 +24,7 @@
#include <stddef.h>
#include <stdbool.h>
#include <string.h>
+#include <sys/param.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
@@ -35,12 +36,24 @@
#define SYS_POWER_STATE "/sys/power/state"
#define SYS_POWER_WAKEUP_COUNT "/sys/power/wakeup_count"
+#define BASE_SLEEP_TIME 100000
+
static int state_fd;
static int wakeup_count_fd;
static pthread_t suspend_thread;
static sem_t suspend_lockout;
static const char *sleep_state = "mem";
static void (*wakeup_func)(bool success) = NULL;
+static int sleep_time = BASE_SLEEP_TIME;
+
+static void update_sleep_time(bool success) {
+ if (success) {
+ sleep_time = BASE_SLEEP_TIME;
+ return;
+ }
+ // double sleep time after each failure up to one minute
+ sleep_time = MIN(sleep_time * 2, 60000000);
+}
static void *suspend_thread_func(void *arg __attribute__((unused)))
{
@@ -48,10 +61,12 @@
char wakeup_count[20];
int wakeup_count_len;
int ret;
- bool success;
+ bool success = true;
while (1) {
- usleep(100000);
+ update_sleep_time(success);
+ usleep(sleep_time);
+ success = false;
ALOGV("%s: read wakeup_count\n", __func__);
lseek(wakeup_count_fd, 0, SEEK_SET);
wakeup_count_len = TEMP_FAILURE_RETRY(read(wakeup_count_fd, wakeup_count,
@@ -75,7 +90,6 @@
continue;
}
- success = true;
ALOGV("%s: write %*s to wakeup_count\n", __func__, wakeup_count_len, wakeup_count);
ret = TEMP_FAILURE_RETRY(write(wakeup_count_fd, wakeup_count, wakeup_count_len));
if (ret < 0) {
@@ -84,8 +98,8 @@
} else {
ALOGV("%s: write %s to %s\n", __func__, sleep_state, SYS_POWER_STATE);
ret = TEMP_FAILURE_RETRY(write(state_fd, sleep_state, strlen(sleep_state)));
- if (ret < 0) {
- success = false;
+ if (ret >= 0) {
+ success = true;
}
void (*func)(bool success) = wakeup_func;
if (func != NULL) {
diff --git a/libsync/Android.bp b/libsync/Android.bp
index b293da4..257d42d 100644
--- a/libsync/Android.bp
+++ b/libsync/Android.bp
@@ -7,7 +7,7 @@
}
ndk_library {
- name: "libsync.ndk",
+ name: "libsync",
symbol_file: "libsync.map.txt",
first_version: "26",
}
@@ -22,6 +22,7 @@
cc_library_shared {
name: "libsync",
+ vendor_available: true,
defaults: ["libsync_defaults"],
}
diff --git a/libsync/include/ndk/sync.h b/libsync/include/ndk/sync.h
index 758a106..3c55783 100644
--- a/libsync/include/ndk/sync.h
+++ b/libsync/include/ndk/sync.h
@@ -69,8 +69,7 @@
* The returned array is owned by the parent sync file info, and has
* info->num_fences entries.
*/
-inline struct sync_fence_info *sync_get_fence_info(
- const struct sync_file_info *info) {
+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
diff --git a/libsystem/Android.bp b/libsystem/Android.bp
new file mode 100644
index 0000000..846a585
--- /dev/null
+++ b/libsystem/Android.bp
@@ -0,0 +1,15 @@
+cc_library_headers {
+ name: "libsystem_headers",
+ vendor_available: true,
+ host_supported: true,
+ export_include_dirs: ["include"],
+
+ target: {
+ linux_bionic: {
+ enabled: true,
+ },
+ windows: {
+ enabled: true,
+ },
+ }
+}
diff --git a/include/system/camera.h b/libsystem/include/system/camera.h
similarity index 100%
rename from include/system/camera.h
rename to libsystem/include/system/camera.h
diff --git a/include/system/graphics-base.h b/libsystem/include/system/graphics-base.h
similarity index 100%
rename from include/system/graphics-base.h
rename to libsystem/include/system/graphics-base.h
diff --git a/include/system/graphics.h b/libsystem/include/system/graphics.h
similarity index 100%
rename from include/system/graphics.h
rename to libsystem/include/system/graphics.h
diff --git a/include/system/radio.h b/libsystem/include/system/radio.h
similarity index 100%
rename from include/system/radio.h
rename to libsystem/include/system/radio.h
diff --git a/include/system/thread_defs.h b/libsystem/include/system/thread_defs.h
similarity index 100%
rename from include/system/thread_defs.h
rename to libsystem/include/system/thread_defs.h
diff --git a/libsysutils/Android.bp b/libsysutils/Android.bp
new file mode 100644
index 0000000..550ef42
--- /dev/null
+++ b/libsysutils/Android.bp
@@ -0,0 +1,27 @@
+cc_library_shared {
+ name: "libsysutils",
+ vendor_available: true,
+
+ srcs: [
+ "src/SocketListener.cpp",
+ "src/FrameworkListener.cpp",
+ "src/NetlinkListener.cpp",
+ "src/NetlinkEvent.cpp",
+ "src/FrameworkCommand.cpp",
+ "src/SocketClient.cpp",
+ "src/ServiceManager.cpp",
+ ],
+
+ logtags: ["EventLogTags.logtags"],
+
+ cflags: ["-Werror"],
+
+ shared_libs: [
+ "libbase",
+ "libcutils",
+ "liblog",
+ "libnl",
+ ],
+
+ export_include_dirs: ["include"],
+}
diff --git a/libsysutils/Android.mk b/libsysutils/Android.mk
deleted file mode 100644
index 584e5a2..0000000
--- a/libsysutils/Android.mk
+++ /dev/null
@@ -1,27 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- src/SocketListener.cpp \
- src/FrameworkListener.cpp \
- src/NetlinkListener.cpp \
- src/NetlinkEvent.cpp \
- src/FrameworkCommand.cpp \
- src/SocketClient.cpp \
- src/ServiceManager.cpp \
- EventLogTags.logtags
-
-LOCAL_MODULE:= libsysutils
-
-LOCAL_CFLAGS := -Werror
-
-LOCAL_SHARED_LIBRARIES := \
- libbase \
- libcutils \
- liblog \
- libnl
-
-LOCAL_EXPORT_C_INCLUDE_DIRS := system/core/libsysutils/include
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index ee646de..32ed6c3 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -47,6 +47,7 @@
srcs: [
"ArmExidx.cpp",
+ "DwarfCfa.cpp",
"DwarfMemory.cpp",
"DwarfOp.cpp",
"Elf.cpp",
@@ -54,6 +55,8 @@
"ElfInterfaceArm.cpp",
"Log.cpp",
"Regs.cpp",
+ "MapInfo.cpp",
+ "Maps.cpp",
"Memory.cpp",
"Symbols.cpp",
],
@@ -90,6 +93,8 @@
srcs: [
"tests/ArmExidxDecodeTest.cpp",
"tests/ArmExidxExtractTest.cpp",
+ "tests/DwarfCfaLogTest.cpp",
+ "tests/DwarfCfaTest.cpp",
"tests/DwarfMemoryTest.cpp",
"tests/DwarfOpLogTest.cpp",
"tests/DwarfOpTest.cpp",
@@ -97,6 +102,8 @@
"tests/ElfInterfaceTest.cpp",
"tests/ElfTest.cpp",
"tests/LogFake.cpp",
+ "tests/MapInfoTest.cpp",
+ "tests/MapsTest.cpp",
"tests/MemoryFake.cpp",
"tests/MemoryFileTest.cpp",
"tests/MemoryLocalTest.cpp",
diff --git a/libunwindstack/DwarfCfa.cpp b/libunwindstack/DwarfCfa.cpp
new file mode 100644
index 0000000..006f039
--- /dev/null
+++ b/libunwindstack/DwarfCfa.cpp
@@ -0,0 +1,713 @@
+/*
+ * 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 <inttypes.h>
+#include <stdint.h>
+
+#include <string>
+#include <type_traits>
+#include <vector>
+
+#include <android-base/stringprintf.h>
+
+#include "DwarfCfa.h"
+#include "DwarfEncoding.h"
+#include "DwarfMemory.h"
+#include "DwarfOp.h"
+#include "DwarfStructs.h"
+#include "Log.h"
+
+template <typename AddressType>
+constexpr typename DwarfCfa<AddressType>::process_func DwarfCfa<AddressType>::kCallbackTable[64];
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::GetLocationInfo(uint64_t pc, uint64_t start_offset, uint64_t end_offset,
+ dwarf_loc_regs_t* loc_regs) {
+ if (cie_loc_regs_ != nullptr) {
+ for (const auto& entry : *cie_loc_regs_) {
+ (*loc_regs)[entry.first] = entry.second;
+ }
+ }
+ last_error_ = DWARF_ERROR_NONE;
+
+ memory_->set_cur_offset(start_offset);
+ uint64_t cfa_offset;
+ cur_pc_ = fde_->pc_start;
+ while ((cfa_offset = memory_->cur_offset()) < end_offset && cur_pc_ <= pc) {
+ operands_.clear();
+ // Read the cfa information.
+ uint8_t cfa_value;
+ if (!memory_->ReadBytes(&cfa_value, 1)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ uint8_t cfa_low = cfa_value & 0x3f;
+ // Check the 2 high bits.
+ switch (cfa_value >> 6) {
+ case 1:
+ cur_pc_ += cfa_low * fde_->cie->code_alignment_factor;
+ break;
+ case 2: {
+ uint64_t offset;
+ if (!memory_->ReadULEB128(&offset)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ SignedType signed_offset =
+ static_cast<SignedType>(offset) * fde_->cie->data_alignment_factor;
+ (*loc_regs)[cfa_low] = {.type = DWARF_LOCATION_OFFSET,
+ .values = {static_cast<uint64_t>(signed_offset)}};
+ break;
+ }
+ case 3: {
+ if (cie_loc_regs_ == nullptr) {
+ log(0, "restore while processing cie");
+ last_error_ = DWARF_ERROR_ILLEGAL_STATE;
+ return false;
+ }
+
+ auto reg_entry = cie_loc_regs_->find(cfa_low);
+ if (reg_entry == cie_loc_regs_->end()) {
+ loc_regs->erase(cfa_low);
+ } else {
+ (*loc_regs)[cfa_low] = reg_entry->second;
+ }
+ break;
+ }
+ case 0: {
+ const auto handle_func = DwarfCfa<AddressType>::kCallbackTable[cfa_low];
+ if (handle_func == nullptr) {
+ last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+
+ const auto cfa = &DwarfCfaInfo::kTable[cfa_low];
+ for (size_t i = 0; i < cfa->num_operands; i++) {
+ if (cfa->operands[i] == DW_EH_PE_block) {
+ uint64_t block_length;
+ if (!memory_->ReadULEB128(&block_length)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ operands_.push_back(block_length);
+ memory_->set_cur_offset(memory_->cur_offset() + block_length);
+ continue;
+ }
+ uint64_t value;
+ if (!memory_->ReadEncodedValue<AddressType>(cfa->operands[i], &value)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ operands_.push_back(value);
+ }
+
+ if (!(this->*handle_func)(loc_regs)) {
+ return false;
+ }
+ break;
+ }
+ }
+ }
+ return true;
+}
+
+template <typename AddressType>
+std::string DwarfCfa<AddressType>::GetOperandString(uint8_t operand, uint64_t value,
+ uint64_t* cur_pc) {
+ std::string string;
+ switch (operand) {
+ case DwarfCfaInfo::DWARF_DISPLAY_REGISTER:
+ string = " register(" + std::to_string(value) + ")";
+ break;
+ case DwarfCfaInfo::DWARF_DISPLAY_SIGNED_NUMBER:
+ string += " " + std::to_string(static_cast<SignedType>(value));
+ break;
+ case DwarfCfaInfo::DWARF_DISPLAY_ADVANCE_LOC:
+ *cur_pc += value;
+ // Fall through to log the value.
+ case DwarfCfaInfo::DWARF_DISPLAY_NUMBER:
+ string += " " + std::to_string(value);
+ break;
+ case DwarfCfaInfo::DWARF_DISPLAY_SET_LOC:
+ *cur_pc = value;
+ // Fall through to log the value.
+ case DwarfCfaInfo::DWARF_DISPLAY_ADDRESS:
+ if (std::is_same<AddressType, uint32_t>::value) {
+ string += android::base::StringPrintf(" 0x%" PRIx32, static_cast<uint32_t>(value));
+ } else {
+ string += android::base::StringPrintf(" 0x%" PRIx64, static_cast<uint64_t>(value));
+ }
+ break;
+ default:
+ string = " unknown";
+ }
+ return string;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::LogOffsetRegisterString(uint32_t indent, uint64_t cfa_offset,
+ uint8_t reg) {
+ uint64_t offset;
+ if (!memory_->ReadULEB128(&offset)) {
+ return false;
+ }
+ uint64_t end_offset = memory_->cur_offset();
+ memory_->set_cur_offset(cfa_offset);
+
+ std::string raw_data = "Raw Data:";
+ for (uint64_t i = cfa_offset; i < end_offset; i++) {
+ uint8_t value;
+ if (!memory_->ReadBytes(&value, 1)) {
+ return false;
+ }
+ raw_data += android::base::StringPrintf(" 0x%02x", value);
+ }
+ log(indent, "DW_CFA_offset register(%d) %" PRId64, reg, offset);
+ log(indent, "%s", raw_data.c_str());
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::LogInstruction(uint32_t indent, uint64_t cfa_offset, uint8_t op,
+ uint64_t* cur_pc) {
+ const auto* cfa = &DwarfCfaInfo::kTable[op];
+ if (cfa->name == nullptr) {
+ log(indent, "Illegal");
+ log(indent, "Raw Data: 0x%02x", op);
+ return true;
+ }
+
+ std::string log_string(cfa->name);
+ std::vector<std::string> expression_lines;
+ for (size_t i = 0; i < cfa->num_operands; i++) {
+ if (cfa->operands[i] == DW_EH_PE_block) {
+ // This is a Dwarf Expression.
+ uint64_t end_offset;
+ if (!memory_->ReadULEB128(&end_offset)) {
+ return false;
+ }
+ log_string += " " + std::to_string(end_offset);
+ end_offset += memory_->cur_offset();
+
+ DwarfOp<AddressType> op(memory_, nullptr);
+ op.GetLogInfo(memory_->cur_offset(), end_offset, &expression_lines);
+ memory_->set_cur_offset(end_offset);
+ } else {
+ uint64_t value;
+ if (!memory_->ReadEncodedValue<AddressType>(cfa->operands[i], &value)) {
+ return false;
+ }
+ log_string += GetOperandString(cfa->display_operands[i], value, cur_pc);
+ }
+ }
+ log(indent, "%s", log_string.c_str());
+
+ // Get the raw bytes of the data.
+ uint64_t end_offset = memory_->cur_offset();
+ memory_->set_cur_offset(cfa_offset);
+ std::string raw_data("Raw Data:");
+ for (uint64_t i = 0; i < end_offset - cfa_offset; i++) {
+ uint8_t value;
+ if (!memory_->ReadBytes(&value, 1)) {
+ return false;
+ }
+
+ // Only show 10 raw bytes per line.
+ if ((i % 10) == 0 && i != 0) {
+ log(indent, "%s", raw_data.c_str());
+ raw_data.clear();
+ }
+ if (raw_data.empty()) {
+ raw_data = "Raw Data:";
+ }
+ raw_data += android::base::StringPrintf(" 0x%02x", value);
+ }
+ if (!raw_data.empty()) {
+ log(indent, "%s", raw_data.c_str());
+ }
+
+ // Log any of the expression data.
+ for (const auto line : expression_lines) {
+ log(indent + 1, "%s", line.c_str());
+ }
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::Log(uint32_t indent, uint64_t pc, uint64_t load_bias,
+ uint64_t start_offset, uint64_t end_offset) {
+ memory_->set_cur_offset(start_offset);
+ uint64_t cfa_offset;
+ uint64_t cur_pc = fde_->pc_start;
+ uint64_t old_pc = cur_pc;
+ while ((cfa_offset = memory_->cur_offset()) < end_offset && cur_pc <= pc) {
+ // Read the cfa information.
+ uint8_t cfa_value;
+ if (!memory_->ReadBytes(&cfa_value, 1)) {
+ return false;
+ }
+
+ // Check the 2 high bits.
+ uint8_t cfa_low = cfa_value & 0x3f;
+ switch (cfa_value >> 6) {
+ case 0:
+ if (!LogInstruction(indent, cfa_offset, cfa_low, &cur_pc)) {
+ return false;
+ }
+ break;
+ case 1:
+ log(indent, "DW_CFA_advance_loc %d", cfa_low);
+ log(indent, "Raw Data: 0x%02x", cfa_value);
+ cur_pc += cfa_low * fde_->cie->code_alignment_factor;
+ break;
+ case 2:
+ if (!LogOffsetRegisterString(indent, cfa_offset, cfa_low)) {
+ return false;
+ }
+ break;
+ case 3:
+ log(indent, "DW_CFA_restore register(%d)", cfa_low);
+ log(indent, "Raw Data: 0x%02x", cfa_value);
+ break;
+ }
+ if (cur_pc != old_pc) {
+ log(indent, "");
+ log(indent, "PC 0x%" PRIx64, cur_pc + load_bias);
+ }
+ old_pc = cur_pc;
+ }
+ return true;
+}
+
+// Static data.
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_nop(dwarf_loc_regs_t*) {
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_set_loc(dwarf_loc_regs_t*) {
+ AddressType cur_pc = cur_pc_;
+ AddressType new_pc = operands_[0];
+ if (new_pc < cur_pc) {
+ if (std::is_same<AddressType, uint32_t>::value) {
+ log(0, "Warning: PC is moving backwards: old 0x%" PRIx32 " new 0x%" PRIx32, cur_pc, new_pc);
+ } else {
+ log(0, "Warning: PC is moving backwards: old 0x%" PRIx64 " new 0x%" PRIx64, cur_pc, new_pc);
+ }
+ }
+ cur_pc_ = new_pc;
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_advance_loc(dwarf_loc_regs_t*) {
+ cur_pc_ += operands_[0] * fde_->cie->code_alignment_factor;
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_offset(dwarf_loc_regs_t* loc_regs) {
+ AddressType reg = operands_[0];
+ (*loc_regs)[reg] = {.type = DWARF_LOCATION_OFFSET, .values = {operands_[1]}};
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_restore(dwarf_loc_regs_t* loc_regs) {
+ AddressType reg = operands_[0];
+ if (cie_loc_regs_ == nullptr) {
+ log(0, "restore while processing cie");
+ last_error_ = DWARF_ERROR_ILLEGAL_STATE;
+ return false;
+ }
+ auto reg_entry = cie_loc_regs_->find(reg);
+ if (reg_entry == cie_loc_regs_->end()) {
+ loc_regs->erase(reg);
+ } else {
+ (*loc_regs)[reg] = reg_entry->second;
+ }
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_undefined(dwarf_loc_regs_t* loc_regs) {
+ AddressType reg = operands_[0];
+ (*loc_regs)[reg] = {.type = DWARF_LOCATION_UNDEFINED};
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_same_value(dwarf_loc_regs_t* loc_regs) {
+ AddressType reg = operands_[0];
+ loc_regs->erase(reg);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_register(dwarf_loc_regs_t* loc_regs) {
+ AddressType reg = operands_[0];
+ AddressType reg_dst = operands_[1];
+ (*loc_regs)[reg] = {.type = DWARF_LOCATION_REGISTER, .values = {reg_dst}};
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_remember_state(dwarf_loc_regs_t* loc_regs) {
+ loc_reg_state_.push(*loc_regs);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_restore_state(dwarf_loc_regs_t* loc_regs) {
+ if (loc_reg_state_.size() == 0) {
+ log(0, "Warning: Attempt to restore without remember.");
+ return true;
+ }
+ *loc_regs = loc_reg_state_.top();
+ loc_reg_state_.pop();
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_def_cfa(dwarf_loc_regs_t* loc_regs) {
+ (*loc_regs)[CFA_REG] = {.type = DWARF_LOCATION_REGISTER, .values = {operands_[0], operands_[1]}};
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_def_cfa_register(dwarf_loc_regs_t* loc_regs) {
+ auto cfa_location = loc_regs->find(CFA_REG);
+ if (cfa_location == loc_regs->end() || cfa_location->second.type != DWARF_LOCATION_REGISTER) {
+ log(0, "Attempt to set new register, but cfa is not already set to a register.");
+ last_error_ = DWARF_ERROR_ILLEGAL_STATE;
+ return false;
+ }
+
+ cfa_location->second.values[0] = operands_[0];
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_def_cfa_offset(dwarf_loc_regs_t* loc_regs) {
+ // Changing the offset if this is not a register is illegal.
+ auto cfa_location = loc_regs->find(CFA_REG);
+ if (cfa_location == loc_regs->end() || cfa_location->second.type != DWARF_LOCATION_REGISTER) {
+ log(0, "Attempt to set offset, but cfa is not set to a register.");
+ last_error_ = DWARF_ERROR_ILLEGAL_STATE;
+ return false;
+ }
+ cfa_location->second.values[1] = operands_[0];
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_def_cfa_expression(dwarf_loc_regs_t* loc_regs) {
+ (*loc_regs)[CFA_REG] = {.type = DWARF_LOCATION_EXPRESSION,
+ .values = {operands_[0], memory_->cur_offset()}};
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_expression(dwarf_loc_regs_t* loc_regs) {
+ AddressType reg = operands_[0];
+ (*loc_regs)[reg] = {.type = DWARF_LOCATION_EXPRESSION,
+ .values = {operands_[1], memory_->cur_offset()}};
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_offset_extended_sf(dwarf_loc_regs_t* loc_regs) {
+ AddressType reg = operands_[0];
+ SignedType value = static_cast<SignedType>(operands_[1]) * fde_->cie->data_alignment_factor;
+ (*loc_regs)[reg] = {.type = DWARF_LOCATION_OFFSET, .values = {static_cast<uint64_t>(value)}};
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_def_cfa_sf(dwarf_loc_regs_t* loc_regs) {
+ SignedType offset = static_cast<SignedType>(operands_[1]) * fde_->cie->data_alignment_factor;
+ (*loc_regs)[CFA_REG] = {.type = DWARF_LOCATION_REGISTER,
+ .values = {operands_[0], static_cast<uint64_t>(offset)}};
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_def_cfa_offset_sf(dwarf_loc_regs_t* loc_regs) {
+ // Changing the offset if this is not a register is illegal.
+ auto cfa_location = loc_regs->find(CFA_REG);
+ if (cfa_location == loc_regs->end() || cfa_location->second.type != DWARF_LOCATION_REGISTER) {
+ log(0, "Attempt to set offset, but cfa is not set to a register.");
+ last_error_ = DWARF_ERROR_ILLEGAL_STATE;
+ return false;
+ }
+ SignedType offset = static_cast<SignedType>(operands_[0]) * fde_->cie->data_alignment_factor;
+ cfa_location->second.values[1] = static_cast<uint64_t>(offset);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_val_offset(dwarf_loc_regs_t* loc_regs) {
+ AddressType reg = operands_[0];
+ SignedType offset = static_cast<SignedType>(operands_[1]) * fde_->cie->data_alignment_factor;
+ (*loc_regs)[reg] = {.type = DWARF_LOCATION_VAL_OFFSET, .values = {static_cast<uint64_t>(offset)}};
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_val_offset_sf(dwarf_loc_regs_t* loc_regs) {
+ AddressType reg = operands_[0];
+ SignedType offset = static_cast<SignedType>(operands_[1]) * fde_->cie->data_alignment_factor;
+ (*loc_regs)[reg] = {.type = DWARF_LOCATION_VAL_OFFSET, .values = {static_cast<uint64_t>(offset)}};
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_val_expression(dwarf_loc_regs_t* loc_regs) {
+ AddressType reg = operands_[0];
+ (*loc_regs)[reg] = {.type = DWARF_LOCATION_VAL_EXPRESSION,
+ .values = {operands_[1], memory_->cur_offset()}};
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_gnu_negative_offset_extended(dwarf_loc_regs_t* loc_regs) {
+ AddressType reg = operands_[0];
+ SignedType offset = -static_cast<SignedType>(operands_[1]);
+ (*loc_regs)[reg] = {.type = DWARF_LOCATION_OFFSET, .values = {static_cast<uint64_t>(offset)}};
+ return true;
+}
+
+const DwarfCfaInfo::Info DwarfCfaInfo::kTable[64] = {
+ {
+ // 0x00 DW_CFA_nop
+ "DW_CFA_nop",
+ 2,
+ 0,
+ {},
+ {},
+ },
+ {
+ "DW_CFA_set_loc", // 0x01 DW_CFA_set_loc
+ 2,
+ 1,
+ {DW_EH_PE_absptr},
+ {DWARF_DISPLAY_SET_LOC},
+ },
+ {
+ "DW_CFA_advance_loc1", // 0x02 DW_CFA_advance_loc1
+ 2,
+ 1,
+ {DW_EH_PE_udata1},
+ {DWARF_DISPLAY_ADVANCE_LOC},
+ },
+ {
+ "DW_CFA_advance_loc2", // 0x03 DW_CFA_advance_loc2
+ 2,
+ 1,
+ {DW_EH_PE_udata2},
+ {DWARF_DISPLAY_ADVANCE_LOC},
+ },
+ {
+ "DW_CFA_advance_loc4", // 0x04 DW_CFA_advance_loc4
+ 2,
+ 1,
+ {DW_EH_PE_udata4},
+ {DWARF_DISPLAY_ADVANCE_LOC},
+ },
+ {
+ "DW_CFA_offset_extended", // 0x05 DW_CFA_offset_extended
+ 2,
+ 2,
+ {DW_EH_PE_uleb128, DW_EH_PE_uleb128},
+ {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_NUMBER},
+ },
+ {
+ "DW_CFA_restore_extended", // 0x06 DW_CFA_restore_extended
+ 2,
+ 1,
+ {DW_EH_PE_uleb128},
+ {DWARF_DISPLAY_REGISTER},
+ },
+ {
+ "DW_CFA_undefined", // 0x07 DW_CFA_undefined
+ 2,
+ 1,
+ {DW_EH_PE_uleb128},
+ {DWARF_DISPLAY_REGISTER},
+ },
+ {
+ "DW_CFA_same_value", // 0x08 DW_CFA_same_value
+ 2,
+ 1,
+ {DW_EH_PE_uleb128},
+ {DWARF_DISPLAY_REGISTER},
+ },
+ {
+ "DW_CFA_register", // 0x09 DW_CFA_register
+ 2,
+ 2,
+ {DW_EH_PE_uleb128, DW_EH_PE_uleb128},
+ {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_REGISTER},
+ },
+ {
+ "DW_CFA_remember_state", // 0x0a DW_CFA_remember_state
+ 2,
+ 0,
+ {},
+ {},
+ },
+ {
+ "DW_CFA_restore_state", // 0x0b DW_CFA_restore_state
+ 2,
+ 0,
+ {},
+ {},
+ },
+ {
+ "DW_CFA_def_cfa", // 0x0c DW_CFA_def_cfa
+ 2,
+ 2,
+ {DW_EH_PE_uleb128, DW_EH_PE_uleb128},
+ {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_NUMBER},
+ },
+ {
+ "DW_CFA_def_cfa_register", // 0x0d DW_CFA_def_cfa_register
+ 2,
+ 1,
+ {DW_EH_PE_uleb128},
+ {DWARF_DISPLAY_REGISTER},
+ },
+ {
+ "DW_CFA_def_cfa_offset", // 0x0e DW_CFA_def_cfa_offset
+ 2,
+ 1,
+ {DW_EH_PE_uleb128},
+ {DWARF_DISPLAY_NUMBER},
+ },
+ {
+ "DW_CFA_def_cfa_expression", // 0x0f DW_CFA_def_cfa_expression
+ 2,
+ 1,
+ {DW_EH_PE_block},
+ {DWARF_DISPLAY_EVAL_BLOCK},
+ },
+ {
+ "DW_CFA_expression", // 0x10 DW_CFA_expression
+ 2,
+ 2,
+ {DW_EH_PE_uleb128, DW_EH_PE_block},
+ {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_EVAL_BLOCK},
+ },
+ {
+ "DW_CFA_offset_extended_sf", // 0x11 DW_CFA_offset_extend_sf
+ 2,
+ 2,
+ {DW_EH_PE_uleb128, DW_EH_PE_sleb128},
+ {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_SIGNED_NUMBER},
+ },
+ {
+ "DW_CFA_def_cfa_sf", // 0x12 DW_CFA_def_cfa_sf
+ 2,
+ 2,
+ {DW_EH_PE_uleb128, DW_EH_PE_sleb128},
+ {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_SIGNED_NUMBER},
+ },
+ {
+ "DW_CFA_def_cfa_offset_sf", // 0x13 DW_CFA_def_cfa_offset_sf
+ 2,
+ 1,
+ {DW_EH_PE_sleb128},
+ {DWARF_DISPLAY_SIGNED_NUMBER},
+ },
+ {
+ "DW_CFA_val_offset", // 0x14 DW_CFA_val_offset
+ 2,
+ 2,
+ {DW_EH_PE_uleb128, DW_EH_PE_uleb128},
+ {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_NUMBER},
+ },
+ {
+ "DW_CFA_val_offset_sf", // 0x15 DW_CFA_val_offset_sf
+ 2,
+ 2,
+ {DW_EH_PE_uleb128, DW_EH_PE_sleb128},
+ {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_SIGNED_NUMBER},
+ },
+ {
+ "DW_CFA_val_expression", // 0x16 DW_CFA_val_expression
+ 2,
+ 2,
+ {DW_EH_PE_uleb128, DW_EH_PE_block},
+ {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_EVAL_BLOCK},
+ },
+ {nullptr, 0, 0, {}, {}}, // 0x17 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x18 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x19 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x1a illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x1b illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x1c DW_CFA_lo_user (Treat as illegal)
+ {nullptr, 0, 0, {}, {}}, // 0x1d illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x1e illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x1f illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x20 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x21 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x22 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x23 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x24 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x25 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x26 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x27 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x28 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x29 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x2a illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x2b illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x2c illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x2d DW_CFA_GNU_window_save (Treat as illegal)
+ {
+ "DW_CFA_GNU_args_size", // 0x2e DW_CFA_GNU_args_size
+ 2,
+ 1,
+ {DW_EH_PE_uleb128},
+ {DWARF_DISPLAY_NUMBER},
+ },
+ {
+ "DW_CFA_GNU_negative_offset_extended", // 0x2f DW_CFA_GNU_negative_offset_extended
+ 2,
+ 2,
+ {DW_EH_PE_uleb128, DW_EH_PE_uleb128},
+ {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_NUMBER},
+ },
+ {nullptr, 0, 0, {}, {}}, // 0x31 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x32 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x33 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x34 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x35 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x36 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x37 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x38 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x39 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x3a illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x3b illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x3c illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x3d illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x3e illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x3f DW_CFA_hi_user (Treat as illegal)
+};
+
+// Explicitly instantiate DwarfCfa.
+template class DwarfCfa<uint32_t>;
+template class DwarfCfa<uint64_t>;
diff --git a/libunwindstack/DwarfCfa.h b/libunwindstack/DwarfCfa.h
new file mode 100644
index 0000000..ce7da4a
--- /dev/null
+++ b/libunwindstack/DwarfCfa.h
@@ -0,0 +1,255 @@
+/*
+ * 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 _LIBUNWINDSTACK_DWARF_CFA_H
+#define _LIBUNWINDSTACK_DWARF_CFA_H
+
+#include <stdint.h>
+
+#include <stack>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+#include "DwarfError.h"
+#include "DwarfLocation.h"
+#include "DwarfMemory.h"
+#include "DwarfStructs.h"
+
+// DWARF Standard home: http://dwarfstd.org/
+// This code is based on DWARF 4: http://http://dwarfstd.org/doc/DWARF4.pdf
+// See section 6.4.2.1 for a description of the DW_CFA_xxx values.
+
+class DwarfCfaInfo {
+ public:
+ enum DisplayType : uint8_t {
+ DWARF_DISPLAY_NONE = 0,
+ DWARF_DISPLAY_REGISTER,
+ DWARF_DISPLAY_NUMBER,
+ DWARF_DISPLAY_SIGNED_NUMBER,
+ DWARF_DISPLAY_EVAL_BLOCK,
+ DWARF_DISPLAY_ADDRESS,
+ DWARF_DISPLAY_SET_LOC,
+ DWARF_DISPLAY_ADVANCE_LOC,
+ };
+
+ struct Info {
+ const char* name;
+ uint8_t supported_version;
+ uint8_t num_operands;
+ uint8_t operands[2];
+ uint8_t display_operands[2];
+ };
+
+ const static Info kTable[64];
+};
+
+template <typename AddressType>
+class DwarfCfa {
+ // Signed version of AddressType
+ typedef typename std::make_signed<AddressType>::type SignedType;
+
+ public:
+ DwarfCfa(DwarfMemory* memory, const DwarfFDE* fde) : memory_(memory), fde_(fde) {}
+ virtual ~DwarfCfa() = default;
+
+ bool GetLocationInfo(uint64_t pc, uint64_t start_offset, uint64_t end_offset,
+ dwarf_loc_regs_t* loc_regs);
+
+ bool Log(uint32_t indent, uint64_t pc, uint64_t load_bias, uint64_t start_offset,
+ uint64_t end_offset);
+
+ DwarfError last_error() { return last_error_; }
+
+ AddressType cur_pc() { return cur_pc_; }
+
+ void set_cie_loc_regs(const dwarf_loc_regs_t* cie_loc_regs) { cie_loc_regs_ = cie_loc_regs; }
+
+ protected:
+ std::string GetOperandString(uint8_t operand, uint64_t value, uint64_t* cur_pc);
+
+ bool LogOffsetRegisterString(uint32_t indent, uint64_t cfa_offset, uint8_t reg);
+
+ bool LogInstruction(uint32_t indent, uint64_t cfa_offset, uint8_t op, uint64_t* cur_pc);
+
+ private:
+ DwarfError last_error_;
+ DwarfMemory* memory_;
+ const DwarfFDE* fde_;
+
+ AddressType cur_pc_;
+ const dwarf_loc_regs_t* cie_loc_regs_ = nullptr;
+ std::vector<AddressType> operands_;
+ std::stack<dwarf_loc_regs_t> loc_reg_state_;
+
+ // CFA processing functions.
+ bool cfa_nop(dwarf_loc_regs_t*);
+ bool cfa_set_loc(dwarf_loc_regs_t*);
+ bool cfa_advance_loc(dwarf_loc_regs_t*);
+ bool cfa_offset(dwarf_loc_regs_t*);
+ bool cfa_restore(dwarf_loc_regs_t*);
+ bool cfa_undefined(dwarf_loc_regs_t*);
+ bool cfa_same_value(dwarf_loc_regs_t*);
+ bool cfa_register(dwarf_loc_regs_t*);
+ bool cfa_remember_state(dwarf_loc_regs_t*);
+ bool cfa_restore_state(dwarf_loc_regs_t*);
+ bool cfa_def_cfa(dwarf_loc_regs_t*);
+ bool cfa_def_cfa_register(dwarf_loc_regs_t*);
+ bool cfa_def_cfa_offset(dwarf_loc_regs_t*);
+ bool cfa_def_cfa_expression(dwarf_loc_regs_t*);
+ bool cfa_expression(dwarf_loc_regs_t*);
+ bool cfa_offset_extended_sf(dwarf_loc_regs_t*);
+ bool cfa_def_cfa_sf(dwarf_loc_regs_t*);
+ bool cfa_def_cfa_offset_sf(dwarf_loc_regs_t*);
+ bool cfa_val_offset(dwarf_loc_regs_t*);
+ bool cfa_val_offset_sf(dwarf_loc_regs_t*);
+ bool cfa_val_expression(dwarf_loc_regs_t*);
+ bool cfa_gnu_negative_offset_extended(dwarf_loc_regs_t*);
+
+ using process_func = bool (DwarfCfa::*)(dwarf_loc_regs_t*);
+ constexpr static process_func kCallbackTable[64] = {
+ // 0x00 DW_CFA_nop
+ &DwarfCfa::cfa_nop,
+ // 0x01 DW_CFA_set_loc
+ &DwarfCfa::cfa_set_loc,
+ // 0x02 DW_CFA_advance_loc1
+ &DwarfCfa::cfa_advance_loc,
+ // 0x03 DW_CFA_advance_loc2
+ &DwarfCfa::cfa_advance_loc,
+ // 0x04 DW_CFA_advance_loc4
+ &DwarfCfa::cfa_advance_loc,
+ // 0x05 DW_CFA_offset_extended
+ &DwarfCfa::cfa_offset,
+ // 0x06 DW_CFA_restore_extended
+ &DwarfCfa::cfa_restore,
+ // 0x07 DW_CFA_undefined
+ &DwarfCfa::cfa_undefined,
+ // 0x08 DW_CFA_same_value
+ &DwarfCfa::cfa_same_value,
+ // 0x09 DW_CFA_register
+ &DwarfCfa::cfa_register,
+ // 0x0a DW_CFA_remember_state
+ &DwarfCfa::cfa_remember_state,
+ // 0x0b DW_CFA_restore_state
+ &DwarfCfa::cfa_restore_state,
+ // 0x0c DW_CFA_def_cfa
+ &DwarfCfa::cfa_def_cfa,
+ // 0x0d DW_CFA_def_cfa_register
+ &DwarfCfa::cfa_def_cfa_register,
+ // 0x0e DW_CFA_def_cfa_offset
+ &DwarfCfa::cfa_def_cfa_offset,
+ // 0x0f DW_CFA_def_cfa_expression
+ &DwarfCfa::cfa_def_cfa_expression,
+ // 0x10 DW_CFA_expression
+ &DwarfCfa::cfa_expression,
+ // 0x11 DW_CFA_offset_extended_sf
+ &DwarfCfa::cfa_offset_extended_sf,
+ // 0x12 DW_CFA_def_cfa_sf
+ &DwarfCfa::cfa_def_cfa_sf,
+ // 0x13 DW_CFA_def_cfa_offset_sf
+ &DwarfCfa::cfa_def_cfa_offset_sf,
+ // 0x14 DW_CFA_val_offset
+ &DwarfCfa::cfa_val_offset,
+ // 0x15 DW_CFA_val_offset_sf
+ &DwarfCfa::cfa_val_offset_sf,
+ // 0x16 DW_CFA_val_expression
+ &DwarfCfa::cfa_val_expression,
+ // 0x17 illegal cfa
+ nullptr,
+ // 0x18 illegal cfa
+ nullptr,
+ // 0x19 illegal cfa
+ nullptr,
+ // 0x1a illegal cfa
+ nullptr,
+ // 0x1b illegal cfa
+ nullptr,
+ // 0x1c DW_CFA_lo_user (Treat this as illegal)
+ nullptr,
+ // 0x1d illegal cfa
+ nullptr,
+ // 0x1e illegal cfa
+ nullptr,
+ // 0x1f illegal cfa
+ nullptr,
+ // 0x20 illegal cfa
+ nullptr,
+ // 0x21 illegal cfa
+ nullptr,
+ // 0x22 illegal cfa
+ nullptr,
+ // 0x23 illegal cfa
+ nullptr,
+ // 0x24 illegal cfa
+ nullptr,
+ // 0x25 illegal cfa
+ nullptr,
+ // 0x26 illegal cfa
+ nullptr,
+ // 0x27 illegal cfa
+ nullptr,
+ // 0x28 illegal cfa
+ nullptr,
+ // 0x29 illegal cfa
+ nullptr,
+ // 0x2a illegal cfa
+ nullptr,
+ // 0x2b illegal cfa
+ nullptr,
+ // 0x2c illegal cfa
+ nullptr,
+ // 0x2d DW_CFA_GNU_window_save (Treat this as illegal)
+ nullptr,
+ // 0x2e DW_CFA_GNU_args_size
+ &DwarfCfa::cfa_nop,
+ // 0x2f DW_CFA_GNU_negative_offset_extended
+ &DwarfCfa::cfa_gnu_negative_offset_extended,
+ // 0x30 illegal cfa
+ nullptr,
+ // 0x31 illegal cfa
+ nullptr,
+ // 0x32 illegal cfa
+ nullptr,
+ // 0x33 illegal cfa
+ nullptr,
+ // 0x34 illegal cfa
+ nullptr,
+ // 0x35 illegal cfa
+ nullptr,
+ // 0x36 illegal cfa
+ nullptr,
+ // 0x37 illegal cfa
+ nullptr,
+ // 0x38 illegal cfa
+ nullptr,
+ // 0x39 illegal cfa
+ nullptr,
+ // 0x3a illegal cfa
+ nullptr,
+ // 0x3b illegal cfa
+ nullptr,
+ // 0x3c illegal cfa
+ nullptr,
+ // 0x3d illegal cfa
+ nullptr,
+ // 0x3e illegal cfa
+ nullptr,
+ // 0x3f DW_CFA_hi_user (Treat this as illegal)
+ nullptr,
+ };
+};
+
+#endif // _LIBUNWINDSTACK_DWARF_CFA_H
diff --git a/libunwindstack/DwarfLocation.h b/libunwindstack/DwarfLocation.h
new file mode 100644
index 0000000..062d125
--- /dev/null
+++ b/libunwindstack/DwarfLocation.h
@@ -0,0 +1,41 @@
+/*
+ * 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 _LIBUNWINDSTACK_DWARF_LOCATION_H
+#define _LIBUNWINDSTACK_DWARF_LOCATION_H
+
+#include <stdint.h>
+
+#include <unordered_map>
+
+enum DwarfLocationEnum : uint8_t {
+ DWARF_LOCATION_INVALID = 0,
+ DWARF_LOCATION_UNDEFINED,
+ DWARF_LOCATION_OFFSET,
+ DWARF_LOCATION_VAL_OFFSET,
+ DWARF_LOCATION_REGISTER,
+ DWARF_LOCATION_EXPRESSION,
+ DWARF_LOCATION_VAL_EXPRESSION,
+};
+
+struct DwarfLocation {
+ DwarfLocationEnum type;
+ uint64_t values[2];
+};
+
+typedef std::unordered_map<uint16_t, DwarfLocation> dwarf_loc_regs_t;
+
+#endif // _LIBUNWINDSTACK_DWARF_LOCATION_H
diff --git a/libunwindstack/DwarfStructs.h b/libunwindstack/DwarfStructs.h
new file mode 100644
index 0000000..57aac88
--- /dev/null
+++ b/libunwindstack/DwarfStructs.h
@@ -0,0 +1,52 @@
+/*
+ * 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 _LIBUNWINDSTACK_DWARF_STRUCTS_H
+#define _LIBUNWINDSTACK_DWARF_STRUCTS_H
+
+#include <stdint.h>
+
+#include <vector>
+
+#include "DwarfEncoding.h"
+
+struct DwarfCIE {
+ uint8_t version = 0;
+ uint8_t fde_address_encoding = DW_EH_PE_absptr;
+ uint8_t lsda_encoding = DW_EH_PE_omit;
+ uint8_t segment_size = 0;
+ std::vector<char> augmentation_string;
+ uint64_t personality_handler = 0;
+ uint64_t cfa_instructions_offset = 0;
+ uint64_t cfa_instructions_end = 0;
+ uint64_t code_alignment_factor = 0;
+ int64_t data_alignment_factor = 0;
+ uint64_t return_address_register = 0;
+};
+
+struct DwarfFDE {
+ uint64_t cie_offset = 0;
+ uint64_t cfa_instructions_offset = 0;
+ uint64_t cfa_instructions_end = 0;
+ uint64_t pc_start = 0;
+ uint64_t pc_end = 0;
+ uint64_t lsda_address = 0;
+ const DwarfCIE* cie = nullptr;
+};
+
+constexpr uint16_t CFA_REG = static_cast<uint16_t>(-1);
+
+#endif // _LIBUNWINDSTACK_DWARF_STRUCTS_H
diff --git a/libunwindstack/MapInfo.cpp b/libunwindstack/MapInfo.cpp
new file mode 100644
index 0000000..051f700
--- /dev/null
+++ b/libunwindstack/MapInfo.cpp
@@ -0,0 +1,86 @@
+/*
+ * 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 <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+
+#include "Elf.h"
+#include "MapInfo.h"
+#include "Maps.h"
+#include "Memory.h"
+
+Memory* MapInfo::CreateMemory(pid_t pid) {
+ if (end <= start) {
+ return nullptr;
+ }
+
+ elf_offset = 0;
+
+ // First try and use the file associated with the info.
+ if (!name.empty()) {
+ // Fail on device maps.
+ if (flags & MAPS_FLAGS_DEVICE_MAP) {
+ return nullptr;
+ }
+
+ std::unique_ptr<MemoryFileAtOffset> file_memory(new MemoryFileAtOffset);
+ uint64_t map_size;
+ if (offset != 0) {
+ // Only map in a piece of the file.
+ map_size = end - start;
+ } else {
+ map_size = UINT64_MAX;
+ }
+ if (file_memory->Init(name, offset, map_size)) {
+ // It's possible that a non-zero offset might not be pointing to
+ // valid elf data. Check if this is a valid elf, and if not assume
+ // that this was meant to incorporate the entire file.
+ if (offset != 0 && !Elf::IsValidElf(file_memory.get())) {
+ // Don't bother checking the validity that will happen on the elf init.
+ if (file_memory->Init(name, 0)) {
+ elf_offset = offset;
+ return file_memory.release();
+ }
+ // Fall through if the init fails.
+ } else {
+ return file_memory.release();
+ }
+ }
+ }
+
+ Memory* memory = nullptr;
+ if (pid == getpid()) {
+ memory = new MemoryLocal();
+ } else {
+ memory = new MemoryRemote(pid);
+ }
+ return new MemoryRange(memory, start, end);
+}
+
+Elf* MapInfo::GetElf(pid_t pid, bool) {
+ if (elf) {
+ return elf;
+ }
+
+ elf = new Elf(CreateMemory(pid));
+ elf->Init();
+ // If the init fails, keep the elf around as an invalid object so we
+ // don't try to reinit the object.
+ return elf;
+}
diff --git a/libunwindstack/MapInfo.h b/libunwindstack/MapInfo.h
index 8342904..79a2ada 100644
--- a/libunwindstack/MapInfo.h
+++ b/libunwindstack/MapInfo.h
@@ -39,6 +39,7 @@
uint64_t elf_offset;
Memory* CreateMemory(pid_t pid);
+ Elf* GetElf(pid_t pid, bool init_gnu_debugdata = false);
};
#endif // _LIBUNWINDSTACK_MAP_INFO_H
diff --git a/libunwindstack/Maps.cpp b/libunwindstack/Maps.cpp
new file mode 100644
index 0000000..b369c43
--- /dev/null
+++ b/libunwindstack/Maps.cpp
@@ -0,0 +1,196 @@
+/*
+ * 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 <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/unique_fd.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "Maps.h"
+
+MapInfo* Maps::Find(uint64_t pc) {
+ if (maps_.empty()) {
+ return nullptr;
+ }
+ size_t first = 0;
+ size_t last = maps_.size();
+ while (first < last) {
+ size_t index = (first + last) / 2;
+ MapInfo* cur = &maps_[index];
+ if (pc >= cur->start && pc < cur->end) {
+ return cur;
+ } else if (pc < cur->start) {
+ last = index;
+ } else {
+ first = index + 1;
+ }
+ }
+ return nullptr;
+}
+
+bool Maps::ParseLine(const char* line, MapInfo* map_info) {
+ char permissions[5];
+ int name_pos;
+ // Linux /proc/<pid>/maps lines:
+ // 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /system/lib/libcomposer.so
+ if (sscanf(line, "%" SCNx64 "-%" SCNx64 " %4s %" SCNx64 " %*x:%*x %*d %n", &map_info->start,
+ &map_info->end, permissions, &map_info->offset, &name_pos) != 4) {
+ return false;
+ }
+ map_info->flags = PROT_NONE;
+ if (permissions[0] == 'r') {
+ map_info->flags |= PROT_READ;
+ }
+ if (permissions[1] == 'w') {
+ map_info->flags |= PROT_WRITE;
+ }
+ if (permissions[2] == 'x') {
+ map_info->flags |= PROT_EXEC;
+ }
+
+ map_info->name = &line[name_pos];
+ size_t length = map_info->name.length() - 1;
+ if (map_info->name[length] == '\n') {
+ map_info->name.erase(length);
+ }
+ // Mark a device map in /dev/and not in /dev/ashmem/ specially.
+ if (!map_info->name.empty() && map_info->name.substr(0, 5) == "/dev/" &&
+ map_info->name.substr(5, 7) != "ashmem/") {
+ map_info->flags |= MAPS_FLAGS_DEVICE_MAP;
+ }
+
+ return true;
+}
+
+bool Maps::Parse() {
+ std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(GetMapsFile().c_str(), "re"), fclose);
+ if (!fp) {
+ return false;
+ }
+
+ bool valid = true;
+ char* line = nullptr;
+ size_t line_len;
+ while (getline(&line, &line_len, fp.get()) > 0) {
+ MapInfo map_info;
+ if (!ParseLine(line, &map_info)) {
+ valid = false;
+ break;
+ }
+
+ maps_.push_back(map_info);
+ }
+ free(line);
+
+ return valid;
+}
+
+Maps::~Maps() {
+ for (auto& map : maps_) {
+ delete map.elf;
+ map.elf = nullptr;
+ }
+}
+
+bool BufferMaps::Parse() {
+ const char* start_of_line = buffer_;
+ do {
+ std::string line;
+ const char* end_of_line = strchr(start_of_line, '\n');
+ if (end_of_line == nullptr) {
+ line = start_of_line;
+ } else {
+ end_of_line++;
+ line = std::string(start_of_line, end_of_line - start_of_line);
+ }
+
+ MapInfo map_info;
+ if (!ParseLine(line.c_str(), &map_info)) {
+ return false;
+ }
+ maps_.push_back(map_info);
+
+ start_of_line = end_of_line;
+ } while (start_of_line != nullptr && *start_of_line != '\0');
+ return true;
+}
+
+const std::string RemoteMaps::GetMapsFile() const {
+ return "/proc/" + std::to_string(pid_) + "/maps";
+}
+
+bool OfflineMaps::Parse() {
+ // Format of maps information:
+ // <uint64_t> StartOffset
+ // <uint64_t> EndOffset
+ // <uint64_t> offset
+ // <uint16_t> flags
+ // <uint16_t> MapNameLength
+ // <VariableLengthValue> MapName
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(file_.c_str(), O_RDONLY)));
+ if (fd == -1) {
+ return false;
+ }
+
+ std::vector<char> name;
+ while (true) {
+ MapInfo map_info;
+ ssize_t bytes = TEMP_FAILURE_RETRY(read(fd, &map_info.start, sizeof(map_info.start)));
+ if (bytes == 0) {
+ break;
+ }
+ if (bytes == -1 || bytes != sizeof(map_info.start)) {
+ return false;
+ }
+ bytes = TEMP_FAILURE_RETRY(read(fd, &map_info.end, sizeof(map_info.end)));
+ if (bytes == -1 || bytes != sizeof(map_info.end)) {
+ return false;
+ }
+ bytes = TEMP_FAILURE_RETRY(read(fd, &map_info.offset, sizeof(map_info.offset)));
+ if (bytes == -1 || bytes != sizeof(map_info.offset)) {
+ return false;
+ }
+ bytes = TEMP_FAILURE_RETRY(read(fd, &map_info.flags, sizeof(map_info.flags)));
+ if (bytes == -1 || bytes != sizeof(map_info.flags)) {
+ return false;
+ }
+ uint16_t len;
+ bytes = TEMP_FAILURE_RETRY(read(fd, &len, sizeof(len)));
+ if (bytes == -1 || bytes != sizeof(len)) {
+ return false;
+ }
+ if (len > 0) {
+ name.resize(len);
+ bytes = TEMP_FAILURE_RETRY(read(fd, name.data(), len));
+ if (bytes == -1 || bytes != len) {
+ return false;
+ }
+ map_info.name = std::string(name.data(), len);
+ }
+ maps_.push_back(map_info);
+ }
+ return true;
+}
diff --git a/libunwindstack/Maps.h b/libunwindstack/Maps.h
new file mode 100644
index 0000000..239b64a
--- /dev/null
+++ b/libunwindstack/Maps.h
@@ -0,0 +1,107 @@
+/*
+ * 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 _LIBUNWINDSTACK_MAPS_H
+#define _LIBUNWINDSTACK_MAPS_H
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+#include "Elf.h"
+#include "MapInfo.h"
+
+// Special flag to indicate a map is in /dev/. However, a map in
+// /dev/ashmem/... does not set this flag.
+static constexpr int MAPS_FLAGS_DEVICE_MAP = 0x8000;
+
+class Maps {
+ public:
+ Maps() = default;
+ virtual ~Maps();
+
+ MapInfo* Find(uint64_t pc);
+
+ bool ParseLine(const char* line, MapInfo* map_info);
+
+ virtual bool Parse();
+
+ virtual const std::string GetMapsFile() const { return ""; }
+
+ typedef std::vector<MapInfo>::iterator iterator;
+ iterator begin() { return maps_.begin(); }
+ iterator end() { return maps_.end(); }
+
+ typedef std::vector<MapInfo>::const_iterator const_iterator;
+ const_iterator begin() const { return maps_.begin(); }
+ const_iterator end() const { return maps_.end(); }
+
+ size_t Total() { return maps_.size(); }
+
+ protected:
+ std::vector<MapInfo> maps_;
+};
+
+class RemoteMaps : public Maps {
+ public:
+ RemoteMaps(pid_t pid) : pid_(pid) {}
+ virtual ~RemoteMaps() = default;
+
+ virtual const std::string GetMapsFile() const override;
+
+ private:
+ pid_t pid_;
+};
+
+class LocalMaps : public RemoteMaps {
+ public:
+ LocalMaps() : RemoteMaps(getpid()) {}
+ virtual ~LocalMaps() = default;
+};
+
+class BufferMaps : public Maps {
+ public:
+ BufferMaps(const char* buffer) : buffer_(buffer) {}
+ virtual ~BufferMaps() = default;
+
+ bool Parse() override;
+
+ private:
+ const char* buffer_;
+};
+
+class FileMaps : public Maps {
+ public:
+ FileMaps(const std::string& file) : file_(file) {}
+ virtual ~FileMaps() = default;
+
+ const std::string GetMapsFile() const override { return file_; }
+
+ protected:
+ const std::string file_;
+};
+
+class OfflineMaps : public FileMaps {
+ public:
+ OfflineMaps(const std::string& file) : FileMaps(file) {}
+ virtual ~OfflineMaps() = default;
+
+ bool Parse() override;
+};
+
+#endif // _LIBUNWINDSTACK_MAPS_H
diff --git a/libunwindstack/tests/DwarfCfaLogTest.cpp b/libunwindstack/tests/DwarfCfaLogTest.cpp
new file mode 100644
index 0000000..3185bc3
--- /dev/null
+++ b/libunwindstack/tests/DwarfCfaLogTest.cpp
@@ -0,0 +1,814 @@
+/*
+ * 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 <memory>
+#include <type_traits>
+#include <unordered_map>
+
+#include <android-base/stringprintf.h>
+#include <gtest/gtest.h>
+
+#include "DwarfCfa.h"
+#include "DwarfLocation.h"
+#include "DwarfMemory.h"
+#include "DwarfStructs.h"
+#include "Log.h"
+
+#include "LogFake.h"
+#include "MemoryFake.h"
+
+template <typename TypeParam>
+class DwarfCfaLogTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ ResetLogs();
+ memory_.Clear();
+
+ dmem_.reset(new DwarfMemory(&memory_));
+
+ cie_.cfa_instructions_offset = 0x1000;
+ cie_.cfa_instructions_end = 0x1030;
+ // These two values should be different to distinguish between
+ // operations that deal with code versus data.
+ cie_.code_alignment_factor = 4;
+ cie_.data_alignment_factor = 8;
+
+ fde_.cfa_instructions_offset = 0x2000;
+ fde_.cfa_instructions_end = 0x2030;
+ fde_.pc_start = 0x2000;
+ fde_.pc_end = 0x2000;
+ fde_.pc_end = 0x10000;
+ fde_.cie = &cie_;
+ cfa_.reset(new DwarfCfa<TypeParam>(dmem_.get(), &fde_));
+ }
+
+ MemoryFake memory_;
+ std::unique_ptr<DwarfMemory> dmem_;
+ std::unique_ptr<DwarfCfa<TypeParam>> cfa_;
+ DwarfCIE cie_;
+ DwarfFDE fde_;
+};
+TYPED_TEST_CASE_P(DwarfCfaLogTest);
+
+// NOTE: All class variable references have to be prefaced with this->.
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_illegal) {
+ for (uint8_t i = 0x17; i < 0x3f; i++) {
+ if (i == 0x2e || i == 0x2f) {
+ // Skip gnu extension ops.
+ continue;
+ }
+ this->memory_.SetMemory(0x2000, std::vector<uint8_t>{i});
+
+ ResetLogs();
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2000, 0x2001));
+ std::string expected = "4 unwind Illegal\n";
+ expected += android::base::StringPrintf("4 unwind Raw Data: 0x%02x\n", i);
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+ }
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_nop) {
+ this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0x00});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2000, 0x2001));
+ std::string expected =
+ "4 unwind DW_CFA_nop\n"
+ "4 unwind Raw Data: 0x00\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_offset) {
+ this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0x83, 0x04});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2000, 0x2002));
+ std::string expected =
+ "4 unwind DW_CFA_offset register(3) 4\n"
+ "4 unwind Raw Data: 0x83 0x04\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x2100, std::vector<uint8_t>{0x83, 0x84, 0x01});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2100, 0x2103));
+ expected =
+ "4 unwind DW_CFA_offset register(3) 132\n"
+ "4 unwind Raw Data: 0x83 0x84 0x01\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_offset_extended) {
+ this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x05, 0x03, 0x02});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x500, 0x503));
+ std::string expected =
+ "4 unwind DW_CFA_offset_extended register(3) 2\n"
+ "4 unwind Raw Data: 0x05 0x03 0x02\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x1500, std::vector<uint8_t>{0x05, 0x81, 0x01, 0x82, 0x12});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x1500, 0x1505));
+ expected =
+ "4 unwind DW_CFA_offset_extended register(129) 2306\n"
+ "4 unwind Raw Data: 0x05 0x81 0x01 0x82 0x12\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_offset_extended_sf) {
+ this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x11, 0x05, 0x10});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x500, 0x503));
+ std::string expected =
+ "4 unwind DW_CFA_offset_extended_sf register(5) 16\n"
+ "4 unwind Raw Data: 0x11 0x05 0x10\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ // Check a negative value for the offset.
+ ResetLogs();
+ this->memory_.SetMemory(0x1500, std::vector<uint8_t>{0x11, 0x86, 0x01, 0xff, 0x7f});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x1500, 0x1505));
+ expected =
+ "4 unwind DW_CFA_offset_extended_sf register(134) -1\n"
+ "4 unwind Raw Data: 0x11 0x86 0x01 0xff 0x7f\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_restore) {
+ this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0xc2});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2000, 0x2001));
+ std::string expected =
+ "4 unwind DW_CFA_restore register(2)\n"
+ "4 unwind Raw Data: 0xc2\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x3000, std::vector<uint8_t>{0x82, 0x04, 0xc2});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x3000, 0x3003));
+ expected =
+ "4 unwind DW_CFA_offset register(2) 4\n"
+ "4 unwind Raw Data: 0x82 0x04\n"
+ "4 unwind DW_CFA_restore register(2)\n"
+ "4 unwind Raw Data: 0xc2\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_restore_extended) {
+ this->memory_.SetMemory(0x4000, std::vector<uint8_t>{0x06, 0x08});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x4000, 0x4002));
+ std::string expected =
+ "4 unwind DW_CFA_restore_extended register(8)\n"
+ "4 unwind Raw Data: 0x06 0x08\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x05, 0x82, 0x02, 0x04, 0x06, 0x82, 0x02});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x5000, 0x5007));
+ expected =
+ "4 unwind DW_CFA_offset_extended register(258) 4\n"
+ "4 unwind Raw Data: 0x05 0x82 0x02 0x04\n"
+ "4 unwind DW_CFA_restore_extended register(258)\n"
+ "4 unwind Raw Data: 0x06 0x82 0x02\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_set_loc) {
+ uint8_t buffer[1 + sizeof(TypeParam)];
+ buffer[0] = 0x1;
+ TypeParam address;
+ std::string raw_data("Raw Data: 0x01 ");
+ std::string address_str;
+ if (std::is_same<TypeParam, uint32_t>::value) {
+ address = 0x81234578U;
+ address_str = "0x81234578";
+ raw_data += "0x78 0x45 0x23 0x81";
+ } else {
+ address = 0x8123456712345678ULL;
+ address_str = "0x8123456712345678";
+ raw_data += "0x78 0x56 0x34 0x12 0x67 0x45 0x23 0x81";
+ }
+ memcpy(&buffer[1], &address, sizeof(address));
+
+ this->memory_.SetMemory(0x50, buffer, sizeof(buffer));
+ ResetLogs();
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x50, 0x51 + sizeof(TypeParam)));
+ std::string expected = "4 unwind DW_CFA_set_loc " + address_str + "\n";
+ expected += "4 unwind " + raw_data + "\n";
+ expected += "4 unwind \n";
+ expected += "4 unwind PC " + address_str + "\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ // Check for a set going back.
+ ResetLogs();
+ this->fde_.pc_start = address + 0x10;
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x50, 0x51 + sizeof(TypeParam)));
+ expected = "4 unwind DW_CFA_set_loc " + address_str + "\n";
+ expected += "4 unwind " + raw_data + "\n";
+ expected += "4 unwind \n";
+ expected += "4 unwind PC " + address_str + "\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_advance_loc) {
+ this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x44});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x201));
+ std::string expected =
+ "4 unwind DW_CFA_advance_loc 4\n"
+ "4 unwind Raw Data: 0x44\n"
+ "4 unwind \n"
+ "4 unwind PC 0x2010\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x200, 0x201));
+ expected =
+ "4 unwind DW_CFA_advance_loc 4\n"
+ "4 unwind Raw Data: 0x44\n"
+ "4 unwind \n"
+ "4 unwind PC 0x2110\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_advance_loc1) {
+ this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x02, 0x04});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x202));
+ std::string expected =
+ "4 unwind DW_CFA_advance_loc1 4\n"
+ "4 unwind Raw Data: 0x02 0x04\n"
+ "4 unwind \n"
+ "4 unwind PC 0x2004\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x10, 0x200, 0x202));
+ expected =
+ "4 unwind DW_CFA_advance_loc1 4\n"
+ "4 unwind Raw Data: 0x02 0x04\n"
+ "4 unwind \n"
+ "4 unwind PC 0x2014\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_advance_loc2) {
+ this->memory_.SetMemory(0x600, std::vector<uint8_t>{0x03, 0x04, 0x03});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x600, 0x603));
+ std::string expected =
+ "4 unwind DW_CFA_advance_loc2 772\n"
+ "4 unwind Raw Data: 0x03 0x04 0x03\n"
+ "4 unwind \n"
+ "4 unwind PC 0x2304\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x1000, 0x600, 0x603));
+ expected =
+ "4 unwind DW_CFA_advance_loc2 772\n"
+ "4 unwind Raw Data: 0x03 0x04 0x03\n"
+ "4 unwind \n"
+ "4 unwind PC 0x3304\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_advance_loc4) {
+ this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x04, 0x04, 0x03, 0x02, 0x01});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x500, 0x505));
+ std::string expected =
+ "4 unwind DW_CFA_advance_loc4 16909060\n"
+ "4 unwind Raw Data: 0x04 0x04 0x03 0x02 0x01\n"
+ "4 unwind \n"
+ "4 unwind PC 0x1022304\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x2000, 0x500, 0x505));
+ expected =
+ "4 unwind DW_CFA_advance_loc4 16909060\n"
+ "4 unwind Raw Data: 0x04 0x04 0x03 0x02 0x01\n"
+ "4 unwind \n"
+ "4 unwind PC 0x1024304\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_undefined) {
+ this->memory_.SetMemory(0xa00, std::vector<uint8_t>{0x07, 0x09});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0xa00, 0xa02));
+ std::string expected =
+ "4 unwind DW_CFA_undefined register(9)\n"
+ "4 unwind Raw Data: 0x07 0x09\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ dwarf_loc_regs_t cie_loc_regs;
+ this->memory_.SetMemory(0x1a00, std::vector<uint8_t>{0x07, 0x81, 0x01});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x1a00, 0x1a03));
+ expected =
+ "4 unwind DW_CFA_undefined register(129)\n"
+ "4 unwind Raw Data: 0x07 0x81 0x01\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_same) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x08, 0x7f});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102));
+ std::string expected =
+ "4 unwind DW_CFA_same_value register(127)\n"
+ "4 unwind Raw Data: 0x08 0x7f\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x2100, std::vector<uint8_t>{0x08, 0xff, 0x01});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2100, 0x2103));
+ expected =
+ "4 unwind DW_CFA_same_value register(255)\n"
+ "4 unwind Raw Data: 0x08 0xff 0x01\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_register) {
+ this->memory_.SetMemory(0x300, std::vector<uint8_t>{0x09, 0x02, 0x01});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x300, 0x303));
+ std::string expected =
+ "4 unwind DW_CFA_register register(2) register(1)\n"
+ "4 unwind Raw Data: 0x09 0x02 0x01\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x4300, std::vector<uint8_t>{0x09, 0xff, 0x01, 0xff, 0x03});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x4300, 0x4305));
+ expected =
+ "4 unwind DW_CFA_register register(255) register(511)\n"
+ "4 unwind Raw Data: 0x09 0xff 0x01 0xff 0x03\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_state) {
+ this->memory_.SetMemory(0x300, std::vector<uint8_t>{0x0a});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x300, 0x301));
+
+ std::string expected =
+ "4 unwind DW_CFA_remember_state\n"
+ "4 unwind Raw Data: 0x0a\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x4300, std::vector<uint8_t>{0x0b});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x4300, 0x4301));
+
+ expected =
+ "4 unwind DW_CFA_restore_state\n"
+ "4 unwind Raw Data: 0x0b\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_state_cfa_offset_restore) {
+ this->memory_.SetMemory(0x3000, std::vector<uint8_t>{0x0a, 0x0e, 0x40, 0x0b});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x3000, 0x3004));
+
+ std::string expected =
+ "4 unwind DW_CFA_remember_state\n"
+ "4 unwind Raw Data: 0x0a\n"
+ "4 unwind DW_CFA_def_cfa_offset 64\n"
+ "4 unwind Raw Data: 0x0e 0x40\n"
+ "4 unwind DW_CFA_restore_state\n"
+ "4 unwind Raw Data: 0x0b\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0c, 0x7f, 0x74});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x103));
+
+ std::string expected =
+ "4 unwind DW_CFA_def_cfa register(127) 116\n"
+ "4 unwind Raw Data: 0x0c 0x7f 0x74\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x0c, 0xff, 0x02, 0xf4, 0x04});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x205));
+
+ expected =
+ "4 unwind DW_CFA_def_cfa register(383) 628\n"
+ "4 unwind Raw Data: 0x0c 0xff 0x02 0xf4 0x04\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa_sf) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x12, 0x30, 0x25});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x103));
+
+ std::string expected =
+ "4 unwind DW_CFA_def_cfa_sf register(48) 37\n"
+ "4 unwind Raw Data: 0x12 0x30 0x25\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ // Test a negative value.
+ ResetLogs();
+ this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x12, 0xa3, 0x01, 0xfa, 0x7f});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x205));
+
+ expected =
+ "4 unwind DW_CFA_def_cfa_sf register(163) -6\n"
+ "4 unwind Raw Data: 0x12 0xa3 0x01 0xfa 0x7f\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa_register) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0d, 0x72});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102));
+
+ std::string expected =
+ "4 unwind DW_CFA_def_cfa_register register(114)\n"
+ "4 unwind Raw Data: 0x0d 0x72\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x0d, 0xf9, 0x20});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x203));
+
+ expected =
+ "4 unwind DW_CFA_def_cfa_register register(4217)\n"
+ "4 unwind Raw Data: 0x0d 0xf9 0x20\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa_offset) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0e, 0x59});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102));
+
+ std::string expected =
+ "4 unwind DW_CFA_def_cfa_offset 89\n"
+ "4 unwind Raw Data: 0x0e 0x59\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102));
+
+ expected =
+ "4 unwind DW_CFA_def_cfa_offset 89\n"
+ "4 unwind Raw Data: 0x0e 0x59\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x0e, 0xd4, 0x0a});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x203));
+
+ expected =
+ "4 unwind DW_CFA_def_cfa_offset 1364\n"
+ "4 unwind Raw Data: 0x0e 0xd4 0x0a\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa_offset_sf) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x13, 0x23});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102));
+
+ std::string expected =
+ "4 unwind DW_CFA_def_cfa_offset_sf 35\n"
+ "4 unwind Raw Data: 0x13 0x23\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102));
+
+ expected =
+ "4 unwind DW_CFA_def_cfa_offset_sf 35\n"
+ "4 unwind Raw Data: 0x13 0x23\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ // Negative offset.
+ ResetLogs();
+ this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x13, 0xf6, 0x7f});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x203));
+
+ expected =
+ "4 unwind DW_CFA_def_cfa_offset_sf -10\n"
+ "4 unwind Raw Data: 0x13 0xf6 0x7f\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa_expression) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0f, 0x04, 0x01, 0x02, 0x04, 0x05});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x106));
+
+ std::string expected =
+ "4 unwind DW_CFA_def_cfa_expression 4\n"
+ "4 unwind Raw Data: 0x0f 0x04 0x01 0x02 0x04 0x05\n"
+ "4 unwind Illegal\n"
+ "4 unwind Raw Data: 0x01\n"
+ "4 unwind Illegal\n"
+ "4 unwind Raw Data: 0x02\n"
+ "4 unwind Illegal\n"
+ "4 unwind Raw Data: 0x04\n"
+ "4 unwind Illegal\n"
+ "4 unwind Raw Data: 0x05\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ std::vector<uint8_t> ops{0x0f, 0x81, 0x01};
+ expected = "4 unwind Raw Data: 0x0f 0x81 0x01";
+ std::string op_string;
+ for (uint8_t i = 3; i < 132; i++) {
+ ops.push_back(0x05);
+ op_string +=
+ "4 unwind Illegal\n"
+ "4 unwind Raw Data: 0x05\n";
+ expected += " 0x05";
+ if (((i + 1) % 10) == 0) {
+ expected += "\n4 unwind Raw Data:";
+ }
+ }
+ expected += '\n';
+ this->memory_.SetMemory(0x200, ops);
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x284));
+
+ expected = "4 unwind DW_CFA_def_cfa_expression 129\n" + expected;
+ ASSERT_EQ(expected + op_string, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_expression) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x10, 0x04, 0x02, 0xc0, 0xc1});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x105));
+
+ std::string expected =
+ "4 unwind DW_CFA_expression register(4) 2\n"
+ "4 unwind Raw Data: 0x10 0x04 0x02 0xc0 0xc1\n"
+ "4 unwind Illegal\n"
+ "4 unwind Raw Data: 0xc0\n"
+ "4 unwind Illegal\n"
+ "4 unwind Raw Data: 0xc1\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ std::vector<uint8_t> ops{0x10, 0xff, 0x01, 0x82, 0x01};
+ expected = "4 unwind Raw Data: 0x10 0xff 0x01 0x82 0x01";
+ std::string op_string;
+ for (uint8_t i = 5; i < 135; i++) {
+ ops.push_back(0xa0 + (i - 5) % 96);
+ op_string += "4 unwind Illegal\n";
+ op_string += android::base::StringPrintf("4 unwind Raw Data: 0x%02x\n", ops.back());
+ expected += android::base::StringPrintf(" 0x%02x", ops.back());
+ if (((i + 1) % 10) == 0) {
+ expected += "\n4 unwind Raw Data:";
+ }
+ }
+ expected = "4 unwind DW_CFA_expression register(255) 130\n" + expected + "\n";
+
+ this->memory_.SetMemory(0x200, ops);
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x287));
+
+ ASSERT_EQ(expected + op_string, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_val_offset) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x14, 0x45, 0x54});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x103));
+
+ std::string expected =
+ "4 unwind DW_CFA_val_offset register(69) 84\n"
+ "4 unwind Raw Data: 0x14 0x45 0x54\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x400, std::vector<uint8_t>{0x14, 0xa2, 0x02, 0xb4, 0x05});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x400, 0x405));
+
+ expected =
+ "4 unwind DW_CFA_val_offset register(290) 692\n"
+ "4 unwind Raw Data: 0x14 0xa2 0x02 0xb4 0x05\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_val_offset_sf) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x15, 0x56, 0x12});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x103));
+
+ std::string expected =
+ "4 unwind DW_CFA_val_offset_sf register(86) 18\n"
+ "4 unwind Raw Data: 0x15 0x56 0x12\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ // Negative value.
+ ResetLogs();
+ this->memory_.SetMemory(0xa00, std::vector<uint8_t>{0x15, 0xff, 0x01, 0xc0, 0x7f});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0xa00, 0xa05));
+
+ expected =
+ "4 unwind DW_CFA_val_offset_sf register(255) -64\n"
+ "4 unwind Raw Data: 0x15 0xff 0x01 0xc0 0x7f\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_val_expression) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x16, 0x05, 0x02, 0xb0, 0xb1});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x105));
+
+ std::string expected =
+ "4 unwind DW_CFA_val_expression register(5) 2\n"
+ "4 unwind Raw Data: 0x16 0x05 0x02 0xb0 0xb1\n"
+ "4 unwind Illegal\n"
+ "4 unwind Raw Data: 0xb0\n"
+ "4 unwind Illegal\n"
+ "4 unwind Raw Data: 0xb1\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ std::vector<uint8_t> ops{0x16, 0x83, 0x10, 0xa8, 0x01};
+ expected = "4 unwind Raw Data: 0x16 0x83 0x10 0xa8 0x01";
+ std::string op_string;
+ for (uint8_t i = 0; i < 168; i++) {
+ ops.push_back(0xa0 + (i % 96));
+ op_string += "4 unwind Illegal\n";
+ op_string += android::base::StringPrintf("4 unwind Raw Data: 0x%02x\n", ops.back());
+ expected += android::base::StringPrintf(" 0x%02x", ops.back());
+ if (((i + 6) % 10) == 0) {
+ expected += "\n4 unwind Raw Data:";
+ }
+ }
+ expected = "4 unwind DW_CFA_val_expression register(2051) 168\n" + expected + "\n";
+
+ this->memory_.SetMemory(0xa00, ops);
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0xa00, 0xaad));
+
+ ASSERT_EQ(expected + op_string, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_gnu_args_size) {
+ this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0x2e, 0x04});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2000, 0x2002));
+
+ std::string expected =
+ "4 unwind DW_CFA_GNU_args_size 4\n"
+ "4 unwind Raw Data: 0x2e 0x04\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x2e, 0xa4, 0x80, 0x04});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x5000, 0x5004));
+
+ expected =
+ "4 unwind DW_CFA_GNU_args_size 65572\n"
+ "4 unwind Raw Data: 0x2e 0xa4 0x80 0x04\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_gnu_negative_offset_extended) {
+ this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x2f, 0x08, 0x10});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x500, 0x503));
+
+ std::string expected =
+ "4 unwind DW_CFA_GNU_negative_offset_extended register(8) 16\n"
+ "4 unwind Raw Data: 0x2f 0x08 0x10\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x1500, std::vector<uint8_t>{0x2f, 0x81, 0x02, 0xff, 0x01});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x1500, 0x1505));
+
+ expected =
+ "4 unwind DW_CFA_GNU_negative_offset_extended register(257) 255\n"
+ "4 unwind Raw Data: 0x2f 0x81 0x02 0xff 0x01\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_register_override) {
+ this->memory_.SetMemory(0x300, std::vector<uint8_t>{0x09, 0x02, 0x01, 0x09, 0x02, 0x04});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x300, 0x306));
+
+ std::string expected =
+ "4 unwind DW_CFA_register register(2) register(1)\n"
+ "4 unwind Raw Data: 0x09 0x02 0x01\n"
+ "4 unwind DW_CFA_register register(2) register(4)\n"
+ "4 unwind Raw Data: 0x09 0x02 0x04\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+REGISTER_TYPED_TEST_CASE_P(DwarfCfaLogTest, cfa_illegal, cfa_nop, cfa_offset, cfa_offset_extended,
+ cfa_offset_extended_sf, cfa_restore, cfa_restore_extended, cfa_set_loc,
+ cfa_advance_loc, cfa_advance_loc1, cfa_advance_loc2, cfa_advance_loc4,
+ cfa_undefined, cfa_same, cfa_register, cfa_state,
+ cfa_state_cfa_offset_restore, cfa_def_cfa, cfa_def_cfa_sf,
+ cfa_def_cfa_register, cfa_def_cfa_offset, cfa_def_cfa_offset_sf,
+ cfa_def_cfa_expression, cfa_expression, cfa_val_offset,
+ cfa_val_offset_sf, cfa_val_expression, cfa_gnu_args_size,
+ cfa_gnu_negative_offset_extended, cfa_register_override);
+
+typedef ::testing::Types<uint32_t, uint64_t> DwarfCfaLogTestTypes;
+INSTANTIATE_TYPED_TEST_CASE_P(, DwarfCfaLogTest, DwarfCfaLogTestTypes);
diff --git a/libunwindstack/tests/DwarfCfaTest.cpp b/libunwindstack/tests/DwarfCfaTest.cpp
new file mode 100644
index 0000000..6cf028a
--- /dev/null
+++ b/libunwindstack/tests/DwarfCfaTest.cpp
@@ -0,0 +1,959 @@
+/*
+ * 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 <memory>
+#include <unordered_map>
+
+#include <gtest/gtest.h>
+
+#include "DwarfCfa.h"
+#include "DwarfLocation.h"
+#include "DwarfMemory.h"
+#include "DwarfStructs.h"
+#include "Log.h"
+
+#include "LogFake.h"
+#include "MemoryFake.h"
+
+template <typename TypeParam>
+class DwarfCfaTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ ResetLogs();
+ memory_.Clear();
+
+ dmem_.reset(new DwarfMemory(&memory_));
+
+ cie_.cfa_instructions_offset = 0x1000;
+ cie_.cfa_instructions_end = 0x1030;
+ // These two values should be different to distinguish between
+ // operations that deal with code versus data.
+ cie_.code_alignment_factor = 4;
+ cie_.data_alignment_factor = 8;
+
+ fde_.cfa_instructions_offset = 0x2000;
+ fde_.cfa_instructions_end = 0x2030;
+ fde_.pc_start = 0x2000;
+ fde_.cie = &cie_;
+
+ cfa_.reset(new DwarfCfa<TypeParam>(dmem_.get(), &fde_));
+ }
+
+ MemoryFake memory_;
+ std::unique_ptr<DwarfMemory> dmem_;
+ std::unique_ptr<DwarfCfa<TypeParam>> cfa_;
+ DwarfCIE cie_;
+ DwarfFDE fde_;
+};
+TYPED_TEST_CASE_P(DwarfCfaTest);
+
+// NOTE: All test class variables need to be referenced as this->.
+
+TYPED_TEST_P(DwarfCfaTest, cfa_illegal) {
+ for (uint8_t i = 0x17; i < 0x3f; i++) {
+ if (i == 0x2e || i == 0x2f) {
+ // Skip gnu extension ops.
+ continue;
+ }
+ this->memory_.SetMemory(0x2000, std::vector<uint8_t>{i});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_FALSE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2000, 0x2001, &loc_regs));
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->cfa_->last_error());
+ ASSERT_EQ(0x2001U, this->dmem_->cur_offset());
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+ }
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_nop) {
+ this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0x00});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2000, 0x2001, &loc_regs));
+ ASSERT_EQ(0x2001U, this->dmem_->cur_offset());
+ ASSERT_EQ(0U, loc_regs.size());
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+// This test needs to be examined.
+TYPED_TEST_P(DwarfCfaTest, cfa_offset) {
+ this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0x83, 0x04});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2000, 0x2002, &loc_regs));
+ ASSERT_EQ(0x2002U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ auto location = loc_regs.find(3);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_OFFSET, location->second.type);
+ ASSERT_EQ(32U, location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x2100, std::vector<uint8_t>{0x83, 0x84, 0x01});
+ loc_regs.clear();
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2100, 0x2103, &loc_regs));
+ ASSERT_EQ(0x2103U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ location = loc_regs.find(3);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_OFFSET, location->second.type);
+ ASSERT_EQ(1056U, location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_offset_extended) {
+ this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x05, 0x03, 0x02});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x500, 0x503, &loc_regs));
+ ASSERT_EQ(0x503U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ auto location = loc_regs.find(3);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_OFFSET, location->second.type);
+ ASSERT_EQ(2U, location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ loc_regs.clear();
+ this->memory_.SetMemory(0x1500, std::vector<uint8_t>{0x05, 0x81, 0x01, 0x82, 0x12});
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x1500, 0x1505, &loc_regs));
+ ASSERT_EQ(0x1505U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ location = loc_regs.find(129);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_OFFSET, location->second.type);
+ ASSERT_EQ(2306U, location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_offset_extended_sf) {
+ this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x11, 0x05, 0x10});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x500, 0x503, &loc_regs));
+ ASSERT_EQ(0x503U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ auto location = loc_regs.find(5);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_OFFSET, location->second.type);
+ ASSERT_EQ(0x80U, location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ // Check a negative value for the offset.
+ ResetLogs();
+ loc_regs.clear();
+ this->memory_.SetMemory(0x1500, std::vector<uint8_t>{0x11, 0x86, 0x01, 0xff, 0x7f});
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x1500, 0x1505, &loc_regs));
+ ASSERT_EQ(0x1505U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ location = loc_regs.find(134);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_OFFSET, location->second.type);
+ ASSERT_EQ(static_cast<uint64_t>(-8), location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_restore) {
+ this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0xc2});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_FALSE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2000, 0x2001, &loc_regs));
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->cfa_->last_error());
+ ASSERT_EQ(0x2001U, this->dmem_->cur_offset());
+ ASSERT_EQ(0U, loc_regs.size());
+
+ ASSERT_EQ("4 unwind restore while processing cie\n", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ dwarf_loc_regs_t cie_loc_regs;
+ cie_loc_regs[2] = {.type = DWARF_LOCATION_REGISTER, .values = {0, 0}};
+ this->cfa_->set_cie_loc_regs(&cie_loc_regs);
+ this->memory_.SetMemory(0x3000, std::vector<uint8_t>{0x82, 0x04, 0xc2});
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x3000, 0x3003, &loc_regs));
+ ASSERT_EQ(0x3003U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ auto location = loc_regs.find(2);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, location->second.type);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_restore_extended) {
+ this->memory_.SetMemory(0x4000, std::vector<uint8_t>{0x06, 0x08});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_FALSE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x4000, 0x4002, &loc_regs));
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->cfa_->last_error());
+ ASSERT_EQ(0x4002U, this->dmem_->cur_offset());
+ ASSERT_EQ(0U, loc_regs.size());
+
+ ASSERT_EQ("4 unwind restore while processing cie\n", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ loc_regs.clear();
+ this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x05, 0x82, 0x02, 0x04, 0x06, 0x82, 0x02});
+ dwarf_loc_regs_t cie_loc_regs;
+ cie_loc_regs[258] = {.type = DWARF_LOCATION_REGISTER, .values = {0, 0}};
+ this->cfa_->set_cie_loc_regs(&cie_loc_regs);
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x5000, 0x5007, &loc_regs));
+ ASSERT_EQ(0x5007U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ auto location = loc_regs.find(258);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, location->second.type);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_set_loc) {
+ uint8_t buffer[1 + sizeof(TypeParam)];
+ buffer[0] = 0x1;
+ TypeParam address;
+ std::string raw_data("Raw Data: 0x01 ");
+ std::string address_str;
+ if (sizeof(TypeParam) == 4) {
+ address = 0x81234578U;
+ address_str = "0x81234578";
+ raw_data += "0x78 0x45 0x23 0x81";
+ } else {
+ address = 0x8123456712345678ULL;
+ address_str = "0x8123456712345678";
+ raw_data += "0x78 0x56 0x34 0x12 0x67 0x45 0x23 0x81";
+ }
+ memcpy(&buffer[1], &address, sizeof(address));
+
+ this->memory_.SetMemory(0x50, buffer, sizeof(buffer));
+ ResetLogs();
+ dwarf_loc_regs_t loc_regs;
+ ASSERT_TRUE(
+ this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x50, 0x51 + sizeof(TypeParam), &loc_regs));
+ ASSERT_EQ(0x51 + sizeof(TypeParam), this->dmem_->cur_offset());
+ ASSERT_EQ(address, this->cfa_->cur_pc());
+ ASSERT_EQ(0U, loc_regs.size());
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ // Check for a set going back.
+ ResetLogs();
+ loc_regs.clear();
+ this->fde_.pc_start = address + 0x10;
+ ASSERT_TRUE(
+ this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x50, 0x51 + sizeof(TypeParam), &loc_regs));
+ ASSERT_EQ(0x51 + sizeof(TypeParam), this->dmem_->cur_offset());
+ ASSERT_EQ(address, this->cfa_->cur_pc());
+ ASSERT_EQ(0U, loc_regs.size());
+
+ std::string cur_address_str(address_str);
+ cur_address_str[cur_address_str.size() - 2] = '8';
+ std::string expected = "4 unwind Warning: PC is moving backwards: old " + cur_address_str +
+ " new " + address_str + "\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_advance_loc1) {
+ this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x02, 0x04});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x200, 0x202, &loc_regs));
+ ASSERT_EQ(0x202U, this->dmem_->cur_offset());
+ ASSERT_EQ(this->fde_.pc_start + 0x10, this->cfa_->cur_pc());
+ ASSERT_EQ(0U, loc_regs.size());
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_advance_loc2) {
+ this->memory_.SetMemory(0x600, std::vector<uint8_t>{0x03, 0x04, 0x03});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x600, 0x603, &loc_regs));
+ ASSERT_EQ(0x603U, this->dmem_->cur_offset());
+ ASSERT_EQ(this->fde_.pc_start + 0xc10U, this->cfa_->cur_pc());
+ ASSERT_EQ(0U, loc_regs.size());
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_advance_loc4) {
+ this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x04, 0x04, 0x03, 0x02, 0x01});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x500, 0x505, &loc_regs));
+ ASSERT_EQ(0x505U, this->dmem_->cur_offset());
+ ASSERT_EQ(this->fde_.pc_start + 0x4080c10, this->cfa_->cur_pc());
+ ASSERT_EQ(0U, loc_regs.size());
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_undefined) {
+ this->memory_.SetMemory(0xa00, std::vector<uint8_t>{0x07, 0x09});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0xa00, 0xa02, &loc_regs));
+ ASSERT_EQ(0xa02U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ auto location = loc_regs.find(9);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_UNDEFINED, location->second.type);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ loc_regs.clear();
+ this->memory_.SetMemory(0x1a00, std::vector<uint8_t>{0x07, 0x81, 0x01});
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x1a00, 0x1a03, &loc_regs));
+ ASSERT_EQ(0x1a03U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ location = loc_regs.find(129);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_UNDEFINED, location->second.type);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_same) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x08, 0x7f});
+ dwarf_loc_regs_t loc_regs;
+
+ loc_regs[127] = {.type = DWARF_LOCATION_REGISTER, .values = {0, 0}};
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x102, &loc_regs));
+ ASSERT_EQ(0x102U, this->dmem_->cur_offset());
+ ASSERT_EQ(0U, loc_regs.size());
+ ASSERT_EQ(0U, loc_regs.count(127));
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ loc_regs.clear();
+ this->memory_.SetMemory(0x2100, std::vector<uint8_t>{0x08, 0xff, 0x01});
+
+ loc_regs[255] = {.type = DWARF_LOCATION_REGISTER, .values = {0, 0}};
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2100, 0x2103, &loc_regs));
+ ASSERT_EQ(0x2103U, this->dmem_->cur_offset());
+ ASSERT_EQ(0U, loc_regs.size());
+ ASSERT_EQ(0U, loc_regs.count(255));
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_register) {
+ this->memory_.SetMemory(0x300, std::vector<uint8_t>{0x09, 0x02, 0x01});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x300, 0x303, &loc_regs));
+ ASSERT_EQ(0x303U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ auto location = loc_regs.find(2);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, location->second.type);
+ ASSERT_EQ(1U, location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ loc_regs.clear();
+ this->memory_.SetMemory(0x4300, std::vector<uint8_t>{0x09, 0xff, 0x01, 0xff, 0x03});
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x4300, 0x4305, &loc_regs));
+ ASSERT_EQ(0x4305U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ location = loc_regs.find(255);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, location->second.type);
+ ASSERT_EQ(511U, location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_state) {
+ this->memory_.SetMemory(0x300, std::vector<uint8_t>{0x0a});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x300, 0x301, &loc_regs));
+ ASSERT_EQ(0x301U, this->dmem_->cur_offset());
+ ASSERT_EQ(0U, loc_regs.size());
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x4300, std::vector<uint8_t>{0x0b});
+
+ loc_regs.clear();
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x4300, 0x4301, &loc_regs));
+ ASSERT_EQ(0x4301U, this->dmem_->cur_offset());
+ ASSERT_EQ(0U, loc_regs.size());
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0x85, 0x02, 0x0a, 0x86, 0x04, 0x0b});
+
+ loc_regs.clear();
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2000, 0x2005, &loc_regs));
+ ASSERT_EQ(0x2005U, this->dmem_->cur_offset());
+ ASSERT_EQ(2U, loc_regs.size());
+ ASSERT_NE(loc_regs.end(), loc_regs.find(5));
+ ASSERT_NE(loc_regs.end(), loc_regs.find(6));
+
+ loc_regs.clear();
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2000, 0x2006, &loc_regs));
+ ASSERT_EQ(0x2006U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ ASSERT_NE(loc_regs.end(), loc_regs.find(5));
+
+ ResetLogs();
+ this->memory_.SetMemory(
+ 0x6000, std::vector<uint8_t>{0x0a, 0x85, 0x02, 0x0a, 0x86, 0x04, 0x0a, 0x87, 0x01, 0x0a, 0x89,
+ 0x05, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b});
+
+ loc_regs.clear();
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x6000, 0x600c, &loc_regs));
+ ASSERT_EQ(0x600cU, this->dmem_->cur_offset());
+ ASSERT_EQ(4U, loc_regs.size());
+ ASSERT_NE(loc_regs.end(), loc_regs.find(5));
+ ASSERT_NE(loc_regs.end(), loc_regs.find(6));
+ ASSERT_NE(loc_regs.end(), loc_regs.find(7));
+ ASSERT_NE(loc_regs.end(), loc_regs.find(9));
+
+ loc_regs.clear();
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x6000, 0x600d, &loc_regs));
+ ASSERT_EQ(0x600dU, this->dmem_->cur_offset());
+ ASSERT_EQ(3U, loc_regs.size());
+ ASSERT_NE(loc_regs.end(), loc_regs.find(5));
+ ASSERT_NE(loc_regs.end(), loc_regs.find(6));
+ ASSERT_NE(loc_regs.end(), loc_regs.find(7));
+
+ loc_regs.clear();
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x6000, 0x600e, &loc_regs));
+ ASSERT_EQ(0x600eU, this->dmem_->cur_offset());
+ ASSERT_EQ(2U, loc_regs.size());
+ ASSERT_NE(loc_regs.end(), loc_regs.find(5));
+ ASSERT_NE(loc_regs.end(), loc_regs.find(6));
+
+ loc_regs.clear();
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x6000, 0x600f, &loc_regs));
+ ASSERT_EQ(0x600fU, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ ASSERT_NE(loc_regs.end(), loc_regs.find(5));
+
+ loc_regs.clear();
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x6000, 0x6010, &loc_regs));
+ ASSERT_EQ(0x6010U, this->dmem_->cur_offset());
+ ASSERT_EQ(0U, loc_regs.size());
+
+ loc_regs.clear();
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x6000, 0x6011, &loc_regs));
+ ASSERT_EQ(0x6011U, this->dmem_->cur_offset());
+ ASSERT_EQ(0U, loc_regs.size());
+}
+
+// This test verifies that the cfa offset is saved and restored properly.
+// Even though the spec is not clear about whether the offset is also
+// restored, the gcc unwinder does, and libunwind does too.
+TYPED_TEST_P(DwarfCfaTest, cfa_state_cfa_offset_restore) {
+ this->memory_.SetMemory(0x3000, std::vector<uint8_t>{0x0a, 0x0e, 0x40, 0x0b});
+ dwarf_loc_regs_t loc_regs;
+ loc_regs[CFA_REG] = {.type = DWARF_LOCATION_REGISTER, .values = {5, 100}};
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x3000, 0x3004, &loc_regs));
+ ASSERT_EQ(0x3004U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type);
+ ASSERT_EQ(5U, loc_regs[CFA_REG].values[0]);
+ ASSERT_EQ(100U, loc_regs[CFA_REG].values[1]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_def_cfa) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0c, 0x7f, 0x74});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x103, &loc_regs));
+ ASSERT_EQ(0x103U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type);
+ ASSERT_EQ(0x7fU, loc_regs[CFA_REG].values[0]);
+ ASSERT_EQ(0x74U, loc_regs[CFA_REG].values[1]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ loc_regs.clear();
+ this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x0c, 0xff, 0x02, 0xf4, 0x04});
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x200, 0x205, &loc_regs));
+ ASSERT_EQ(0x205U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type);
+ ASSERT_EQ(0x17fU, loc_regs[CFA_REG].values[0]);
+ ASSERT_EQ(0x274U, loc_regs[CFA_REG].values[1]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_def_cfa_sf) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x12, 0x30, 0x25});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x103, &loc_regs));
+ ASSERT_EQ(0x103U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type);
+ ASSERT_EQ(0x30U, loc_regs[CFA_REG].values[0]);
+ ASSERT_EQ(0x128U, loc_regs[CFA_REG].values[1]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ // Test a negative value.
+ ResetLogs();
+ loc_regs.clear();
+ this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x12, 0xa3, 0x01, 0xfa, 0x7f});
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x200, 0x205, &loc_regs));
+ ASSERT_EQ(0x205U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type);
+ ASSERT_EQ(0xa3U, loc_regs[CFA_REG].values[0]);
+ ASSERT_EQ(static_cast<uint64_t>(-48), loc_regs[CFA_REG].values[1]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_def_cfa_register) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0d, 0x72});
+ dwarf_loc_regs_t loc_regs;
+
+ // This fails because the cfa is not defined as a register.
+ ASSERT_FALSE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x102, &loc_regs));
+ ASSERT_EQ(0U, loc_regs.size());
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->cfa_->last_error());
+
+ ASSERT_EQ("4 unwind Attempt to set new register, but cfa is not already set to a register.\n",
+ GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ loc_regs.clear();
+ loc_regs[CFA_REG] = {.type = DWARF_LOCATION_REGISTER, .values = {3, 20}};
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x102, &loc_regs));
+ ASSERT_EQ(0x102U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type);
+ ASSERT_EQ(0x72U, loc_regs[CFA_REG].values[0]);
+ ASSERT_EQ(20U, loc_regs[CFA_REG].values[1]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x0d, 0xf9, 0x20});
+ loc_regs.clear();
+ loc_regs[CFA_REG] = {.type = DWARF_LOCATION_REGISTER, .values = {3, 60}};
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x200, 0x203, &loc_regs));
+ ASSERT_EQ(0x203U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type);
+ ASSERT_EQ(0x1079U, loc_regs[CFA_REG].values[0]);
+ ASSERT_EQ(60U, loc_regs[CFA_REG].values[1]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_def_cfa_offset) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0e, 0x59});
+ dwarf_loc_regs_t loc_regs;
+
+ // This fails because the cfa is not defined as a register.
+ ASSERT_FALSE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x102, &loc_regs));
+ ASSERT_EQ(0U, loc_regs.size());
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->cfa_->last_error());
+
+ ASSERT_EQ("4 unwind Attempt to set offset, but cfa is not set to a register.\n",
+ GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ loc_regs.clear();
+ loc_regs[CFA_REG] = {.type = DWARF_LOCATION_REGISTER, .values = {3}};
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x102, &loc_regs));
+ ASSERT_EQ(0x102U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type);
+ ASSERT_EQ(3U, loc_regs[CFA_REG].values[0]);
+ ASSERT_EQ(0x59U, loc_regs[CFA_REG].values[1]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x0e, 0xd4, 0x0a});
+ loc_regs.clear();
+ loc_regs[CFA_REG] = {.type = DWARF_LOCATION_REGISTER, .values = {3}};
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x200, 0x203, &loc_regs));
+ ASSERT_EQ(0x203U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type);
+ ASSERT_EQ(3U, loc_regs[CFA_REG].values[0]);
+ ASSERT_EQ(0x554U, loc_regs[CFA_REG].values[1]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_def_cfa_offset_sf) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x13, 0x23});
+ dwarf_loc_regs_t loc_regs;
+
+ // This fails because the cfa is not defined as a register.
+ ASSERT_FALSE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x102, &loc_regs));
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->cfa_->last_error());
+
+ ASSERT_EQ("4 unwind Attempt to set offset, but cfa is not set to a register.\n",
+ GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ loc_regs.clear();
+ loc_regs[CFA_REG] = {.type = DWARF_LOCATION_REGISTER, .values = {3}};
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x102, &loc_regs));
+ ASSERT_EQ(0x102U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type);
+ ASSERT_EQ(3U, loc_regs[CFA_REG].values[0]);
+ ASSERT_EQ(0x118U, loc_regs[CFA_REG].values[1]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ // Negative offset.
+ ResetLogs();
+ this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x13, 0xf6, 0x7f});
+ loc_regs.clear();
+ loc_regs[CFA_REG] = {.type = DWARF_LOCATION_REGISTER, .values = {3}};
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x200, 0x203, &loc_regs));
+ ASSERT_EQ(0x203U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type);
+ ASSERT_EQ(3U, loc_regs[CFA_REG].values[0]);
+ ASSERT_EQ(static_cast<TypeParam>(-80), static_cast<TypeParam>(loc_regs[CFA_REG].values[1]));
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_def_cfa_expression) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0f, 0x04, 0x01, 0x02, 0x03, 0x04});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x106, &loc_regs));
+ ASSERT_EQ(0x106U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ std::vector<uint8_t> ops{0x0f, 0x81, 0x01};
+ for (uint8_t i = 3; i < 132; i++) {
+ ops.push_back(i - 1);
+ }
+ this->memory_.SetMemory(0x200, ops);
+ loc_regs.clear();
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x200, 0x284, &loc_regs));
+ ASSERT_EQ(0x284U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_expression) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x10, 0x04, 0x02, 0x40, 0x20});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x105, &loc_regs));
+ ASSERT_EQ(0x105U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ auto location = loc_regs.find(4);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_EXPRESSION, location->second.type);
+ ASSERT_EQ(2U, location->second.values[0]);
+ ASSERT_EQ(0x105U, location->second.values[1]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ std::vector<uint8_t> ops{0x10, 0xff, 0x01, 0x82, 0x01};
+ for (uint8_t i = 5; i < 135; i++) {
+ ops.push_back(i - 4);
+ }
+
+ this->memory_.SetMemory(0x200, ops);
+ loc_regs.clear();
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x200, 0x287, &loc_regs));
+ ASSERT_EQ(0x287U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ location = loc_regs.find(255);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_EXPRESSION, location->second.type);
+ ASSERT_EQ(130U, location->second.values[0]);
+ ASSERT_EQ(0x287U, location->second.values[1]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_val_offset) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x14, 0x45, 0x54});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x103, &loc_regs));
+ ASSERT_EQ(0x103U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ auto location = loc_regs.find(69);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_VAL_OFFSET, location->second.type);
+ ASSERT_EQ(0x2a0U, location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ loc_regs.clear();
+ this->memory_.SetMemory(0x400, std::vector<uint8_t>{0x14, 0xa2, 0x02, 0xb4, 0x05});
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x400, 0x405, &loc_regs));
+ ASSERT_EQ(0x405U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ location = loc_regs.find(290);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_VAL_OFFSET, location->second.type);
+ ASSERT_EQ(0x15a0U, location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_val_offset_sf) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x15, 0x56, 0x12});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x103, &loc_regs));
+ ASSERT_EQ(0x103U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ auto location = loc_regs.find(86);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_VAL_OFFSET, location->second.type);
+ ASSERT_EQ(0x90U, location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ // Negative value.
+ ResetLogs();
+ loc_regs.clear();
+ this->memory_.SetMemory(0xa00, std::vector<uint8_t>{0x15, 0xff, 0x01, 0xc0, 0x7f});
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0xa00, 0xa05, &loc_regs));
+ ASSERT_EQ(0xa05U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ location = loc_regs.find(255);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_VAL_OFFSET, location->second.type);
+ ASSERT_EQ(static_cast<uint64_t>(-512), location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_val_expression) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x16, 0x05, 0x02, 0x10, 0x20});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x105, &loc_regs));
+ ASSERT_EQ(0x105U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ auto location = loc_regs.find(5);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_VAL_EXPRESSION, location->second.type);
+ ASSERT_EQ(2U, location->second.values[0]);
+ ASSERT_EQ(0x105U, location->second.values[1]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ std::vector<uint8_t> ops{0x16, 0x83, 0x10, 0xa8, 0x01};
+ for (uint8_t i = 0; i < 168; i++) {
+ ops.push_back(i);
+ }
+
+ this->memory_.SetMemory(0xa00, ops);
+ loc_regs.clear();
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0xa00, 0xaad, &loc_regs));
+ ASSERT_EQ(0xaadU, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ location = loc_regs.find(2051);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_VAL_EXPRESSION, location->second.type);
+ ASSERT_EQ(168U, location->second.values[0]);
+ ASSERT_EQ(0xaadU, location->second.values[1]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_gnu_args_size) {
+ this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0x2e, 0x04});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2000, 0x2002, &loc_regs));
+ ASSERT_EQ(0x2002U, this->dmem_->cur_offset());
+ ASSERT_EQ(0U, loc_regs.size());
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ loc_regs.clear();
+ this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x2e, 0xa4, 0x80, 0x04});
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x5000, 0x5004, &loc_regs));
+ ASSERT_EQ(0x5004U, this->dmem_->cur_offset());
+ ASSERT_EQ(0U, loc_regs.size());
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_gnu_negative_offset_extended) {
+ this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x2f, 0x08, 0x10});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x500, 0x503, &loc_regs));
+ ASSERT_EQ(0x503U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ auto location = loc_regs.find(8);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_OFFSET, location->second.type);
+ ASSERT_EQ(static_cast<uint64_t>(-16), location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ loc_regs.clear();
+ this->memory_.SetMemory(0x1500, std::vector<uint8_t>{0x2f, 0x81, 0x02, 0xff, 0x01});
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x1500, 0x1505, &loc_regs));
+ ASSERT_EQ(0x1505U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ location = loc_regs.find(257);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_OFFSET, location->second.type);
+ ASSERT_EQ(static_cast<uint64_t>(-255), location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_register_override) {
+ this->memory_.SetMemory(0x300, std::vector<uint8_t>{0x09, 0x02, 0x01, 0x09, 0x02, 0x04});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x300, 0x306, &loc_regs));
+ ASSERT_EQ(0x306U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ auto location = loc_regs.find(2);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, location->second.type);
+ ASSERT_EQ(4U, location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+REGISTER_TYPED_TEST_CASE_P(DwarfCfaTest, cfa_illegal, cfa_nop, cfa_offset, cfa_offset_extended,
+ cfa_offset_extended_sf, cfa_restore, cfa_restore_extended, cfa_set_loc,
+ cfa_advance_loc1, cfa_advance_loc2, cfa_advance_loc4, cfa_undefined,
+ cfa_same, cfa_register, cfa_state, cfa_state_cfa_offset_restore,
+ cfa_def_cfa, cfa_def_cfa_sf, cfa_def_cfa_register, cfa_def_cfa_offset,
+ cfa_def_cfa_offset_sf, cfa_def_cfa_expression, cfa_expression,
+ cfa_val_offset, cfa_val_offset_sf, cfa_val_expression, cfa_gnu_args_size,
+ cfa_gnu_negative_offset_extended, cfa_register_override);
+
+typedef ::testing::Types<uint32_t, uint64_t> DwarfCfaTestTypes;
+INSTANTIATE_TYPED_TEST_CASE_P(, DwarfCfaTest, DwarfCfaTestTypes);
diff --git a/libunwindstack/tests/MapInfoTest.cpp b/libunwindstack/tests/MapInfoTest.cpp
new file mode 100644
index 0000000..c846ad7
--- /dev/null
+++ b/libunwindstack/tests/MapInfoTest.cpp
@@ -0,0 +1,220 @@
+/*
+ * 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 <elf.h>
+#include <errno.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+
+#include "Elf.h"
+#include "MapInfo.h"
+#include "Memory.h"
+
+class MapInfoTest : public ::testing::Test {
+ protected:
+ static void SetUpTestCase() {
+ std::vector<uint8_t> buffer(1024);
+ memcpy(buffer.data(), ELFMAG, SELFMAG);
+ for (size_t i = SELFMAG; i < buffer.size(); i++) {
+ buffer[i] = i / 256 + 1;
+ }
+ ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
+
+ for (size_t i = 0; i < 0x100; i++) {
+ buffer[i] = i / 256 + 1;
+ }
+ memcpy(&buffer[0x100], ELFMAG, SELFMAG);
+ for (size_t i = 0x100 + SELFMAG; i < buffer.size(); i++) {
+ buffer[i] = i / 256 + 1;
+ }
+ ASSERT_TRUE(android::base::WriteFully(elf_at_100_.fd, buffer.data(), buffer.size()));
+ }
+
+ static TemporaryFile elf_;
+
+ static TemporaryFile elf_at_100_;
+};
+TemporaryFile MapInfoTest::elf_;
+TemporaryFile MapInfoTest::elf_at_100_;
+
+TEST_F(MapInfoTest, end_le_start) {
+ MapInfo info{.start = 0x100, .end = 0x100, .offset = 0, .name = elf_.path};
+
+ std::unique_ptr<Memory> memory;
+ memory.reset(info.CreateMemory(getpid()));
+ ASSERT_TRUE(memory.get() == nullptr);
+
+ info.end = 0xff;
+ memory.reset(info.CreateMemory(getpid()));
+ ASSERT_TRUE(memory.get() == nullptr);
+
+ // Make sure this test is valid.
+ info.end = 0x101;
+ memory.reset(info.CreateMemory(getpid()));
+ ASSERT_FALSE(info.CreateMemory(getpid()) == nullptr);
+}
+
+// Verify that if the offset is non-zero but there is no elf at the offset,
+// that the full file is used.
+TEST_F(MapInfoTest, create_memory_file_backed_non_zero_offset_full_file) {
+ MapInfo info{.start = 0x100, .end = 0x200, .offset = 0x100, .name = elf_.path};
+
+ std::unique_ptr<Memory> memory(info.CreateMemory(getpid()));
+ ASSERT_TRUE(memory.get() != nullptr);
+ ASSERT_EQ(0x100U, info.elf_offset);
+
+ // Read the entire file.
+ std::vector<uint8_t> buffer(1024);
+ ASSERT_TRUE(memory->Read(0, buffer.data(), 1024));
+ ASSERT_TRUE(memcmp(buffer.data(), ELFMAG, SELFMAG) == 0);
+ for (size_t i = SELFMAG; i < buffer.size(); i++) {
+ ASSERT_EQ(i / 256 + 1, buffer[i]) << "Failed at byte " << i;
+ }
+
+ ASSERT_FALSE(memory->Read(1024, buffer.data(), 1));
+}
+
+// Verify that if the offset is non-zero and there is an elf at that
+// offset, that only part of the file is used.
+TEST_F(MapInfoTest, create_memory_file_backed_non_zero_offset_partial_file) {
+ MapInfo info{.start = 0x100, .end = 0x200, .offset = 0x100, .name = elf_at_100_.path};
+
+ std::unique_ptr<Memory> memory(info.CreateMemory(getpid()));
+ ASSERT_TRUE(memory.get() != nullptr);
+ ASSERT_EQ(0U, info.elf_offset);
+
+ // Read the valid part of the file.
+ std::vector<uint8_t> buffer(0x100);
+ ASSERT_TRUE(memory->Read(0, buffer.data(), 0x100));
+ ASSERT_TRUE(memcmp(buffer.data(), ELFMAG, SELFMAG) == 0);
+ for (size_t i = SELFMAG; i < buffer.size(); i++) {
+ ASSERT_EQ(2, buffer[i]) << "Failed at byte " << i;
+ }
+
+ ASSERT_FALSE(memory->Read(0x100, buffer.data(), 1));
+}
+
+// Verify that device file names will never result in Memory object creation.
+TEST_F(MapInfoTest, create_memory_check_device_maps) {
+ // Set up some memory so that a valid local memory object would
+ // be returned if the file mapping fails, but the device check is incorrect.
+ std::vector<uint8_t> buffer(1024);
+ MapInfo info;
+ info.start = reinterpret_cast<uint64_t>(buffer.data());
+ info.end = info.start + buffer.size();
+ info.offset = 0;
+ std::unique_ptr<Memory> memory;
+
+ info.flags = 0x8000;
+ info.name = "/dev/something";
+ memory.reset(info.CreateMemory(getpid()));
+ ASSERT_TRUE(memory.get() == nullptr);
+}
+
+TEST_F(MapInfoTest, create_memory_local_memory) {
+ // Set up some memory for a valid local memory object.
+ std::vector<uint8_t> buffer(1024);
+ for (size_t i = 0; i < buffer.size(); i++) {
+ buffer[i] = i % 256;
+ }
+
+ MapInfo info;
+ info.start = reinterpret_cast<uint64_t>(buffer.data());
+ info.end = info.start + buffer.size();
+ info.offset = 0;
+
+ std::unique_ptr<Memory> memory;
+ memory.reset(info.CreateMemory(getpid()));
+ ASSERT_TRUE(memory.get() != nullptr);
+
+ std::vector<uint8_t> read_buffer(1024);
+ ASSERT_TRUE(memory->Read(0, read_buffer.data(), read_buffer.size()));
+ for (size_t i = 0; i < read_buffer.size(); i++) {
+ ASSERT_EQ(i % 256, read_buffer[i]) << "Failed at byte " << i;
+ }
+
+ ASSERT_FALSE(memory->Read(read_buffer.size(), read_buffer.data(), 1));
+}
+
+TEST_F(MapInfoTest, create_memory_remote_memory) {
+ std::vector<uint8_t> buffer(1024);
+ memset(buffer.data(), 0xa, buffer.size());
+
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ while (true)
+ ;
+ exit(1);
+ }
+ ASSERT_LT(0, pid);
+
+ ASSERT_TRUE(ptrace(PTRACE_ATTACH, pid, 0, 0) != -1);
+ uint64_t iterations = 0;
+ siginfo_t si;
+ while (TEMP_FAILURE_RETRY(ptrace(PTRACE_GETSIGINFO, pid, 0, &si)) < 0 && errno == ESRCH) {
+ usleep(30);
+ iterations++;
+ ASSERT_LT(iterations, 500000000ULL);
+ }
+
+ MapInfo info;
+ info.start = reinterpret_cast<uint64_t>(buffer.data());
+ info.end = info.start + buffer.size();
+ info.offset = 0;
+
+ std::unique_ptr<Memory> memory;
+ memory.reset(info.CreateMemory(pid));
+ ASSERT_TRUE(memory.get() != nullptr);
+ // Set the local memory to a different value to guarantee we are reading
+ // from the remote process.
+ memset(buffer.data(), 0x1, buffer.size());
+ std::vector<uint8_t> read_buffer(1024);
+ ASSERT_TRUE(memory->Read(0, read_buffer.data(), read_buffer.size()));
+ for (size_t i = 0; i < read_buffer.size(); i++) {
+ ASSERT_EQ(0xaU, read_buffer[i]) << "Failed at byte " << i;
+ }
+
+ ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0);
+
+ kill(pid, SIGKILL);
+}
+
+TEST_F(MapInfoTest, get_elf) {
+ // Create a map to use as initialization data.
+ void* map = mmap(nullptr, 1024, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ ASSERT_NE(MAP_FAILED, map);
+
+ uint64_t start = reinterpret_cast<uint64_t>(map);
+ MapInfo info{.start = start, .end = start + 1024, .offset = 0, .name = ""};
+
+ // The map contains garbage, but this should still produce an elf object.
+ Elf* elf = info.GetElf(getpid(), false);
+ ASSERT_TRUE(elf != nullptr);
+ ASSERT_FALSE(elf->valid());
+
+ ASSERT_EQ(0, munmap(map, 1024));
+}
diff --git a/libunwindstack/tests/MapsTest.cpp b/libunwindstack/tests/MapsTest.cpp
new file mode 100644
index 0000000..7eb9bae
--- /dev/null
+++ b/libunwindstack/tests/MapsTest.cpp
@@ -0,0 +1,237 @@
+/*
+ * 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 <sys/mman.h>
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+
+#include "Maps.h"
+
+TEST(MapsTest, parse_permissions) {
+ BufferMaps maps(
+ "1000-2000 ---- 00000000 00:00 0\n"
+ "2000-3000 r--- 00000000 00:00 0\n"
+ "3000-4000 -w-- 00000000 00:00 0\n"
+ "4000-5000 --x- 00000000 00:00 0\n"
+ "5000-6000 rwx- 00000000 00:00 0\n");
+
+ ASSERT_TRUE(maps.Parse());
+ ASSERT_EQ(5U, maps.Total());
+ auto it = maps.begin();
+ ASSERT_EQ(PROT_NONE, it->flags);
+ ASSERT_EQ(0x1000U, it->start);
+ ASSERT_EQ(0x2000U, it->end);
+ ASSERT_EQ(0U, it->offset);
+ ASSERT_EQ("", it->name);
+ ++it;
+ ASSERT_EQ(PROT_READ, it->flags);
+ ASSERT_EQ(0x2000U, it->start);
+ ASSERT_EQ(0x3000U, it->end);
+ ASSERT_EQ(0U, it->offset);
+ ASSERT_EQ("", it->name);
+ ++it;
+ ASSERT_EQ(PROT_WRITE, it->flags);
+ ASSERT_EQ(0x3000U, it->start);
+ ASSERT_EQ(0x4000U, it->end);
+ ASSERT_EQ(0U, it->offset);
+ ASSERT_EQ("", it->name);
+ ++it;
+ ASSERT_EQ(PROT_EXEC, it->flags);
+ ASSERT_EQ(0x4000U, it->start);
+ ASSERT_EQ(0x5000U, it->end);
+ ASSERT_EQ(0U, it->offset);
+ ASSERT_EQ("", it->name);
+ ++it;
+ ASSERT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, it->flags);
+ ASSERT_EQ(0x5000U, it->start);
+ ASSERT_EQ(0x6000U, it->end);
+ ASSERT_EQ(0U, it->offset);
+ ASSERT_EQ("", it->name);
+ ++it;
+ ASSERT_EQ(it, maps.end());
+}
+
+TEST(MapsTest, parse_name) {
+ BufferMaps maps(
+ "720b29b000-720b29e000 rw-p 00000000 00:00 0\n"
+ "720b29e000-720b29f000 rw-p 00000000 00:00 0 /system/lib/fake.so\n"
+ "720b29f000-720b2a0000 rw-p 00000000 00:00 0");
+
+ ASSERT_TRUE(maps.Parse());
+ ASSERT_EQ(3U, maps.Total());
+ auto it = maps.begin();
+ ASSERT_EQ("", it->name);
+ ASSERT_EQ(0x720b29b000U, it->start);
+ ASSERT_EQ(0x720b29e000U, it->end);
+ ASSERT_EQ(0U, it->offset);
+ ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags);
+ ++it;
+ ASSERT_EQ("/system/lib/fake.so", it->name);
+ ASSERT_EQ(0x720b29e000U, it->start);
+ ASSERT_EQ(0x720b29f000U, it->end);
+ ASSERT_EQ(0U, it->offset);
+ ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags);
+ ++it;
+ ASSERT_EQ("", it->name);
+ ASSERT_EQ(0x720b29f000U, it->start);
+ ASSERT_EQ(0x720b2a0000U, it->end);
+ ASSERT_EQ(0U, it->offset);
+ ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags);
+ ++it;
+ ASSERT_EQ(it, maps.end());
+}
+
+TEST(MapsTest, parse_offset) {
+ BufferMaps maps(
+ "a000-e000 rw-p 00000000 00:00 0 /system/lib/fake.so\n"
+ "e000-f000 rw-p 00a12345 00:00 0 /system/lib/fake.so\n");
+
+ ASSERT_TRUE(maps.Parse());
+ ASSERT_EQ(2U, maps.Total());
+ auto it = maps.begin();
+ ASSERT_EQ(0U, it->offset);
+ ASSERT_EQ(0xa000U, it->start);
+ ASSERT_EQ(0xe000U, it->end);
+ ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags);
+ ASSERT_EQ("/system/lib/fake.so", it->name);
+ ++it;
+ ASSERT_EQ(0xa12345U, it->offset);
+ ASSERT_EQ(0xe000U, it->start);
+ ASSERT_EQ(0xf000U, it->end);
+ ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags);
+ ASSERT_EQ("/system/lib/fake.so", it->name);
+ ++it;
+ ASSERT_EQ(maps.end(), it);
+}
+
+TEST(MapsTest, device) {
+ BufferMaps maps(
+ "a000-e000 rw-p 00000000 00:00 0 /dev/\n"
+ "f000-f100 rw-p 00000000 00:00 0 /dev/does_not_exist\n"
+ "f100-f200 rw-p 00000000 00:00 0 /dev/ashmem/does_not_exist\n"
+ "f200-f300 rw-p 00000000 00:00 0 /devsomething/does_not_exist\n");
+
+ ASSERT_TRUE(maps.Parse());
+ ASSERT_EQ(4U, maps.Total());
+ auto it = maps.begin();
+ ASSERT_TRUE(it->flags & 0x8000);
+ ASSERT_EQ("/dev/", it->name);
+ ++it;
+ ASSERT_TRUE(it->flags & 0x8000);
+ ASSERT_EQ("/dev/does_not_exist", it->name);
+ ++it;
+ ASSERT_FALSE(it->flags & 0x8000);
+ ASSERT_EQ("/dev/ashmem/does_not_exist", it->name);
+ ++it;
+ ASSERT_FALSE(it->flags & 0x8000);
+ ASSERT_EQ("/devsomething/does_not_exist", it->name);
+}
+
+TEST(MapsTest, file_smoke) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+
+ ASSERT_TRUE(
+ android::base::WriteStringToFile("720b29b000-720b29e000 r-xp a0000000 00:00 0 /fake.so\n"
+ "720b2b0000-720b2e0000 r-xp b0000000 00:00 0 /fake2.so\n"
+ "720b2e0000-720b2f0000 r-xp c0000000 00:00 0 /fake3.so\n",
+ tf.path, 0660, getuid(), getgid()));
+
+ FileMaps maps(tf.path);
+
+ ASSERT_TRUE(maps.Parse());
+ ASSERT_EQ(3U, maps.Total());
+ auto it = maps.begin();
+ ASSERT_EQ(0x720b29b000U, it->start);
+ ASSERT_EQ(0x720b29e000U, it->end);
+ ASSERT_EQ(0xa0000000U, it->offset);
+ ASSERT_EQ(PROT_READ | PROT_EXEC, it->flags);
+ ASSERT_EQ("/fake.so", it->name);
+ ++it;
+ ASSERT_EQ(0x720b2b0000U, it->start);
+ ASSERT_EQ(0x720b2e0000U, it->end);
+ ASSERT_EQ(0xb0000000U, it->offset);
+ ASSERT_EQ(PROT_READ | PROT_EXEC, it->flags);
+ ASSERT_EQ("/fake2.so", it->name);
+ ++it;
+ ASSERT_EQ(0x720b2e0000U, it->start);
+ ASSERT_EQ(0x720b2f0000U, it->end);
+ ASSERT_EQ(0xc0000000U, it->offset);
+ ASSERT_EQ(PROT_READ | PROT_EXEC, it->flags);
+ ASSERT_EQ("/fake3.so", it->name);
+ ++it;
+ ASSERT_EQ(it, maps.end());
+}
+
+TEST(MapsTest, find) {
+ BufferMaps maps(
+ "1000-2000 r--p 00000010 00:00 0 /system/lib/fake1.so\n"
+ "3000-4000 -w-p 00000020 00:00 0 /system/lib/fake2.so\n"
+ "6000-8000 --xp 00000030 00:00 0 /system/lib/fake3.so\n"
+ "a000-b000 rw-p 00000040 00:00 0 /system/lib/fake4.so\n"
+ "e000-f000 rwxp 00000050 00:00 0 /system/lib/fake5.so\n");
+ ASSERT_TRUE(maps.Parse());
+ ASSERT_EQ(5U, maps.Total());
+
+ ASSERT_TRUE(maps.Find(0x500) == nullptr);
+ ASSERT_TRUE(maps.Find(0x2000) == nullptr);
+ ASSERT_TRUE(maps.Find(0x5010) == nullptr);
+ ASSERT_TRUE(maps.Find(0x9a00) == nullptr);
+ ASSERT_TRUE(maps.Find(0xf000) == nullptr);
+ ASSERT_TRUE(maps.Find(0xf010) == nullptr);
+
+ MapInfo* info = maps.Find(0x1000);
+ ASSERT_TRUE(info != nullptr);
+ ASSERT_EQ(0x1000U, info->start);
+ ASSERT_EQ(0x2000U, info->end);
+ ASSERT_EQ(0x10U, info->offset);
+ ASSERT_EQ(PROT_READ, info->flags);
+ ASSERT_EQ("/system/lib/fake1.so", info->name);
+
+ info = maps.Find(0x3020);
+ ASSERT_TRUE(info != nullptr);
+ ASSERT_EQ(0x3000U, info->start);
+ ASSERT_EQ(0x4000U, info->end);
+ ASSERT_EQ(0x20U, info->offset);
+ ASSERT_EQ(PROT_WRITE, info->flags);
+ ASSERT_EQ("/system/lib/fake2.so", info->name);
+
+ info = maps.Find(0x6020);
+ ASSERT_TRUE(info != nullptr);
+ ASSERT_EQ(0x6000U, info->start);
+ ASSERT_EQ(0x8000U, info->end);
+ ASSERT_EQ(0x30U, info->offset);
+ ASSERT_EQ(PROT_EXEC, info->flags);
+ ASSERT_EQ("/system/lib/fake3.so", info->name);
+
+ info = maps.Find(0xafff);
+ ASSERT_TRUE(info != nullptr);
+ ASSERT_EQ(0xa000U, info->start);
+ ASSERT_EQ(0xb000U, info->end);
+ ASSERT_EQ(0x40U, info->offset);
+ ASSERT_EQ(PROT_READ | PROT_WRITE, info->flags);
+ ASSERT_EQ("/system/lib/fake4.so", info->name);
+
+ info = maps.Find(0xe500);
+ ASSERT_TRUE(info != nullptr);
+ ASSERT_EQ(0xe000U, info->start);
+ ASSERT_EQ(0xf000U, info->end);
+ ASSERT_EQ(0x50U, info->offset);
+ ASSERT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, info->flags);
+ ASSERT_EQ("/system/lib/fake5.so", info->name);
+}
diff --git a/libusbhost/Android.bp b/libusbhost/Android.bp
new file mode 100644
index 0000000..a0d6b9b
--- /dev/null
+++ b/libusbhost/Android.bp
@@ -0,0 +1,35 @@
+//
+// 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.
+//
+
+cc_library {
+ name: "libusbhost",
+ host_supported: true,
+ srcs: ["usbhost.c"],
+ cflags: ["-Werror"],
+ export_include_dirs: ["include"],
+ target: {
+ android: {
+ cflags: [
+ "-g",
+ "-DUSE_LIBLOG",
+ ],
+ shared_libs: ["liblog"],
+ },
+ darwin: {
+ enabled: false,
+ },
+ },
+}
diff --git a/libusbhost/Android.mk b/libusbhost/Android.mk
deleted file mode 100644
index 9439846..0000000
--- a/libusbhost/Android.mk
+++ /dev/null
@@ -1,56 +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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-# Static library for Linux host
-# ========================================================
-
-ifeq ($(HOST_OS),linux)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libusbhost
-LOCAL_SRC_FILES := usbhost.c
-LOCAL_CFLAGS := -Werror
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-include $(BUILD_HOST_STATIC_LIBRARY)
-
-endif
-
-# Shared library for target
-# ========================================================
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libusbhost
-LOCAL_SRC_FILES := usbhost.c
-LOCAL_CFLAGS := -g -DUSE_LIBLOG -Werror
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-# needed for logcat
-LOCAL_SHARED_LIBRARIES := libcutils
-include $(BUILD_SHARED_LIBRARY)
-
-# Static library for target
-# ========================================================
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libusbhost
-LOCAL_SRC_FILES := usbhost.c
-LOCAL_CFLAGS := -Werror
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-include $(BUILD_STATIC_LIBRARY)
diff --git a/libusbhost/usbhost.c b/libusbhost/usbhost.c
index 7adb4f2..44b878d 100644
--- a/libusbhost/usbhost.c
+++ b/libusbhost/usbhost.c
@@ -23,7 +23,7 @@
#ifdef USE_LIBLOG
#define LOG_TAG "usbhost"
-#include "utils/Log.h"
+#include "log/log.h"
#define D ALOGD
#else
#define D printf
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 9c300e0..508f553 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -14,9 +14,24 @@
cc_library_headers {
name: "libutils_headers",
+ vendor_available: true,
host_supported: true,
+
+ header_libs: [
+ "libsystem_headers",
+ "libcutils_headers"
+ ],
+ export_header_lib_headers: [
+ "libsystem_headers",
+ "libcutils_headers"
+ ],
export_include_dirs: ["include"],
+
target: {
+ android: {
+ header_libs: ["libbacktrace_headers"],
+ export_header_lib_headers: ["libbacktrace_headers"],
+ },
linux_bionic: {
enabled: true,
},
@@ -28,6 +43,7 @@
cc_library {
name: "libutils",
+ vendor_available: true,
host_supported: true,
srcs: [
@@ -56,8 +72,12 @@
cflags: ["-Werror"],
include_dirs: ["external/safe-iop/include"],
- header_libs: ["libutils_headers"],
- export_header_lib_headers: ["libutils_headers"],
+ header_libs: [
+ "libutils_headers",
+ ],
+ export_header_lib_headers: [
+ "libutils_headers",
+ ],
arch: {
mips: {
@@ -80,6 +100,7 @@
"libcutils",
"libdl",
"liblog",
+ "libvndksupport",
],
sanitize: {
diff --git a/libutils/FileMap.cpp b/libutils/FileMap.cpp
index 1afa1ec..3c4d81c 100644
--- a/libutils/FileMap.cpp
+++ b/libutils/FileMap.cpp
@@ -48,8 +48,16 @@
// Constructor. Create an empty object.
FileMap::FileMap(void)
- : mFileName(NULL), mBasePtr(NULL), mBaseLength(0),
- mDataPtr(NULL), mDataLength(0)
+ : mFileName(NULL),
+ mBasePtr(NULL),
+ mBaseLength(0),
+ mDataPtr(NULL),
+ mDataLength(0)
+#if defined(__MINGW32__)
+ ,
+ mFileHandle(INVALID_HANDLE_VALUE),
+ mFileMapping(NULL)
+#endif
{
}
@@ -65,8 +73,8 @@
other.mBasePtr = NULL;
other.mDataPtr = NULL;
#if defined(__MINGW32__)
- other.mFileHandle = 0;
- other.mFileMapping = 0;
+ other.mFileHandle = INVALID_HANDLE_VALUE;
+ other.mFileMapping = NULL;
#endif
}
@@ -84,8 +92,8 @@
#if defined(__MINGW32__)
mFileHandle = other.mFileHandle;
mFileMapping = other.mFileMapping;
- other.mFileHandle = 0;
- other.mFileMapping = 0;
+ other.mFileHandle = INVALID_HANDLE_VALUE;
+ other.mFileMapping = NULL;
#endif
return *this;
}
@@ -101,7 +109,7 @@
ALOGD("UnmapViewOfFile(%p) failed, error = %lu\n", mBasePtr,
GetLastError() );
}
- if (mFileMapping != INVALID_HANDLE_VALUE) {
+ if (mFileMapping != NULL) {
CloseHandle(mFileMapping);
}
#else
@@ -156,7 +164,7 @@
ALOGE("MapViewOfFile(%" PRId64 ", %zu) failed with error %lu\n",
adjOffset, adjLength, GetLastError() );
CloseHandle(mFileMapping);
- mFileMapping = INVALID_HANDLE_VALUE;
+ mFileMapping = NULL;
return false;
}
#else // !defined(__MINGW32__)
diff --git a/libutils/ProcessCallStack.cpp b/libutils/ProcessCallStack.cpp
index ff8b32a..b8fb6dc 100644
--- a/libutils/ProcessCallStack.cpp
+++ b/libutils/ProcessCallStack.cpp
@@ -20,6 +20,8 @@
#include <utils/ProcessCallStack.h>
#include <dirent.h>
+#include <unistd.h>
+
#include <memory>
#include <utils/Printer.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/misc.cpp b/libutils/misc.cpp
index 2608a65..d95fd05 100644
--- a/libutils/misc.cpp
+++ b/libutils/misc.cpp
@@ -16,16 +16,19 @@
#define LOG_TAG "misc"
-//
-// Miscellaneous utility functions.
-//
#include <utils/misc.h>
-#include <utils/Log.h>
-#if !defined(_WIN32)
+#include <pthread.h>
+
+#include <utils/Log.h>
+#include <utils/Vector.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;
@@ -65,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;
@@ -80,5 +112,3 @@
}
#endif
}
-
-}; // namespace android
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/include/system/window.h b/libvndksupport/include/vndksupport/linker.h
similarity index 63%
copy from include/system/window.h
copy to libvndksupport/include/vndksupport/linker.h
index efa10d6..f509564 100644
--- a/include/system/window.h
+++ b/libvndksupport/include/vndksupport/linker.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 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,10 +13,19 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#ifndef VNDKSUPPORT_LINKER_H_
+#define VNDKSUPPORT_LINKER_H_
-#ifndef SYSTEM_CORE_INCLUDE_ANDROID_WINDOW_H
-#define SYSTEM_CORE_INCLUDE_ANDROID_WINDOW_H
+#ifdef __cplusplus
+extern "C" {
+#endif
-#include <system/window-deprecated.h>
+void* android_load_sphal_library(const char* name, int flag);
-#endif /* SYSTEM_CORE_INCLUDE_ANDROID_WINDOW_H */
+int android_unload_sphal_library(void* handle);
+
+#ifdef __cplusplus
+}
+#endif
+
+#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..696e978
--- /dev/null
+++ b/libvndksupport/linker.c
@@ -0,0 +1,50 @@
+/*
+ * 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/.clang-format b/libziparchive/.clang-format
new file mode 120000
index 0000000..fd0645f
--- /dev/null
+++ b/libziparchive/.clang-format
@@ -0,0 +1 @@
+../.clang-format-2
\ No newline at end of file
diff --git a/libziparchive/Android.bp b/libziparchive/Android.bp
index 44daf36..287a99c 100644
--- a/libziparchive/Android.bp
+++ b/libziparchive/Android.bp
@@ -56,6 +56,8 @@
cc_library {
name: "libziparchive",
host_supported: true,
+ vendor_available:true,
+
defaults: ["libziparchive_defaults", "libziparchive_flags"],
shared_libs: ["liblog", "libbase"],
target: {
@@ -122,3 +124,29 @@
},
},
}
+
+// Performance benchmarks.
+cc_benchmark {
+ name: "ziparchive-benchmarks",
+ defaults: ["libziparchive_flags"],
+
+ srcs: [
+ "zip_archive_benchmark.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ "liblog",
+ ],
+
+ static_libs: [
+ "libziparchive",
+ "libz",
+ "libutils",
+ ],
+
+ target: {
+ host: {
+ cppflags: ["-Wno-unnamed-type-template-args"],
+ },
+ },
+}
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index c2055b7..78de40a 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -386,6 +386,14 @@
const uint8_t* const cd_end = cd_ptr + cd_length;
const uint8_t* ptr = cd_ptr;
for (uint16_t i = 0; i < num_entries; i++) {
+ if (ptr > cd_end - sizeof(CentralDirectoryRecord)) {
+ ALOGW("Zip: ran off the end (at %" PRIu16 ")", i);
+#if defined(__ANDROID__)
+ android_errorWriteLog(0x534e4554, "36392138");
+#endif
+ return -1;
+ }
+
const CentralDirectoryRecord* cdr =
reinterpret_cast<const CentralDirectoryRecord*>(ptr);
if (cdr->record_signature != CentralDirectoryRecord::kSignature) {
@@ -393,11 +401,6 @@
return -1;
}
- if (ptr + sizeof(CentralDirectoryRecord) > cd_end) {
- ALOGW("Zip: ran off the end (at %" PRIu16 ")", i);
- return -1;
- }
-
const off64_t local_header_offset = cdr->local_file_header_offset;
if (local_header_offset >= archive->directory_offset) {
ALOGW("Zip: bad LFH offset %" PRId64 " at entry %" PRIu16,
@@ -573,6 +576,17 @@
// Paranoia: Match the values specified in the local file header
// to those specified in the central directory.
+
+ // Verify that the central directory and local file header have the same general purpose bit
+ // flags set.
+ if (lfh->gpb_flags != cdr->gpb_flags) {
+ ALOGW("Zip: gpb flag mismatch. expected {%04" PRIx16 "}, was {%04" PRIx16 "}",
+ cdr->gpb_flags, lfh->gpb_flags);
+ return kInconsistentInformation;
+ }
+
+ // If there is no trailing data descriptor, verify that the central directory and local file
+ // header agree on the crc, compressed, and uncompressed sizes of the entry.
if ((lfh->gpb_flags & kGPBDDFlagMask) == 0) {
data->has_data_descriptor = 0;
if (data->compressed_length != lfh->compressed_size
diff --git a/libziparchive/zip_archive_benchmark.cpp b/libziparchive/zip_archive_benchmark.cpp
new file mode 100644
index 0000000..cd3e164
--- /dev/null
+++ b/libziparchive/zip_archive_benchmark.cpp
@@ -0,0 +1,87 @@
+/*
+ * 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 <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <iostream>
+#include <string>
+#include <tuple>
+#include <vector>
+
+#include <android-base/test_utils.h>
+#include <benchmark/benchmark.h>
+#include <ziparchive/zip_archive.h>
+#include <ziparchive/zip_archive_stream_entry.h>
+#include <ziparchive/zip_writer.h>
+
+static TemporaryFile* CreateZip() {
+ TemporaryFile* result = new TemporaryFile;
+ FILE* fp = fdopen(result->fd, "w");
+
+ ZipWriter writer(fp);
+ std::string lastName = "file";
+ for (size_t i = 0; i < 1000; i++) {
+ // Make file names longer and longer.
+ lastName = lastName + std::to_string(i);
+ writer.StartEntry(lastName.c_str(), ZipWriter::kCompress);
+ writer.WriteBytes("helo", 4);
+ writer.FinishEntry();
+ }
+ writer.Finish();
+ fclose(fp);
+
+ return result;
+}
+
+static void FindEntry_no_match(benchmark::State& state) {
+ // Create a temporary zip archive.
+ std::unique_ptr<TemporaryFile> temp_file(CreateZip());
+ ZipArchiveHandle handle;
+ ZipEntry data;
+
+ // In order to walk through all file names in the archive, look for a name
+ // that does not exist in the archive.
+ ZipString name("thisFileNameDoesNotExist");
+
+ // Start the benchmark.
+ while (state.KeepRunning()) {
+ OpenArchive(temp_file->path, &handle);
+ FindEntry(handle, name, &data);
+ CloseArchive(handle);
+ }
+}
+BENCHMARK(FindEntry_no_match);
+
+static void Iterate_all_files(benchmark::State& state) {
+ std::unique_ptr<TemporaryFile> temp_file(CreateZip());
+ ZipArchiveHandle handle;
+ void* iteration_cookie;
+ ZipEntry data;
+ ZipString name;
+
+ while (state.KeepRunning()) {
+ OpenArchive(temp_file->path, &handle);
+ StartIteration(handle, &iteration_cookie, nullptr, nullptr);
+ while (Next(iteration_cookie, &data, &name) == 0) {
+ }
+ EndIteration(iteration_cookie);
+ CloseArchive(handle);
+ }
+}
+BENCHMARK(Iterate_all_files);
+
+BENCHMARK_MAIN()
diff --git a/libziparchive/zip_writer.cc b/libziparchive/zip_writer.cc
index 7600528..6d28bdb 100644
--- a/libziparchive/zip_writer.cc
+++ b/libziparchive/zip_writer.cc
@@ -18,6 +18,7 @@
#include <cstdio>
#include <sys/param.h>
+#include <sys/stat.h>
#include <zlib.h>
#define DEF_MEM_LEVEL 8 // normally in zutil.h?
@@ -84,11 +85,19 @@
delete stream;
}
-ZipWriter::ZipWriter(FILE* f) : file_(f), current_offset_(0), state_(State::kWritingZip),
- z_stream_(nullptr, DeleteZStream), buffer_(kBufSize) {
+ZipWriter::ZipWriter(FILE* f) : file_(f), seekable_(false), current_offset_(0),
+ state_(State::kWritingZip), z_stream_(nullptr, DeleteZStream),
+ buffer_(kBufSize) {
+ // Check if the file is seekable (regular file). If fstat fails, that's fine, subsequent calls
+ // will fail as well.
+ struct stat file_stats;
+ if (fstat(fileno(f), &file_stats) == 0) {
+ seekable_ = S_ISREG(file_stats.st_mode);
+ }
}
ZipWriter::ZipWriter(ZipWriter&& writer) : file_(writer.file_),
+ seekable_(writer.seekable_),
current_offset_(writer.current_offset_),
state_(writer.state_),
files_(std::move(writer.files_)),
@@ -100,6 +109,7 @@
ZipWriter& ZipWriter::operator=(ZipWriter&& writer) {
file_ = writer.file_;
+ seekable_ = writer.seekable_;
current_offset_ = writer.current_offset_;
state_ = writer.state_;
files_ = std::move(writer.files_);
@@ -159,6 +169,30 @@
*out_time = ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1;
}
+static void CopyFromFileEntry(const ZipWriter::FileEntry& src, bool use_data_descriptor,
+ LocalFileHeader* dst) {
+ dst->lfh_signature = LocalFileHeader::kSignature;
+ if (use_data_descriptor) {
+ // Set this flag to denote that a DataDescriptor struct will appear after the data,
+ // containing the crc and size fields.
+ dst->gpb_flags |= kGPBDDFlagMask;
+
+ // The size and crc fields must be 0.
+ dst->compressed_size = 0u;
+ dst->uncompressed_size = 0u;
+ dst->crc32 = 0u;
+ } else {
+ dst->compressed_size = src.compressed_size;
+ dst->uncompressed_size = src.uncompressed_size;
+ dst->crc32 = src.crc32;
+ }
+ dst->compression_method = src.compression_method;
+ dst->last_mod_time = src.last_mod_time;
+ dst->last_mod_date = src.last_mod_date;
+ dst->file_name_length = src.path.size();
+ dst->extra_field_length = src.padding_length;
+}
+
int32_t ZipWriter::StartAlignedEntryWithTime(const char* path, size_t flags,
time_t time, uint32_t alignment) {
if (state_ != State::kWritingZip) {
@@ -173,66 +207,58 @@
return kInvalidAlignment;
}
- current_file_entry_ = {};
- current_file_entry_.path = path;
- current_file_entry_.local_file_header_offset = current_offset_;
+ FileEntry file_entry = {};
+ file_entry.local_file_header_offset = current_offset_;
+ file_entry.path = path;
- if (!IsValidEntryName(reinterpret_cast<const uint8_t*>(current_file_entry_.path.data()),
- current_file_entry_.path.size())) {
+ if (!IsValidEntryName(reinterpret_cast<const uint8_t*>(file_entry.path.data()),
+ file_entry.path.size())) {
return kInvalidEntryName;
}
- LocalFileHeader header = {};
- header.lfh_signature = LocalFileHeader::kSignature;
-
- // Set this flag to denote that a DataDescriptor struct will appear after the data,
- // containing the crc and size fields.
- header.gpb_flags |= kGPBDDFlagMask;
-
if (flags & ZipWriter::kCompress) {
- current_file_entry_.compression_method = kCompressDeflated;
+ file_entry.compression_method = kCompressDeflated;
int32_t result = PrepareDeflate();
if (result != kNoError) {
return result;
}
} else {
- current_file_entry_.compression_method = kCompressStored;
+ file_entry.compression_method = kCompressStored;
}
- header.compression_method = current_file_entry_.compression_method;
- ExtractTimeAndDate(time, ¤t_file_entry_.last_mod_time, ¤t_file_entry_.last_mod_date);
- header.last_mod_time = current_file_entry_.last_mod_time;
- header.last_mod_date = current_file_entry_.last_mod_date;
+ ExtractTimeAndDate(time, &file_entry.last_mod_time, &file_entry.last_mod_date);
- header.file_name_length = current_file_entry_.path.size();
-
- off64_t offset = current_offset_ + sizeof(header) + current_file_entry_.path.size();
+ off_t offset = current_offset_ + sizeof(LocalFileHeader) + file_entry.path.size();
std::vector<char> zero_padding;
if (alignment != 0 && (offset & (alignment - 1))) {
// Pad the extra field so the data will be aligned.
uint16_t padding = alignment - (offset % alignment);
- header.extra_field_length = padding;
+ file_entry.padding_length = padding;
offset += padding;
- zero_padding.resize(padding);
- memset(zero_padding.data(), 0, zero_padding.size());
+ zero_padding.resize(padding, 0);
}
+ LocalFileHeader header = {};
+ // Always start expecting a data descriptor. When the data has finished being written,
+ // if it is possible to seek back, the GPB flag will reset and the sizes written.
+ CopyFromFileEntry(file_entry, true /*use_data_descriptor*/, &header);
+
if (fwrite(&header, sizeof(header), 1, file_) != 1) {
return HandleError(kIoError);
}
- if (fwrite(path, sizeof(*path), current_file_entry_.path.size(), file_)
- != current_file_entry_.path.size()) {
+ if (fwrite(path, sizeof(*path), file_entry.path.size(), file_) != file_entry.path.size()) {
return HandleError(kIoError);
}
- if (header.extra_field_length != 0 &&
- fwrite(zero_padding.data(), 1, header.extra_field_length, file_)
- != header.extra_field_length) {
+ if (file_entry.padding_length != 0 &&
+ fwrite(zero_padding.data(), 1, file_entry.padding_length, file_)
+ != file_entry.padding_length) {
return HandleError(kIoError);
}
+ current_file_entry_ = std::move(file_entry);
current_offset_ = offset;
state_ = State::kWritingEntry;
return kNoError;
@@ -405,23 +431,41 @@
}
}
- const uint32_t sig = DataDescriptor::kOptSignature;
- if (fwrite(&sig, sizeof(sig), 1, file_) != 1) {
- state_ = State::kError;
- return kIoError;
- }
+ if ((current_file_entry_.compression_method & kCompressDeflated) || !seekable_) {
+ // Some versions of ZIP don't allow STORED data to have a trailing DataDescriptor.
+ // If this file is not seekable, or if the data is compressed, write a DataDescriptor.
+ const uint32_t sig = DataDescriptor::kOptSignature;
+ if (fwrite(&sig, sizeof(sig), 1, file_) != 1) {
+ return HandleError(kIoError);
+ }
- DataDescriptor dd = {};
- dd.crc32 = current_file_entry_.crc32;
- dd.compressed_size = current_file_entry_.compressed_size;
- dd.uncompressed_size = current_file_entry_.uncompressed_size;
- if (fwrite(&dd, sizeof(dd), 1, file_) != 1) {
- return HandleError(kIoError);
+ DataDescriptor dd = {};
+ dd.crc32 = current_file_entry_.crc32;
+ dd.compressed_size = current_file_entry_.compressed_size;
+ dd.uncompressed_size = current_file_entry_.uncompressed_size;
+ if (fwrite(&dd, sizeof(dd), 1, file_) != 1) {
+ return HandleError(kIoError);
+ }
+ current_offset_ += sizeof(DataDescriptor::kOptSignature) + sizeof(dd);
+ } else {
+ // Seek back to the header and rewrite to include the size.
+ if (fseeko(file_, current_file_entry_.local_file_header_offset, SEEK_SET) != 0) {
+ return HandleError(kIoError);
+ }
+
+ LocalFileHeader header = {};
+ CopyFromFileEntry(current_file_entry_, false /*use_data_descriptor*/, &header);
+
+ if (fwrite(&header, sizeof(header), 1, file_) != 1) {
+ return HandleError(kIoError);
+ }
+
+ if (fseeko(file_, current_offset_, SEEK_SET) != 0) {
+ return HandleError(kIoError);
+ }
}
files_.emplace_back(std::move(current_file_entry_));
-
- current_offset_ += sizeof(DataDescriptor::kOptSignature) + sizeof(dd);
state_ = State::kWritingZip;
return kNoError;
}
@@ -431,11 +475,13 @@
return kInvalidState;
}
- off64_t startOfCdr = current_offset_;
+ off_t startOfCdr = current_offset_;
for (FileEntry& file : files_) {
CentralDirectoryRecord cdr = {};
cdr.record_signature = CentralDirectoryRecord::kSignature;
- cdr.gpb_flags |= kGPBDDFlagMask;
+ if ((file.compression_method & kCompressDeflated) || !seekable_) {
+ cdr.gpb_flags |= kGPBDDFlagMask;
+ }
cdr.compression_method = file.compression_method;
cdr.last_mod_time = file.last_mod_time;
cdr.last_mod_date = file.last_mod_date;
@@ -443,7 +489,7 @@
cdr.compressed_size = file.compressed_size;
cdr.uncompressed_size = file.uncompressed_size;
cdr.file_name_length = file.path.size();
- cdr.local_file_header_offset = file.local_file_header_offset;
+ cdr.local_file_header_offset = static_cast<uint32_t>(file.local_file_header_offset);
if (fwrite(&cdr, sizeof(cdr), 1, file_) != 1) {
return HandleError(kIoError);
}
@@ -473,7 +519,7 @@
// Since we can BackUp() and potentially finish writing at an offset less than one we had
// already written at, we must truncate the file.
- if (ftruncate64(fileno(file_), current_offset_) != 0) {
+ if (ftruncate(fileno(file_), current_offset_) != 0) {
return HandleError(kIoError);
}
diff --git a/libziparchive/zip_writer_test.cc b/libziparchive/zip_writer_test.cc
index 30f4950..5b526a4 100644
--- a/libziparchive/zip_writer_test.cc
+++ b/libziparchive/zip_writer_test.cc
@@ -64,6 +64,7 @@
ZipEntry data;
ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
EXPECT_EQ(kCompressStored, data.method);
+ EXPECT_EQ(0u, data.has_data_descriptor);
EXPECT_EQ(strlen(expected), data.compressed_length);
ASSERT_EQ(strlen(expected), data.uncompressed_length);
ASSERT_TRUE(AssertFileEntryContentsEq(expected, handle, &data));
diff --git a/lmkd/Android.bp b/lmkd/Android.bp
new file mode 100644
index 0000000..3f8a503
--- /dev/null
+++ b/lmkd/Android.bp
@@ -0,0 +1,13 @@
+cc_binary {
+ name: "lmkd",
+
+ srcs: ["lmkd.c"],
+ shared_libs: [
+ "liblog",
+ "libprocessgroup",
+ "libcutils",
+ ],
+ cflags: ["-Werror"],
+
+ init_rc: ["lmkd.rc"],
+}
diff --git a/lmkd/Android.mk b/lmkd/Android.mk
deleted file mode 100644
index 8980d1c..0000000
--- a/lmkd/Android.mk
+++ /dev/null
@@ -1,12 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := lmkd.c
-LOCAL_SHARED_LIBRARIES := liblog libm libc libprocessgroup libcutils
-LOCAL_CFLAGS := -Werror
-
-LOCAL_MODULE := lmkd
-
-LOCAL_INIT_RC := lmkd.rc
-
-include $(BUILD_EXECUTABLE)
diff --git a/logcat/event.logtags b/logcat/event.logtags
index 9f05351..efcc817 100644
--- a/logcat/event.logtags
+++ b/logcat/event.logtags
@@ -30,6 +30,7 @@
# 4: Number of allocations
# 5: Id
# 6: Percent
+# s: Number of seconds (monotonic time)
# Default value for data of type int/long is 2 (bytes).
#
# TODO: generate ".java" and ".h" files with integer constants from this file.
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 64d1d2f..e9ef9cc 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -530,6 +530,7 @@
" process — Display PID only.\n"
" raw — Display the raw log message, with no other metadata fields.\n"
" tag — Display the priority/tag only.\n"
+ " thread — Display priority, PID and TID of process issuing the message.\n"
" threadtime — Display the date, invocation time, priority, tag, and the PID\n"
" and TID of the thread issuing the message. (the default format).\n"
" time — Display the date, invocation time, priority/tag, and PID of the\n"
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index 21868f2..786fb14 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -17,6 +17,7 @@
#include <ctype.h>
#include <dirent.h>
#include <signal.h>
+#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -30,7 +31,9 @@
#include <string>
#include <android-base/file.h>
+#include <android-base/stringprintf.h>
#include <gtest/gtest.h>
+#include <log/event_tag_map.h>
#include <log/log.h>
#include <log/log_event_list.h>
@@ -47,6 +50,16 @@
#define BIG_BUFFER (5 * 1024)
+// rest(), let the logs settle.
+//
+// logd is in a background cgroup and under extreme load can take up to
+// 3 seconds to land a log entry. Under moderate load we can do with 200ms.
+static void rest() {
+ static const useconds_t restPeriod = 200000;
+
+ usleep(restPeriod);
+}
+
// enhanced version of LOG_FAILURE_RETRY to add support for EAGAIN and
// non-syscall libs. Since we are only using this in the emergency of
// a signal to stuff a terminating code into the logs, we will spin rather
@@ -68,9 +81,14 @@
logcat_define(ctx);
#undef LOG_TAG
-#define LOG_TAG "inject"
- RLOGE(logcat_executable ".buckets");
- sleep(1);
+#define LOG_TAG "inject.buckets"
+ // inject messages into radio, system, main and events buffers to
+ // ensure that we see all the begin[] bucket messages.
+ RLOGE(logcat_executable);
+ SLOGE(logcat_executable);
+ ALOGE(logcat_executable);
+ __android_log_bswrite(0, logcat_executable ".inject.buckets");
+ rest();
ASSERT_TRUE(NULL !=
(fp = logcat_popen(
@@ -95,32 +113,45 @@
logcat_pclose(ctx, fp);
- EXPECT_EQ(15, ids);
+ EXPECT_EQ(ids, 15);
- EXPECT_EQ(4, count);
+ EXPECT_EQ(count, 4);
}
TEST(logcat, event_tag_filter) {
FILE* fp;
logcat_define(ctx);
- ASSERT_TRUE(NULL !=
- (fp = logcat_popen(ctx, logcat_executable
- " -b events -d -s auditd "
- "am_proc_start am_pss am_proc_bound "
- "dvm_lock_sample am_wtf 2>/dev/null")));
+#undef LOG_TAG
+#define LOG_TAG "inject.filter"
+ // inject messages into radio, system and main buffers
+ // with our unique log tag to test logcat filter.
+ RLOGE(logcat_executable);
+ SLOGE(logcat_executable);
+ ALOGE(logcat_executable);
+ rest();
+
+ std::string command = android::base::StringPrintf(
+ logcat_executable
+ " -b radio -b system -b main --pid=%d -d -s inject.filter 2>/dev/null",
+ getpid());
+ ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, command.c_str())));
char buffer[BIG_BUFFER];
int count = 0;
while (fgets(buffer, sizeof(buffer), fp)) {
- ++count;
+ if (strncmp(begin, buffer, sizeof(begin) - 1)) ++count;
}
logcat_pclose(ctx, fp);
- EXPECT_LT(4, count);
+ // logcat, liblogcat and logcatd test instances result in the progression
+ // of 3, 6 and 9 for our counts as each round is performed.
+ EXPECT_GE(count, 3);
+ EXPECT_LE(count, 9);
+ EXPECT_EQ(count % 3, 0);
}
// If there is not enough background noise in the logs, then spam the logs to
@@ -364,7 +395,7 @@
} while ((count < 10) && --tries && inject(10 - count));
- EXPECT_EQ(10, count); // We want _some_ history, too small, falses below
+ EXPECT_EQ(count, 10); // We want _some_ history, too small, falses below
EXPECT_TRUE(last_timestamp != NULL);
EXPECT_TRUE(first_timestamp != NULL);
EXPECT_TRUE(second_timestamp != NULL);
@@ -689,9 +720,9 @@
pclose(fp);
- EXPECT_LE(2, count);
+ EXPECT_GE(count, 2);
- EXPECT_EQ(1, signals);
+ EXPECT_EQ(signals, 1);
}
static void caught_blocking_tail(int signum) {
@@ -759,9 +790,9 @@
pclose(fp);
- EXPECT_LE(2, count);
+ EXPECT_GE(count, 2);
- EXPECT_EQ(1, signals);
+ EXPECT_EQ(signals, 1);
}
#endif
@@ -869,7 +900,7 @@
ADD_FAILURE();
}
pclose(fp);
- EXPECT_EQ(11, log_file_count);
+ EXPECT_EQ(log_file_count, 11);
}
snprintf(command, sizeof(command), "rm -rf %s", tmp_out_dir);
EXPECT_FALSE(IsFalse(system(command), command));
@@ -1122,17 +1153,17 @@
char tmp_out_dir[strlen(tmp_out_dir_form) + 1];
ASSERT_TRUE(NULL != mkdtemp(strcpy(tmp_out_dir, tmp_out_dir_form)));
- EXPECT_EQ(34, logrotate_count_id(logcat_cmd, tmp_out_dir));
- EXPECT_EQ(34, logrotate_count_id(logcat_short_cmd, tmp_out_dir));
+ EXPECT_EQ(logrotate_count_id(logcat_cmd, tmp_out_dir), 34);
+ EXPECT_EQ(logrotate_count_id(logcat_short_cmd, tmp_out_dir), 34);
char id_file[strlen(tmp_out_dir_form) + strlen(log_filename) + 5];
snprintf(id_file, sizeof(id_file), "%s/%s.id", tmp_out_dir, log_filename);
if (getuid() != 0) {
chmod(id_file, 0);
- EXPECT_EQ(34, logrotate_count_id(logcat_short_cmd, tmp_out_dir));
+ EXPECT_EQ(logrotate_count_id(logcat_short_cmd, tmp_out_dir), 34);
}
unlink(id_file);
- EXPECT_EQ(34, logrotate_count_id(logcat_short_cmd, tmp_out_dir));
+ EXPECT_EQ(logrotate_count_id(logcat_short_cmd, tmp_out_dir), 34);
FILE* fp = fopen(id_file, "w");
if (fp) {
@@ -1147,9 +1178,9 @@
}
int new_signature;
- EXPECT_LE(
- 2, (new_signature = logrotate_count_id(logcat_short_cmd, tmp_out_dir)));
- EXPECT_GT(34, new_signature);
+ EXPECT_GE(
+ (new_signature = logrotate_count_id(logcat_short_cmd, tmp_out_dir)), 2);
+ EXPECT_LT(new_signature, 34);
static const char cleanup_cmd[] = "rm -rf %s";
char command[strlen(cleanup_cmd) + strlen(tmp_out_dir_form)];
@@ -1204,7 +1235,12 @@
signal(SIGALRM, caught_blocking_clear);
alarm(2);
while (fgets(buffer, sizeof(buffer), fp)) {
- if (!strncmp(buffer, "clearLog: ", 10)) {
+ if (!strncmp(buffer, "clearLog: ", strlen("clearLog: "))) {
+ fprintf(stderr, "WARNING: Test lacks permission to run :-(\n");
+ count = signals = 1;
+ break;
+ }
+ if (!strncmp(buffer, "failed to clear", strlen("failed to clear"))) {
fprintf(stderr, "WARNING: Test lacks permission to run :-(\n");
count = signals = 1;
break;
@@ -1285,10 +1321,10 @@
pclose(fp);
- EXPECT_LE(1, count);
- EXPECT_EQ(1, minus_g);
+ EXPECT_GE(count, 1);
+ EXPECT_EQ(minus_g, 1);
- EXPECT_EQ(1, signals);
+ EXPECT_EQ(signals, 1);
}
#endif
@@ -1412,7 +1448,7 @@
LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, logcat_regex_prefix,
logcat_regex_prefix "_aaaa"));
// Let the logs settle
- sleep(1);
+ rest();
ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, buffer)));
@@ -1450,8 +1486,7 @@
LOG_FAILURE_RETRY(
__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test"));
- // Let the logs settle
- sleep(1);
+ rest();
ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, buffer)));
@@ -1476,8 +1511,7 @@
static bool End_to_End(const char* tag, const char* fmt, ...) {
logcat_define(ctx);
- FILE* fp = logcat_popen(ctx,
- "logcat"
+ FILE* fp = logcat_popen(ctx, logcat_executable
" -v brief"
" -b events"
" -v descriptive"
@@ -1523,13 +1557,12 @@
// Help us pinpoint where things went wrong ...
fprintf(stderr, "Closest match for\n %s\n is\n %s",
expect.c_str(), lastMatch.c_str());
- } else if (count > 2) {
+ } else if (count > 3) {
fprintf(stderr, "Too many matches (%d) for %s\n", count, expect.c_str());
}
- // Expect one the first time around as either liblogcat.descriptive or
- // logcat.descriptive. Expect two the second time as the other.
- return count == 1 || count == 2;
+ // Three different known tests, we can see pollution from the others
+ return count && (count <= 3);
}
TEST(logcat, descriptive) {
@@ -1537,24 +1570,28 @@
uint32_t tagNo;
const char* tagStr;
};
+ int ret;
{
static const struct tag hhgtg = { 42, "answer" };
android_log_event_list ctx(hhgtg.tagNo);
static const char theAnswer[] = "what is five by seven";
ctx << theAnswer;
- ctx.write();
+ // crafted to rest at least once after, and rest between retries.
+ for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+ EXPECT_GE(ret, 0);
EXPECT_TRUE(
End_to_End(hhgtg.tagStr, "to life the universe etc=%s", theAnswer));
}
{
static const struct tag sync = { 2720, "sync" };
- static const char id[] = "logcat.decriptive";
+ static const char id[] = ___STRING(logcat) ".descriptive-sync";
{
android_log_event_list ctx(sync.tagNo);
ctx << id << (int32_t)42 << (int32_t)-1 << (int32_t)0;
- ctx.write();
+ for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+ EXPECT_GE(ret, 0);
EXPECT_TRUE(End_to_End(sync.tagStr,
"[id=%s,event=42,source=-1,account=0]", id));
}
@@ -1563,7 +1600,8 @@
{
android_log_event_list ctx(sync.tagNo);
ctx << id << (int32_t)43 << (int64_t)-1 << (int32_t)0;
- ctx.write();
+ for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+ EXPECT_GE(ret, 0);
EXPECT_TRUE(End_to_End(sync.tagStr, "[id=%s,event=43,-1,0]", id));
}
@@ -1571,7 +1609,8 @@
{
android_log_event_list ctx(sync.tagNo);
ctx << id << (int32_t)44 << (int32_t)-1 << (int64_t)0;
- ctx.write();
+ for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+ EXPECT_GE(ret, 0);
fprintf(stderr, "Expect a \"Closest match\" message\n");
EXPECT_FALSE(End_to_End(
sync.tagStr, "[id=%s,event=44,source=-1,account=0]", id));
@@ -1583,7 +1622,8 @@
{
android_log_event_list ctx(sync.tagNo);
ctx << (uint64_t)30 << (int32_t)2;
- ctx.write();
+ for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+ EXPECT_GE(ret, 0);
EXPECT_TRUE(
End_to_End(sync.tagStr, "[aggregation time=30ms,count=2]"));
}
@@ -1591,7 +1631,8 @@
{
android_log_event_list ctx(sync.tagNo);
ctx << (uint64_t)31570 << (int32_t)911;
- ctx.write();
+ for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+ EXPECT_GE(ret, 0);
EXPECT_TRUE(
End_to_End(sync.tagStr, "[aggregation time=31.57s,count=911]"));
}
@@ -1602,42 +1643,48 @@
{
android_log_event_list ctx(sync.tagNo);
ctx << (uint32_t)512;
- ctx.write();
+ for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+ EXPECT_GE(ret, 0);
EXPECT_TRUE(End_to_End(sync.tagStr, "current=512B"));
}
{
android_log_event_list ctx(sync.tagNo);
ctx << (uint32_t)3072;
- ctx.write();
+ for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+ EXPECT_GE(ret, 0);
EXPECT_TRUE(End_to_End(sync.tagStr, "current=3KB"));
}
{
android_log_event_list ctx(sync.tagNo);
ctx << (uint32_t)2097152;
- ctx.write();
+ for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+ EXPECT_GE(ret, 0);
EXPECT_TRUE(End_to_End(sync.tagStr, "current=2MB"));
}
{
android_log_event_list ctx(sync.tagNo);
ctx << (uint32_t)2097153;
- ctx.write();
+ for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+ EXPECT_GE(ret, 0);
EXPECT_TRUE(End_to_End(sync.tagStr, "current=2097153B"));
}
{
android_log_event_list ctx(sync.tagNo);
ctx << (uint32_t)1073741824;
- ctx.write();
+ for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+ EXPECT_GE(ret, 0);
EXPECT_TRUE(End_to_End(sync.tagStr, "current=1GB"));
}
{
android_log_event_list ctx(sync.tagNo);
ctx << (uint32_t)3221225472; // 3MB, but on purpose overflowed
- ctx.write();
+ for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+ EXPECT_GE(ret, 0);
EXPECT_TRUE(End_to_End(sync.tagStr, "current=-1GB"));
}
}
@@ -1645,9 +1692,52 @@
{
static const struct tag sync = { 27501, "notification_panel_hidden" };
android_log_event_list ctx(sync.tagNo);
- ctx.write();
+ for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+ EXPECT_GE(ret, 0);
EXPECT_TRUE(End_to_End(sync.tagStr, ""));
}
+
+ {
+ // Invent new entries because existing can not serve
+ EventTagMap* map = android_openEventTagMap(nullptr);
+ ASSERT_TRUE(nullptr != map);
+ static const char name[] = ___STRING(logcat) ".descriptive-monotonic";
+ int myTag = android_lookupEventTagNum(map, name, "(new|1|s)",
+ ANDROID_LOG_UNKNOWN);
+ android_closeEventTagMap(map);
+ ASSERT_NE(-1, myTag);
+
+ const struct tag sync = { (uint32_t)myTag, name };
+
+ {
+ android_log_event_list ctx(sync.tagNo);
+ ctx << (uint32_t)7;
+ for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+ EXPECT_GE(ret, 0);
+ EXPECT_TRUE(End_to_End(sync.tagStr, "new=7s"));
+ }
+ {
+ android_log_event_list ctx(sync.tagNo);
+ ctx << (uint32_t)62;
+ for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+ EXPECT_GE(ret, 0);
+ EXPECT_TRUE(End_to_End(sync.tagStr, "new=1:02"));
+ }
+ {
+ android_log_event_list ctx(sync.tagNo);
+ ctx << (uint32_t)3673;
+ for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+ EXPECT_GE(ret, 0);
+ EXPECT_TRUE(End_to_End(sync.tagStr, "new=1:01:13"));
+ }
+ {
+ android_log_event_list ctx(sync.tagNo);
+ ctx << (uint32_t)(86400 + 7200 + 180 + 58);
+ for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+ EXPECT_GE(ret, 0);
+ EXPECT_TRUE(End_to_End(sync.tagStr, "new=1d 2:03:58"));
+ }
+ }
}
static bool reportedSecurity(const char* command) {
@@ -1685,7 +1775,7 @@
TEST(logcat, help) {
size_t logcatHelpTextSize = commandOutputSize(logcat_executable " -h 2>&1");
- EXPECT_LT(4096UL, logcatHelpTextSize);
+ EXPECT_GT(logcatHelpTextSize, 4096UL);
size_t logcatLastHelpTextSize =
commandOutputSize(logcat_executable " -L -h 2>&1");
#ifdef USING_LOGCAT_EXECUTABLE_DEFAULT // logcat and liblogcat
diff --git a/logd/Android.mk b/logd/Android.mk
index 9211037..fb51992 100644
--- a/logd/Android.mk
+++ b/logd/Android.mk
@@ -2,12 +2,9 @@
include $(CLEAR_VARS)
-LOCAL_MODULE:= logd
-
-LOCAL_INIT_RC := logd.rc
+LOCAL_MODULE:= liblogd
LOCAL_SRC_FILES := \
- main.cpp \
LogCommand.cpp \
CommandListener.cpp \
LogListener.cpp \
@@ -15,6 +12,7 @@
FlushCommand.cpp \
LogBuffer.cpp \
LogBufferElement.cpp \
+ LogBufferInterface.cpp \
LogTimes.cpp \
LogStatistics.cpp \
LogWhiteBlackList.cpp \
@@ -25,12 +23,9 @@
event.logtags
LOCAL_SHARED_LIBRARIES := \
- libsysutils \
- liblog \
- libcutils \
- libbase \
- libpackagelistparser \
- libcap
+ libbase
+
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
# This is what we want to do:
# event_logtags = $(shell \
@@ -46,6 +41,30 @@
LOCAL_CFLAGS := -Werror $(event_flag)
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE:= logd
+
+LOCAL_INIT_RC := logd.rc
+
+LOCAL_SRC_FILES := \
+ main.cpp
+
+LOCAL_STATIC_LIBRARIES := \
+ liblogd
+
+LOCAL_SHARED_LIBRARIES := \
+ libsysutils \
+ liblog \
+ libcutils \
+ libbase \
+ libpackagelistparser \
+ libcap
+
+LOCAL_CFLAGS := -Werror
+
include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
diff --git a/logd/FlushCommand.cpp b/logd/FlushCommand.cpp
index c67d2bf..a9edc3e 100644
--- a/logd/FlushCommand.cpp
+++ b/logd/FlushCommand.cpp
@@ -44,14 +44,14 @@
// LogTimeEntrys, and spawn a transitory per-client thread to
// work at filing data to the socket.
//
-// global LogTimeEntry::lock() is used to protect access,
+// global LogTimeEntry::wrlock() is used to protect access,
// reference counts are used to ensure that individual
// LogTimeEntry lifetime is managed when not protected.
void FlushCommand::runSocketCommand(SocketClient* client) {
LogTimeEntry* entry = NULL;
LastLogTimes& times = mReader.logbuf().mTimes;
- LogTimeEntry::lock();
+ LogTimeEntry::wrlock();
LastLogTimes::iterator it = times.begin();
while (it != times.end()) {
entry = (*it);
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
index 2d9024a..ee38d1c 100644
--- a/logd/LogAudit.cpp
+++ b/logd/LogAudit.cpp
@@ -277,7 +277,7 @@
++cp;
}
tid = pid;
- logbuf->lock();
+ logbuf->wrlock();
uid = logbuf->pidToUid(pid);
logbuf->unlock();
memmove(pidptr, cp, strlen(cp) + 1);
@@ -322,7 +322,7 @@
pid = tid;
comm = "auditd";
} else {
- logbuf->lock();
+ logbuf->wrlock();
comm = commfree = logbuf->pidToName(pid);
logbuf->unlock();
if (!comm) {
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index 8ef9328..7498325 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -43,6 +43,8 @@
// Default
#define log_buffer_size(id) mMaxSize[id]
+const log_time LogBuffer::pruneMargin(3, 0);
+
void LogBuffer::init() {
log_id_for_each(i) {
mLastSet[i] = false;
@@ -70,22 +72,28 @@
// as the act of mounting /data would trigger persist.logd.timestamp to
// be corrected. 1/30 corner case YMMV.
//
- pthread_mutex_lock(&mLogElementsLock);
+ rdlock();
LogBufferElementCollection::iterator it = mLogElements.begin();
while ((it != mLogElements.end())) {
LogBufferElement* e = *it;
if (monotonic) {
if (!android::isMonotonic(e->mRealTime)) {
LogKlog::convertRealToMonotonic(e->mRealTime);
+ if ((e->mRealTime.tv_nsec % 1000) == 0) {
+ e->mRealTime.tv_nsec++;
+ }
}
} else {
if (android::isMonotonic(e->mRealTime)) {
LogKlog::convertMonotonicToReal(e->mRealTime);
+ if ((e->mRealTime.tv_nsec % 1000) == 0) {
+ e->mRealTime.tv_nsec++;
+ }
}
}
++it;
}
- pthread_mutex_unlock(&mLogElementsLock);
+ unlock();
}
// We may have been triggered by a SIGHUP. Release any sleeping reader
@@ -93,7 +101,7 @@
//
// NB: this is _not_ performed in the context of a SIGHUP, it is
// performed during startup, and in context of reinit administrative thread
- LogTimeEntry::lock();
+ LogTimeEntry::wrlock();
LastLogTimes::iterator times = mTimes.begin();
while (times != mTimes.end()) {
@@ -109,7 +117,7 @@
LogBuffer::LogBuffer(LastLogTimes* times)
: monotonic(android_log_clockid() == CLOCK_MONOTONIC), mTimes(*times) {
- pthread_mutex_init(&mLogElementsLock, nullptr);
+ pthread_rwlock_init(&mLogElementsLock, nullptr);
log_id_for_each(i) {
lastLoggedElements[i] = nullptr;
@@ -194,6 +202,11 @@
return -EINVAL;
}
+ // Slip the time by 1 nsec if the incoming lands on xxxxxx000 ns.
+ // This prevents any chance that an outside source can request an
+ // exact entry with time specified in ms or us precision.
+ if ((realtime.tv_nsec % 1000) == 0) ++realtime.tv_nsec;
+
LogBufferElement* elem =
new LogBufferElement(log_id, realtime, uid, pid, tid, msg, len);
if (log_id != LOG_ID_SECURITY) {
@@ -207,16 +220,15 @@
}
if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
// Log traffic received to total
- pthread_mutex_lock(&mLogElementsLock);
- stats.add(elem);
- stats.subtract(elem);
- pthread_mutex_unlock(&mLogElementsLock);
+ wrlock();
+ stats.addTotal(elem);
+ unlock();
delete elem;
return -EACCES;
}
}
- pthread_mutex_lock(&mLogElementsLock);
+ wrlock();
LogBufferElement* currentLast = lastLoggedElements[log_id];
if (currentLast) {
LogBufferElement* dropped = droppedElements[log_id];
@@ -317,15 +329,14 @@
// check for overflow
if (total >= UINT32_MAX) {
log(currentLast);
- pthread_mutex_unlock(&mLogElementsLock);
+ unlock();
return len;
}
- stats.add(currentLast);
- stats.subtract(currentLast);
+ stats.addTotal(currentLast);
delete currentLast;
swab = total;
event->payload.data = htole32(swab);
- pthread_mutex_unlock(&mLogElementsLock);
+ unlock();
return len;
}
if (count == USHRT_MAX) {
@@ -337,13 +348,12 @@
}
}
if (count) {
- stats.add(currentLast);
- stats.subtract(currentLast);
+ stats.addTotal(currentLast);
currentLast->setDropped(count);
}
droppedElements[log_id] = currentLast;
lastLoggedElements[log_id] = elem;
- pthread_mutex_unlock(&mLogElementsLock);
+ unlock();
return len;
}
if (dropped) { // State 1 or 2
@@ -361,12 +371,12 @@
lastLoggedElements[log_id] = new LogBufferElement(*elem);
log(elem);
- pthread_mutex_unlock(&mLogElementsLock);
+ unlock();
return len;
}
-// assumes mLogElementsLock held, owns elem, will look after garbage collection
+// assumes LogBuffer::wrlock() held, owns elem, look after garbage collection
void LogBuffer::log(LogBufferElement* elem) {
// cap on how far back we will sort in-place, otherwise append
static uint32_t too_far_back = 5; // five seconds
@@ -387,7 +397,7 @@
bool end_set = false;
bool end_always = false;
- LogTimeEntry::lock();
+ LogTimeEntry::rdlock();
LastLogTimes::iterator times = mTimes.begin();
while (times != mTimes.end()) {
@@ -429,7 +439,7 @@
// Prune at most 10% of the log entries or maxPrune, whichever is less.
//
-// mLogElementsLock must be held when this function is called.
+// LogBuffer::wrlock() must be held when this function is called.
void LogBuffer::maybePrune(log_id_t id) {
size_t sizes = stats.sizes(id);
unsigned long maxSize = log_buffer_size(id);
@@ -608,6 +618,33 @@
}
};
+// Determine if watermark is within pruneMargin + 1s from the end of the list,
+// the caller will use this result to set an internal busy flag indicating
+// the prune operation could not be completed because a reader is blocking
+// the request.
+bool LogBuffer::isBusy(log_time watermark) {
+ LogBufferElementCollection::iterator ei = mLogElements.end();
+ --ei;
+ return watermark < ((*ei)->getRealTime() - pruneMargin - log_time(1, 0));
+}
+
+// If the selected reader is blocking our pruning progress, decide on
+// what kind of mitigation is necessary to unblock the situation.
+void LogBuffer::kickMe(LogTimeEntry* me, log_id_t id, unsigned long pruneRows) {
+ if (stats.sizes(id) > (2 * log_buffer_size(id))) { // +100%
+ // A misbehaving or slow reader has its connection
+ // dropped if we hit too much memory pressure.
+ me->release_Locked();
+ } else if (me->mTimeout.tv_sec || me->mTimeout.tv_nsec) {
+ // Allow a blocked WRAP timeout reader to
+ // trigger and start reporting the log data.
+ me->triggerReader_Locked();
+ } else {
+ // tell slow reader to skip entries to catch up
+ me->triggerSkip_Locked(id, pruneRows);
+ }
+}
+
// prune "pruneRows" of type "id" from the buffer.
//
// This garbage collection task is used to expire log entries. It is called to
@@ -653,14 +690,14 @@
// The third thread is optional, and only gets hit if there was a whitelist
// and more needs to be pruned against the backstop of the region lock.
//
-// mLogElementsLock must be held when this function is called.
+// LogBuffer::wrlock() must be held when this function is called.
//
bool LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
LogTimeEntry* oldest = nullptr;
bool busy = false;
bool clearAll = pruneRows == ULONG_MAX;
- LogTimeEntry::lock();
+ LogTimeEntry::rdlock();
// Region locked?
LastLogTimes::iterator times = mTimes.begin();
@@ -674,6 +711,8 @@
}
times++;
}
+ log_time watermark(log_time::tv_sec_max, log_time::tv_nsec_max);
+ if (oldest) watermark = oldest->mStart - pruneMargin;
LogBufferElementCollection::iterator it;
@@ -695,13 +734,9 @@
mLastSet[id] = true;
}
- if (oldest && (oldest->mStart <= element->getRealTime().nsec())) {
- busy = true;
- if (oldest->mTimeout.tv_sec || oldest->mTimeout.tv_nsec) {
- oldest->triggerReader_Locked();
- } else {
- oldest->triggerSkip_Locked(id, pruneRows);
- }
+ if (oldest && (watermark <= element->getRealTime())) {
+ busy = isBusy(watermark);
+ if (busy) kickMe(oldest, id, pruneRows);
break;
}
@@ -787,11 +822,9 @@
while (it != mLogElements.end()) {
LogBufferElement* element = *it;
- if (oldest && (oldest->mStart <= element->getRealTime().nsec())) {
- busy = true;
- if (oldest->mTimeout.tv_sec || oldest->mTimeout.tv_nsec) {
- oldest->triggerReader_Locked();
- }
+ if (oldest && (watermark <= element->getRealTime())) {
+ busy = isBusy(watermark);
+ // Do not let chatty eliding trigger any reader mitigation
break;
}
@@ -941,20 +974,9 @@
mLastSet[id] = true;
}
- if (oldest && (oldest->mStart <= element->getRealTime().nsec())) {
- busy = true;
- if (whitelist) {
- break;
- }
-
- if (stats.sizes(id) > (2 * log_buffer_size(id))) {
- // kick a misbehaving log reader client off the island
- oldest->release_Locked();
- } else if (oldest->mTimeout.tv_sec || oldest->mTimeout.tv_nsec) {
- oldest->triggerReader_Locked();
- } else {
- oldest->triggerSkip_Locked(id, pruneRows);
- }
+ if (oldest && (watermark <= element->getRealTime())) {
+ busy = isBusy(watermark);
+ if (!whitelist && busy) kickMe(oldest, id, pruneRows);
break;
}
@@ -985,16 +1007,9 @@
mLastSet[id] = true;
}
- if (oldest && (oldest->mStart <= element->getRealTime().nsec())) {
- busy = true;
- if (stats.sizes(id) > (2 * log_buffer_size(id))) {
- // kick a misbehaving log reader client off the island
- oldest->release_Locked();
- } else if (oldest->mTimeout.tv_sec || oldest->mTimeout.tv_nsec) {
- oldest->triggerReader_Locked();
- } else {
- oldest->triggerSkip_Locked(id, pruneRows);
- }
+ if (oldest && (watermark <= element->getRealTime())) {
+ busy = isBusy(watermark);
+ if (busy) kickMe(oldest, id, pruneRows);
break;
}
@@ -1018,15 +1033,15 @@
// one entry, not another clear run, so we are looking for
// the quick side effect of the return value to tell us if
// we have a _blocked_ reader.
- pthread_mutex_lock(&mLogElementsLock);
+ wrlock();
busy = prune(id, 1, uid);
- pthread_mutex_unlock(&mLogElementsLock);
+ unlock();
// It is still busy, blocked reader(s), lets kill them all!
// otherwise, lets be a good citizen and preserve the slow
// readers and let the clear run (below) deal with determining
// if we are still blocked and return an error code to caller.
if (busy) {
- LogTimeEntry::lock();
+ LogTimeEntry::wrlock();
LastLogTimes::iterator times = mTimes.begin();
while (times != mTimes.end()) {
LogTimeEntry* entry = (*times);
@@ -1039,9 +1054,9 @@
LogTimeEntry::unlock();
}
}
- pthread_mutex_lock(&mLogElementsLock);
+ wrlock();
busy = prune(id, ULONG_MAX, uid);
- pthread_mutex_unlock(&mLogElementsLock);
+ unlock();
if (!busy || !--retry) {
break;
}
@@ -1052,9 +1067,9 @@
// get the used space associated with "id".
unsigned long LogBuffer::getSizeUsed(log_id_t id) {
- pthread_mutex_lock(&mLogElementsLock);
+ rdlock();
size_t retval = stats.sizes(id);
- pthread_mutex_unlock(&mLogElementsLock);
+ unlock();
return retval;
}
@@ -1064,17 +1079,17 @@
if (!__android_logger_valid_buffer_size(size)) {
return -1;
}
- pthread_mutex_lock(&mLogElementsLock);
+ wrlock();
log_buffer_size(id) = size;
- pthread_mutex_unlock(&mLogElementsLock);
+ unlock();
return 0;
}
// get the total space allocated to "id"
unsigned long LogBuffer::getSize(log_id_t id) {
- pthread_mutex_lock(&mLogElementsLock);
+ rdlock();
size_t retval = log_buffer_size(id);
- pthread_mutex_unlock(&mLogElementsLock);
+ unlock();
return retval;
}
@@ -1086,25 +1101,30 @@
LogBufferElementCollection::iterator it;
uid_t uid = reader->getUid();
- pthread_mutex_lock(&mLogElementsLock);
+ rdlock();
if (start == log_time::EPOCH) {
// client wants to start from the beginning
it = mLogElements.begin();
} else {
- LogBufferElementCollection::iterator last;
// 3 second limit to continue search for out-of-order entries.
- log_time min = start - log_time(3, 0);
+ log_time min = start - pruneMargin;
+
// Cap to 300 iterations we look back for out-of-order entries.
size_t count = 300;
+
// Client wants to start from some specified time. Chances are
// we are better off starting from the end of the time sorted list.
+ LogBufferElementCollection::iterator last;
for (last = it = mLogElements.end(); it != mLogElements.begin();
/* do nothing */) {
--it;
LogBufferElement* element = *it;
if (element->getRealTime() > start) {
last = it;
+ } else if (element->getRealTime() == start) {
+ last = ++it;
+ break;
} else if (!--count || (element->getRealTime() < min)) {
break;
}
@@ -1112,11 +1132,24 @@
it = last;
}
- log_time max = start;
+ log_time curr = start;
+ LogBufferElement* lastElement = nullptr; // iterator corruption paranoia
+ static const size_t maxSkip = 4194304; // maximum entries to skip
+ size_t skip = maxSkip;
for (; it != mLogElements.end(); ++it) {
LogBufferElement* element = *it;
+ if (!--skip) {
+ android::prdebug("reader.per: too many elements skipped");
+ break;
+ }
+ if (element == lastElement) {
+ android::prdebug("reader.per: identical elements");
+ break;
+ }
+ lastElement = element;
+
if (!privileged && (element->getUid() != uid)) {
continue;
}
@@ -1125,11 +1158,7 @@
continue;
}
- if (element->getRealTime() <= start) {
- continue;
- }
-
- // NB: calling out to another object with mLogElementsLock held (safe)
+ // NB: calling out to another object with wrlock() held (safe)
if (filter) {
int ret = (*filter)(element, arg);
if (ret == false) {
@@ -1152,29 +1181,30 @@
(element->getDropped() && !sameTid) ? 0 : element->getTid();
}
- pthread_mutex_unlock(&mLogElementsLock);
+ unlock();
// range locking in LastLogTimes looks after us
- max = element->flushTo(reader, this, privileged, sameTid);
+ curr = element->flushTo(reader, this, privileged, sameTid);
- if (max == element->FLUSH_ERROR) {
- return max;
+ if (curr == element->FLUSH_ERROR) {
+ return curr;
}
- pthread_mutex_lock(&mLogElementsLock);
+ skip = maxSkip;
+ rdlock();
}
- pthread_mutex_unlock(&mLogElementsLock);
+ unlock();
- return max;
+ return curr;
}
std::string LogBuffer::formatStatistics(uid_t uid, pid_t pid,
unsigned int logMask) {
- pthread_mutex_lock(&mLogElementsLock);
+ wrlock();
std::string ret = stats.format(uid, pid, logMask);
- pthread_mutex_unlock(&mLogElementsLock);
+ unlock();
return ret;
}
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index 19d11cb..0942987 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -27,6 +27,7 @@
#include <sysutils/SocketClient.h>
#include "LogBufferElement.h"
+#include "LogBufferInterface.h"
#include "LogStatistics.h"
#include "LogTags.h"
#include "LogTimes.h"
@@ -74,9 +75,9 @@
typedef std::list<LogBufferElement*> LogBufferElementCollection;
-class LogBuffer {
+class LogBuffer : public LogBufferInterface {
LogBufferElementCollection mLogElements;
- pthread_mutex_t mLogElementsLock;
+ pthread_rwlock_t mLogElementsLock;
LogStatistics stats;
@@ -107,14 +108,14 @@
LastLogTimes& mTimes;
explicit LogBuffer(LastLogTimes* times);
- ~LogBuffer();
+ ~LogBuffer() override;
void init();
bool isMonotonic() {
return monotonic;
}
int log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid, pid_t tid,
- const char* msg, unsigned short len);
+ const char* msg, unsigned short len) override;
// lastTid is an optional context to help detect if the last previous
// valid message was from the same source so we can differentiate chatty
// filter types (identical or expired)
@@ -154,28 +155,38 @@
return tags.tagToName(tag);
}
- // helper must be protected directly or implicitly by lock()/unlock()
+ // helper must be protected directly or implicitly by wrlock()/unlock()
const char* pidToName(pid_t pid) {
return stats.pidToName(pid);
}
- uid_t pidToUid(pid_t pid) {
+ virtual uid_t pidToUid(pid_t pid) override {
return stats.pidToUid(pid);
}
+ virtual pid_t tidToPid(pid_t tid) override {
+ return stats.tidToPid(tid);
+ }
const char* uidToName(uid_t uid) {
return stats.uidToName(uid);
}
- void lock() {
- pthread_mutex_lock(&mLogElementsLock);
+ void wrlock() {
+ pthread_rwlock_wrlock(&mLogElementsLock);
+ }
+ void rdlock() {
+ pthread_rwlock_rdlock(&mLogElementsLock);
}
void unlock() {
- pthread_mutex_unlock(&mLogElementsLock);
+ pthread_rwlock_unlock(&mLogElementsLock);
}
private:
static constexpr size_t minPrune = 4;
static constexpr size_t maxPrune = 256;
+ static const log_time pruneMargin;
void maybePrune(log_id_t id);
+ bool isBusy(log_time watermark);
+ void kickMe(LogTimeEntry* me, log_id_t id, unsigned long pruneRows);
+
bool prune(log_id_t id, unsigned long pruneRows, uid_t uid = AID_ROOT);
LogBufferElementCollection::iterator erase(
LogBufferElementCollection::iterator it, bool coalesce = false);
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index 04a620c..381c974 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -121,7 +121,7 @@
}
static const char format_uid[] = "uid=%u%s%s %s %u line%s";
- parent->lock();
+ parent->wrlock();
const char* name = parent->uidToName(mUid);
parent->unlock();
const char* commName = android::tidToName(mTid);
@@ -129,7 +129,7 @@
commName = android::tidToName(mPid);
}
if (!commName) {
- parent->lock();
+ parent->wrlock();
commName = parent->pidToName(mPid);
parent->unlock();
}
diff --git a/include/system/window.h b/logd/LogBufferInterface.cpp
similarity index 60%
copy from include/system/window.h
copy to logd/LogBufferInterface.cpp
index efa10d6..4b6d363 100644
--- a/include/system/window.h
+++ b/logd/LogBufferInterface.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 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.
@@ -14,9 +14,16 @@
* limitations under the License.
*/
-#ifndef SYSTEM_CORE_INCLUDE_ANDROID_WINDOW_H
-#define SYSTEM_CORE_INCLUDE_ANDROID_WINDOW_H
+#include "LogBufferInterface.h"
+#include "LogUtils.h"
-#include <system/window-deprecated.h>
-
-#endif /* SYSTEM_CORE_INCLUDE_ANDROID_WINDOW_H */
+LogBufferInterface::LogBufferInterface() {
+}
+LogBufferInterface::~LogBufferInterface() {
+}
+uid_t LogBufferInterface::pidToUid(pid_t pid) {
+ return android::pidToUid(pid);
+}
+pid_t LogBufferInterface::tidToPid(pid_t tid) {
+ return android::tidToPid(tid);
+}
diff --git a/logd/LogBufferInterface.h b/logd/LogBufferInterface.h
new file mode 100644
index 0000000..ff73a22
--- /dev/null
+++ b/logd/LogBufferInterface.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2012-2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LOGD_LOG_BUFFER_INTERFACE_H__
+#define _LOGD_LOG_BUFFER_INTERFACE_H__
+
+#include <sys/types.h>
+
+#include <android-base/macros.h>
+#include <log/log_id.h>
+#include <log/log_time.h>
+
+// Abstract interface that handles log when log available.
+class LogBufferInterface {
+ public:
+ LogBufferInterface();
+ virtual ~LogBufferInterface();
+ // Handles a log entry when available in LogListener.
+ // Returns the size of the handled log message.
+ virtual int log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid,
+ pid_t tid, const char* msg, unsigned short len) = 0;
+
+ virtual uid_t pidToUid(pid_t pid);
+ virtual pid_t tidToPid(pid_t tid);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(LogBufferInterface);
+};
+
+#endif // _LOGD_LOG_BUFFER_INTERFACE_H__
diff --git a/logd/LogKlog.cpp b/logd/LogKlog.cpp
index d23254d..a7e7208 100644
--- a/logd/LogKlog.cpp
+++ b/logd/LogKlog.cpp
@@ -579,7 +579,7 @@
const pid_t tid = pid;
uid_t uid = AID_ROOT;
if (pid) {
- logbuf->lock();
+ logbuf->wrlock();
uid = logbuf->pidToUid(pid);
logbuf->unlock();
}
diff --git a/logd/LogListener.cpp b/logd/LogListener.cpp
index dadc75f..d2df68e 100644
--- a/logd/LogListener.cpp
+++ b/logd/LogListener.cpp
@@ -14,7 +14,9 @@
* limitations under the License.
*/
+#include <ctype.h>
#include <limits.h>
+#include <stdio.h>
#include <sys/cdefs.h>
#include <sys/prctl.h>
#include <sys/socket.h>
@@ -30,7 +32,7 @@
#include "LogListener.h"
#include "LogUtils.h"
-LogListener::LogListener(LogBuffer* buf, LogReader* reader)
+LogListener::LogListener(LogBufferInterface* buf, LogReader* reader)
: SocketListener(getLogSocket(), false), logbuf(buf), reader(reader) {
}
@@ -72,8 +74,11 @@
cmsg = CMSG_NXTHDR(&hdr, cmsg);
}
+ struct ucred fake_cred;
if (cred == NULL) {
- return false;
+ cred = &fake_cred;
+ cred->pid = 0;
+ cred->uid = DEFAULT_OVERFLOWUID;
}
if (cred->uid == AID_LOGD) {
@@ -96,17 +101,41 @@
return false;
}
+ // Check credential validity, acquire corrected details if not supplied.
+ if (cred->pid == 0) {
+ cred->pid = logbuf ? logbuf->tidToPid(header->tid)
+ : android::tidToPid(header->tid);
+ if (cred->pid == getpid()) {
+ // We expect that /proc/<tid>/ is accessible to self even without
+ // readproc group, so that we will always drop messages that come
+ // from any of our logd threads and their library calls.
+ return false; // ignore self
+ }
+ }
+ if (cred->uid == DEFAULT_OVERFLOWUID) {
+ uid_t uid =
+ logbuf ? logbuf->pidToUid(cred->pid) : android::pidToUid(cred->pid);
+ if (uid == AID_LOGD) {
+ uid = logbuf ? logbuf->pidToUid(header->tid)
+ : android::pidToUid(cred->pid);
+ }
+ if (uid != AID_LOGD) cred->uid = uid;
+ }
+
char* msg = ((char*)buffer) + sizeof(android_log_header_t);
n -= sizeof(android_log_header_t);
// NB: hdr.msg_flags & MSG_TRUNC is not tested, silently passing a
// truncated message to the logs.
- if (logbuf->log((log_id_t)header->id, header->realtime, cred->uid,
- cred->pid, header->tid, msg,
- ((size_t)n <= USHRT_MAX) ? (unsigned short)n : USHRT_MAX) >=
- 0) {
- reader->notifyNewLog();
+ if (logbuf != nullptr) {
+ int res = logbuf->log(
+ (log_id_t)header->id, header->realtime, cred->uid, cred->pid,
+ header->tid, msg,
+ ((size_t)n <= USHRT_MAX) ? (unsigned short)n : USHRT_MAX);
+ if (res > 0 && reader != nullptr) {
+ reader->notifyNewLog();
+ }
}
return true;
@@ -116,14 +145,14 @@
static const char socketName[] = "logdw";
int sock = android_get_control_socket(socketName);
- if (sock < 0) {
+ if (sock < 0) { // logd started up in init.sh
sock = socket_local_server(
socketName, ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_DGRAM);
- }
- int on = 1;
- if (setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) {
- return -1;
+ int on = 1;
+ if (setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on))) {
+ return -1;
+ }
}
return sock;
}
diff --git a/logd/LogListener.h b/logd/LogListener.h
index 2973b8b..a562a54 100644
--- a/logd/LogListener.h
+++ b/logd/LogListener.h
@@ -20,12 +20,22 @@
#include <sysutils/SocketListener.h>
#include "LogReader.h"
+// DEFAULT_OVERFLOWUID is defined in linux/highuid.h, which is not part of
+// the uapi headers for userspace to use. This value is filled in on the
+// out-of-band socket credentials if the OS fails to find one available.
+// One of the causes of this is if SO_PASSCRED is set, all the packets before
+// that point will have this value. We also use it in a fake credential if
+// no socket credentials are supplied.
+#ifndef DEFAULT_OVERFLOWUID
+#define DEFAULT_OVERFLOWUID 65534
+#endif
+
class LogListener : public SocketListener {
- LogBuffer* logbuf;
+ LogBufferInterface* logbuf;
LogReader* reader;
public:
- LogListener(LogBuffer* buf, LogReader* reader);
+ LogListener(LogBufferInterface* buf, LogReader* reader /* nullable */);
protected:
virtual bool onDataAvailable(SocketClient* cli);
diff --git a/logd/LogReader.cpp b/logd/LogReader.cpp
index af19279..6d69316 100644
--- a/logd/LogReader.cpp
+++ b/logd/LogReader.cpp
@@ -15,6 +15,7 @@
*/
#include <ctype.h>
+#include <inttypes.h>
#include <poll.h>
#include <sys/prctl.h>
#include <sys/socket.h>
@@ -110,7 +111,7 @@
if (!fastcmp<strncmp>(buffer, "dumpAndClose", 12)) {
// Allow writer to get some cycles, and wait for pending notifications
sched_yield();
- LogTimeEntry::lock();
+ LogTimeEntry::wrlock();
LogTimeEntry::unlock();
sched_yield();
nonBlock = true;
@@ -192,6 +193,12 @@
}
}
+ android::prdebug(
+ "logdr: UID=%d GID=%d PID=%d %c tail=%lu logMask=%x pid=%d "
+ "start=%" PRIu64 "ns timeout=%" PRIu64 "ns\n",
+ cli->getUid(), cli->getGid(), cli->getPid(), nonBlock ? 'n' : 'b', tail,
+ logMask, (int)pid, sequence.nsec(), timeout);
+
FlushCommand command(*this, nonBlock, tail, logMask, pid, sequence, timeout);
// Set acceptable upper limit to wait for slow reader processing b/27242723
@@ -205,7 +212,7 @@
void LogReader::doSocketDelete(SocketClient* cli) {
LastLogTimes& times = mLogbuf.mTimes;
- LogTimeEntry::lock();
+ LogTimeEntry::wrlock();
LastLogTimes::iterator it = times.begin();
while (it != times.end()) {
LogTimeEntry* entry = (*it);
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp
index cc30f77..af59ddc 100644
--- a/logd/LogStatistics.cpp
+++ b/logd/LogStatistics.cpp
@@ -14,7 +14,9 @@
* limitations under the License.
*/
+#include <ctype.h>
#include <fcntl.h>
+#include <inttypes.h>
#include <pwd.h>
#include <stdio.h>
#include <string.h>
@@ -23,19 +25,26 @@
#include <list>
-#include <android/log.h>
+#include <private/android_logger.h>
#include "LogStatistics.h"
+static const uint64_t hourSec = 60 * 60;
+static const uint64_t monthSec = 31 * 24 * hourSec;
+
size_t LogStatistics::SizesTotal;
LogStatistics::LogStatistics() : enable(false) {
+ log_time now(CLOCK_REALTIME);
log_id_for_each(id) {
mSizes[id] = 0;
mElements[id] = 0;
mDroppedElements[id] = 0;
mSizesTotal[id] = 0;
mElementsTotal[id] = 0;
+ mOldest[id] = now;
+ mNewest[id] = now;
+ mNewestDropped[id] = now;
}
}
@@ -70,25 +79,56 @@
}
}
+void LogStatistics::addTotal(LogBufferElement* element) {
+ if (element->getDropped()) return;
+
+ log_id_t log_id = element->getLogId();
+ unsigned short size = element->getMsgLen();
+ mSizesTotal[log_id] += size;
+ SizesTotal += size;
+ ++mElementsTotal[log_id];
+}
+
void LogStatistics::add(LogBufferElement* element) {
log_id_t log_id = element->getLogId();
unsigned short size = element->getMsgLen();
mSizes[log_id] += size;
++mElements[log_id];
+ // When caller adding a chatty entry, they will have already
+ // called add() and subtract() for each entry as they are
+ // evaluated and trimmed, thus recording size and number of
+ // elements, but we must recognize the manufactured dropped
+ // entry as not contributing to the lifetime totals.
if (element->getDropped()) {
++mDroppedElements[log_id];
} else {
- // When caller adding a chatty entry, they will have already
- // called add() and subtract() for each entry as they are
- // evaluated and trimmed, thus recording size and number of
- // elements, but we must recognize the manufactured dropped
- // entry as not contributing to the lifetime totals.
mSizesTotal[log_id] += size;
SizesTotal += size;
++mElementsTotal[log_id];
}
+ log_time stamp(element->getRealTime());
+ if (mNewest[log_id] < stamp) {
+ // A major time update invalidates the statistics :-(
+ log_time diff = stamp - mNewest[log_id];
+ mNewest[log_id] = stamp;
+
+ if (diff.tv_sec > hourSec) {
+ // approximate Do-Your-Best fixup
+ diff += mOldest[log_id];
+ if ((diff > stamp) && ((diff - stamp).tv_sec < hourSec)) {
+ diff = stamp;
+ }
+ if (diff <= stamp) {
+ mOldest[log_id] = diff;
+ if (mNewestDropped[log_id] < diff) {
+ mNewestDropped[log_id] = diff;
+ }
+ }
+ }
+ }
+
if (log_id == LOG_ID_KERNEL) {
return;
}
@@ -113,6 +153,10 @@
tagTable.add(tag, element);
}
}
+
+ if (!element->getDropped()) {
+ tagNameTable.add(TagNameKey(element), element);
+ }
}
void LogStatistics::subtract(LogBufferElement* element) {
@@ -124,6 +168,10 @@
--mDroppedElements[log_id];
}
+ if (mOldest[log_id] < element->getRealTime()) {
+ mOldest[log_id] = element->getRealTime();
+ }
+
if (log_id == LOG_ID_KERNEL) {
return;
}
@@ -148,6 +196,10 @@
tagTable.subtract(tag, element);
}
}
+
+ if (!element->getDropped()) {
+ tagNameTable.subtract(TagNameKey(element), element);
+ }
}
// Atomically set an entry to drop
@@ -158,6 +210,10 @@
mSizes[log_id] -= size;
++mDroppedElements[log_id];
+ if (mNewestDropped[log_id] < element->getRealTime()) {
+ mNewestDropped[log_id] = element->getRealTime();
+ }
+
uidTable[log_id].drop(element->getUid(), element);
if (element->getUid() == AID_SYSTEM) {
pidSystemTable[log_id].drop(element->getPid(), element);
@@ -178,9 +234,12 @@
tagTable.drop(tag, element);
}
}
+
+ tagNameTable.subtract(TagNameKey(element), element);
}
// caller must own and free character string
+// Requires parent LogBuffer::wrlock() to be held
const char* LogStatistics::uidToName(uid_t uid) const {
// Local hard coded favourites
if (uid == AID_LOGD) {
@@ -248,18 +307,38 @@
std::string(isprune ? "NUM" : ""));
}
+// Helper to truncate name, if too long, and add name dressings
+static void formatTmp(const LogStatistics& stat, const char* nameTmp, uid_t uid,
+ std::string& name, std::string& size, size_t nameLen) {
+ const char* allocNameTmp = nullptr;
+ if (!nameTmp) nameTmp = allocNameTmp = stat.uidToName(uid);
+ if (nameTmp) {
+ size_t lenSpace = std::max(nameLen - name.length(), (size_t)1);
+ size_t len = EntryBaseConstants::total_len -
+ EntryBaseConstants::pruned_len - size.length() -
+ name.length() - lenSpace - 2;
+ size_t lenNameTmp = strlen(nameTmp);
+ while ((len < lenNameTmp) && (lenSpace > 1)) {
+ ++len;
+ --lenSpace;
+ }
+ name += android::base::StringPrintf("%*s", (int)lenSpace, "");
+ if (len < lenNameTmp) {
+ name += "...";
+ nameTmp += lenNameTmp - std::max(len - 3, (size_t)1);
+ }
+ name += nameTmp;
+ free(const_cast<char*>(allocNameTmp));
+ }
+}
+
std::string UidEntry::format(const LogStatistics& stat, log_id_t id) const {
uid_t uid = getUid();
std::string name = android::base::StringPrintf("%u", uid);
- const char* nameTmp = stat.uidToName(uid);
- if (nameTmp) {
- name += android::base::StringPrintf(
- "%*s%s", (int)std::max(6 - name.length(), (size_t)1), "", nameTmp);
- free(const_cast<char*>(nameTmp));
- }
-
std::string size = android::base::StringPrintf("%zu", getSizes());
+ formatTmp(stat, nullptr, uid, name, size, 6);
+
std::string pruned = "";
if (worstUidEnabledForLogid(id)) {
size_t totalDropped = 0;
@@ -366,18 +445,10 @@
uid_t uid = getUid();
pid_t pid = getPid();
std::string name = android::base::StringPrintf("%5u/%u", pid, uid);
- const char* nameTmp = getName();
- if (nameTmp) {
- name += android::base::StringPrintf(
- "%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", nameTmp);
- } else if ((nameTmp = stat.uidToName(uid))) {
- name += android::base::StringPrintf(
- "%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", nameTmp);
- free(const_cast<char*>(nameTmp));
- }
-
std::string size = android::base::StringPrintf("%zu", getSizes());
+ formatTmp(stat, getName(), uid, name, size, 12);
+
std::string pruned = "";
size_t dropped = getDropped();
if (dropped) {
@@ -398,21 +469,10 @@
log_id_t /* id */) const {
uid_t uid = getUid();
std::string name = android::base::StringPrintf("%5u/%u", getTid(), uid);
- const char* nameTmp = getName();
- if (nameTmp) {
- name += android::base::StringPrintf(
- "%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", nameTmp);
- } else if ((nameTmp = stat.uidToName(uid))) {
- // if we do not have a PID name, lets punt to try UID name?
- name += android::base::StringPrintf(
- "%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", nameTmp);
- free(const_cast<char*>(nameTmp));
- // We tried, better to not have a name at all, we still
- // have TID/UID by number to report in any case.
- }
-
std::string size = android::base::StringPrintf("%zu", getSizes());
+ formatTmp(stat, getName(), uid, name, size, 12);
+
std::string pruned = "";
size_t dropped = getDropped();
if (dropped) {
@@ -456,6 +516,101 @@
return formatLine(name, size, pruned);
}
+std::string TagNameEntry::formatHeader(const std::string& name,
+ log_id_t /* id */) const {
+ return formatLine(name, std::string("Size"), std::string("")) +
+ formatLine(std::string(" TID/PID/UID LOG_TAG NAME"),
+ std::string("BYTES"), std::string(""));
+}
+
+std::string TagNameEntry::format(const LogStatistics& /* stat */,
+ log_id_t /* id */) const {
+ std::string name;
+ pid_t tid = getTid();
+ pid_t pid = getPid();
+ std::string pidstr;
+ if (pid != (pid_t)-1) {
+ pidstr = android::base::StringPrintf("%u", pid);
+ if ((tid != (pid_t)-1) && (tid != pid)) pidstr = "/" + pidstr;
+ }
+ int len = 9 - pidstr.length();
+ if (len < 0) len = 0;
+ if ((tid == (pid_t)-1) || (tid == pid)) {
+ name = android::base::StringPrintf("%*s", len, "");
+ } else {
+ name = android::base::StringPrintf("%*u", len, tid);
+ }
+ name += pidstr;
+ uid_t uid = getUid();
+ if (uid != (uid_t)-1) {
+ name += android::base::StringPrintf("/%u", uid);
+ }
+
+ std::string size = android::base::StringPrintf("%zu", getSizes());
+
+ const char* nameTmp = getName();
+ if (nameTmp) {
+ size_t lenSpace = std::max(16 - name.length(), (size_t)1);
+ size_t len = EntryBaseConstants::total_len -
+ EntryBaseConstants::pruned_len - size.length() -
+ name.length() - lenSpace - 2;
+ size_t lenNameTmp = strlen(nameTmp);
+ while ((len < lenNameTmp) && (lenSpace > 1)) {
+ ++len;
+ --lenSpace;
+ }
+ name += android::base::StringPrintf("%*s", (int)lenSpace, "");
+ if (len < lenNameTmp) {
+ name += "...";
+ nameTmp += lenNameTmp - std::max(len - 3, (size_t)1);
+ }
+ name += nameTmp;
+ }
+
+ std::string pruned = "";
+
+ return formatLine(name, size, pruned);
+}
+
+static std::string formatMsec(uint64_t val) {
+ static const unsigned subsecDigits = 3;
+ static const uint64_t sec = MS_PER_SEC;
+
+ static const uint64_t minute = 60 * sec;
+ static const uint64_t hour = 60 * minute;
+ static const uint64_t day = 24 * hour;
+
+ std::string output;
+ if (val < sec) return output;
+
+ if (val >= day) {
+ output = android::base::StringPrintf("%" PRIu64 "d ", val / day);
+ val = (val % day) + day;
+ }
+ if (val >= minute) {
+ if (val >= hour) {
+ output += android::base::StringPrintf("%" PRIu64 ":",
+ (val / hour) % (day / hour));
+ }
+ output += android::base::StringPrintf(
+ (val >= hour) ? "%02" PRIu64 ":" : "%" PRIu64 ":",
+ (val / minute) % (hour / minute));
+ }
+ output +=
+ android::base::StringPrintf((val >= minute) ? "%02" PRIu64 : "%" PRIu64,
+ (val / sec) % (minute / sec));
+ val %= sec;
+ unsigned digits = subsecDigits;
+ while (digits && ((val % 10) == 0)) {
+ val /= 10;
+ --digits;
+ }
+ if (digits) {
+ output += android::base::StringPrintf(".%0*" PRIu64, digits, val);
+ }
+ return output;
+}
+
std::string LogStatistics::format(uid_t uid, pid_t pid,
unsigned int logMask) const {
static const unsigned short spaces_total = 19;
@@ -525,6 +680,67 @@
output += android::base::StringPrintf("%*s%zu/%zu", spaces, "", totalSize,
totalEls);
+ static const char SpanStr[] = "\nLogspan";
+ spaces = 10 - strlen(SpanStr);
+ output += SpanStr;
+
+ // Total reports the greater of the individual maximum time span, or the
+ // validated minimum start and maximum end time span if it makes sense.
+ uint64_t minTime = UINT64_MAX;
+ uint64_t maxTime = 0;
+ uint64_t maxSpan = 0;
+ totalSize = 0;
+
+ log_id_for_each(id) {
+ if (!(logMask & (1 << id))) continue;
+
+ // validity checking
+ uint64_t oldest = mOldest[id].msec();
+ uint64_t newest = mNewest[id].msec();
+ if (newest <= oldest) {
+ spaces += spaces_total;
+ continue;
+ }
+
+ uint64_t span = newest - oldest;
+ if (span > (monthSec * MS_PER_SEC)) {
+ spaces += spaces_total;
+ continue;
+ }
+
+ // total span
+ if (minTime > oldest) minTime = oldest;
+ if (maxTime < newest) maxTime = newest;
+ if (span > maxSpan) maxSpan = span;
+ totalSize += span;
+
+ uint64_t dropped = mNewestDropped[id].msec();
+ if (dropped < oldest) dropped = oldest;
+ if (dropped > newest) dropped = newest;
+
+ oldLength = output.length();
+ output += android::base::StringPrintf("%*s%s", spaces, "",
+ formatMsec(span).c_str());
+ unsigned permille = ((newest - dropped) * 1000 + (span / 2)) / span;
+ if ((permille > 1) && (permille < 999)) {
+ output += android::base::StringPrintf("(%u", permille / 10);
+ permille %= 10;
+ if (permille) {
+ output += android::base::StringPrintf(".%u", permille);
+ }
+ output += android::base::StringPrintf("%%)");
+ }
+ spaces -= output.length() - oldLength;
+ spaces += spaces_total;
+ }
+ if ((maxTime > minTime) && ((maxTime -= minTime) < totalSize) &&
+ (maxTime > maxSpan)) {
+ maxSpan = maxTime;
+ }
+ if (spaces < 0) spaces = 0;
+ output += android::base::StringPrintf("%*s%s", spaces, "",
+ formatMsec(maxSpan).c_str());
+
static const char OverheadStr[] = "\nOverhead";
spaces = 10 - strlen(OverheadStr);
output += OverheadStr;
@@ -591,6 +807,13 @@
securityTagTable.format(*this, uid, pid, name, LOG_ID_SECURITY);
}
+ if (enable) {
+ name = "Chattiest TAGs";
+ if (pid) name += android::base::StringPrintf(" for PID %d", pid);
+ name += ":";
+ output += tagNameTable.format(*this, uid, pid, name);
+ }
+
return output;
}
@@ -602,8 +825,10 @@
FILE* fp = fopen(buffer, "r");
if (fp) {
while (fgets(buffer, sizeof(buffer), fp)) {
- int uid;
- if (sscanf(buffer, "Uid: %d", &uid) == 1) {
+ int uid = AID_LOGD;
+ char space = 0;
+ if ((sscanf(buffer, "Uid: %d%c", &uid, &space) == 2) &&
+ isspace(space)) {
fclose(fp);
return uid;
}
@@ -612,12 +837,35 @@
}
return AID_LOGD; // associate this with the logger
}
+
+pid_t tidToPid(pid_t tid) {
+ char buffer[512];
+ snprintf(buffer, sizeof(buffer), "/proc/%u/status", tid);
+ FILE* fp = fopen(buffer, "r");
+ if (fp) {
+ while (fgets(buffer, sizeof(buffer), fp)) {
+ int pid = tid;
+ char space = 0;
+ if ((sscanf(buffer, "Tgid: %d%c", &pid, &space) == 2) &&
+ isspace(space)) {
+ fclose(fp);
+ return pid;
+ }
+ }
+ fclose(fp);
+ }
+ return tid;
+}
}
uid_t LogStatistics::pidToUid(pid_t pid) {
return pidTable.add(pid)->second.getUid();
}
+pid_t LogStatistics::tidToPid(pid_t tid) {
+ return tidTable.add(tid)->second.getPid();
+}
+
// caller must free character string
const char* LogStatistics::pidToName(pid_t pid) const {
// An inconvenient truth ... getName() can alter the object
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index 066b7de..8808aac 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -18,17 +18,23 @@
#define _LOGD_LOG_STATISTICS_H__
#include <ctype.h>
+#include <inttypes.h>
+#include <stdint.h>
#include <stdlib.h>
+#include <string.h>
#include <sys/types.h>
#include <algorithm> // std::max
+#include <experimental/string_view>
#include <memory>
#include <string> // std::string
#include <unordered_map>
#include <android-base/stringprintf.h>
#include <android/log.h>
+#include <log/log_time.h>
#include <private/android_filesystem_config.h>
+#include <utils/FastStrcmp.h>
#include "LogBufferElement.h"
#include "LogUtils.h"
@@ -76,7 +82,7 @@
std::unique_ptr<const TEntry* []> sort(uid_t uid, pid_t pid,
size_t len) const {
if (!len) {
- std::unique_ptr<const TEntry* []> sorted(NULL);
+ std::unique_ptr<const TEntry* []> sorted(nullptr);
return sorted;
}
@@ -111,7 +117,7 @@
return sorted;
}
- inline iterator add(TKey key, LogBufferElement* element) {
+ inline iterator add(const TKey& key, const LogBufferElement* element) {
iterator it = map.find(key);
if (it == map.end()) {
it = map.insert(std::make_pair(key, TEntry(element))).first;
@@ -131,14 +137,21 @@
return it;
}
- void subtract(TKey key, LogBufferElement* element) {
+ void subtract(TKey&& key, const LogBufferElement* element) {
+ iterator it = map.find(std::move(key));
+ if ((it != map.end()) && it->second.subtract(element)) {
+ map.erase(it);
+ }
+ }
+
+ void subtract(const TKey& key, const LogBufferElement* element) {
iterator it = map.find(key);
if ((it != map.end()) && it->second.subtract(element)) {
map.erase(it);
}
}
- inline void drop(TKey key, LogBufferElement* element) {
+ inline void drop(TKey key, const LogBufferElement* element) {
iterator it = map.find(key);
if (it != map.end()) {
it->second.drop(element);
@@ -198,17 +211,18 @@
EntryBase() : size(0) {
}
- explicit EntryBase(LogBufferElement* element) : size(element->getMsgLen()) {
+ explicit EntryBase(const LogBufferElement* element)
+ : size(element->getMsgLen()) {
}
size_t getSizes() const {
return size;
}
- inline void add(LogBufferElement* element) {
+ inline void add(const LogBufferElement* element) {
size += element->getMsgLen();
}
- inline bool subtract(LogBufferElement* element) {
+ inline bool subtract(const LogBufferElement* element) {
size -= element->getMsgLen();
return !size;
}
@@ -239,7 +253,7 @@
EntryBaseDropped() : dropped(0) {
}
- explicit EntryBaseDropped(LogBufferElement* element)
+ explicit EntryBaseDropped(const LogBufferElement* element)
: EntryBase(element), dropped(element->getDropped()) {
}
@@ -247,15 +261,15 @@
return dropped;
}
- inline void add(LogBufferElement* element) {
+ inline void add(const LogBufferElement* element) {
dropped += element->getDropped();
EntryBase::add(element);
}
- inline bool subtract(LogBufferElement* element) {
+ inline bool subtract(const LogBufferElement* element) {
dropped -= element->getDropped();
return EntryBase::subtract(element) && !dropped;
}
- inline void drop(LogBufferElement* element) {
+ inline void drop(const LogBufferElement* element) {
dropped += 1;
EntryBase::subtract(element);
}
@@ -265,7 +279,7 @@
const uid_t uid;
pid_t pid;
- explicit UidEntry(LogBufferElement* element)
+ explicit UidEntry(const LogBufferElement* element)
: EntryBaseDropped(element),
uid(element->getUid()),
pid(element->getPid()) {
@@ -281,7 +295,7 @@
return pid;
}
- inline void add(LogBufferElement* element) {
+ inline void add(const LogBufferElement* element) {
if (pid != element->getPid()) {
pid = -1;
}
@@ -292,10 +306,6 @@
std::string format(const LogStatistics& stat, log_id_t id) const;
};
-namespace android {
-uid_t pidToUid(pid_t pid);
-}
-
struct PidEntry : public EntryBaseDropped {
const pid_t pid;
uid_t uid;
@@ -307,7 +317,7 @@
uid(android::pidToUid(pid)),
name(android::pidToName(pid)) {
}
- explicit PidEntry(LogBufferElement* element)
+ explicit PidEntry(const LogBufferElement* element)
: EntryBaseDropped(element),
pid(element->getPid()),
uid(element->getUid()),
@@ -317,7 +327,7 @@
: EntryBaseDropped(element),
pid(element.pid),
uid(element.uid),
- name(element.name ? strdup(element.name) : NULL) {
+ name(element.name ? strdup(element.name) : nullptr) {
}
~PidEntry() {
free(name);
@@ -339,14 +349,14 @@
inline void add(pid_t newPid) {
if (name && !fastcmp<strncmp>(name, "zygote", 6)) {
free(name);
- name = NULL;
+ name = nullptr;
}
if (!name) {
name = android::pidToName(newPid);
}
}
- inline void add(LogBufferElement* element) {
+ inline void add(const LogBufferElement* element) {
uid_t incomingUid = element->getUid();
if (getUid() != incomingUid) {
uid = incomingUid;
@@ -375,7 +385,14 @@
uid(android::pidToUid(tid)),
name(android::tidToName(tid)) {
}
- explicit TidEntry(LogBufferElement* element)
+ TidEntry(pid_t tid)
+ : EntryBaseDropped(),
+ tid(tid),
+ pid(android::tidToPid(tid)),
+ uid(android::pidToUid(tid)),
+ name(android::tidToName(tid)) {
+ }
+ explicit TidEntry(const LogBufferElement* element)
: EntryBaseDropped(element),
tid(element->getTid()),
pid(element->getPid()),
@@ -387,7 +404,7 @@
tid(element.tid),
pid(element.pid),
uid(element.uid),
- name(element.name ? strdup(element.name) : NULL) {
+ name(element.name ? strdup(element.name) : nullptr) {
}
~TidEntry() {
free(name);
@@ -412,14 +429,14 @@
inline void add(pid_t incomingTid) {
if (name && !fastcmp<strncmp>(name, "zygote", 6)) {
free(name);
- name = NULL;
+ name = nullptr;
}
if (!name) {
name = android::tidToName(incomingTid);
}
}
- inline void add(LogBufferElement* element) {
+ inline void add(const LogBufferElement* element) {
uid_t incomingUid = element->getUid();
pid_t incomingPid = element->getPid();
if ((getUid() != incomingUid) || (getPid() != incomingPid)) {
@@ -442,7 +459,7 @@
pid_t pid;
uid_t uid;
- explicit TagEntry(LogBufferElement* element)
+ explicit TagEntry(const LogBufferElement* element)
: EntryBaseDropped(element),
tag(element->getTag()),
pid(element->getPid()),
@@ -462,7 +479,7 @@
return android::tagToName(tag);
}
- inline void add(LogBufferElement* element) {
+ inline void add(const LogBufferElement* element) {
if (uid != element->getUid()) {
uid = -1;
}
@@ -476,6 +493,144 @@
std::string format(const LogStatistics& stat, log_id_t id) const;
};
+struct TagNameKey {
+ std::string* alloc;
+ std::experimental::string_view name; // Saves space if const char*
+
+ explicit TagNameKey(const LogBufferElement* element)
+ : alloc(nullptr), name("", strlen("")) {
+ if (element->isBinary()) {
+ uint32_t tag = element->getTag();
+ if (tag) {
+ const char* cp = android::tagToName(tag);
+ if (cp) {
+ name = std::experimental::string_view(cp, strlen(cp));
+ return;
+ }
+ }
+ alloc = new std::string(
+ android::base::StringPrintf("[%" PRIu32 "]", tag));
+ if (!alloc) return;
+ name = std::experimental::string_view(alloc->c_str(), alloc->size());
+ return;
+ }
+ const char* msg = element->getMsg();
+ if (!msg) {
+ name = std::experimental::string_view("chatty", strlen("chatty"));
+ return;
+ }
+ ++msg;
+ unsigned short len = element->getMsgLen();
+ len = (len <= 1) ? 0 : strnlen(msg, len - 1);
+ if (!len) {
+ name = std::experimental::string_view("<NULL>", strlen("<NULL>"));
+ return;
+ }
+ alloc = new std::string(msg, len);
+ if (!alloc) return;
+ name = std::experimental::string_view(alloc->c_str(), alloc->size());
+ }
+
+ explicit TagNameKey(TagNameKey&& rval)
+ : alloc(rval.alloc), name(rval.name.data(), rval.name.length()) {
+ rval.alloc = nullptr;
+ }
+
+ explicit TagNameKey(const TagNameKey& rval)
+ : alloc(rval.alloc ? new std::string(*rval.alloc) : nullptr),
+ name(alloc ? alloc->data() : rval.name.data(), rval.name.length()) {
+ }
+
+ ~TagNameKey() {
+ if (alloc) delete alloc;
+ }
+
+ operator const std::experimental::string_view() const {
+ return name;
+ }
+
+ const char* data() const {
+ return name.data();
+ }
+ size_t length() const {
+ return name.length();
+ }
+
+ bool operator==(const TagNameKey& rval) const {
+ if (length() != rval.length()) return false;
+ if (length() == 0) return true;
+ return fastcmp<strncmp>(data(), rval.data(), length()) == 0;
+ }
+ bool operator!=(const TagNameKey& rval) const {
+ return !(*this == rval);
+ }
+
+ size_t getAllocLength() const {
+ return alloc ? alloc->length() + 1 + sizeof(std::string) : 0;
+ }
+};
+
+// Hash for TagNameKey
+template <>
+struct std::hash<TagNameKey>
+ : public std::unary_function<const TagNameKey&, size_t> {
+ size_t operator()(const TagNameKey& __t) const noexcept {
+ if (!__t.length()) return 0;
+ return std::hash<std::experimental::string_view>()(
+ std::experimental::string_view(__t));
+ }
+};
+
+struct TagNameEntry : public EntryBase {
+ pid_t tid;
+ pid_t pid;
+ uid_t uid;
+ TagNameKey name;
+
+ explicit TagNameEntry(const LogBufferElement* element)
+ : EntryBase(element),
+ tid(element->getTid()),
+ pid(element->getPid()),
+ uid(element->getUid()),
+ name(element) {
+ }
+
+ const TagNameKey& getKey() const {
+ return name;
+ }
+ const pid_t& getTid() const {
+ return tid;
+ }
+ const pid_t& getPid() const {
+ return pid;
+ }
+ const uid_t& getUid() const {
+ return uid;
+ }
+ const char* getName() const {
+ return name.data();
+ }
+ size_t getNameAllocLength() const {
+ return name.getAllocLength();
+ }
+
+ inline void add(const LogBufferElement* element) {
+ if (uid != element->getUid()) {
+ uid = -1;
+ }
+ if (pid != element->getPid()) {
+ pid = -1;
+ }
+ if (tid != element->getTid()) {
+ tid = -1;
+ }
+ EntryBase::add(element);
+ }
+
+ std::string formatHeader(const std::string& name, log_id_t id) const;
+ std::string format(const LogStatistics& stat, log_id_t id) const;
+};
+
template <typename TEntry>
class LogFindWorst {
std::unique_ptr<const TEntry* []> sorted;
@@ -520,6 +675,9 @@
size_t mDroppedElements[LOG_ID_MAX];
size_t mSizesTotal[LOG_ID_MAX];
size_t mElementsTotal[LOG_ID_MAX];
+ log_time mOldest[LOG_ID_MAX];
+ log_time mNewest[LOG_ID_MAX];
+ log_time mNewestDropped[LOG_ID_MAX];
static size_t SizesTotal;
bool enable;
@@ -546,9 +704,14 @@
// security tag list
tagTable_t securityTagTable;
+ // global tag list
+ typedef LogHashtable<TagNameKey, TagNameEntry> tagNameTable_t;
+ tagNameTable_t tagNameTable;
+
size_t sizeOf() const {
size_t size = sizeof(*this) + pidTable.sizeOf() + tidTable.sizeOf() +
tagTable.sizeOf() + securityTagTable.sizeOf() +
+ tagNameTable.sizeOf() +
(pidTable.size() * sizeof(pidTable_t::iterator)) +
(tagTable.size() * sizeof(tagTable_t::iterator));
for (auto it : pidTable) {
@@ -559,6 +722,7 @@
const char* name = it.second.getName();
if (name) size += strlen(name) + 1;
}
+ for (auto it : tagNameTable) size += it.second.getNameAllocLength();
log_id_for_each(id) {
size += uidTable[id].sizeOf();
size += uidTable[id].size() * sizeof(uidTable_t::iterator);
@@ -576,6 +740,7 @@
enable = true;
}
+ void addTotal(LogBufferElement* entry);
void add(LogBufferElement* entry);
void subtract(LogBufferElement* entry);
// entry->setDropped(1) must follow this call
@@ -623,6 +788,7 @@
// helper (must be locked directly or implicitly by mLogElementsLock)
const char* pidToName(pid_t pid) const;
uid_t pidToUid(pid_t pid);
+ pid_t tidToPid(pid_t tid);
const char* uidToName(uid_t uid) const;
};
diff --git a/logd/LogTags.cpp b/logd/LogTags.cpp
index 67649b1..fcd45bd 100644
--- a/logd/LogTags.cpp
+++ b/logd/LogTags.cpp
@@ -32,8 +32,8 @@
#include <android-base/macros.h>
#include <android-base/stringprintf.h>
#include <log/log_event_list.h>
+#include <log/log_properties.h>
#include <private/android_filesystem_config.h>
-#include <private/android_logger.h>
#include "LogTags.h"
#include "LogUtils.h"
diff --git a/logd/LogTimes.cpp b/logd/LogTimes.cpp
index ccc550a..25c2ad2 100644
--- a/logd/LogTimes.cpp
+++ b/logd/LogTimes.cpp
@@ -78,7 +78,7 @@
void LogTimeEntry::threadStop(void* obj) {
LogTimeEntry* me = reinterpret_cast<LogTimeEntry*>(obj);
- lock();
+ wrlock();
if (me->mNonBlock) {
me->error_Locked();
@@ -134,7 +134,7 @@
me->leadingDropped = true;
- lock();
+ wrlock();
log_time start = me->mStart;
@@ -160,7 +160,7 @@
start = logbuf.flushTo(client, start, me->mLastTid, privileged,
security, FilterSecondPass, me);
- lock();
+ wrlock();
if (start == LogBufferElement::FLUSH_ERROR) {
me->error_Locked();
@@ -191,7 +191,7 @@
int LogTimeEntry::FilterFirstPass(const LogBufferElement* element, void* obj) {
LogTimeEntry* me = reinterpret_cast<LogTimeEntry*>(obj);
- LogTimeEntry::lock();
+ LogTimeEntry::wrlock();
if (me->leadingDropped) {
if (element->getDropped()) {
@@ -219,7 +219,7 @@
int LogTimeEntry::FilterSecondPass(const LogBufferElement* element, void* obj) {
LogTimeEntry* me = reinterpret_cast<LogTimeEntry*>(obj);
- LogTimeEntry::lock();
+ LogTimeEntry::wrlock();
me->mStart = element->getRealTime();
diff --git a/logd/LogTimes.h b/logd/LogTimes.h
index ec8252e..9ca2aea 100644
--- a/logd/LogTimes.h
+++ b/logd/LogTimes.h
@@ -61,7 +61,10 @@
const log_time mEnd; // only relevant if mNonBlock
// Protect List manipulations
- static void lock(void) {
+ static void wrlock(void) {
+ pthread_mutex_lock(×Lock);
+ }
+ static void rdlock(void) {
pthread_mutex_lock(×Lock);
}
static void unlock(void) {
@@ -104,7 +107,7 @@
mError = true;
}
void error(void) {
- lock();
+ wrlock();
error_Locked();
unlock();
}
diff --git a/logd/LogUtils.h b/logd/LogUtils.h
index fa9f398..4dcd3e7 100644
--- a/logd/LogUtils.h
+++ b/logd/LogUtils.h
@@ -38,6 +38,8 @@
// Caller must own and free returned value
char* pidToName(pid_t pid);
char* tidToName(pid_t tid);
+uid_t pidToUid(pid_t pid);
+pid_t tidToPid(pid_t tid);
// Furnished in LogTags.cpp. Thread safe.
const char* tagToName(uint32_t tag);
diff --git a/logd/event.logtags b/logd/event.logtags
index 39063a9..fa13a62 100644
--- a/logd/event.logtags
+++ b/logd/event.logtags
@@ -29,6 +29,7 @@
# 4: Number of allocations
# 5: Id
# 6: Percent
+# s: Number of seconds (monotonic time)
# Default value for data of type int/long is 2 (bytes).
#
# TODO: generate ".java" and ".h" files with integer constants from this file.
diff --git a/logd/logd.rc b/logd/logd.rc
index ee89b83..8804246 100644
--- a/logd/logd.rc
+++ b/logd/logd.rc
@@ -1,11 +1,11 @@
service logd /system/bin/logd
socket logd stream 0666 logd logd
socket logdr seqpacket 0666 logd logd
- socket logdw dgram 0222 logd logd
+ socket logdw dgram+passcred 0222 logd logd
file /proc/kmsg r
file /dev/kmsg w
user logd
- group logd system readproc
+ group logd system package_info readproc
writepid /dev/cpuset/system-background/tasks
service logd-reinit /system/bin/logd --reinit
diff --git a/logd/main.cpp b/logd/main.cpp
index 946485b..c8183f0 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -240,23 +240,36 @@
set_sched_policy(0, SP_BACKGROUND);
setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND);
+ // We should drop to AID_LOGD, if we are anything else, we have
+ // even lesser privileges and accept our fate.
+ gid_t groups[] = {
+ AID_SYSTEM, // search access to /data/system path
+ AID_PACKAGE_INFO, // readonly access to /data/system/packages.list
+ };
+ if (setgroups(arraysize(groups), groups) == -1) {
+ android::prdebug(
+ "logd.daemon: failed to set AID_SYSTEM AID_PACKAGE_INFO groups");
+ }
+ if (setgid(AID_LOGD) != 0) {
+ android::prdebug("logd.daemon: failed to set AID_LOGD gid");
+ }
+ if (setuid(AID_LOGD) != 0) {
+ android::prdebug("logd.daemon: failed to set AID_LOGD uid");
+ }
+
cap_t caps = cap_init();
(void)cap_clear(caps);
(void)cap_set_proc(caps);
(void)cap_free(caps);
- // If we are AID_ROOT, we should drop to AID_LOGD+AID_SYSTEM, if we are
- // anything else, we have even lesser privileges and accept our fate. Not
- // worth checking for error returns setting this thread's privileges.
- (void)setgid(AID_SYSTEM); // readonly access to /data/system/packages.list
- (void)setuid(AID_LOGD); // access to everything logd, eg /data/misc/logd
-
while (reinit_running && !sem_wait(&reinit) && reinit_running) {
// uidToName Privileged Worker
if (uid) {
name = nullptr;
- packagelist_parse(package_list_parser_cb, nullptr);
+ // if we got the perms wrong above, this would spam if we reported
+ // problems with acquisition of an uid name from the packages.
+ (void)packagelist_parse(package_list_parser_cb, nullptr);
uid = 0;
sem_post(&uidName);
@@ -407,6 +420,11 @@
// logging plugins like auditd and restart control. Additional
// transitory per-client threads are created for each reader.
int main(int argc, char* argv[]) {
+ // logd is written under the assumption that the timezone is UTC.
+ // If TZ is not set, persist.sys.timezone is looked up in some time utility
+ // libc functions, including mktime. It confuses the logd time handling,
+ // so here explicitly set TZ to UTC, which overrides the property.
+ setenv("TZ", "UTC", 1);
// issue reinit command. KISS argument parsing.
if ((argc > 1) && argv[1] && !strcmp(argv[1], "--reinit")) {
return issueReinit();
diff --git a/logd/tests/Android.mk b/logd/tests/Android.mk
index c053993..1915677 100644
--- a/logd/tests/Android.mk
+++ b/logd/tests/Android.mk
@@ -49,3 +49,38 @@
LOCAL_SHARED_LIBRARIES := libbase libcutils liblog libselinux
LOCAL_SRC_FILES := $(test_src_files)
include $(BUILD_NATIVE_TEST)
+
+cts_executable := CtsLogdTestCases
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := $(cts_executable)
+LOCAL_MODULE_TAGS := tests
+LOCAL_CFLAGS += $(test_c_flags)
+LOCAL_SRC_FILES := $(test_src_files)
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/nativetest
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+LOCAL_SHARED_LIBRARIES := libbase libcutils liblog libselinux
+LOCAL_STATIC_LIBRARIES := libgtest libgtest_main
+LOCAL_COMPATIBILITY_SUITE := cts
+LOCAL_CTS_TEST_PACKAGE := android.core.logd
+include $(BUILD_CTS_EXECUTABLE)
+
+ifeq ($(HOST_OS)-$(HOST_ARCH),$(filter $(HOST_OS)-$(HOST_ARCH),linux-x86 linux-x86_64))
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := $(cts_executable)_list
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS := $(test_c_flags) -DHOST
+LOCAL_C_INCLUDES := external/gtest/include
+LOCAL_SRC_FILES := $(test_src_files)
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+LOCAL_CXX_STL := libc++
+LOCAL_SHARED_LIBRARIES := libbase libcutils liblog
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+include $(BUILD_HOST_NATIVE_TEST)
+
+endif # ifeq ($(HOST_OS)-$(HOST_ARCH),$(filter $(HOST_OS)-$(HOST_ARCH),linux-x86 linux-x86_64))
diff --git a/init/AndroidTest.xml b/logd/tests/AndroidTest.xml
similarity index 63%
copy from init/AndroidTest.xml
copy to logd/tests/AndroidTest.xml
index 3de69ed..b16bdfd 100644
--- a/init/AndroidTest.xml
+++ b/logd/tests/AndroidTest.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 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.
@@ -13,14 +13,15 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<configuration description="Config for init_tests">
- <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+<configuration description="Config for CTS Logging Daemon test cases">
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
<option name="cleanup" value="true" />
- <option name="push" value="init_tests->/data/local/tmp/init_tests" />
+ <option name="push" value="CtsLogdTestCases->/data/local/tmp/CtsLogdTestCases" />
+ <option name="append-bitness" value="true" />
</target_preparer>
- <option name="test-suite-tag" value="apct" />
<test class="com.android.tradefed.testtype.GTest" >
<option name="native-test-device-path" value="/data/local/tmp" />
- <option name="module-name" value="init_tests" />
+ <option name="module-name" value="CtsLogdTestCases" />
+ <option name="runtime-hint" value="65s" />
</test>
</configuration>
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
index d0101ed..cd80212 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/tests/logd_test.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <ctype.h>
#include <fcntl.h>
#include <inttypes.h>
#include <poll.h>
@@ -26,12 +27,13 @@
#include <string>
+#include <android-base/file.h>
#include <android-base/macros.h>
#include <android-base/stringprintf.h>
#include <cutils/sockets.h>
#include <gtest/gtest.h>
-#include <log/log.h>
#include <private/android_filesystem_config.h>
+#include <private/android_logger.h>
#ifdef __ANDROID__
#include <selinux/selinux.h>
#endif
@@ -39,6 +41,7 @@
#include "../LogReader.h" // pickup LOGD_SNDTIMEO
#include "../libaudit.h" // pickup AUDIT_RATE_LIMIT_*
+#ifdef __ANDROID__
static void send_to_control(char* buf, size_t len) {
int sock = socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_RESERVED,
SOCK_STREAM);
@@ -84,7 +87,7 @@
size_t ret = atol(buf) + 1;
if (ret < 4) {
delete[] buf;
- buf = NULL;
+ buf = nullptr;
break;
}
bool check = ret <= len;
@@ -106,7 +109,7 @@
// UID PACKAGE BYTES LINES
// 0 root 54164 147569
//
- char* benchmark = NULL;
+ char* benchmark = nullptr;
do {
static const char signature[] = "\n0 root ";
@@ -121,7 +124,7 @@
benchmark = cp;
#ifdef DEBUG
char* end = strstr(benchmark, "\n");
- if (end == NULL) {
+ if (end == nullptr) {
end = benchmark + strlen(benchmark);
}
fprintf(stderr, "parse for spam counter in \"%.*s\"\n",
@@ -153,18 +156,25 @@
if (value > 10UL) {
break;
}
- benchmark = NULL;
+ benchmark = nullptr;
} while (*cp);
return benchmark;
}
+#endif
TEST(logd, statistics) {
+#ifdef __ANDROID__
size_t len;
char* buf;
+ // Drop cache so that any access problems can be discovered.
+ if (!android::base::WriteStringToFile("3\n", "/proc/sys/vm/drop_caches")) {
+ GTEST_LOG_(INFO) << "Could not open trigger dropping inode cache";
+ }
+
alloc_statistics(&buf, &len);
- ASSERT_TRUE(NULL != buf);
+ ASSERT_TRUE(nullptr != buf);
// remove trailing FF
char* cp = buf + len - 1;
@@ -189,23 +199,46 @@
EXPECT_EQ(0, truncated);
char* main_logs = strstr(cp, "\nChattiest UIDs in main ");
- EXPECT_TRUE(NULL != main_logs);
+ EXPECT_TRUE(nullptr != main_logs);
char* radio_logs = strstr(cp, "\nChattiest UIDs in radio ");
if (!radio_logs)
- GTEST_LOG_(INFO) << "Value of: NULL != radio_logs\n"
+ GTEST_LOG_(INFO) << "Value of: nullptr != radio_logs\n"
"Actual: false\n"
"Expected: false\n";
char* system_logs = strstr(cp, "\nChattiest UIDs in system ");
- EXPECT_TRUE(NULL != system_logs);
+ EXPECT_TRUE(nullptr != system_logs);
char* events_logs = strstr(cp, "\nChattiest UIDs in events ");
- EXPECT_TRUE(NULL != events_logs);
+ EXPECT_TRUE(nullptr != events_logs);
+
+ // Check if there is any " u0_a#### " as this means packagelistparser broken
+ char* used_getpwuid = nullptr;
+ int used_getpwuid_len;
+ char* uid_name = cp;
+ static const char getpwuid_prefix[] = " u0_a";
+ while ((uid_name = strstr(uid_name, getpwuid_prefix)) != nullptr) {
+ used_getpwuid = uid_name + 1;
+ uid_name += strlen(getpwuid_prefix);
+ while (isdigit(*uid_name)) ++uid_name;
+ used_getpwuid_len = uid_name - used_getpwuid;
+ if (isspace(*uid_name)) break;
+ used_getpwuid = nullptr;
+ }
+ EXPECT_TRUE(nullptr == used_getpwuid);
+ if (used_getpwuid) {
+ fprintf(stderr, "libpackagelistparser failed to pick up %.*s\n",
+ used_getpwuid_len, used_getpwuid);
+ }
delete[] buf;
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
}
+#ifdef __ANDROID__
static void caught_signal(int /* signum */) {
}
@@ -315,8 +348,10 @@
fprintf(stderr, "}\n");
fflush(stderr);
}
+#endif
TEST(logd, both) {
+#ifdef __ANDROID__
log_msg msg;
// check if we can read any logs from logd
@@ -343,7 +378,7 @@
}
alarm(old_alarm);
- sigaction(SIGALRM, &old_sigaction, NULL);
+ sigaction(SIGALRM, &old_sigaction, nullptr);
close(fd);
}
@@ -390,8 +425,12 @@
EXPECT_EQ(0, !user_logger_available && !kernel_logger_available);
EXPECT_EQ(0, user_logger_content && kernel_logger_content);
EXPECT_EQ(0, !user_logger_content && !kernel_logger_content);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
}
+#ifdef __ANDROID__
// BAD ROBOT
// Benchmark threshold are generally considered bad form unless there is
// is some human love applied to the continued maintenance and whether the
@@ -421,7 +460,7 @@
// Introduce some extreme spam for the worst UID filter
ASSERT_TRUE(
- NULL !=
+ nullptr !=
(fp = popen("/data/nativetest/liblog-benchmarks/liblog-benchmarks"
" BM_log_maximum_retry"
" BM_log_maximum"
@@ -492,10 +531,10 @@
bool collected_statistics = !!buf;
EXPECT_EQ(true, collected_statistics);
- ASSERT_TRUE(NULL != buf);
+ ASSERT_TRUE(nullptr != buf);
char* benchmark_statistics_found = find_benchmark_spam(buf);
- ASSERT_TRUE(benchmark_statistics_found != NULL);
+ ASSERT_TRUE(benchmark_statistics_found != nullptr);
// Check how effective the SPAM filter is, parse out Now size.
// 0 root 54164 147569
@@ -560,9 +599,11 @@
// 50% threshold for SPAM filter (<20% typical, lots of engineering margin)
ASSERT_GT(totalSize, nowSpamSize * 2);
}
+#endif
// b/26447386 confirm fixed
void timeout_negative(const char* command) {
+#ifdef __ANDROID__
log_msg msg_wrap, msg_timeout;
bool content_wrap = false, content_timeout = false, written = false;
unsigned int alarm_wrap = 0, alarm_timeout = 0;
@@ -588,7 +629,7 @@
written = write(fd, ask.c_str(), len) == (ssize_t)len;
if (!written) {
alarm(old_alarm);
- sigaction(SIGALRM, &old_sigaction, NULL);
+ sigaction(SIGALRM, &old_sigaction, nullptr);
close(fd);
continue;
}
@@ -610,7 +651,7 @@
: (old_alarm > (1 + 3 - alarm_wrap))
? old_alarm - 3 + alarm_wrap
: 2);
- sigaction(SIGALRM, &old_sigaction, NULL);
+ sigaction(SIGALRM, &old_sigaction, nullptr);
close(fd);
@@ -632,6 +673,10 @@
EXPECT_NE(0U, alarm_wrap);
EXPECT_TRUE(content_timeout);
EXPECT_NE(0U, alarm_timeout);
+#else
+ command = nullptr;
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
}
TEST(logd, timeout_no_start) {
@@ -645,6 +690,7 @@
// b/26447386 refined behavior
TEST(logd, timeout) {
+#ifdef __ANDROID__
// b/33962045 This test interferes with other log reader tests that
// follow because of file descriptor socket persistence in the same
// process. So let's fork it to isolate it from giving us pain.
@@ -670,8 +716,12 @@
while (--i) {
int fd = socket_local_client("logdr", ANDROID_SOCKET_NAMESPACE_RESERVED,
SOCK_SEQPACKET);
- EXPECT_LT(0, fd);
- if (fd < 0) _exit(fd);
+ int save_errno = errno;
+ if (fd < 0) {
+ fprintf(stderr, "failed to open /dev/socket/logdr %s\n",
+ strerror(save_errno));
+ _exit(fd);
+ }
std::string ask = android::base::StringPrintf(
"dumpAndClose lids=0,1,2,3,4,5 timeout=6 start=%" PRIu32
@@ -689,7 +739,7 @@
written = write(fd, ask.c_str(), len) == (ssize_t)len;
if (!written) {
alarm(old_alarm);
- sigaction(SIGALRM, &old_sigaction, NULL);
+ sigaction(SIGALRM, &old_sigaction, nullptr);
close(fd);
continue;
}
@@ -711,7 +761,7 @@
: (old_alarm > (1 + 3 - alarm_wrap))
? old_alarm - 3 + alarm_wrap
: 2);
- sigaction(SIGALRM, &old_sigaction, NULL);
+ sigaction(SIGALRM, &old_sigaction, nullptr);
close(fd);
@@ -723,8 +773,12 @@
// active _or_ inactive during the test.
if (content_timeout) {
log_time msg(msg_timeout.entry.sec, msg_timeout.entry.nsec);
- EXPECT_FALSE(msg < now);
- if (msg < now) _exit(-1);
+ if (msg < now) {
+ fprintf(stderr, "%u.%09u < %u.%09u\n", msg_timeout.entry.sec,
+ msg_timeout.entry.nsec, (unsigned)now.tv_sec,
+ (unsigned)now.tv_nsec);
+ _exit(-1);
+ }
if (msg > now) {
now = msg;
now.tv_sec += 30;
@@ -760,10 +814,14 @@
_exit(!written + content_wrap + alarm_wrap + !content_timeout +
!alarm_timeout);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
}
// b/27242723 confirmed fixed
TEST(logd, SNDTIMEO) {
+#ifdef __ANDROID__
static const unsigned sndtimeo =
LOGD_SNDTIMEO; // <sigh> it has to be done!
static const unsigned sleep_time = sndtimeo + 3;
@@ -805,7 +863,7 @@
int save_errno = (recv_ret < 0) ? errno : 0;
EXPECT_NE(0U, alarm(old_alarm));
- sigaction(SIGALRM, &old_sigaction, NULL);
+ sigaction(SIGALRM, &old_sigaction, nullptr);
EXPECT_EQ(0, recv_ret);
if (recv_ret > 0) {
@@ -814,6 +872,9 @@
EXPECT_EQ(0, save_errno);
close(fd);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
}
TEST(logd, getEventTag_list) {
@@ -841,8 +902,8 @@
char* cp;
long ret = strtol(buffer, &cp, 10);
EXPECT_GT(ret, 16);
- EXPECT_TRUE(strstr(buffer, "\t(to life the universe etc|3)") != NULL);
- EXPECT_TRUE(strstr(buffer, "answer") != NULL);
+ EXPECT_TRUE(strstr(buffer, "\t(to life the universe etc|3)") != nullptr);
+ EXPECT_TRUE(strstr(buffer, "answer") != nullptr);
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
@@ -862,8 +923,8 @@
char* cp;
long ret = strtol(buffer, &cp, 10);
EXPECT_GT(ret, 16);
- EXPECT_TRUE(strstr(buffer, "\t(new|1)") != NULL);
- EXPECT_TRUE(strstr(buffer, name) != NULL);
+ EXPECT_TRUE(strstr(buffer, "\t(new|1)") != nullptr);
+ EXPECT_TRUE(strstr(buffer, name) != nullptr);
// ToDo: also look for this in /data/misc/logd/event-log-tags and
// /dev/event-log-tags.
#else
@@ -871,11 +932,14 @@
#endif
}
+#ifdef __ANDROID__
static inline int32_t get4LE(const char* src) {
return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
}
+#endif
void __android_log_btwrite_multiple__helper(int count) {
+#ifdef __ANDROID__
log_time ts(CLOCK_MONOTONIC);
log_time ts1(CLOCK_MONOTONIC);
@@ -904,7 +968,7 @@
ASSERT_EQ(0, info.si_status);
struct logger_list* logger_list;
- ASSERT_TRUE(NULL !=
+ ASSERT_TRUE(nullptr !=
(logger_list = android_logger_list_open(
LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK,
0, pid)));
@@ -968,6 +1032,10 @@
EXPECT_EQ(expected_chatty_count, chatty_count);
EXPECT_EQ(expected_identical_count, identical_count);
EXPECT_EQ(expected_expire_count, expire_count);
+#else
+ count = 0;
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
}
TEST(logd, multiple_test_1) {
@@ -993,22 +1061,24 @@
if (pid) {
siginfo_t info = {};
- if (TEMP_FAILURE_RETRY(waitid(P_PID, pid, &info, WEXITED))) return 0;
- if (info.si_status) return 0;
+ if (TEMP_FAILURE_RETRY(waitid(P_PID, pid, &info, WEXITED))) return -1;
+ if (info.si_status) return -1;
return pid;
}
// We may have DAC, but let's not have MAC
- if (setcon("u:object_r:shell:s0") < 0) {
+ if ((setcon("u:object_r:shell:s0") < 0) && (setcon("u:r:shell:s0") < 0)) {
int save_errno = errno;
security_context_t context;
getcon(&context);
- fprintf(stderr, "setcon(\"u:r:shell:s0\") failed @\"%s\" %s\n", context,
- strerror(save_errno));
- freecon(context);
- _exit(-1);
- // NOTREACHED
- return 0;
+ if (strcmp(context, "u:r:shell:s0")) {
+ fprintf(stderr, "setcon(\"u:r:shell:s0\") failed @\"%s\" %s\n",
+ context, strerror(save_errno));
+ freecon(context);
+ _exit(-1);
+ // NOTREACHED
+ return -1;
+ }
}
// The key here is we are root, but we are in u:r:shell:s0,
@@ -1036,25 +1106,39 @@
if (access(android::base::StringPrintf(file, num).c_str(), F_OK) == 0) {
_exit(-1);
// NOTREACHED
- return 0;
+ return -1;
}
usleep(usec);
--num;
}
_exit(0);
// NOTREACHED
- return 0;
+ return -1;
}
+static constexpr int background_period = 10;
+
static int count_avc(pid_t pid) {
int count = 0;
- if (pid == 0) return count;
+ // pid=-1 skip as pid is in error
+ if (pid == (pid_t)-1) return count;
- struct logger_list* logger_list;
- if (!(logger_list = android_logger_list_open(
- LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 0, pid)))
+ // pid=0 means we want to report the background count of avc: activities
+ struct logger_list* logger_list =
+ pid ? android_logger_list_alloc(
+ ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 0, pid)
+ : android_logger_list_alloc_time(
+ ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK,
+ log_time(android_log_clockid()) -
+ log_time(background_period, 0),
+ 0);
+ if (!logger_list) return count;
+ struct logger* logger = android_logger_open(logger_list, LOG_ID_EVENTS);
+ if (!logger) {
+ android_logger_list_close(logger_list);
return count;
+ }
for (;;) {
log_msg log_msg;
@@ -1086,56 +1170,64 @@
}
#endif
-TEST(logd, sepolicy_rate_limiter_maximum) {
+TEST(logd, sepolicy_rate_limiter) {
#ifdef __ANDROID__
- static const int rate = AUDIT_RATE_LIMIT_MAX;
- static const int duration = 2;
- // Two seconds of a liveable sustained rate
- EXPECT_EQ(rate * duration, count_avc(sepolicy_rate(rate, rate * duration)));
-#else
- GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
-}
-
-TEST(logd, sepolicy_rate_limiter_sub_burst) {
-#ifdef __ANDROID__
- // maximum period below half way between sustainable and burst rate.
- static const int threshold =
- ((AUDIT_RATE_LIMIT_BURST_DURATION *
- (AUDIT_RATE_LIMIT_DEFAULT + AUDIT_RATE_LIMIT_MAX)) +
- 1) /
- 2;
- static const int rate = (threshold / AUDIT_RATE_LIMIT_BURST_DURATION) - 1;
- static const int duration = AUDIT_RATE_LIMIT_BURST_DURATION;
- EXPECT_EQ(rate * duration, count_avc(sepolicy_rate(rate, rate * duration)));
-#else
- GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
-}
-
-TEST(logd, sepolicy_rate_limiter_spam) {
-#ifdef __ANDROID__
- // maximum period of double the maximum burst rate
- static const int threshold =
- ((AUDIT_RATE_LIMIT_BURST_DURATION *
- (AUDIT_RATE_LIMIT_DEFAULT + AUDIT_RATE_LIMIT_MAX)) +
- 1) /
- 2;
- static const int rate = AUDIT_RATE_LIMIT_DEFAULT * 2;
- static const int duration = threshold / AUDIT_RATE_LIMIT_DEFAULT;
- EXPECT_GE(
- ((AUDIT_RATE_LIMIT_DEFAULT * duration) * 115) / 100, // +15% margin
- count_avc(sepolicy_rate(rate, rate * duration)));
- // give logd another 3 seconds to react to the burst before checking
- sepolicy_rate(rate, rate * 3);
- // maximum period at double the maximum burst rate (spam filter kicked in)
- EXPECT_GE(
- threshold * 2,
- count_avc(sepolicy_rate(rate, rate * AUDIT_RATE_LIMIT_BURST_DURATION)));
- // cool down, and check unspammy rate still works
- sleep(2);
- EXPECT_LE(AUDIT_RATE_LIMIT_BURST_DURATION - 1, // allow _one_ to be lost
- count_avc(sepolicy_rate(1, AUDIT_RATE_LIMIT_BURST_DURATION)));
+ int background_selinux_activity_too_high = count_avc(0);
+ if (background_selinux_activity_too_high > 2) {
+ GTEST_LOG_(ERROR) << "Too much background selinux activity "
+ << background_selinux_activity_too_high * 60 /
+ background_period
+ << "/minute on the device, this test\n"
+ << "can not measure the functionality of the "
+ << "sepolicy rate limiter. Expect test to\n"
+ << "fail as this device is in a bad state, "
+ << "but is not strictly a unit test failure.";
+ }
+ // sepolicy_rate_limiter_maximum
+ { // maximum precharch test block.
+ static constexpr int rate = AUDIT_RATE_LIMIT_MAX;
+ static constexpr int duration = 2;
+ // Two seconds of a liveable sustained rate
+ EXPECT_EQ(rate * duration,
+ count_avc(sepolicy_rate(rate, rate * duration)));
+ }
+ // sepolicy_rate_limiter_sub_burst
+ { // maximum period below half way between sustainable and burst rate
+ static constexpr int threshold =
+ ((AUDIT_RATE_LIMIT_BURST_DURATION *
+ (AUDIT_RATE_LIMIT_DEFAULT + AUDIT_RATE_LIMIT_MAX)) +
+ 1) /
+ 2;
+ static constexpr int rate =
+ (threshold / AUDIT_RATE_LIMIT_BURST_DURATION) - 1;
+ static constexpr int duration = AUDIT_RATE_LIMIT_BURST_DURATION;
+ EXPECT_EQ(rate * duration,
+ count_avc(sepolicy_rate(rate, rate * duration)));
+ }
+ // sepolicy_rate_limiter_spam
+ { // hit avc: hard beyond reason block.
+ // maximum period of double the maximum burst rate
+ static constexpr int threshold =
+ ((AUDIT_RATE_LIMIT_BURST_DURATION *
+ (AUDIT_RATE_LIMIT_DEFAULT + AUDIT_RATE_LIMIT_MAX)) +
+ 1) /
+ 2;
+ static constexpr int rate = AUDIT_RATE_LIMIT_DEFAULT * 2;
+ static constexpr int duration = threshold / AUDIT_RATE_LIMIT_DEFAULT;
+ EXPECT_GE(
+ ((AUDIT_RATE_LIMIT_DEFAULT * duration) * 115) / 100, // +15% margin
+ count_avc(sepolicy_rate(rate, rate * duration)));
+ // give logd another 3 seconds to react to the burst before checking
+ sepolicy_rate(rate, rate * 3);
+ // maximum period at double maximum burst rate (spam filter kicked in)
+ EXPECT_GE(threshold * 2,
+ count_avc(sepolicy_rate(
+ rate, rate * AUDIT_RATE_LIMIT_BURST_DURATION)));
+ // cool down, and check unspammy rate still works
+ sleep(2);
+ EXPECT_LE(AUDIT_RATE_LIMIT_BURST_DURATION - 1, // allow _one_ lost
+ count_avc(sepolicy_rate(1, AUDIT_RATE_LIMIT_BURST_DURATION)));
+ }
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
diff --git a/platform_tools_tool_version.mk b/platform_tools_tool_version.mk
new file mode 100644
index 0000000..eed2ab5
--- /dev/null
+++ b/platform_tools_tool_version.mk
@@ -0,0 +1,22 @@
+# Copyright (C) 2017 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.
+
+# We rewrite ${PLATFORM_SDK_VERSION} with 0 rather than $(PLATFORM_SDK_VERSION)
+# because on the actual platform tools release branches the file contains a
+# literal instead. Using 0 lets us easily distinguish non-canonical builds.
+platform_tools_version := $(shell sed \
+ 's/$${PLATFORM_SDK_VERSION}/0/ ; s/^Pkg.Revision=\(.*\)/\1/p ; d' \
+ development/sdk/plat_tools_source.prop_template \
+ )
+tool_version := $(platform_tools_version)-$(BUILD_NUMBER_FROM_FILE)
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 345097f..2e5575f 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -188,4 +188,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_extract.rc b/rootdir/asan_extract.rc
index 705d872..4aea6a3 100644
--- a/rootdir/asan_extract.rc
+++ b/rootdir/asan_extract.rc
@@ -1,6 +1,3 @@
# When /data is available, look for /system/asan.tar.gz and potentially extract.
on post-fs-data
exec - system system -- /system/bin/asan_extract
-
-on early-boot && property:asan.restore_reboot=1
- powerctl reboot
diff --git a/rootdir/asan_extract.sh b/rootdir/asan_extract.sh
index c7d2fdf..2d72320 100644
--- a/rootdir/asan_extract.sh
+++ b/rootdir/asan_extract.sh
@@ -26,6 +26,24 @@
SRC=/system/asan.tar.bz2
MD5_FILE=/data/asan.md5sum
ASAN_DIR=/data/asan
+# Minimum /data size in blocks. Arbitrarily 512M.
+MIN_DATA_SIZE=131072
+
+# Checks for FDE pre-decrypt state.
+
+VOLD_STATUS=$(getprop vold.decrypt)
+if [ "$VOLD_STATUS" = "trigger_restart_min_framework" ] ; then
+ log -p i -t asan_install "Pre-decrypt FDE detected (by vold property)!"
+ exit 1
+fi
+
+STATFS_BLOCKS=$(stat -f -c '%b' /data)
+if [ "$STATFS_BLOCKS" -le "$MIN_DATA_SIZE" ] ; then
+ log -p i -t asan_install "Pre-decrypt FDE detected (by /data size)!"
+ exit 1
+fi
+
+# Check for ASAN source.
if ! test -f $SRC ; then
log -p i -t asan_install "Did not find $SRC!"
@@ -34,6 +52,8 @@
log -p i -t asan_install "Found $SRC, checking whether we need to apply it."
+# Checksum check.
+
ASAN_TAR_MD5=$(md5sum $SRC)
if test -f $MD5_FILE ; then
INSTALLED_MD5=$(cat $MD5_FILE)
@@ -43,6 +63,8 @@
fi
fi
+# Actually apply the source.
+
# Just clean up, helps with restorecon.
rm -rf $ASAN_DIR
@@ -53,10 +75,16 @@
# Cannot log here, log would run with system_data_file.
+# Set correct permission bits.
+chmod -R 744 $ASAN_DIR
+cd $ASAN_DIR ; find . -type d -exec chmod 755 {} \;
+
restorecon -R -F $ASAN_DIR/*/lib*
log -p i -t asan_install "Fixed selinux labels..."
+
+# Now write down our checksum to mark the extraction complete.
echo "$ASAN_TAR_MD5" > $MD5_FILE
# We want to reboot now. It seems it is not possible to run "reboot" here, the device will
@@ -64,4 +92,4 @@
log -p i -t asan_install "Signaling init to reboot..."
-setprop asan.restore_reboot 1
+setprop sys.powerctl reboot
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..773ca06 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: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:libhwbinder.so:libbase.so:libcutils.so:libhardware.so:libhidlbase.so:libhidltransport.so:libutils.so:libc++.so:libz.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: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:libhwbinder.so:libbase.so:libcutils.so:libhardware.so:libhidlbase.so:libhidltransport.so:libutils.so:libc++.so:libz.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 = libc.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/init.rc b/rootdir/init.rc
index f064fed..c006d80 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -7,6 +7,7 @@
import /init.environ.rc
import /init.usb.rc
import /init.${ro.hardware}.rc
+import /vendor/etc/init/hw/init.${ro.hardware}.rc
import /init.usb.configfs.rc
import /init.${ro.zygote}.rc
@@ -52,18 +53,22 @@
mkdir /dev/stune/foreground
mkdir /dev/stune/background
mkdir /dev/stune/top-app
+ mkdir /dev/stune/rt
chown system system /dev/stune
chown system system /dev/stune/foreground
chown system system /dev/stune/background
chown system system /dev/stune/top-app
+ chown system system /dev/stune/rt
chown system system /dev/stune/tasks
chown system system /dev/stune/foreground/tasks
chown system system /dev/stune/background/tasks
chown system system /dev/stune/top-app/tasks
+ chown system system /dev/stune/rt/tasks
chmod 0664 /dev/stune/tasks
chmod 0664 /dev/stune/foreground/tasks
chmod 0664 /dev/stune/background/tasks
chmod 0664 /dev/stune/top-app/tasks
+ chmod 0664 /dev/stune/rt/tasks
# Mount staging areas for devices managed by vold
# See storage config details at http://source.android.com/tech/storage/
@@ -157,25 +162,25 @@
# this ensures that the cpusets are present and usable, but the device's
# init.rc must actually set the correct cpus
mkdir /dev/cpuset/foreground
- write /dev/cpuset/foreground/cpus 0
- write /dev/cpuset/foreground/mems 0
+ copy /dev/cpuset/cpus /dev/cpuset/foreground/cpus
+ copy /dev/cpuset/mems /dev/cpuset/foreground/mems
mkdir /dev/cpuset/foreground/boost
- write /dev/cpuset/foreground/boost/cpus 0
- write /dev/cpuset/foreground/boost/mems 0
+ copy /dev/cpuset/cpus /dev/cpuset/foreground/boost/cpus
+ copy /dev/cpuset/mems /dev/cpuset/foreground/boost/mems
mkdir /dev/cpuset/background
- write /dev/cpuset/background/cpus 0
- write /dev/cpuset/background/mems 0
+ copy /dev/cpuset/cpus /dev/cpuset/background/cpus
+ copy /dev/cpuset/mems /dev/cpuset/background/mems
# system-background is for system tasks that should only run on
# little cores, not on bigs
# to be used only by init, so don't change system-bg permissions
mkdir /dev/cpuset/system-background
- write /dev/cpuset/system-background/cpus 0
- write /dev/cpuset/system-background/mems 0
+ copy /dev/cpuset/cpus /dev/cpuset/system-background/cpus
+ copy /dev/cpuset/mems /dev/cpuset/system-background/mems
mkdir /dev/cpuset/top-app
- write /dev/cpuset/top-app/cpus 0
- write /dev/cpuset/top-app/mems 0
+ copy /dev/cpuset/cpus /dev/cpuset/top-app/cpus
+ copy /dev/cpuset/mems /dev/cpuset/top-app/mems
# change permissions for all cpusets we'll touch at runtime
chown system system /dev/cpuset
@@ -245,10 +250,6 @@
class_stop charger
trigger late-init
-# Load properties from /system/ + /factory after fs mount.
-on load_system_props_action
- load_system_props
-
on load_persist_props_action
load_persist_props
start logd
@@ -269,11 +270,6 @@
trigger fs
trigger post-fs
- # Load properties from /system/ + /factory after fs mount. Place
- # this in another action so that the load will be scheduled after the prior
- # issued fs triggers have completed.
- trigger load_system_props_action
-
# Mount fstab in init.{$device}.rc by mount_all with '--late' parameter
# to only mount entries with 'latemount'. This is needed if '--early' is
# specified in the previous mount_all command on the fs stage.
@@ -298,11 +294,17 @@
trigger boot
on post-fs
+ # Load properties from
+ # /system/build.prop,
+ # /odm/build.prop,
+ # /vendor/build.prop and
+ # /factory/factory.prop
+ load_system_props
+ # start essential services
start logd
+ start servicemanager
start hwservicemanager
-
- # HALs required before data is mounted
- class_start early_hal
+ start vndservicemanager
# once everything is setup, no need to modify /
mount rootfs rootfs / ro remount
@@ -352,6 +354,10 @@
# create the lost+found directories, so as to enforce our permissions
mkdir /cache/lost+found 0770 root root
+on late-fs
+ # HALs required before storage encryption can get unlocked (FBE/FDE)
+ class_start early_hal
+
on post-fs-data
# We chown/chmod /data again so because mount is run as root + defaults
chown system system /data
@@ -386,6 +392,7 @@
mkdir /data/misc/radio 0770 system radio
mkdir /data/misc/sms 0770 system radio
mkdir /data/misc/zoneinfo 0775 system system
+ mkdir /data/misc/textclassifier 0771 system system
mkdir /data/misc/vpn 0770 system vpn
mkdir /data/misc/shared_relro 0771 shared_relro shared_relro
mkdir /data/misc/systemkeys 0700 system system
@@ -406,7 +413,7 @@
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 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
@@ -497,12 +504,28 @@
# Set indication (checked by vold) that we have finished this action
#setprop vold.post_fs_data_done 1
-# This trigger will be triggered before 'zygote-start' since there is no zygote-start defined in
-# current init.rc. It is recommended to put unnecessary data/ initialization from post-fs-data
-# to start-zygote to unblock zygote start.
+# It is recommended to put unnecessary data/ initialization from post-fs-data
+# to start-zygote in device's init.rc to unblock zygote start.
+on zygote-start && property:ro.crypto.state=unencrypted
+ # A/B update verifier that marks a successful boot.
+ exec_start update_verifier_nonencrypted
+ start netd
+ start zygote
+ start zygote_secondary
+
+on zygote-start && property:ro.crypto.state=unsupported
+ # A/B update verifier that marks a successful boot.
+ exec_start update_verifier_nonencrypted
+ start netd
+ start zygote
+ start zygote_secondary
+
on zygote-start && property:ro.crypto.state=encrypted && property:ro.crypto.type=file
- start netd
- start zygote
+ # A/B update verifier that marks a successful boot.
+ exec_start update_verifier_nonencrypted
+ start netd
+ start zygote
+ start zygote_secondary
on boot
# basic network init
@@ -601,8 +624,6 @@
class_start core
on nonencrypted
- # A/B update verifier that marks a successful boot.
- exec_start update_verifier_nonencrypted
class_start main
class_start late_start
@@ -638,9 +659,6 @@
class_reset late_start
class_reset main
-on property:sys.powerctl=*
- powerctl ${sys.powerctl}
-
on property:sys.boot_completed=1
bootchart stop
diff --git a/rootdir/init.usb.configfs.rc b/rootdir/init.usb.configfs.rc
index 32f0198..de1aab3 100644
--- a/rootdir/init.usb.configfs.rc
+++ b/rootdir/init.usb.configfs.rc
@@ -2,6 +2,7 @@
write /config/usb_gadget/g1/UDC "none"
stop adbd
setprop sys.usb.ffs.ready 0
+ setprop sys.usb.ffs.mtp.ready 0
write /config/usb_gadget/g1/bDeviceClass 0
write /config/usb_gadget/g1/bDeviceSubClass 0
write /config/usb_gadget/g1/bDeviceProtocol 0
@@ -11,6 +12,9 @@
rmdir /config/usb_gadget/g1/functions/rndis.gs4
setprop sys.usb.state ${sys.usb.config}
+on property:init.svc.adbd=stopped
+ setprop sys.usb.ffs.ready 0
+
on property:sys.usb.config=adb && property:sys.usb.configfs=1
start adbd
@@ -20,7 +24,7 @@
write /config/usb_gadget/g1/UDC ${sys.usb.controller}
setprop sys.usb.state ${sys.usb.config}
-on property:sys.usb.config=mtp && property:sys.usb.configfs=1
+on property:sys.usb.ffs.mtp.ready=1 && property:sys.usb.config=mtp && property:sys.usb.configfs=1
write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "mtp"
symlink /config/usb_gadget/g1/functions/mtp.gs0 /config/usb_gadget/g1/configs/b.1/f1
write /config/usb_gadget/g1/UDC ${sys.usb.controller}
@@ -29,14 +33,15 @@
on property:sys.usb.config=mtp,adb && property:sys.usb.configfs=1
start adbd
-on property:sys.usb.ffs.ready=1 && property:sys.usb.config=mtp,adb && property:sys.usb.configfs=1
+on property:sys.usb.ffs.ready=1 && property:sys.usb.ffs.mtp.ready=1 && \
+property:sys.usb.config=mtp,adb && property:sys.usb.configfs=1
write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "mtp_adb"
symlink /config/usb_gadget/g1/functions/mtp.gs0 /config/usb_gadget/g1/configs/b.1/f1
symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f2
write /config/usb_gadget/g1/UDC ${sys.usb.controller}
setprop sys.usb.state ${sys.usb.config}
-on property:sys.usb.config=ptp && property:sys.usb.configfs=1
+on property:sys.usb.ffs.mtp.ready=1 && property:sys.usb.config=ptp && property:sys.usb.configfs=1
write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "ptp"
symlink /config/usb_gadget/g1/functions/ptp.gs1 /config/usb_gadget/g1/configs/b.1/f1
write /config/usb_gadget/g1/UDC ${sys.usb.controller}
@@ -45,7 +50,8 @@
on property:sys.usb.config=ptp,adb && property:sys.usb.configfs=1
start adbd
-on property:sys.usb.ffs.ready=1 && property:sys.usb.config=ptp,adb && property:sys.usb.configfs=1
+on property:sys.usb.ffs.ready=1 && property:sys.usb.ffs.mtp.ready=1 && \
+property:sys.usb.config=ptp,adb && property:sys.usb.configfs=1
write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "ptp_adb"
symlink /config/usb_gadget/g1/functions/ptp.gs1 /config/usb_gadget/g1/configs/b.1/f1
symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f2
diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc
index f5c93b7..eadf219 100644
--- a/rootdir/ueventd.rc
+++ b/rootdir/ueventd.rc
@@ -1,5 +1,37 @@
subsystem adf
- devname uevent_devname
+ devname uevent_devname
+
+subsystem graphics
+ devname uevent_devpath
+ dirname /dev/graphics
+
+subsystem drm
+ devname uevent_devpath
+ dirname /dev/dri
+
+subsystem oncrpc
+ devname uevent_devpath
+ dirname /dev/oncrpc
+
+subsystem adsp
+ devname uevent_devpath
+ dirname /dev/adsp
+
+subsystem msm_camera
+ devname uevent_devpath
+ dirname /dev/msm_camera
+
+subsystem input
+ devname uevent_devpath
+ dirname /dev/input
+
+subsystem mtd
+ devname uevent_devpath
+ dirname /dev/mtd
+
+subsystem sound
+ devname uevent_devpath
+ dirname /dev/snd
# ueventd can only set permissions on device nodes and their associated
# sysfs attributes, not on arbitrary paths.
@@ -22,9 +54,6 @@
/dev/hwbinder 0666 root root
/dev/vndbinder 0666 root root
-# Anyone can read the logs, but if they're not in the "logs"
-# group, then they'll only see log entries for their UID.
-/dev/log/* 0666 root log
/dev/pmsg0 0222 root log
# the msm hw3d client device node is world writable/readable.
@@ -40,7 +69,7 @@
/dev/diag 0660 radio radio
/dev/diag_arm9 0660 radio radio
/dev/ttyMSM0 0600 bluetooth bluetooth
-/dev/uhid 0660 system bluetooth
+/dev/uhid 0660 uhid uhid
/dev/uinput 0660 system bluetooth
/dev/alarm 0664 system radio
/dev/rtc0 0640 system system
diff --git a/shell_and_utilities/Android.bp b/shell_and_utilities/Android.bp
index 81cf315..4f4fc5d 100644
--- a/shell_and_utilities/Android.bp
+++ b/shell_and_utilities/Android.bp
@@ -5,9 +5,12 @@
"grep",
"gzip",
"mkshrc",
+ "mkshrc_vendor",
"reboot",
"sh",
+ "sh_vendor",
"toolbox",
"toybox",
+ "toybox_vendor",
],
}
diff --git a/shell_and_utilities/README.md b/shell_and_utilities/README.md
new file mode 100644
index 0000000..5d10c18
--- /dev/null
+++ b/shell_and_utilities/README.md
@@ -0,0 +1,157 @@
+Android's shell and utilities
+=============================
+
+Since IceCreamSandwich Android has used
+[mksh](https://www.mirbsd.org/mksh.htm) as its shell. Before then it used
+[ash](https://en.wikipedia.org/wiki/Almquist_shell) (which actually
+remained in the tree up to and including KitKat).
+
+Initially Android had a very limited command-line provided by its
+own "toolbox" binary. These days almost everything is supplied by
+[toybox](http://landley.net/toybox/) instead.
+
+We started moving a few of the more important tools to full
+BSD implementations in JellyBean before we started in earnest in
+Lollipop. Lollipop was a major break with the past in many ways (LP64
+support and the switch to ART both having lots of knock-on effects around
+the system), so although this was the beginning of the end of toolbox it
+(a) didn't stand out given all the other systems-level changes and (b)
+in Marshmallow we changed direction and started the move to toybox.
+
+The lists below show what tools were provided and where they came from in
+each release starting with Gingerbread. This doesn't tell the full story,
+because the toolbox implementations did have bugs fixed and options added
+over the years. Gingerbread's rm, for example, supported `-r`/`-R` but not
+`-f`. But this gives you an idea of what was available in any given release,
+and how usable it was likely to be.
+
+
+Android 2.3 (Gingerbread)
+-------------------------
+
+BSD: cat dd newfs\_msdos
+
+toolbox: chmod chown cmp date df dmesg getevent getprop hd id ifconfig
+iftop insmod ioctl ionice kill ln log ls lsmod lsof mkdir mount mv
+nandread netstat notify printenv ps reboot renice rm rmdir rmmod route
+schedtop sendevent setconsole setprop sleep smd start stop sync top
+umount uptime vmstat watchprops wipe
+
+
+Android 4.0 (IceCreamSandwich)
+------------------------------
+
+BSD: cat dd newfs\_msdos
+
+toolbox: chmod chown cmp date df dmesg getevent getprop hd id ifconfig
+iftop insmod ioctl ionice kill ln log ls lsmod lsof mkdir mount mv
+nandread netstat notify printenv ps reboot renice rm rmdir rmmod route
+schedtop sendevent setconsole setprop sleep smd start stop sync top
+touch umount uptime vmstat watchprops wipe
+
+
+Android 4.1-4.3 (JellyBean)
+---------------------------
+
+BSD: cat cp dd du grep newfs\_msdos
+
+toolbox: chcon chmod chown clear cmp date df dmesg getenforce getevent
+getprop getsebool hd id ifconfig iftop insmod ioctl ionice kill ln
+load\_policy log ls lsmod lsof md5 mkdir mount mv nandread netstat notify
+printenv ps reboot renice restorecon rm rmdir rmmod route runcon schedtop
+sendevent setconsole setenforce setprop setsebool sleep smd start stop
+sync top touch umount uptime vmstat watchprops wipe
+
+
+Android 4.4 (KitKat)
+--------------------
+
+BSD: cat cp dd du grep newfs\_msdos
+
+toolbox: chcon chmod chown clear cmp date df dmesg getenforce getevent
+getprop getsebool hd id ifconfig iftop insmod ioctl ionice kill ln
+load\_policy log ls lsmod lsof md5 mkdir mkswap mount mv nandread netstat
+notify printenv ps readlink renice restorecon rm rmdir rmmod route runcon
+schedtop sendevent setconsole setenforce setprop setsebool sleep smd start
+stop swapoff swapon sync top touch umount uptime vmstat watchprops wipe
+
+
+Android 5.0 (Lollipop)
+----------------------
+
+BSD: cat chown cp dd du grep kill ln mv printenv rm rmdir sleep sync
+
+toolbox: chcon chmod clear cmp date df dmesg getenforce getevent getprop
+getsebool hd id ifconfig iftop insmod ioctl ionice load\_policy log ls
+lsmod lsof md5 mkdir mknod mkswap mount nandread netstat newfs\_msdos
+nohup notify ps readlink renice restorecon rmmod route runcon schedtop
+sendevent setenforce setprop setsebool smd start stop swapoff swapon
+top touch umount uptime vmstat watchprops wipe
+
+
+Android 6.0 (Marshmallow)
+-------------------------
+
+BSD: dd du grep
+
+toolbox: df getevent iftop ioctl ionice log ls lsof mount nandread
+newfs\_msdos ps prlimit renice sendevent start stop top uptime watchprops
+
+toybox: acpi basename blockdev bzcat cal cat chcon chgrp chmod chown
+chroot cksum clear comm cmp cp cpio cut date dirname dmesg dos2unix echo
+env expand expr fallocate false find free getenforce getprop groups
+head hostname hwclock id ifconfig inotifyd insmod kill load\_policy ln
+logname losetup lsmod lsusb md5sum mkdir mknod mkswap mktemp modinfo
+more mountpoint mv netstat nice nl nohup od paste patch pgrep pidof
+pkill pmap printenv printf pwd readlink realpath restorecon rm rmdir
+rmmod route runcon sed seq setenforce setprop setsid sha1sum sleep sort
+split stat strings swapoff swapon sync sysctl tac tail tar taskset tee
+time timeout touch tr true truncate umount uname uniq unix2dos usleep
+vmstat wc which whoami xargs yes
+
+
+Android 7.0 (Nougat)
+--------------------
+
+BSD: dd grep
+
+toolbox: getevent iftop ioctl log nandread newfs\_msdos ps prlimit
+sendevent start stop top
+
+toybox: acpi base64 basename blockdev bzcat cal cat chcon chgrp chmod
+chown chroot cksum clear comm cmp cp cpio cut date df dirname dmesg
+dos2unix du echo env expand expr fallocate false find flock free
+getenforce getprop groups head hostname hwclock id ifconfig inotifyd
+insmod ionice iorenice kill killall load\_policy ln logname losetup ls
+lsmod lsof lsusb md5sum mkdir mknod mkswap mktemp modinfo more mount
+mountpoint mv netstat nice nl nohup od paste patch pgrep pidof pkill
+pmap printenv printf pwd readlink realpath renice restorecon rm rmdir
+rmmod route runcon sed seq setenforce setprop setsid sha1sum sleep sort
+split stat strings swapoff swapon sync sysctl tac tail tar taskset tee
+time timeout touch tr true truncate tty ulimit umount uname uniq unix2dos
+uptime usleep vmstat wc which whoami xargs xxd yes
+
+
+Current AOSP
+------------
+
+BSD: dd grep
+
+bzip2: bzcat bzip2 bunzip2
+
+toolbox: getevent gzip newfs\_msdos gunzip zcat
+
+toybox: acpi base64 basename blockdev cal cat chcon chgrp chmod chown
+chroot chrt cksum clear cmp comm cp cpio cut date df diff dirname dmesg
+dos2unix du echo env expand expr fallocate false file find flock free
+getenforce getprop groups head hostname hwclock id ifconfig inotifyd
+insmod ionice iorenice kill killall ln load\_policy log logname losetup
+ls lsmod lsof lsusb md5sum microcom mkdir mknod mkswap mktemp modinfo
+modprobe more mount mountpoint mv netstat nice nl nohup od paste patch
+pgrep pidof pkill pmap printenv printf ps pwd readlink realpath renice
+restorecon rm rmdir rmmod runcon sed sendevent seq setenforce setprop
+setsid sha1sum sha224sum sha256sum sha384sum sha512sum sleep sort split
+start stat stop strings swapoff swapon sync sysctl tac tail tar taskset
+tee time timeout top touch tr true truncate tty ulimit umount uname uniq
+unix2dos uptime usleep uudecode uuencode vmstat wc which whoami xargs
+xxd yes
diff --git a/toolbox/Android.bp b/toolbox/Android.bp
new file mode 100644
index 0000000..1c9fb20
--- /dev/null
+++ b/toolbox/Android.bp
@@ -0,0 +1,44 @@
+common_cflags = [
+ "-Werror",
+ "-Wno-unused-parameter",
+ "-Wno-unused-const-variable",
+ "-include bsd-compatibility.h"
+]
+
+cc_library_static {
+ srcs: [
+ "upstream-netbsd/bin/dd/args.c",
+ "upstream-netbsd/bin/dd/conv.c",
+ "upstream-netbsd/bin/dd/dd.c",
+ "upstream-netbsd/bin/dd/dd_hostops.c",
+ "upstream-netbsd/bin/dd/misc.c",
+ "upstream-netbsd/bin/dd/position.c",
+ "upstream-netbsd/lib/libc/gen/getbsize.c",
+ "upstream-netbsd/lib/libc/gen/humanize_number.c",
+ "upstream-netbsd/lib/libc/stdlib/strsuftoll.c",
+ "upstream-netbsd/lib/libc/string/swab.c",
+ "upstream-netbsd/lib/libutil/raise_default_signal.c",
+ ],
+ cflags: common_cflags + [
+ "-Dmain=dd_main",
+ "-DNO_CONV",
+ ],
+ local_include_dirs: ["upstream-netbsd/include/"],
+ name: "libtoolbox_dd",
+}
+
+// We build BSD grep separately, so it can provide egrep and fgrep too.
+cc_binary {
+ name: "grep",
+ srcs: [
+ "upstream-netbsd/usr.bin/grep/fastgrep.c",
+ "upstream-netbsd/usr.bin/grep/file.c",
+ "upstream-netbsd/usr.bin/grep/grep.c",
+ "upstream-netbsd/usr.bin/grep/queue.c",
+ "upstream-netbsd/usr.bin/grep/util.c",
+ ],
+ cflags: common_cflags,
+ local_include_dirs: ["upstream-netbsd/include/"],
+ symlinks: ["egrep", "fgrep"],
+
+}
diff --git a/toolbox/Android.mk b/toolbox/Android.mk
index d6ead1a..94029d8 100644
--- a/toolbox/Android.mk
+++ b/toolbox/Android.mk
@@ -1,30 +1,9 @@
LOCAL_PATH:= $(call my-dir)
-
common_cflags := \
-Werror -Wno-unused-parameter -Wno-unused-const-variable \
-include bsd-compatibility.h \
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := \
- upstream-netbsd/bin/dd/args.c \
- upstream-netbsd/bin/dd/conv.c \
- upstream-netbsd/bin/dd/dd.c \
- upstream-netbsd/bin/dd/dd_hostops.c \
- upstream-netbsd/bin/dd/misc.c \
- upstream-netbsd/bin/dd/position.c \
- upstream-netbsd/lib/libc/gen/getbsize.c \
- upstream-netbsd/lib/libc/gen/humanize_number.c \
- upstream-netbsd/lib/libc/stdlib/strsuftoll.c \
- upstream-netbsd/lib/libc/string/swab.c \
- upstream-netbsd/lib/libutil/raise_default_signal.c
-LOCAL_CFLAGS += $(common_cflags) -Dmain=dd_main -DNO_CONV
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/upstream-netbsd/include/
-LOCAL_MODULE := libtoolbox_dd
-include $(BUILD_STATIC_LIBRARY)
-
-
include $(CLEAR_VARS)
BSD_TOOLS := \
@@ -80,18 +59,3 @@
$(INPUT_H_LABELS_H): $(LOCAL_PATH)/Android.mk $(LOCAL_PATH)/generate-input.h-labels.py $(UAPI_INPUT_EVENT_CODES_H)
$(INPUT_H_LABELS_H):
$(transform-generated-source)
-
-
-# We build BSD grep separately, so it can provide egrep and fgrep too.
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := \
- upstream-netbsd/usr.bin/grep/fastgrep.c \
- upstream-netbsd/usr.bin/grep/file.c \
- upstream-netbsd/usr.bin/grep/grep.c \
- upstream-netbsd/usr.bin/grep/queue.c \
- upstream-netbsd/usr.bin/grep/util.c
-LOCAL_CFLAGS += $(common_cflags)
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/upstream-netbsd/include/
-LOCAL_MODULE := grep
-LOCAL_POST_INSTALL_CMD := $(hide) $(foreach t,egrep fgrep,ln -sf grep $(TARGET_OUT)/bin/$(t);)
-include $(BUILD_EXECUTABLE)
diff --git a/trusty/Android.bp b/trusty/Android.bp
new file mode 100644
index 0000000..386fbe6
--- /dev/null
+++ b/trusty/Android.bp
@@ -0,0 +1,7 @@
+subdirs = [
+ "gatekeeper",
+ "keymaster",
+ "libtrusty",
+ "nvram",
+ "storage/*",
+]
diff --git a/trusty/gatekeeper/Android.bp b/trusty/gatekeeper/Android.bp
new file mode 100644
index 0000000..a9566a1
--- /dev/null
+++ b/trusty/gatekeeper/Android.bp
@@ -0,0 +1,46 @@
+//
+// Copyright (C) 2015 The Android Open-Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// WARNING: Everything listed here will be built on ALL platforms,
+// including x86, the emulator, and the SDK. Modules must be uniquely
+// named (liblights.panda), and must build everywhere, or limit themselves
+// to only building on ARM if they include assembly. Individual makefiles
+// are responsible for having their own logic, for fine-grained control.
+
+cc_library_shared {
+ name: "gatekeeper.trusty",
+
+ relative_install_path: "hw",
+
+ srcs: [
+ "module.cpp",
+ "trusty_gatekeeper_ipc.c",
+ "trusty_gatekeeper.cpp",
+ ],
+
+ cflags: [
+ "-fvisibility=hidden",
+ "-Wall",
+ "-Werror",
+ ],
+
+ shared_libs: [
+ "libgatekeeper",
+ "liblog",
+ "libcutils",
+ "libtrusty",
+ ],
+}
diff --git a/trusty/gatekeeper/Android.mk b/trusty/gatekeeper/Android.mk
deleted file mode 100644
index 3982c8f..0000000
--- a/trusty/gatekeeper/Android.mk
+++ /dev/null
@@ -1,46 +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.
-#
-
-# WARNING: Everything listed here will be built on ALL platforms,
-# including x86, the emulator, and the SDK. Modules must be uniquely
-# named (liblights.panda), and must build everywhere, or limit themselves
-# to only building on ARM if they include assembly. Individual makefiles
-# are responsible for having their own logic, for fine-grained control.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := gatekeeper.trusty
-
-LOCAL_MODULE_RELATIVE_PATH := hw
-
-LOCAL_SRC_FILES := \
- module.cpp \
- trusty_gatekeeper_ipc.c \
- trusty_gatekeeper.cpp
-
-LOCAL_CLFAGS = -fvisibility=hidden -Wall -Werror
-
-LOCAL_SHARED_LIBRARIES := \
- libgatekeeper \
- liblog \
- libcutils \
- libtrusty
-
-LOCAL_MODULE_TAGS := optional
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/trusty/keymaster/Android.bp b/trusty/keymaster/Android.bp
new file mode 100644
index 0000000..6b9d723
--- /dev/null
+++ b/trusty/keymaster/Android.bp
@@ -0,0 +1,68 @@
+//
+// 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.
+//
+
+// WARNING: Everything listed here will be built on ALL platforms,
+// including x86, the emulator, and the SDK. Modules must be uniquely
+// named (liblights.panda), and must build everywhere, or limit themselves
+// to only building on ARM if they include assembly. Individual makefiles
+// are responsible for having their own logic, for fine-grained control.
+
+// trusty_keymaster is a binary used only for on-device testing. It
+// runs Trusty Keymaster through a basic set of operations with RSA
+// and ECDSA keys.
+cc_binary {
+ name: "trusty_keymaster_tipc",
+ srcs: [
+ "trusty_keymaster_device.cpp",
+ "trusty_keymaster_ipc.cpp",
+ "trusty_keymaster_main.cpp",
+ ],
+ shared_libs: [
+ "libcrypto",
+ "libcutils",
+ "libkeymaster_portable",
+ "libkeymaster_staging",
+ "libtrusty",
+ "libkeymaster_messages",
+ "libsoftkeymasterdevice",
+ "liblog",
+ ],
+}
+
+// keystore.trusty is the HAL used by keystore on Trusty devices.
+cc_library_shared {
+ name: "keystore.trusty",
+ relative_install_path: "hw",
+ srcs: [
+ "module.cpp",
+ "trusty_keymaster_ipc.cpp",
+ "trusty_keymaster_device.cpp",
+ ],
+
+ cflags: [
+ "-fvisibility=hidden",
+ "-Wall",
+ "-Werror",
+ ],
+
+ shared_libs: [
+ "libcrypto",
+ "libkeymaster_messages",
+ "libtrusty",
+ "liblog",
+ "libcutils",
+ ],
+}
diff --git a/trusty/keymaster/Android.mk b/trusty/keymaster/Android.mk
deleted file mode 100644
index 0ebf52d..0000000
--- a/trusty/keymaster/Android.mk
+++ /dev/null
@@ -1,67 +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.
-#
-
-# WARNING: Everything listed here will be built on ALL platforms,
-# including x86, the emulator, and the SDK. Modules must be uniquely
-# named (liblights.panda), and must build everywhere, or limit themselves
-# to only building on ARM if they include assembly. Individual makefiles
-# are responsible for having their own logic, for fine-grained control.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-###
-# trusty_keymaster is a binary used only for on-device testing. It
-# runs Trusty Keymaster through a basic set of operations with RSA
-# and ECDSA keys.
-###
-LOCAL_MODULE := trusty_keymaster_tipc
-LOCAL_SRC_FILES := \
- trusty_keymaster_device.cpp \
- trusty_keymaster_ipc.c \
- trusty_keymaster_main.cpp
-LOCAL_SHARED_LIBRARIES := \
- libcrypto \
- libcutils \
- libkeymaster1 \
- libtrusty \
- libkeymaster_messages \
- liblog
-
-include $(BUILD_EXECUTABLE)
-
-###
-# keystore.trusty is the HAL used by keystore on Trusty devices.
-##
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := keystore.trusty
-LOCAL_MODULE_RELATIVE_PATH := hw
-LOCAL_SRC_FILES := module.cpp \
- trusty_keymaster_ipc.c \
- trusty_keymaster_device.cpp
-LOCAL_CLFAGS = -fvisibility=hidden -Wall -Werror
-LOCAL_SHARED_LIBRARIES := \
- libcrypto \
- libkeymaster_messages \
- libtrusty \
- liblog \
- libcutils
-LOCAL_MODULE_TAGS := optional
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/trusty/keymaster/keymaster_ipc.h b/trusty/keymaster/keymaster_ipc.h
index 48fa53d..b38eb05 100644
--- a/trusty/keymaster/keymaster_ipc.h
+++ b/trusty/keymaster/keymaster_ipc.h
@@ -16,13 +16,15 @@
#pragma once
+// clang-format off
+
#define KEYMASTER_PORT "com.android.trusty.keymaster"
#define KEYMASTER_MAX_BUFFER_LENGTH 4096
// Commands
-enum keymaster_command {
- KEYMASTER_RESP_BIT = 1,
- KEYMASTER_REQ_SHIFT = 1,
+enum keymaster_command : uint32_t {
+ KEYMASTER_RESP_BIT = 1,
+ KEYMASTER_REQ_SHIFT = 1,
KM_GENERATE_KEY = (0 << KEYMASTER_REQ_SHIFT),
KM_BEGIN_OPERATION = (1 << KEYMASTER_REQ_SHIFT),
@@ -40,6 +42,9 @@
KM_GET_SUPPORTED_IMPORT_FORMATS = (13 << KEYMASTER_REQ_SHIFT),
KM_GET_SUPPORTED_EXPORT_FORMATS = (14 << KEYMASTER_REQ_SHIFT),
KM_GET_KEY_CHARACTERISTICS = (15 << KEYMASTER_REQ_SHIFT),
+ KM_ATTEST_KEY = (16 << KEYMASTER_REQ_SHIFT),
+ KM_UPGRADE_KEY = (17 << KEYMASTER_REQ_SHIFT),
+ KM_CONFIGURE = (18 << KEYMASTER_REQ_SHIFT),
};
#ifdef __ANDROID__
@@ -50,8 +55,8 @@
* @payload: start of the serialized command specific payload
*/
struct keymaster_message {
- uint32_t cmd;
- uint8_t payload[0];
+ uint32_t cmd;
+ uint8_t payload[0];
};
#endif
diff --git a/trusty/keymaster/module.cpp b/trusty/keymaster/module.cpp
index 81597d9..b472680 100644
--- a/trusty/keymaster/module.cpp
+++ b/trusty/keymaster/module.cpp
@@ -26,14 +26,15 @@
/*
* Generic device handling
*/
-static int trusty_keymaster_open(const hw_module_t* module, const char* name,
- hw_device_t** device) {
- if (strcmp(name, KEYSTORE_KEYMASTER) != 0)
+static int trusty_keymaster_open(const hw_module_t* module, const char* name, hw_device_t** device) {
+ if (strcmp(name, KEYSTORE_KEYMASTER) != 0) {
return -EINVAL;
+ }
TrustyKeymasterDevice* dev = new TrustyKeymasterDevice(module);
- if (dev == NULL)
+ if (dev == NULL) {
return -ENOMEM;
+ }
*device = dev->hw_device();
// Do not delete dev; it will get cleaned up when the caller calls device->close(), and must
// exist until then.
@@ -47,14 +48,14 @@
struct keystore_module HAL_MODULE_INFO_SYM __attribute__((visibility("default"))) = {
.common =
{
- .tag = HARDWARE_MODULE_TAG,
- .module_api_version = KEYMASTER_MODULE_API_VERSION_0_3,
- .hal_api_version = HARDWARE_HAL_API_VERSION,
- .id = KEYSTORE_HARDWARE_MODULE_ID,
- .name = "Trusty Keymaster HAL",
- .author = "The Android Open Source Project",
- .methods = &keystore_module_methods,
- .dso = 0,
- .reserved = {},
+ .tag = HARDWARE_MODULE_TAG,
+ .module_api_version = KEYMASTER_MODULE_API_VERSION_2_0,
+ .hal_api_version = HARDWARE_HAL_API_VERSION,
+ .id = KEYSTORE_HARDWARE_MODULE_ID,
+ .name = "Trusty Keymaster HAL",
+ .author = "The Android Open Source Project",
+ .methods = &keystore_module_methods,
+ .dso = 0,
+ .reserved = {},
},
};
diff --git a/trusty/keymaster/trusty_keymaster_device.cpp b/trusty/keymaster/trusty_keymaster_device.cpp
index 1368f88..5f16fd0 100644
--- a/trusty/keymaster/trusty_keymaster_device.cpp
+++ b/trusty/keymaster/trusty_keymaster_device.cpp
@@ -25,49 +25,53 @@
#include <string.h>
#include <time.h>
+#include <algorithm>
#include <type_traits>
-#include <hardware/keymaster0.h>
+#include <hardware/keymaster2.h>
#include <keymaster/authorization_set.h>
#include <log/log.h>
+#include "keymaster_ipc.h"
#include "trusty_keymaster_device.h"
#include "trusty_keymaster_ipc.h"
-#include "keymaster_ipc.h"
-const uint32_t SEND_BUF_SIZE = 8192;
-const uint32_t RECV_BUF_SIZE = 8192;
+const uint32_t RECV_BUF_SIZE = PAGE_SIZE;
+const uint32_t SEND_BUF_SIZE = (PAGE_SIZE - sizeof(struct keymaster_message) - 16 /* tipc header */);
+
+const size_t kMaximumAttestationChallengeLength = 128;
+const size_t kMaximumFinishInputLength = 2048;
namespace keymaster {
static keymaster_error_t translate_error(int err) {
switch (err) {
- case 0:
- return KM_ERROR_OK;
- case -EPERM:
- case -EACCES:
- return KM_ERROR_SECURE_HW_ACCESS_DENIED;
+ case 0:
+ return KM_ERROR_OK;
+ case -EPERM:
+ case -EACCES:
+ return KM_ERROR_SECURE_HW_ACCESS_DENIED;
- case -ECANCELED:
- return KM_ERROR_OPERATION_CANCELLED;
+ case -ECANCELED:
+ return KM_ERROR_OPERATION_CANCELLED;
- case -ENODEV:
- return KM_ERROR_UNIMPLEMENTED;
+ case -ENODEV:
+ return KM_ERROR_UNIMPLEMENTED;
- case -ENOMEM:
- return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ case -ENOMEM:
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
- case -EBUSY:
- return KM_ERROR_SECURE_HW_BUSY;
+ case -EBUSY:
+ return KM_ERROR_SECURE_HW_BUSY;
- case -EIO:
- return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED;
+ case -EIO:
+ return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED;
- case -EOVERFLOW:
- return KM_ERROR_INVALID_INPUT_LENGTH;
+ case -EOVERFLOW:
+ return KM_ERROR_INVALID_INPUT_LENGTH;
- default:
- return KM_ERROR_UNKNOWN_ERROR;
+ default:
+ return KM_ERROR_UNKNOWN_ERROR;
}
}
@@ -75,31 +79,36 @@
static_assert(std::is_standard_layout<TrustyKeymasterDevice>::value,
"TrustyKeymasterDevice must be standard layout");
static_assert(offsetof(TrustyKeymasterDevice, device_) == 0,
- "device_ must be the first member of KeymasterOpenSsl");
+ "device_ must be the first member of TrustyKeymasterDevice");
static_assert(offsetof(TrustyKeymasterDevice, device_.common) == 0,
- "common must be the first member of keymaster_device");
+ "common must be the first member of keymaster2_device");
ALOGI("Creating device");
ALOGD("Device address: %p", this);
- memset(&device_, 0, sizeof(device_));
+ device_ = {};
device_.common.tag = HARDWARE_DEVICE_TAG;
device_.common.version = 1;
device_.common.module = const_cast<hw_module_t*>(module);
device_.common.close = close_device;
- device_.flags = KEYMASTER_BLOBS_ARE_STANDALONE | KEYMASTER_SUPPORTS_EC;
+ device_.flags = KEYMASTER_SUPPORTS_EC;
- device_.generate_keypair = generate_keypair;
- device_.import_keypair = import_keypair;
- device_.get_keypair_public = get_keypair_public;
- device_.delete_keypair = NULL;
- device_.delete_all = NULL;
- device_.sign_data = sign_data;
- device_.verify_data = verify_data;
-
- device_.context = NULL;
+ device_.configure = configure;
+ device_.add_rng_entropy = add_rng_entropy;
+ device_.generate_key = generate_key;
+ device_.get_key_characteristics = get_key_characteristics;
+ device_.import_key = import_key;
+ device_.export_key = export_key;
+ device_.attest_key = attest_key;
+ device_.upgrade_key = upgrade_key;
+ device_.delete_key = nullptr;
+ device_.delete_all_keys = nullptr;
+ device_.begin = begin;
+ device_.update = update;
+ device_.finish = finish;
+ device_.abort = abort;
int rc = trusty_keymaster_connect();
error_ = translate_error(rc);
@@ -110,11 +119,11 @@
GetVersionRequest version_request;
GetVersionResponse version_response;
- error_ = Send(version_request, &version_response);
+ error_ = Send(KM_GET_VERSION, version_request, &version_response);
if (error_ == KM_ERROR_INVALID_ARGUMENT || error_ == KM_ERROR_UNIMPLEMENTED) {
- ALOGI("\"Bad parameters\" error on GetVersion call. Assuming version 0.");
- message_version_ = 0;
- error_ = KM_ERROR_OK;
+ ALOGE("\"Bad parameters\" error on GetVersion call. Version 0 is not supported.");
+ error_ = KM_ERROR_VERSION_MISMATCH;
+ return;
}
message_version_ = MessageVersion(version_response.major_ver, version_response.minor_ver,
version_response.subminor_ver);
@@ -130,261 +139,510 @@
trusty_keymaster_disconnect();
}
-const uint64_t HUNDRED_YEARS = 1000LL * 60 * 60 * 24 * 365 * 100;
+namespace {
-int TrustyKeymasterDevice::generate_keypair(const keymaster_keypair_t key_type,
- const void* key_params, uint8_t** key_blob,
- size_t* key_blob_length) {
- ALOGD("Device received generate_keypair");
+// Allocates a new buffer with malloc and copies the contents of |buffer| to it. Caller takes
+// ownership of the returned buffer.
+uint8_t* DuplicateBuffer(const uint8_t* buffer, size_t size) {
+ uint8_t* tmp = reinterpret_cast<uint8_t*>(malloc(size));
+ if (tmp) {
+ memcpy(tmp, buffer, size);
+ }
+ return tmp;
+}
- if (error_ != KM_ERROR_OK)
+template <typename RequestType>
+void AddClientAndAppData(const keymaster_blob_t* client_id, const keymaster_blob_t* app_data,
+ RequestType* request) {
+ request->additional_params.Clear();
+ if (client_id) {
+ request->additional_params.push_back(TAG_APPLICATION_ID, *client_id);
+ }
+ if (app_data) {
+ request->additional_params.push_back(TAG_APPLICATION_DATA, *app_data);
+ }
+}
+
+} // unnamed namespace
+
+keymaster_error_t TrustyKeymasterDevice::configure(const keymaster_key_param_set_t* params) {
+ ALOGD("Device received configure\n");
+
+ if (error_ != KM_ERROR_OK) {
return error_;
-
- GenerateKeyRequest req(message_version_);
- StoreNewKeyParams(&req.key_description);
-
- switch (key_type) {
- case TYPE_RSA: {
- req.key_description.push_back(TAG_ALGORITHM, KM_ALGORITHM_RSA);
- const keymaster_rsa_keygen_params_t* rsa_params =
- static_cast<const keymaster_rsa_keygen_params_t*>(key_params);
- ALOGD("Generating RSA pair, modulus size: %u, public exponent: %lu",
- rsa_params->modulus_size, rsa_params->public_exponent);
- req.key_description.push_back(TAG_KEY_SIZE, rsa_params->modulus_size);
- req.key_description.push_back(TAG_RSA_PUBLIC_EXPONENT, rsa_params->public_exponent);
- break;
+ }
+ if (!params) {
+ return KM_ERROR_UNEXPECTED_NULL_POINTER;
}
- case TYPE_EC: {
- req.key_description.push_back(TAG_ALGORITHM, KM_ALGORITHM_EC);
- const keymaster_ec_keygen_params_t* ec_params =
- static_cast<const keymaster_ec_keygen_params_t*>(key_params);
- ALOGD("Generating ECDSA pair, key size: %u", ec_params->field_size);
- req.key_description.push_back(TAG_KEY_SIZE, ec_params->field_size);
- break;
- }
- default:
- ALOGD("Received request for unsuported key type %d", key_type);
- return KM_ERROR_UNSUPPORTED_ALGORITHM;
+ AuthorizationSet params_copy(*params);
+ ConfigureRequest request;
+ 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;
}
- GenerateKeyResponse rsp(message_version_);
- ALOGD("Sending generate request");
- keymaster_error_t err = Send(req, &rsp);
+ ConfigureResponse response;
+ keymaster_error_t err = Send(KM_CONFIGURE, request, &response);
if (err != KM_ERROR_OK) {
- ALOGE("Got error %d from send", err);
return err;
}
- *key_blob_length = rsp.key_blob.key_material_size;
- *key_blob = static_cast<uint8_t*>(malloc(*key_blob_length));
- memcpy(*key_blob, rsp.key_blob.key_material, *key_blob_length);
- ALOGD("Returning %d bytes in key blob\n", (int)*key_blob_length);
-
return KM_ERROR_OK;
}
-struct EVP_PKEY_Delete {
- void operator()(EVP_PKEY* p) const { EVP_PKEY_free(p); }
-};
+keymaster_error_t TrustyKeymasterDevice::add_rng_entropy(const uint8_t* data, size_t data_length) {
+ ALOGD("Device received add_rng_entropy");
-struct PKCS8_PRIV_KEY_INFO_Delete {
- void operator()(PKCS8_PRIV_KEY_INFO* p) const { PKCS8_PRIV_KEY_INFO_free(p); }
-};
-
-int TrustyKeymasterDevice::import_keypair(const uint8_t* key, const size_t key_length,
- uint8_t** key_blob, size_t* key_blob_length) {
- ALOGD("Device received import_keypair");
- if (error_ != KM_ERROR_OK)
+ if (error_ != KM_ERROR_OK) {
return error_;
+ }
- if (!key)
+ AddEntropyRequest request;
+ request.random_data.Reinitialize(data, data_length);
+ AddEntropyResponse response;
+ return Send(KM_ADD_RNG_ENTROPY, request, &response);
+}
+
+keymaster_error_t TrustyKeymasterDevice::generate_key(
+ const keymaster_key_param_set_t* params, keymaster_key_blob_t* key_blob,
+ keymaster_key_characteristics_t* characteristics) {
+ ALOGD("Device received generate_key");
+
+ if (error_ != KM_ERROR_OK) {
+ return error_;
+ }
+ if (!params) {
return KM_ERROR_UNEXPECTED_NULL_POINTER;
-
- if (!key_blob || !key_blob_length)
+ }
+ if (!key_blob) {
return KM_ERROR_OUTPUT_PARAMETER_NULL;
+ }
+
+ GenerateKeyRequest request(message_version_);
+ request.key_description.Reinitialize(*params);
+ request.key_description.push_back(TAG_CREATION_DATETIME, java_time(time(NULL)));
+
+ GenerateKeyResponse response(message_version_);
+ keymaster_error_t err = Send(KM_GENERATE_KEY, request, &response);
+ if (err != KM_ERROR_OK) {
+ return err;
+ }
+
+ key_blob->key_material_size = response.key_blob.key_material_size;
+ key_blob->key_material =
+ DuplicateBuffer(response.key_blob.key_material, response.key_blob.key_material_size);
+ if (!key_blob->key_material) {
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ }
+
+ if (characteristics) {
+ response.enforced.CopyToParamSet(&characteristics->hw_enforced);
+ response.unenforced.CopyToParamSet(&characteristics->sw_enforced);
+ }
+
+ return KM_ERROR_OK;
+}
+
+keymaster_error_t TrustyKeymasterDevice::get_key_characteristics(
+ const keymaster_key_blob_t* key_blob, const keymaster_blob_t* client_id,
+ const keymaster_blob_t* app_data, keymaster_key_characteristics_t* characteristics) {
+ ALOGD("Device received get_key_characteristics");
+
+ if (error_ != KM_ERROR_OK) {
+ return error_;
+ }
+ if (!key_blob || !key_blob->key_material) {
+ return KM_ERROR_UNEXPECTED_NULL_POINTER;
+ }
+ if (!characteristics) {
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
+ }
+
+ GetKeyCharacteristicsRequest request;
+ request.SetKeyMaterial(*key_blob);
+ AddClientAndAppData(client_id, app_data, &request);
+
+ GetKeyCharacteristicsResponse response;
+ keymaster_error_t err = Send(KM_GET_KEY_CHARACTERISTICS, request, &response);
+ if (err != KM_ERROR_OK) {
+ return err;
+ }
+
+ response.enforced.CopyToParamSet(&characteristics->hw_enforced);
+ response.unenforced.CopyToParamSet(&characteristics->sw_enforced);
+
+ return KM_ERROR_OK;
+}
+
+keymaster_error_t TrustyKeymasterDevice::import_key(
+ const keymaster_key_param_set_t* params, keymaster_key_format_t key_format,
+ const keymaster_blob_t* key_data, keymaster_key_blob_t* key_blob,
+ keymaster_key_characteristics_t* characteristics) {
+ ALOGD("Device received import_key");
+
+ if (error_ != KM_ERROR_OK) {
+ return error_;
+ }
+ if (!params || !key_data) {
+ return KM_ERROR_UNEXPECTED_NULL_POINTER;
+ }
+ if (!key_blob) {
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
+ }
ImportKeyRequest request(message_version_);
- StoreNewKeyParams(&request.key_description);
- keymaster_algorithm_t algorithm;
- keymaster_error_t err = GetPkcs8KeyAlgorithm(key, key_length, &algorithm);
- if (err != KM_ERROR_OK)
- return err;
- request.key_description.push_back(TAG_ALGORITHM, algorithm);
+ request.key_description.Reinitialize(*params);
+ request.key_description.push_back(TAG_CREATION_DATETIME, java_time(time(NULL)));
- request.SetKeyMaterial(key, key_length);
- request.key_format = KM_KEY_FORMAT_PKCS8;
+ request.key_format = key_format;
+ request.SetKeyMaterial(key_data->data, key_data->data_length);
+
ImportKeyResponse response(message_version_);
- err = Send(request, &response);
- if (err != KM_ERROR_OK)
+ keymaster_error_t err = Send(KM_IMPORT_KEY, request, &response);
+ if (err != KM_ERROR_OK) {
return err;
+ }
- *key_blob_length = response.key_blob.key_material_size;
- *key_blob = static_cast<uint8_t*>(malloc(*key_blob_length));
- memcpy(*key_blob, response.key_blob.key_material, *key_blob_length);
- printf("Returning %d bytes in key blob\n", (int)*key_blob_length);
+ key_blob->key_material_size = response.key_blob.key_material_size;
+ key_blob->key_material =
+ DuplicateBuffer(response.key_blob.key_material, response.key_blob.key_material_size);
+ if (!key_blob->key_material) {
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ }
+
+ if (characteristics) {
+ response.enforced.CopyToParamSet(&characteristics->hw_enforced);
+ response.unenforced.CopyToParamSet(&characteristics->sw_enforced);
+ }
return KM_ERROR_OK;
}
-keymaster_error_t TrustyKeymasterDevice::GetPkcs8KeyAlgorithm(const uint8_t* key, size_t key_length,
- keymaster_algorithm_t* algorithm) {
- if (key == NULL) {
- ALOGE("No key specified for import");
+keymaster_error_t TrustyKeymasterDevice::export_key(keymaster_key_format_t export_format,
+ const keymaster_key_blob_t* key_to_export,
+ const keymaster_blob_t* client_id,
+ const keymaster_blob_t* app_data,
+ keymaster_blob_t* export_data) {
+ ALOGD("Device received export_key");
+
+ if (error_ != KM_ERROR_OK) {
+ return error_;
+ }
+ if (!key_to_export || !key_to_export->key_material) {
return KM_ERROR_UNEXPECTED_NULL_POINTER;
}
-
- UniquePtr<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_Delete> pkcs8(
- d2i_PKCS8_PRIV_KEY_INFO(NULL, &key, key_length));
- if (pkcs8.get() == NULL) {
- ALOGE("Could not parse PKCS8 key blob");
- return KM_ERROR_INVALID_KEY_BLOB;
+ if (!export_data) {
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
}
- UniquePtr<EVP_PKEY, EVP_PKEY_Delete> pkey(EVP_PKCS82PKEY(pkcs8.get()));
- if (pkey.get() == NULL) {
- ALOGE("Could not extract key from PKCS8 key blob");
- return KM_ERROR_INVALID_KEY_BLOB;
- }
-
- switch (EVP_PKEY_type(pkey->type)) {
- case EVP_PKEY_RSA:
- *algorithm = KM_ALGORITHM_RSA;
- break;
- case EVP_PKEY_EC:
- *algorithm = KM_ALGORITHM_EC;
- break;
- default:
- ALOGE("Unsupported algorithm %d", EVP_PKEY_type(pkey->type));
- return KM_ERROR_UNSUPPORTED_ALGORITHM;
- }
-
- return KM_ERROR_OK;
-}
-
-int TrustyKeymasterDevice::get_keypair_public(const uint8_t* key_blob, const size_t key_blob_length,
- uint8_t** x509_data, size_t* x509_data_length) {
- ALOGD("Device received get_keypair_public");
- if (error_ != KM_ERROR_OK)
- return error_;
+ export_data->data = nullptr;
+ export_data->data_length = 0;
ExportKeyRequest request(message_version_);
- request.SetKeyMaterial(key_blob, key_blob_length);
- request.key_format = KM_KEY_FORMAT_X509;
+ request.key_format = export_format;
+ request.SetKeyMaterial(*key_to_export);
+ AddClientAndAppData(client_id, app_data, &request);
+
ExportKeyResponse response(message_version_);
- keymaster_error_t err = Send(request, &response);
- if (err != KM_ERROR_OK)
+ keymaster_error_t err = Send(KM_EXPORT_KEY, request, &response);
+ if (err != KM_ERROR_OK) {
return err;
+ }
- *x509_data_length = response.key_data_length;
- *x509_data = static_cast<uint8_t*>(malloc(*x509_data_length));
- memcpy(*x509_data, response.key_data, *x509_data_length);
- printf("Returning %d bytes in x509 key\n", (int)*x509_data_length);
+ export_data->data_length = response.key_data_length;
+ export_data->data = DuplicateBuffer(response.key_data, response.key_data_length);
+ if (!export_data->data) {
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ }
return KM_ERROR_OK;
}
-int TrustyKeymasterDevice::sign_data(const void* signing_params, const uint8_t* key_blob,
- const size_t key_blob_length, const uint8_t* data,
- const size_t data_length, uint8_t** signed_data,
- size_t* signed_data_length) {
- ALOGD("Device received sign_data, %d", error_);
- if (error_ != KM_ERROR_OK)
+keymaster_error_t TrustyKeymasterDevice::attest_key(const keymaster_key_blob_t* key_to_attest,
+ const keymaster_key_param_set_t* attest_params,
+ keymaster_cert_chain_t* cert_chain) {
+ ALOGD("Device received attest_key");
+
+ if (error_ != KM_ERROR_OK) {
return error_;
+ }
+ if (!key_to_attest || !attest_params) {
+ return KM_ERROR_UNEXPECTED_NULL_POINTER;
+ }
+ if (!cert_chain) {
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
+ }
- BeginOperationRequest begin_request(message_version_);
- begin_request.purpose = KM_PURPOSE_SIGN;
- begin_request.SetKeyMaterial(key_blob, key_blob_length);
- keymaster_error_t err = StoreSigningParams(signing_params, key_blob, key_blob_length,
- &begin_request.additional_params);
+ cert_chain->entry_count = 0;
+ cert_chain->entries = nullptr;
+
+ AttestKeyRequest request;
+ request.SetKeyMaterial(*key_to_attest);
+ request.attest_params.Reinitialize(*attest_params);
+
+ keymaster_blob_t attestation_challenge = {};
+ request.attest_params.GetTagValue(TAG_ATTESTATION_CHALLENGE, &attestation_challenge);
+ if (attestation_challenge.data_length > kMaximumAttestationChallengeLength) {
+ ALOGE("%zu-byte attestation challenge; only %zu bytes allowed",
+ attestation_challenge.data_length, kMaximumAttestationChallengeLength);
+ return KM_ERROR_INVALID_INPUT_LENGTH;
+ }
+
+ AttestKeyResponse response;
+ keymaster_error_t err = Send(KM_ATTEST_KEY, request, &response);
if (err != KM_ERROR_OK) {
- ALOGE("Error extracting signing params: %d", err);
return err;
}
- BeginOperationResponse begin_response(message_version_);
- ALOGD("Sending signing request begin");
- err = Send(begin_request, &begin_response);
- if (err != KM_ERROR_OK) {
- ALOGE("Error sending sign begin: %d", err);
- return err;
+ // Allocate and clear storage for cert_chain.
+ keymaster_cert_chain_t& rsp_chain = response.certificate_chain;
+ cert_chain->entries = reinterpret_cast<keymaster_blob_t*>(
+ malloc(rsp_chain.entry_count * sizeof(*cert_chain->entries)));
+ if (!cert_chain->entries) {
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ }
+ cert_chain->entry_count = rsp_chain.entry_count;
+ for (keymaster_blob_t& entry : array_range(cert_chain->entries, cert_chain->entry_count)) {
+ entry = {};
}
- UpdateOperationRequest update_request(message_version_);
- update_request.op_handle = begin_response.op_handle;
- update_request.input.Reinitialize(data, data_length);
- UpdateOperationResponse update_response(message_version_);
- ALOGD("Sending signing request update");
- err = Send(update_request, &update_response);
- if (err != KM_ERROR_OK) {
- ALOGE("Error sending sign update: %d", err);
- return err;
+ // Copy cert_chain contents
+ size_t i = 0;
+ for (keymaster_blob_t& entry : array_range(rsp_chain.entries, rsp_chain.entry_count)) {
+ cert_chain->entries[i].data = DuplicateBuffer(entry.data, entry.data_length);
+ if (!cert_chain->entries[i].data) {
+ keymaster_free_cert_chain(cert_chain);
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ }
+ cert_chain->entries[i].data_length = entry.data_length;
+ ++i;
}
- FinishOperationRequest finish_request(message_version_);
- finish_request.op_handle = begin_response.op_handle;
- FinishOperationResponse finish_response(message_version_);
- ALOGD("Sending signing request finish");
- err = Send(finish_request, &finish_response);
- if (err != KM_ERROR_OK) {
- ALOGE("Error sending sign finish: %d", err);
- return err;
- }
-
- *signed_data_length = finish_response.output.available_read();
- *signed_data = static_cast<uint8_t*>(malloc(*signed_data_length));
- if (!finish_response.output.read(*signed_data, *signed_data_length)) {
- ALOGE("Error reading response data: %d", err);
- return KM_ERROR_UNKNOWN_ERROR;
- }
return KM_ERROR_OK;
}
-int TrustyKeymasterDevice::verify_data(const void* signing_params, const uint8_t* key_blob,
- const size_t key_blob_length, const uint8_t* signed_data,
- const size_t signed_data_length, const uint8_t* signature,
- const size_t signature_length) {
- ALOGD("Device received verify_data");
- if (error_ != KM_ERROR_OK)
+keymaster_error_t TrustyKeymasterDevice::upgrade_key(const keymaster_key_blob_t* key_to_upgrade,
+ const keymaster_key_param_set_t* upgrade_params,
+ keymaster_key_blob_t* upgraded_key) {
+ ALOGD("Device received upgrade_key");
+
+ if (error_ != KM_ERROR_OK) {
return error_;
+ }
+ if (!key_to_upgrade || !upgrade_params) {
+ return KM_ERROR_UNEXPECTED_NULL_POINTER;
+ }
+ if (!upgraded_key) {
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
+ }
- BeginOperationRequest begin_request(message_version_);
- begin_request.purpose = KM_PURPOSE_VERIFY;
- begin_request.SetKeyMaterial(key_blob, key_blob_length);
- keymaster_error_t err = StoreSigningParams(signing_params, key_blob, key_blob_length,
- &begin_request.additional_params);
- if (err != KM_ERROR_OK)
- return err;
+ UpgradeKeyRequest request;
+ request.SetKeyMaterial(*key_to_upgrade);
+ request.upgrade_params.Reinitialize(*upgrade_params);
- BeginOperationResponse begin_response(message_version_);
- err = Send(begin_request, &begin_response);
- if (err != KM_ERROR_OK)
+ UpgradeKeyResponse response;
+ keymaster_error_t err = Send(KM_UPGRADE_KEY, request, &response);
+ if (err != KM_ERROR_OK) {
return err;
+ }
- UpdateOperationRequest update_request(message_version_);
- update_request.op_handle = begin_response.op_handle;
- update_request.input.Reinitialize(signed_data, signed_data_length);
- UpdateOperationResponse update_response(message_version_);
- err = Send(update_request, &update_response);
- if (err != KM_ERROR_OK)
- return err;
+ upgraded_key->key_material_size = response.upgraded_key.key_material_size;
+ upgraded_key->key_material = DuplicateBuffer(response.upgraded_key.key_material,
+ response.upgraded_key.key_material_size);
+ if (!upgraded_key->key_material) {
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ }
- FinishOperationRequest finish_request(message_version_);
- finish_request.op_handle = begin_response.op_handle;
- finish_request.signature.Reinitialize(signature, signature_length);
- FinishOperationResponse finish_response(message_version_);
- err = Send(finish_request, &finish_response);
- if (err != KM_ERROR_OK)
- return err;
return KM_ERROR_OK;
}
+keymaster_error_t TrustyKeymasterDevice::begin(keymaster_purpose_t purpose,
+ const keymaster_key_blob_t* key,
+ const keymaster_key_param_set_t* in_params,
+ keymaster_key_param_set_t* out_params,
+ keymaster_operation_handle_t* operation_handle) {
+ ALOGD("Device received begin");
+
+ if (error_ != KM_ERROR_OK) {
+ return error_;
+ }
+ if (!key || !key->key_material) {
+ return KM_ERROR_UNEXPECTED_NULL_POINTER;
+ }
+ if (!operation_handle) {
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
+ }
+
+ if (out_params) {
+ *out_params = {};
+ }
+
+ BeginOperationRequest request;
+ request.purpose = purpose;
+ request.SetKeyMaterial(*key);
+ request.additional_params.Reinitialize(*in_params);
+
+ BeginOperationResponse response;
+ keymaster_error_t err = Send(KM_BEGIN_OPERATION, request, &response);
+ if (err != KM_ERROR_OK) {
+ return err;
+ }
+
+ if (response.output_params.size() > 0) {
+ if (out_params) {
+ response.output_params.CopyToParamSet(out_params);
+ } else {
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
+ }
+ }
+ *operation_handle = response.op_handle;
+
+ return KM_ERROR_OK;
+}
+
+keymaster_error_t TrustyKeymasterDevice::update(keymaster_operation_handle_t operation_handle,
+ const keymaster_key_param_set_t* in_params,
+ const keymaster_blob_t* input,
+ size_t* input_consumed,
+ keymaster_key_param_set_t* out_params,
+ keymaster_blob_t* output) {
+ ALOGD("Device received update");
+
+ if (error_ != KM_ERROR_OK) {
+ return error_;
+ }
+ if (!input) {
+ return KM_ERROR_UNEXPECTED_NULL_POINTER;
+ }
+ if (!input_consumed) {
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
+ }
+
+ if (out_params) {
+ *out_params = {};
+ }
+ if (output) {
+ *output = {};
+ }
+
+ UpdateOperationRequest request;
+ request.op_handle = operation_handle;
+ if (in_params) {
+ request.additional_params.Reinitialize(*in_params);
+ }
+ if (input && input->data_length > 0) {
+ size_t max_input_size = SEND_BUF_SIZE - request.SerializedSize();
+ request.input.Reinitialize(input->data, std::min(input->data_length, max_input_size));
+ }
+
+ UpdateOperationResponse response;
+ keymaster_error_t err = Send(KM_UPDATE_OPERATION, request, &response);
+ if (err != KM_ERROR_OK) {
+ return err;
+ }
+
+ if (response.output_params.size() > 0) {
+ if (out_params) {
+ response.output_params.CopyToParamSet(out_params);
+ } else {
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
+ }
+ }
+ *input_consumed = response.input_consumed;
+ if (output) {
+ output->data_length = response.output.available_read();
+ output->data = DuplicateBuffer(response.output.peek_read(), output->data_length);
+ if (!output->data) {
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ }
+ } else if (response.output.available_read() > 0) {
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
+ }
+
+ return KM_ERROR_OK;
+}
+
+keymaster_error_t TrustyKeymasterDevice::finish(keymaster_operation_handle_t operation_handle,
+ const keymaster_key_param_set_t* in_params,
+ const keymaster_blob_t* input,
+ const keymaster_blob_t* signature,
+ keymaster_key_param_set_t* out_params,
+ keymaster_blob_t* output) {
+ ALOGD("Device received finish");
+
+ if (error_ != KM_ERROR_OK) {
+ return error_;
+ }
+ if (input && input->data_length > kMaximumFinishInputLength) {
+ return KM_ERROR_INVALID_ARGUMENT;
+ }
+
+ if (out_params) {
+ *out_params = {};
+ }
+ if (output) {
+ *output = {};
+ }
+
+ FinishOperationRequest request;
+ request.op_handle = operation_handle;
+ if (signature && signature->data && signature->data_length > 0) {
+ request.signature.Reinitialize(signature->data, signature->data_length);
+ }
+ if (input && input->data && input->data_length) {
+ request.input.Reinitialize(input->data, input->data_length);
+ }
+ if (in_params) {
+ request.additional_params.Reinitialize(*in_params);
+ }
+
+ FinishOperationResponse response;
+ keymaster_error_t err = Send(KM_FINISH_OPERATION, request, &response);
+ if (err != KM_ERROR_OK) {
+ return err;
+ }
+
+ if (response.output_params.size() > 0) {
+ if (out_params) {
+ response.output_params.CopyToParamSet(out_params);
+ } else {
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
+ }
+ }
+ if (output) {
+ output->data_length = response.output.available_read();
+ output->data = DuplicateBuffer(response.output.peek_read(), output->data_length);
+ if (!output->data) {
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ }
+ } else if (response.output.available_read() > 0) {
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
+ }
+
+ return KM_ERROR_OK;
+}
+
+keymaster_error_t TrustyKeymasterDevice::abort(keymaster_operation_handle_t operation_handle) {
+ ALOGD("Device received abort");
+
+ if (error_ != KM_ERROR_OK) {
+ return error_;
+ }
+
+ AbortOperationRequest request;
+ request.op_handle = operation_handle;
+ AbortOperationResponse response;
+ return Send(KM_ABORT_OPERATION, request, &response);
+}
+
hw_device_t* TrustyKeymasterDevice::hw_device() {
return &device_.common;
}
-static inline TrustyKeymasterDevice* convert_device(const keymaster0_device_t* dev) {
- return reinterpret_cast<TrustyKeymasterDevice*>(const_cast<keymaster0_device_t*>(dev));
+static inline TrustyKeymasterDevice* convert_device(const keymaster2_device_t* dev) {
+ return reinterpret_cast<TrustyKeymasterDevice*>(const_cast<keymaster2_device_t*>(dev));
}
/* static */
@@ -394,52 +652,111 @@
}
/* static */
-int TrustyKeymasterDevice::generate_keypair(const keymaster0_device_t* dev,
- const keymaster_keypair_t key_type,
- const void* key_params, uint8_t** keyBlob,
- size_t* keyBlobLength) {
- ALOGD("Generate keypair, sending to device: %p", convert_device(dev));
- return convert_device(dev)->generate_keypair(key_type, key_params, keyBlob, keyBlobLength);
+keymaster_error_t TrustyKeymasterDevice::configure(const keymaster2_device_t* dev,
+ const keymaster_key_param_set_t* params) {
+ return convert_device(dev)->configure(params);
}
/* static */
-int TrustyKeymasterDevice::import_keypair(const keymaster0_device_t* dev, const uint8_t* key,
- const size_t key_length, uint8_t** key_blob,
- size_t* key_blob_length) {
- return convert_device(dev)->import_keypair(key, key_length, key_blob, key_blob_length);
+keymaster_error_t TrustyKeymasterDevice::add_rng_entropy(const keymaster2_device_t* dev,
+ const uint8_t* data, size_t data_length) {
+ return convert_device(dev)->add_rng_entropy(data, data_length);
}
/* static */
-int TrustyKeymasterDevice::get_keypair_public(const keymaster0_device_t* dev,
- const uint8_t* key_blob, const size_t key_blob_length,
- uint8_t** x509_data, size_t* x509_data_length) {
- return convert_device(dev)
- ->get_keypair_public(key_blob, key_blob_length, x509_data, x509_data_length);
+keymaster_error_t TrustyKeymasterDevice::generate_key(
+ const keymaster2_device_t* dev, const keymaster_key_param_set_t* params,
+ keymaster_key_blob_t* key_blob, keymaster_key_characteristics_t* characteristics) {
+ return convert_device(dev)->generate_key(params, key_blob, characteristics);
}
/* static */
-int TrustyKeymasterDevice::sign_data(const keymaster0_device_t* dev, const void* params,
- const uint8_t* keyBlob, const size_t keyBlobLength,
- const uint8_t* data, const size_t dataLength,
- uint8_t** signedData, size_t* signedDataLength) {
- return convert_device(dev)
- ->sign_data(params, keyBlob, keyBlobLength, data, dataLength, signedData, signedDataLength);
+keymaster_error_t TrustyKeymasterDevice::get_key_characteristics(
+ const keymaster2_device_t* dev, const keymaster_key_blob_t* key_blob,
+ const keymaster_blob_t* client_id, const keymaster_blob_t* app_data,
+ keymaster_key_characteristics_t* characteristics) {
+ return convert_device(dev)->get_key_characteristics(key_blob, client_id, app_data,
+ characteristics);
}
/* static */
-int TrustyKeymasterDevice::verify_data(const keymaster0_device_t* dev, const void* params,
- const uint8_t* keyBlob, const size_t keyBlobLength,
- const uint8_t* signedData, const size_t signedDataLength,
- const uint8_t* signature, const size_t signatureLength) {
- return convert_device(dev)->verify_data(params, keyBlob, keyBlobLength, signedData,
- signedDataLength, signature, signatureLength);
+keymaster_error_t TrustyKeymasterDevice::import_key(
+ const keymaster2_device_t* dev, const keymaster_key_param_set_t* params,
+ keymaster_key_format_t key_format, const keymaster_blob_t* key_data,
+ keymaster_key_blob_t* key_blob, keymaster_key_characteristics_t* characteristics) {
+ return convert_device(dev)->import_key(params, key_format, key_data, key_blob, characteristics);
+}
+
+/* static */
+keymaster_error_t TrustyKeymasterDevice::export_key(const keymaster2_device_t* dev,
+ keymaster_key_format_t export_format,
+ const keymaster_key_blob_t* key_to_export,
+ const keymaster_blob_t* client_id,
+ const keymaster_blob_t* app_data,
+ keymaster_blob_t* export_data) {
+ return convert_device(dev)->export_key(export_format, key_to_export, client_id, app_data,
+ export_data);
+}
+
+/* static */
+keymaster_error_t TrustyKeymasterDevice::attest_key(const keymaster2_device_t* dev,
+ const keymaster_key_blob_t* key_to_attest,
+ const keymaster_key_param_set_t* attest_params,
+ keymaster_cert_chain_t* cert_chain) {
+ return convert_device(dev)->attest_key(key_to_attest, attest_params, cert_chain);
+}
+
+/* static */
+keymaster_error_t TrustyKeymasterDevice::upgrade_key(const keymaster2_device_t* dev,
+ const keymaster_key_blob_t* key_to_upgrade,
+ const keymaster_key_param_set_t* upgrade_params,
+ keymaster_key_blob_t* upgraded_key) {
+ return convert_device(dev)->upgrade_key(key_to_upgrade, upgrade_params, upgraded_key);
+}
+
+/* static */
+keymaster_error_t TrustyKeymasterDevice::begin(const keymaster2_device_t* dev,
+ keymaster_purpose_t purpose,
+ const keymaster_key_blob_t* key,
+ const keymaster_key_param_set_t* in_params,
+ keymaster_key_param_set_t* out_params,
+ keymaster_operation_handle_t* operation_handle) {
+ return convert_device(dev)->begin(purpose, key, in_params, out_params, operation_handle);
+}
+
+/* static */
+keymaster_error_t TrustyKeymasterDevice::update(
+ const keymaster2_device_t* dev, keymaster_operation_handle_t operation_handle,
+ const keymaster_key_param_set_t* in_params, const keymaster_blob_t* input,
+ size_t* input_consumed, keymaster_key_param_set_t* out_params, keymaster_blob_t* output) {
+ return convert_device(dev)->update(operation_handle, in_params, input, input_consumed,
+ out_params, output);
+}
+
+/* static */
+keymaster_error_t TrustyKeymasterDevice::finish(const keymaster2_device_t* dev,
+ keymaster_operation_handle_t operation_handle,
+ const keymaster_key_param_set_t* in_params,
+ const keymaster_blob_t* input,
+ const keymaster_blob_t* signature,
+ keymaster_key_param_set_t* out_params,
+ keymaster_blob_t* output) {
+ return convert_device(dev)->finish(operation_handle, in_params, input, signature, out_params,
+ output);
+}
+
+/* static */
+keymaster_error_t TrustyKeymasterDevice::abort(const keymaster2_device_t* dev,
+ keymaster_operation_handle_t operation_handle) {
+ return convert_device(dev)->abort(operation_handle);
}
keymaster_error_t TrustyKeymasterDevice::Send(uint32_t command, const Serializable& req,
KeymasterResponse* rsp) {
uint32_t req_size = req.SerializedSize();
- if (req_size > SEND_BUF_SIZE)
+ if (req_size > SEND_BUF_SIZE) {
return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ }
uint8_t send_buf[SEND_BUF_SIZE];
Eraser send_buf_eraser(send_buf, SEND_BUF_SIZE);
req.Serialize(send_buf, send_buf + req_size);
@@ -448,7 +765,7 @@
uint8_t recv_buf[RECV_BUF_SIZE];
Eraser recv_buf_eraser(recv_buf, RECV_BUF_SIZE);
uint32_t rsp_size = RECV_BUF_SIZE;
- printf("Sending %d byte request\n", (int)req.SerializedSize());
+ 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) {
ALOGE("tipc error: %d\n", rc);
@@ -458,8 +775,8 @@
ALOGV("Received %d byte response\n", rsp_size);
}
- const keymaster_message* msg = (keymaster_message *) recv_buf;
- const uint8_t *p = msg->payload;
+ const keymaster_message* msg = (keymaster_message*)recv_buf;
+ const uint8_t* p = msg->payload;
if (!rsp->Deserialize(&p, p + rsp_size)) {
ALOGE("Error deserializing response of size %d\n", (int)rsp_size);
return KM_ERROR_UNKNOWN_ERROR;
@@ -470,65 +787,4 @@
return rsp->error;
}
-keymaster_error_t TrustyKeymasterDevice::StoreSigningParams(const void* signing_params,
- const uint8_t* key_blob,
- size_t key_blob_length,
- AuthorizationSet* auth_set) {
- uint8_t* pub_key_data;
- size_t pub_key_data_length;
- int err = get_keypair_public(&device_, key_blob, key_blob_length, &pub_key_data,
- &pub_key_data_length);
- if (err < 0) {
- ALOGE("Error %d extracting public key to determine algorithm", err);
- return KM_ERROR_INVALID_KEY_BLOB;
- }
- UniquePtr<uint8_t, Malloc_Delete> pub_key(pub_key_data);
-
- const uint8_t* p = pub_key_data;
- UniquePtr<EVP_PKEY, EVP_PKEY_Delete> pkey(
- d2i_PUBKEY(nullptr /* allocate new struct */, &p, pub_key_data_length));
-
- switch (EVP_PKEY_type(pkey->type)) {
- case EVP_PKEY_RSA: {
- const keymaster_rsa_sign_params_t* rsa_params =
- reinterpret_cast<const keymaster_rsa_sign_params_t*>(signing_params);
- if (rsa_params->digest_type != DIGEST_NONE)
- return KM_ERROR_UNSUPPORTED_DIGEST;
- if (rsa_params->padding_type != PADDING_NONE)
- return KM_ERROR_UNSUPPORTED_PADDING_MODE;
- if (!auth_set->push_back(TAG_DIGEST, KM_DIGEST_NONE) ||
- !auth_set->push_back(TAG_PADDING, KM_PAD_NONE))
- return KM_ERROR_MEMORY_ALLOCATION_FAILED;
- } break;
- case EVP_PKEY_EC: {
- const keymaster_ec_sign_params_t* ecdsa_params =
- reinterpret_cast<const keymaster_ec_sign_params_t*>(signing_params);
- if (ecdsa_params->digest_type != DIGEST_NONE)
- return KM_ERROR_UNSUPPORTED_DIGEST;
- if (!auth_set->push_back(TAG_DIGEST, KM_DIGEST_NONE))
- return KM_ERROR_MEMORY_ALLOCATION_FAILED;
- } break;
- default:
- return KM_ERROR_UNSUPPORTED_ALGORITHM;
- }
- return KM_ERROR_OK;
-}
-
-void TrustyKeymasterDevice::StoreNewKeyParams(AuthorizationSet* auth_set) {
- auth_set->push_back(TAG_PURPOSE, KM_PURPOSE_SIGN);
- auth_set->push_back(TAG_PURPOSE, KM_PURPOSE_VERIFY);
- auth_set->push_back(TAG_ALL_USERS);
- auth_set->push_back(TAG_NO_AUTH_REQUIRED);
- uint64_t now = java_time(time(NULL));
- auth_set->push_back(TAG_CREATION_DATETIME, now);
- auth_set->push_back(TAG_ORIGINATION_EXPIRE_DATETIME, now + HUNDRED_YEARS);
- if (message_version_ == 0) {
- auth_set->push_back(TAG_DIGEST_OLD, KM_DIGEST_NONE);
- auth_set->push_back(TAG_PADDING_OLD, KM_PAD_NONE);
- } else {
- auth_set->push_back(TAG_DIGEST, KM_DIGEST_NONE);
- auth_set->push_back(TAG_PADDING, KM_PAD_NONE);
- }
-}
-
} // namespace keymaster
diff --git a/trusty/keymaster/trusty_keymaster_device.h b/trusty/keymaster/trusty_keymaster_device.h
index 68cf40c..cfada1b 100644
--- a/trusty/keymaster/trusty_keymaster_device.h
+++ b/trusty/keymaster/trusty_keymaster_device.h
@@ -14,19 +14,16 @@
* limitations under the License.
*/
-#ifndef EXTERNAL_KEYMASTER_TRUSTY_KEYMASTER_DEVICE_H_
-#define EXTERNAL_KEYMASTER_TRUSTY_KEYMASTER_DEVICE_H_
+#ifndef TRUSTY_KEYMASTER_TRUSTY_KEYMASTER_DEVICE_H_
+#define TRUSTY_KEYMASTER_TRUSTY_KEYMASTER_DEVICE_H_
-#include <hardware/keymaster0.h>
-
+#include <hardware/keymaster2.h>
#include <keymaster/android_keymaster_messages.h>
-#include "keymaster_ipc.h"
-
namespace keymaster {
/**
- * Software OpenSSL-based Keymaster device.
+ * Trusty Keymaster device.
*
* IMPORTANT MAINTAINER NOTE: Pointers to instances of this class must be castable to hw_device_t
* and keymaster_device. This means it must remain a standard layout class (no virtual functions and
@@ -46,79 +43,111 @@
keymaster_error_t session_error() { return error_; }
- int generate_keypair(const keymaster_keypair_t key_type, const void* key_params,
- uint8_t** key_blob, size_t* key_blob_length);
- int import_keypair(const uint8_t* key, const size_t key_length, uint8_t** key_blob,
- size_t* key_blob_length);
- int get_keypair_public(const uint8_t* key_blob, const size_t key_blob_length,
- uint8_t** x509_data, size_t* x509_data_length);
- int sign_data(const void* signing_params, const uint8_t* key_blob, const size_t key_blob_length,
- const uint8_t* data, const size_t data_length, uint8_t** signed_data,
- size_t* signed_data_length);
- int verify_data(const void* signing_params, const uint8_t* key_blob,
- const size_t key_blob_length, const uint8_t* signed_data,
- const size_t signed_data_length, const uint8_t* signature,
- const size_t signature_length);
+ keymaster_error_t configure(const keymaster_key_param_set_t* params);
+ keymaster_error_t add_rng_entropy(const uint8_t* data, size_t data_length);
+ keymaster_error_t generate_key(const keymaster_key_param_set_t* params,
+ keymaster_key_blob_t* key_blob,
+ keymaster_key_characteristics_t* characteristics);
+ keymaster_error_t get_key_characteristics(const keymaster_key_blob_t* key_blob,
+ const keymaster_blob_t* client_id,
+ const keymaster_blob_t* app_data,
+ keymaster_key_characteristics_t* character);
+ keymaster_error_t import_key(const keymaster_key_param_set_t* params,
+ keymaster_key_format_t key_format,
+ const keymaster_blob_t* key_data, keymaster_key_blob_t* key_blob,
+ keymaster_key_characteristics_t* characteristics);
+ keymaster_error_t export_key(keymaster_key_format_t export_format,
+ const keymaster_key_blob_t* key_to_export,
+ const keymaster_blob_t* client_id,
+ const keymaster_blob_t* app_data, keymaster_blob_t* export_data);
+ keymaster_error_t attest_key(const keymaster_key_blob_t* key_to_attest,
+ const keymaster_key_param_set_t* attest_params,
+ keymaster_cert_chain_t* cert_chain);
+ keymaster_error_t upgrade_key(const keymaster_key_blob_t* key_to_upgrade,
+ const keymaster_key_param_set_t* upgrade_params,
+ keymaster_key_blob_t* upgraded_key);
+ keymaster_error_t begin(keymaster_purpose_t purpose, const keymaster_key_blob_t* key,
+ const keymaster_key_param_set_t* in_params,
+ keymaster_key_param_set_t* out_params,
+ keymaster_operation_handle_t* operation_handle);
+ keymaster_error_t update(keymaster_operation_handle_t operation_handle,
+ const keymaster_key_param_set_t* in_params,
+ const keymaster_blob_t* input, size_t* input_consumed,
+ keymaster_key_param_set_t* out_params, keymaster_blob_t* output);
+ keymaster_error_t finish(keymaster_operation_handle_t operation_handle,
+ const keymaster_key_param_set_t* in_params,
+ const keymaster_blob_t* input, const keymaster_blob_t* signature,
+ keymaster_key_param_set_t* out_params, keymaster_blob_t* output);
+ keymaster_error_t abort(keymaster_operation_handle_t operation_handle);
private:
keymaster_error_t Send(uint32_t command, const Serializable& request,
KeymasterResponse* response);
- keymaster_error_t Send(const GenerateKeyRequest& request, GenerateKeyResponse* response) {
- return Send(KM_GENERATE_KEY, request, response);
- }
- keymaster_error_t Send(const BeginOperationRequest& request, BeginOperationResponse* response) {
- return Send(KM_BEGIN_OPERATION, request, response);
- }
- keymaster_error_t Send(const UpdateOperationRequest& request,
- UpdateOperationResponse* response) {
- return Send(KM_UPDATE_OPERATION, request, response);
- }
- keymaster_error_t Send(const FinishOperationRequest& request,
- FinishOperationResponse* response) {
- return Send(KM_FINISH_OPERATION, request, response);
- }
- keymaster_error_t Send(const ImportKeyRequest& request, ImportKeyResponse* response) {
- return Send(KM_IMPORT_KEY, request, response);
- }
- keymaster_error_t Send(const ExportKeyRequest& request, ExportKeyResponse* response) {
- return Send(KM_EXPORT_KEY, request, response);
- }
- keymaster_error_t Send(const GetVersionRequest& request, GetVersionResponse* response) {
- return Send(KM_GET_VERSION, request, response);
- }
-
- keymaster_error_t StoreSigningParams(const void* signing_params, const uint8_t* key_blob,
- size_t key_blob_length, AuthorizationSet* auth_set);
- void StoreNewKeyParams(AuthorizationSet* auth_set);
- keymaster_error_t GetPkcs8KeyAlgorithm(const uint8_t* key, size_t key_length,
- keymaster_algorithm_t* algorithm);
/*
* These static methods are the functions referenced through the function pointers in
* keymaster_device. They're all trivial wrappers.
*/
static int close_device(hw_device_t* dev);
- static int generate_keypair(const keymaster0_device_t* dev, const keymaster_keypair_t key_type,
- const void* key_params, uint8_t** keyBlob, size_t* keyBlobLength);
- static int import_keypair(const keymaster0_device_t* dev, const uint8_t* key,
- const size_t key_length, uint8_t** key_blob, size_t* key_blob_length);
- static int get_keypair_public(const keymaster0_device_t* dev, const uint8_t* key_blob,
- const size_t key_blob_length, uint8_t** x509_data,
- size_t* x509_data_length);
- static int sign_data(const keymaster0_device_t* dev, const void* signing_params,
- const uint8_t* key_blob, const size_t key_blob_length, const uint8_t* data,
- const size_t data_length, uint8_t** signed_data,
- size_t* signed_data_length);
- static int verify_data(const keymaster0_device_t* dev, const void* signing_params,
- const uint8_t* key_blob, const size_t key_blob_length,
- const uint8_t* signed_data, const size_t signed_data_length,
- const uint8_t* signature, const size_t signature_length);
+ static keymaster_error_t configure(const keymaster2_device_t* dev,
+ const keymaster_key_param_set_t* params);
+ static keymaster_error_t add_rng_entropy(const keymaster2_device_t* dev, const uint8_t* data,
+ size_t data_length);
+ static keymaster_error_t generate_key(const keymaster2_device_t* dev,
+ const keymaster_key_param_set_t* params,
+ keymaster_key_blob_t* key_blob,
+ keymaster_key_characteristics_t* characteristics);
+ static keymaster_error_t get_key_characteristics(const keymaster2_device_t* dev,
+ const keymaster_key_blob_t* key_blob,
+ const keymaster_blob_t* client_id,
+ const keymaster_blob_t* app_data,
+ keymaster_key_characteristics_t* character);
+ static keymaster_error_t import_key(const keymaster2_device_t* dev,
+ const keymaster_key_param_set_t* params,
+ keymaster_key_format_t key_format,
+ const keymaster_blob_t* key_data,
+ keymaster_key_blob_t* key_blob,
+ keymaster_key_characteristics_t* characteristics);
+ static keymaster_error_t export_key(const keymaster2_device_t* dev,
+ keymaster_key_format_t export_format,
+ const keymaster_key_blob_t* key_to_export,
+ const keymaster_blob_t* client_id,
+ const keymaster_blob_t* app_data,
+ keymaster_blob_t* export_data);
+ static keymaster_error_t attest_key(const keymaster2_device_t* dev,
+ const keymaster_key_blob_t* key_to_attest,
+ const keymaster_key_param_set_t* attest_params,
+ keymaster_cert_chain_t* cert_chain);
+ static keymaster_error_t upgrade_key(const keymaster2_device_t* dev,
+ const keymaster_key_blob_t* key_to_upgrade,
+ const keymaster_key_param_set_t* upgrade_params,
+ keymaster_key_blob_t* upgraded_key);
+ static keymaster_error_t delete_key(const keymaster2_device_t* dev,
+ const keymaster_key_blob_t* key);
+ static keymaster_error_t delete_all_keys(const keymaster2_device_t* dev);
+ static keymaster_error_t begin(const keymaster2_device_t* dev, keymaster_purpose_t purpose,
+ const keymaster_key_blob_t* key,
+ const keymaster_key_param_set_t* in_params,
+ keymaster_key_param_set_t* out_params,
+ keymaster_operation_handle_t* operation_handle);
+ static keymaster_error_t update(const keymaster2_device_t* dev,
+ keymaster_operation_handle_t operation_handle,
+ const keymaster_key_param_set_t* in_params,
+ const keymaster_blob_t* input, size_t* input_consumed,
+ keymaster_key_param_set_t* out_params, keymaster_blob_t* output);
+ static keymaster_error_t finish(const keymaster2_device_t* dev,
+ keymaster_operation_handle_t operation_handle,
+ const keymaster_key_param_set_t* in_params,
+ const keymaster_blob_t* input, const keymaster_blob_t* signature,
+ keymaster_key_param_set_t* out_params, keymaster_blob_t* output);
+ static keymaster_error_t abort(const keymaster2_device_t* dev,
+ keymaster_operation_handle_t operation_handle);
- keymaster0_device_t device_;
+ keymaster2_device_t device_;
keymaster_error_t error_;
int32_t message_version_;
};
} // namespace keymaster
-#endif // EXTERNAL_KEYMASTER_TRUSTY_KEYMASTER_DEVICE_H_
+#endif // TRUSTY_KEYMASTER_TRUSTY_KEYMASTER_DEVICE_H_
diff --git a/trusty/keymaster/trusty_keymaster_ipc.c b/trusty/keymaster/trusty_keymaster_ipc.cpp
similarity index 75%
rename from trusty/keymaster/trusty_keymaster_ipc.c
rename to trusty/keymaster/trusty_keymaster_ipc.cpp
index 88546af..cdc2778 100644
--- a/trusty/keymaster/trusty_keymaster_ipc.c
+++ b/trusty/keymaster/trusty_keymaster_ipc.cpp
@@ -26,8 +26,8 @@
#include <log/log.h>
#include <trusty/tipc.h>
-#include "trusty_keymaster_ipc.h"
#include "keymaster_ipc.h"
+#include "trusty_keymaster_ipc.h"
#define TRUSTY_DEVICE_NAME "/dev/trusty-ipc-dev0"
@@ -43,15 +43,15 @@
return 0;
}
-int trusty_keymaster_call(uint32_t cmd, void *in, uint32_t in_size, uint8_t *out,
- uint32_t *out_size) {
+int trusty_keymaster_call(uint32_t cmd, void* in, uint32_t in_size, uint8_t* out,
+ uint32_t* out_size) {
if (handle_ == 0) {
ALOGE("not connected\n");
return -EINVAL;
}
size_t msg_size = in_size + sizeof(struct keymaster_message);
- struct keymaster_message *msg = malloc(msg_size);
+ struct keymaster_message* msg = reinterpret_cast<struct keymaster_message*>(malloc(msg_size));
msg->cmd = cmd;
memcpy(msg->payload, in, in_size);
@@ -59,31 +59,30 @@
free(msg);
if (rc < 0) {
- ALOGE("failed to send cmd (%d) to %s: %s\n", cmd,
- KEYMASTER_PORT, strerror(errno));
+ ALOGE("failed to send 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));
+ 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);
+ if ((size_t)rc < sizeof(struct keymaster_message)) {
+ ALOGE("invalid response size (%d)\n", (int)rc);
return -EINVAL;
}
- msg = (struct keymaster_message *) out;
+ 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);
+ *out_size = ((size_t)rc) - sizeof(struct keymaster_message);
return rc;
}
@@ -92,4 +91,3 @@
tipc_close(handle_);
}
}
-
diff --git a/trusty/keymaster/trusty_keymaster_ipc.h b/trusty/keymaster/trusty_keymaster_ipc.h
index 9785247..c15f7c1 100644
--- a/trusty/keymaster/trusty_keymaster_ipc.h
+++ b/trusty/keymaster/trusty_keymaster_ipc.h
@@ -14,11 +14,16 @@
* limitations under the License.
*/
+#ifndef TRUSTY_KEYMASTER_TRUSTY_KEYMASTER_IPC_H_
+#define TRUSTY_KEYMASTER_TRUSTY_KEYMASTER_IPC_H_
+
__BEGIN_DECLS
int trusty_keymaster_connect(void);
-int trusty_keymaster_call(uint32_t cmd, void *in, uint32_t in_size, uint8_t *out,
- uint32_t *out_size);
+int trusty_keymaster_call(uint32_t cmd, void* in, uint32_t in_size, uint8_t* out,
+ uint32_t* out_size);
void trusty_keymaster_disconnect(void);
__END_DECLS
+
+#endif // TRUSTY_KEYMASTER_TRUSTY_KEYMASTER_IPC_H_
diff --git a/trusty/keymaster/trusty_keymaster_main.cpp b/trusty/keymaster/trusty_keymaster_main.cpp
index 7ed880e..9c2ae2d 100644
--- a/trusty/keymaster/trusty_keymaster_main.cpp
+++ b/trusty/keymaster/trusty_keymaster_main.cpp
@@ -14,7 +14,10 @@
* limitations under the License.
*/
+#include <keymaster/keymaster_configuration.h>
+
#include <stdio.h>
+#include <memory>
#include <openssl/evp.h>
#include <openssl/x509.h>
@@ -102,6 +105,28 @@
0xd1, 0x1f, 0xd4, 0x49, 0x49, 0xe0, 0xb2, 0x18, 0x3b, 0xfe};
unsigned int ec_privkey_pk8_der_len = 138;
+keymaster_key_param_t ec_params[] = {
+ keymaster_param_enum(KM_TAG_ALGORITHM, KM_ALGORITHM_EC),
+ keymaster_param_long(KM_TAG_EC_CURVE, KM_EC_CURVE_P_521),
+ keymaster_param_enum(KM_TAG_PURPOSE, KM_PURPOSE_SIGN),
+ keymaster_param_enum(KM_TAG_PURPOSE, KM_PURPOSE_VERIFY),
+ keymaster_param_enum(KM_TAG_DIGEST, KM_DIGEST_NONE),
+ keymaster_param_bool(KM_TAG_NO_AUTH_REQUIRED),
+};
+keymaster_key_param_set_t ec_param_set = {ec_params, sizeof(ec_params) / sizeof(*ec_params)};
+
+keymaster_key_param_t rsa_params[] = {
+ keymaster_param_enum(KM_TAG_ALGORITHM, KM_ALGORITHM_RSA),
+ keymaster_param_int(KM_TAG_KEY_SIZE, 1024),
+ keymaster_param_long(KM_TAG_RSA_PUBLIC_EXPONENT, 65537),
+ keymaster_param_enum(KM_TAG_PURPOSE, KM_PURPOSE_SIGN),
+ keymaster_param_enum(KM_TAG_PURPOSE, KM_PURPOSE_VERIFY),
+ keymaster_param_enum(KM_TAG_PADDING, KM_PAD_NONE),
+ keymaster_param_enum(KM_TAG_DIGEST, KM_DIGEST_NONE),
+ keymaster_param_bool(KM_TAG_NO_AUTH_REQUIRED),
+};
+keymaster_key_param_set_t rsa_param_set = {rsa_params, sizeof(rsa_params) / sizeof(*rsa_params)};
+
struct EVP_PKEY_Delete {
void operator()(EVP_PKEY* p) const { EVP_PKEY_free(p); }
};
@@ -110,41 +135,70 @@
void operator()(EVP_PKEY_CTX* p) { EVP_PKEY_CTX_free(p); }
};
+static bool do_operation(TrustyKeymasterDevice* device, keymaster_purpose_t purpose,
+ keymaster_key_blob_t* key, keymaster_blob_t* input,
+ keymaster_blob_t* signature, keymaster_blob_t* output) {
+ keymaster_key_param_t params[] = {
+ keymaster_param_enum(KM_TAG_PADDING, KM_PAD_NONE),
+ keymaster_param_enum(KM_TAG_DIGEST, KM_DIGEST_NONE),
+ };
+ keymaster_key_param_set_t param_set = {params, sizeof(params) / sizeof(*params)};
+ keymaster_operation_handle_t op_handle;
+ keymaster_error_t error = device->begin(purpose, key, ¶m_set, nullptr, &op_handle);
+ if (error != KM_ERROR_OK) {
+ printf("Keymaster begin() failed: %d\n", error);
+ return false;
+ }
+ size_t input_consumed;
+ error = device->update(op_handle, nullptr, input, &input_consumed, nullptr, nullptr);
+ if (error != KM_ERROR_OK) {
+ printf("Keymaster update() failed: %d\n", error);
+ return false;
+ }
+ if (input_consumed != input->data_length) {
+ // This should never happen. If it does, it's a bug in the keymaster implementation.
+ printf("Keymaster update() did not consume all data.\n");
+ device->abort(op_handle);
+ return false;
+ }
+ error = device->finish(op_handle, nullptr, nullptr, signature, nullptr, output);
+ if (error != KM_ERROR_OK) {
+ printf("Keymaster finish() failed: %d\n", error);
+ return false;
+ }
+ return true;
+}
+
static bool test_import_rsa(TrustyKeymasterDevice* device) {
printf("===================\n");
printf("= RSA Import Test =\n");
printf("===================\n\n");
printf("=== Importing RSA keypair === \n");
- uint8_t* key;
- size_t size;
- int error = device->import_keypair(rsa_privkey_pk8_der, rsa_privkey_pk8_der_len, &key, &size);
+ keymaster_key_blob_t key;
+ keymaster_blob_t private_key = {rsa_privkey_pk8_der, rsa_privkey_pk8_der_len};
+ int error = device->import_key(&rsa_param_set, KM_KEY_FORMAT_PKCS8, &private_key, &key, nullptr);
if (error != KM_ERROR_OK) {
- printf("Error importing key pair: %d\n\n", error);
+ printf("Error importing RSA key: %d\n\n", error);
return false;
}
- UniquePtr<uint8_t[]> key_deleter(key);
+ std::unique_ptr<const uint8_t[]> key_deleter(key.key_material);
printf("=== Signing with imported RSA key ===\n");
- keymaster_rsa_sign_params_t sign_params = {DIGEST_NONE, PADDING_NONE};
size_t message_len = 1024 / 8;
- UniquePtr<uint8_t[]> message(new uint8_t[message_len]);
+ std::unique_ptr<uint8_t[]> message(new uint8_t[message_len]);
memset(message.get(), 'a', message_len);
- uint8_t* signature;
- size_t signature_len;
- error = device->sign_data(&sign_params, key, size, message.get(), message_len, &signature,
- &signature_len);
- if (error != KM_ERROR_OK) {
- printf("Error signing data with imported RSA key: %d\n\n", error);
+ keymaster_blob_t input = {message.get(), message_len}, signature;
+
+ if (!do_operation(device, KM_PURPOSE_SIGN, &key, &input, nullptr, &signature)) {
+ printf("Error signing data with imported RSA key\n\n");
return false;
}
- UniquePtr<uint8_t[]> signature_deleter(signature);
+ std::unique_ptr<const uint8_t[]> signature_deleter(signature.data);
printf("=== Verifying with imported RSA key === \n");
- error = device->verify_data(&sign_params, key, size, message.get(), message_len, signature,
- signature_len);
- if (error != KM_ERROR_OK) {
- printf("Error verifying data with imported RSA key: %d\n\n", error);
+ if (!do_operation(device, KM_PURPOSE_VERIFY, &key, &input, &signature, nullptr)) {
+ printf("Error verifying data with imported RSA key\n\n");
return false;
}
@@ -158,67 +212,58 @@
printf("============\n\n");
printf("=== Generating RSA key pair ===\n");
- keymaster_rsa_keygen_params_t params;
- params.public_exponent = 65537;
- params.modulus_size = 2048;
-
- uint8_t* key;
- size_t size;
- int error = device->generate_keypair(TYPE_RSA, ¶ms, &key, &size);
+ keymaster_key_blob_t key;
+ int error = device->generate_key(&rsa_param_set, &key, nullptr);
if (error != KM_ERROR_OK) {
printf("Error generating RSA key pair: %d\n\n", error);
return false;
}
- UniquePtr<uint8_t[]> deleter(key);
+ std::unique_ptr<const uint8_t[]> key_deleter(key.key_material);
printf("=== Signing with RSA key === \n");
- keymaster_rsa_sign_params_t sign_params = {DIGEST_NONE, PADDING_NONE};
- size_t message_len = params.modulus_size / 8;
- UniquePtr<uint8_t[]> message(new uint8_t[message_len]);
+ size_t message_len = 1024 / 8;
+ std::unique_ptr<uint8_t[]> message(new uint8_t[message_len]);
memset(message.get(), 'a', message_len);
- uint8_t* signature;
- size_t signature_len;
- error = device->sign_data(&sign_params, key, size, message.get(), message_len, &signature,
- &signature_len);
- if (error != KM_ERROR_OK) {
- printf("Error signing data with RSA key: %d\n\n", error);
+ keymaster_blob_t input = {message.get(), message_len}, signature;
+
+ if (!do_operation(device, KM_PURPOSE_SIGN, &key, &input, nullptr, &signature)) {
+ printf("Error signing data with RSA key\n\n");
return false;
}
- UniquePtr<uint8_t[]> signature_deleter(signature);
+ std::unique_ptr<const uint8_t[]> signature_deleter(signature.data);
printf("=== Verifying with RSA key === \n");
- error = device->verify_data(&sign_params, key, size, message.get(), message_len, signature,
- signature_len);
- if (error != KM_ERROR_OK) {
- printf("Error verifying data with RSA key: %d\n\n", error);
+ if (!do_operation(device, KM_PURPOSE_VERIFY, &key, &input, &signature, nullptr)) {
+ printf("Error verifying data with RSA key\n\n");
return false;
}
printf("=== Exporting RSA public key ===\n");
- uint8_t* exported_key;
- size_t exported_size;
- error = device->get_keypair_public(key, size, &exported_key, &exported_size);
+ keymaster_blob_t exported_key;
+ error = device->export_key(KM_KEY_FORMAT_X509, &key, nullptr, nullptr, &exported_key);
if (error != KM_ERROR_OK) {
printf("Error exporting RSA public key: %d\n\n", error);
return false;
}
printf("=== Verifying with exported key ===\n");
- const uint8_t* tmp = exported_key;
- UniquePtr<EVP_PKEY, EVP_PKEY_Delete> pkey(d2i_PUBKEY(NULL, &tmp, exported_size));
- UniquePtr<EVP_PKEY_CTX, EVP_PKEY_CTX_Delete> ctx(EVP_PKEY_CTX_new(pkey.get(), NULL));
+ const uint8_t* tmp = exported_key.data;
+ std::unique_ptr<EVP_PKEY, EVP_PKEY_Delete> pkey(
+ d2i_PUBKEY(NULL, &tmp, exported_key.data_length));
+ std::unique_ptr<EVP_PKEY_CTX, EVP_PKEY_CTX_Delete> ctx(EVP_PKEY_CTX_new(pkey.get(), NULL));
if (EVP_PKEY_verify_init(ctx.get()) != 1) {
- printf("Error initializing openss EVP context\n");
+ printf("Error initializing openss EVP context\n\n");
return false;
}
if (EVP_PKEY_type(pkey->type) != EVP_PKEY_RSA) {
- printf("Exported key was the wrong type?!?\n");
+ printf("Exported key was the wrong type?!?\n\n");
return false;
}
EVP_PKEY_CTX_set_rsa_padding(ctx.get(), RSA_NO_PADDING);
- if (EVP_PKEY_verify(ctx.get(), signature, signature_len, message.get(), message_len) != 1) {
- printf("Verification with exported pubkey failed.\n");
+ if (EVP_PKEY_verify(ctx.get(), signature.data, signature.data_length, message.get(),
+ message_len) != 1) {
+ printf("Verification with exported pubkey failed.\n\n");
return false;
} else {
printf("Verification succeeded\n");
@@ -234,35 +279,31 @@
printf("=====================\n\n");
printf("=== Importing ECDSA keypair === \n");
- uint8_t* key;
- size_t size;
- int error = device->import_keypair(ec_privkey_pk8_der, ec_privkey_pk8_der_len, &key, &size);
+ keymaster_key_blob_t key;
+ keymaster_blob_t private_key = {ec_privkey_pk8_der, ec_privkey_pk8_der_len};
+ int error = device->import_key(&ec_param_set, KM_KEY_FORMAT_PKCS8, &private_key, &key, nullptr);
if (error != KM_ERROR_OK) {
- printf("Error importing key pair: %d\n\n", error);
+ printf("Error importing ECDSA key: %d\n\n", error);
return false;
}
- UniquePtr<uint8_t[]> deleter(key);
+ std::unique_ptr<const uint8_t[]> deleter(key.key_material);
printf("=== Signing with imported ECDSA key ===\n");
keymaster_ec_sign_params_t sign_params = {DIGEST_NONE};
size_t message_len = 30 /* arbitrary */;
- UniquePtr<uint8_t[]> message(new uint8_t[message_len]);
+ std::unique_ptr<uint8_t[]> message(new uint8_t[message_len]);
memset(message.get(), 'a', message_len);
- uint8_t* signature;
- size_t signature_len;
- error = device->sign_data(&sign_params, key, size, message.get(), message_len, &signature,
- &signature_len);
- if (error != KM_ERROR_OK) {
- printf("Error signing data with imported ECDSA key: %d\n\n", error);
+ keymaster_blob_t input = {message.get(), message_len}, signature;
+
+ if (!do_operation(device, KM_PURPOSE_SIGN, &key, &input, nullptr, &signature)) {
+ printf("Error signing data with imported ECDSA key\n\n");
return false;
}
- UniquePtr<uint8_t[]> signature_deleter(signature);
+ std::unique_ptr<const uint8_t[]> signature_deleter(signature.data);
printf("=== Verifying with imported ECDSA key === \n");
- error = device->verify_data(&sign_params, key, size, message.get(), message_len, signature,
- signature_len);
- if (error != KM_ERROR_OK) {
- printf("Error verifying data with imported ECDSA key: %d\n\n", error);
+ if (!do_operation(device, KM_PURPOSE_VERIFY, &key, &input, &signature, nullptr)) {
+ printf("Error verifying data with imported ECDSA key\n\n");
return false;
}
@@ -276,64 +317,57 @@
printf("==============\n\n");
printf("=== Generating ECDSA key pair ===\n");
- keymaster_ec_keygen_params_t params;
- params.field_size = 521;
- uint8_t* key;
- size_t size;
- int error = device->generate_keypair(TYPE_EC, ¶ms, &key, &size);
- if (error != 0) {
+ keymaster_key_blob_t key;
+ int error = device->generate_key(&ec_param_set, &key, nullptr);
+ if (error != KM_ERROR_OK) {
printf("Error generating ECDSA key pair: %d\n\n", error);
return false;
}
- UniquePtr<uint8_t[]> deleter(key);
+ std::unique_ptr<const uint8_t[]> key_deleter(key.key_material);
printf("=== Signing with ECDSA key === \n");
- keymaster_ec_sign_params_t sign_params = {DIGEST_NONE};
size_t message_len = 30 /* arbitrary */;
- UniquePtr<uint8_t[]> message(new uint8_t[message_len]);
+ std::unique_ptr<uint8_t[]> message(new uint8_t[message_len]);
memset(message.get(), 'a', message_len);
- uint8_t* signature;
- size_t signature_len;
- error = device->sign_data(&sign_params, key, size, message.get(), message_len, &signature,
- &signature_len);
- if (error != KM_ERROR_OK) {
- printf("Error signing data with ECDSA key: %d\n\n", error);
+ keymaster_blob_t input = {message.get(), message_len}, signature;
+
+ if (!do_operation(device, KM_PURPOSE_SIGN, &key, &input, nullptr, &signature)) {
+ printf("Error signing data with ECDSA key\n\n");
return false;
}
- UniquePtr<uint8_t[]> signature_deleter(signature);
+ std::unique_ptr<const uint8_t[]> signature_deleter(signature.data);
printf("=== Verifying with ECDSA key === \n");
- error = device->verify_data(&sign_params, key, size, message.get(), message_len, signature,
- signature_len);
- if (error != KM_ERROR_OK) {
- printf("Error verifying data with ECDSA key: %d\n\n", error);
+ if (!do_operation(device, KM_PURPOSE_VERIFY, &key, &input, &signature, nullptr)) {
+ printf("Error verifying data with ECDSA key\n\n");
return false;
}
printf("=== Exporting ECDSA public key ===\n");
- uint8_t* exported_key;
- size_t exported_size;
- error = device->get_keypair_public(key, size, &exported_key, &exported_size);
+ keymaster_blob_t exported_key;
+ error = device->export_key(KM_KEY_FORMAT_X509, &key, nullptr, nullptr, &exported_key);
if (error != KM_ERROR_OK) {
printf("Error exporting ECDSA public key: %d\n\n", error);
return false;
}
printf("=== Verifying with exported key ===\n");
- const uint8_t* tmp = exported_key;
- UniquePtr<EVP_PKEY, EVP_PKEY_Delete> pkey(d2i_PUBKEY(NULL, &tmp, exported_size));
- UniquePtr<EVP_PKEY_CTX, EVP_PKEY_CTX_Delete> ctx(EVP_PKEY_CTX_new(pkey.get(), NULL));
+ const uint8_t* tmp = exported_key.data;
+ std::unique_ptr<EVP_PKEY, EVP_PKEY_Delete> pkey(
+ d2i_PUBKEY(NULL, &tmp, exported_key.data_length));
+ std::unique_ptr<EVP_PKEY_CTX, EVP_PKEY_CTX_Delete> ctx(EVP_PKEY_CTX_new(pkey.get(), NULL));
if (EVP_PKEY_verify_init(ctx.get()) != 1) {
- printf("Error initializing openss EVP context\n");
+ printf("Error initializing openssl EVP context\n\n");
return false;
}
if (EVP_PKEY_type(pkey->type) != EVP_PKEY_EC) {
- printf("Exported key was the wrong type?!?\n");
+ printf("Exported key was the wrong type?!?\n\n");
return false;
}
- if (EVP_PKEY_verify(ctx.get(), signature, signature_len, message.get(), message_len) != 1) {
- printf("Verification with exported pubkey failed.\n");
+ if (EVP_PKEY_verify(ctx.get(), signature.data, signature.data_length, message.get(),
+ message_len) != 1) {
+ printf("Verification with exported pubkey failed.\n\n");
return false;
} else {
printf("Verification succeeded\n");
@@ -344,8 +378,8 @@
}
int main(void) {
-
TrustyKeymasterDevice device(NULL);
+ keymaster::ConfigureDevice(reinterpret_cast<keymaster2_device_t*>(&device));
if (device.session_error() != KM_ERROR_OK) {
printf("Failed to initialize Trusty session: %d\n", device.session_error());
return 1;
diff --git a/trusty/libtrusty/Android.bp b/trusty/libtrusty/Android.bp
new file mode 100644
index 0000000..f316da2
--- /dev/null
+++ b/trusty/libtrusty/Android.bp
@@ -0,0 +1,26 @@
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+subdirs = [
+ "tipc-test",
+]
+
+cc_library {
+ name: "libtrusty",
+
+ srcs: ["trusty.c"],
+ export_include_dirs: ["include"],
+
+ shared_libs: ["liblog"],
+}
diff --git a/trusty/libtrusty/Android.mk b/trusty/libtrusty/Android.mk
deleted file mode 100644
index 45fc079..0000000
--- a/trusty/libtrusty/Android.mk
+++ /dev/null
@@ -1,36 +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)
-
-# == libtrusty Static library ==
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := libtrusty
-LOCAL_MODULE_TAGS := optional
-LOCAL_SRC_FILES := trusty.c
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-
-include $(BUILD_STATIC_LIBRARY)
-
-# == libtrusty shared library ==
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := libtrusty
-LOCAL_MODULE_TAGS := optional
-LOCAL_SRC_FILES := trusty.c
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_SHARED_LIBRARIES := liblog
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/trusty/libtrusty/tipc-test/Android.bp b/trusty/libtrusty/tipc-test/Android.bp
new file mode 100644
index 0000000..cb00fe7
--- /dev/null
+++ b/trusty/libtrusty/tipc-test/Android.bp
@@ -0,0 +1,26 @@
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_test {
+ name: "tipc-test",
+ static_executable: true,
+
+ srcs: ["tipc_test.c"],
+ static_libs: [
+ "libc",
+ "libtrusty",
+ "liblog",
+ ],
+ gtest: false,
+}
diff --git a/trusty/libtrusty/tipc-test/Android.mk b/trusty/libtrusty/tipc-test/Android.mk
deleted file mode 100644
index 80030fe..0000000
--- a/trusty/libtrusty/tipc-test/Android.mk
+++ /dev/null
@@ -1,29 +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_MODULE := tipc-test
-LOCAL_FORCE_STATIC_EXECUTABLE := true
-LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
-LOCAL_MODULE_TAGS := optional
-LOCAL_SRC_FILES := tipc_test.c
-LOCAL_STATIC_LIBRARIES := libc libtrusty liblog
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-
-include $(BUILD_EXECUTABLE)
diff --git a/trusty/nvram/Android.bp b/trusty/nvram/Android.bp
new file mode 100644
index 0000000..15e6c3e
--- /dev/null
+++ b/trusty/nvram/Android.bp
@@ -0,0 +1,61 @@
+//
+// 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.
+//
+
+// nvram.trusty is the Trusty NVRAM HAL module.
+cc_library_shared {
+ name: "nvram.trusty",
+ relative_install_path: "hw",
+ srcs: [
+ "module.c",
+ "trusty_nvram_device.cpp",
+ "trusty_nvram_implementation.cpp",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ "-fvisibility=hidden",
+ ],
+ static_libs: ["libnvram-hal"],
+ shared_libs: [
+ "libtrusty",
+ "libnvram-messages",
+ "liblog",
+ ],
+}
+
+// nvram-wipe is a helper tool for clearing NVRAM state.
+cc_binary {
+ name: "nvram-wipe",
+ srcs: [
+ "nvram_wipe.cpp",
+ "trusty_nvram_implementation.cpp",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ "-fvisibility=hidden",
+ ],
+ static_libs: ["libnvram-hal"],
+ shared_libs: [
+ "libtrusty",
+ "libnvram-messages",
+ "liblog",
+ ],
+}
diff --git a/trusty/nvram/Android.mk b/trusty/nvram/Android.mk
deleted file mode 100644
index 44e2212..0000000
--- a/trusty/nvram/Android.mk
+++ /dev/null
@@ -1,43 +0,0 @@
-#
-# 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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-# nvram.trusty is the Trusty NVRAM HAL module.
-include $(CLEAR_VARS)
-LOCAL_MODULE := nvram.trusty
-LOCAL_MODULE_RELATIVE_PATH := hw
-LOCAL_SRC_FILES := \
- module.c \
- trusty_nvram_device.cpp \
- trusty_nvram_implementation.cpp
-LOCAL_MODULE_TAGS := optional
-LOCAL_CFLAGS := -Wall -Werror -Wextra -fvisibility=hidden
-LOCAL_STATIC_LIBRARIES := libnvram-hal
-LOCAL_SHARED_LIBRARIES := libtrusty libnvram-messages liblog
-include $(BUILD_SHARED_LIBRARY)
-
-# nvram-wipe is a helper tool for clearing NVRAM state.
-include $(CLEAR_VARS)
-LOCAL_MODULE := nvram-wipe
-LOCAL_SRC_FILES := \
- nvram_wipe.cpp \
- trusty_nvram_implementation.cpp
-LOCAL_MODULE_TAGS := optional
-LOCAL_CFLAGS := -Wall -Werror -Wextra -fvisibility=hidden
-LOCAL_STATIC_LIBRARIES := libnvram-hal
-LOCAL_SHARED_LIBRARIES := libtrusty libnvram-messages liblog
-include $(BUILD_EXECUTABLE)
diff --git a/trusty/storage/interface/Android.bp b/trusty/storage/interface/Android.bp
new file mode 100644
index 0000000..a551c37
--- /dev/null
+++ b/trusty/storage/interface/Android.bp
@@ -0,0 +1,20 @@
+//
+// 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.
+//
+
+cc_library_static {
+ name: "libtrustystorageinterface",
+ export_include_dirs: ["include"],
+}
diff --git a/trusty/storage/interface/Android.mk b/trusty/storage/interface/Android.mk
deleted file mode 100644
index 15cb6f3..0000000
--- a/trusty/storage/interface/Android.mk
+++ /dev/null
@@ -1,25 +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_MODULE := libtrustystorageinterface
-
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-
-include $(BUILD_STATIC_LIBRARY)
diff --git a/trusty/storage/lib/Android.bp b/trusty/storage/lib/Android.bp
new file mode 100644
index 0000000..5eb3f07
--- /dev/null
+++ b/trusty/storage/lib/Android.bp
@@ -0,0 +1,35 @@
+//
+// Copyright (C) 2015 The Android Open-Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_library_static {
+ name: "libtrustystorage",
+
+ srcs: ["storage.c"],
+
+ export_include_dirs: ["include"],
+
+ static_libs: [
+ "liblog",
+ "libtrusty",
+ "libtrustystorageinterface",
+ ],
+
+ cflags: [
+ "-fvisibility=hidden",
+ "-Wall",
+ "-Werror",
+ ]
+}
diff --git a/trusty/storage/lib/Android.mk b/trusty/storage/lib/Android.mk
deleted file mode 100644
index 7e0fc9d..0000000
--- a/trusty/storage/lib/Android.mk
+++ /dev/null
@@ -1,37 +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_MODULE := libtrustystorage
-
-LOCAL_SRC_FILES := \
- storage.c \
-
-LOCAL_CLFAGS = -fvisibility=hidden -Wall -Werror
-
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-
-LOCAL_STATIC_LIBRARIES := \
- liblog \
- libtrusty \
- libtrustystorageinterface
-
-include $(BUILD_STATIC_LIBRARY)
-
diff --git a/trusty/storage/proxy/Android.bp b/trusty/storage/proxy/Android.bp
new file mode 100644
index 0000000..eb34df0
--- /dev/null
+++ b/trusty/storage/proxy/Android.bp
@@ -0,0 +1,38 @@
+//
+// 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.
+//
+
+cc_binary {
+ name: "storageproxyd",
+
+ srcs: [
+ "ipc.c",
+ "rpmb.c",
+ "storage.c",
+ "proxy.c",
+ ],
+
+ shared_libs: ["liblog"],
+
+ static_libs: [
+ "libtrustystorageinterface",
+ "libtrusty",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ]
+}
diff --git a/trusty/storage/proxy/Android.mk b/trusty/storage/proxy/Android.mk
deleted file mode 100644
index 745e302..0000000
--- a/trusty/storage/proxy/Android.mk
+++ /dev/null
@@ -1,41 +0,0 @@
-#
-# 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.
-#
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := storageproxyd
-
-LOCAL_C_INCLUDES += bionic/libc/kernel/uapi
-
-LOCAL_SRC_FILES := \
- ipc.c \
- rpmb.c \
- storage.c \
- proxy.c
-
-LOCAL_CLFAGS = -Wall -Werror
-
-LOCAL_SHARED_LIBRARIES := \
- liblog \
-
-LOCAL_STATIC_LIBRARIES := \
- libtrustystorageinterface \
- libtrusty
-
-include $(BUILD_EXECUTABLE)
-
diff --git a/trusty/storage/proxy/proxy.c b/trusty/storage/proxy/proxy.c
index d645ac0..27e5891 100644
--- a/trusty/storage/proxy/proxy.c
+++ b/trusty/storage/proxy/proxy.c
@@ -229,7 +229,6 @@
int main(int argc, char *argv[])
{
int rc;
- uint retry_cnt;
/* drop privileges */
if (drop_privs() < 0)
diff --git a/trusty/storage/tests/Android.bp b/trusty/storage/tests/Android.bp
new file mode 100644
index 0000000..3eff3f2
--- /dev/null
+++ b/trusty/storage/tests/Android.bp
@@ -0,0 +1,36 @@
+//
+// 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.
+//
+
+cc_test {
+ name: "secure-storage-unit-test",
+
+ cflags: [
+ "-g",
+ "-Wall",
+ "-Werror",
+ "-std=gnu++11",
+ "-Wno-missing-field-initializers",
+ ],
+
+ static_libs: [
+ "libtrustystorageinterface",
+ "libtrustystorage",
+ "libtrusty",
+ "liblog",
+ ],
+
+ srcs: ["main.cpp"],
+}
diff --git a/trusty/storage/tests/Android.mk b/trusty/storage/tests/Android.mk
deleted file mode 100644
index 71c904d..0000000
--- a/trusty/storage/tests/Android.mk
+++ /dev/null
@@ -1,29 +0,0 @@
-#
-# 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.
-#
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := secure-storage-unit-test
-LOCAL_CFLAGS += -g -Wall -Werror -std=gnu++11 -Wno-missing-field-initializers
-LOCAL_STATIC_LIBRARIES := \
- libtrustystorageinterface \
- libtrustystorage \
- libtrusty \
- liblog
-LOCAL_SRC_FILES := main.cpp
-include $(BUILD_NATIVE_TEST)
-