Merge "androidboot.lrap => logical_partitions"
diff --git a/adb/Android.bp b/adb/Android.bp
index 99de54e..553473f 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -96,6 +96,7 @@
"adb_io.cpp",
"adb_listeners.cpp",
"adb_trace.cpp",
+ "adb_unique_fd.cpp",
"adb_utils.cpp",
"fdevent.cpp",
"services.cpp",
@@ -122,6 +123,7 @@
"sysdeps_test.cpp",
"sysdeps/stat_test.cpp",
"transport_test.cpp",
+ "types_test.cpp",
]
cc_library_host_static {
@@ -275,6 +277,7 @@
cc_library_static {
name: "libadbd",
defaults: ["adb_defaults"],
+ recovery_available: true,
// libminadbd wants both, for some reason.
compile_multilib: "both",
@@ -301,6 +304,7 @@
// adbd must be static, as it is copied into the recovery image.
static_executable: true,
+ recovery_available: true,
srcs: [
"daemon/main.cpp",
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 76ca19a..f8a54c6 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -885,9 +885,8 @@
}
#else /* !defined(_WIN32) */
// set up a pipe so the child can tell us when it is ready.
- // fd[0] will be parent's end, and the child will write on fd[1]
- int fd[2];
- if (pipe(fd)) {
+ unique_fd pipe_read, pipe_write;
+ if (!Pipe(&pipe_read, &pipe_write)) {
fprintf(stderr, "pipe failed in launch_server, errno: %d\n", errno);
return -1;
}
@@ -899,11 +898,10 @@
if (pid == 0) {
// child side of the fork
-
- adb_close(fd[0]);
+ pipe_read.reset();
char reply_fd[30];
- snprintf(reply_fd, sizeof(reply_fd), "%d", fd[1]);
+ snprintf(reply_fd, sizeof(reply_fd), "%d", pipe_write.get());
// child process
int result = execl(path.c_str(), "adb", "-L", socket_spec.c_str(), "fork-server", "server",
"--reply-fd", reply_fd, NULL);
@@ -913,10 +911,10 @@
// parent side of the fork
char temp[3] = {};
// wait for the "OK\n" message
- adb_close(fd[1]);
- int ret = adb_read(fd[0], temp, 3);
+ pipe_write.reset();
+ int ret = adb_read(pipe_read.get(), temp, 3);
int saved_errno = errno;
- adb_close(fd[0]);
+ pipe_read.reset();
if (ret < 0) {
fprintf(stderr, "could not read ok from ADB Server, errno = %d\n", saved_errno);
return -1;
diff --git a/adb/adb_listeners.cpp b/adb/adb_listeners.cpp
index fecf452..ea5a44e 100644
--- a/adb/adb_listeners.cpp
+++ b/adb/adb_listeners.cpp
@@ -42,7 +42,7 @@
alistener(const std::string& _local_name, const std::string& _connect_to);
~alistener();
- fdevent fde;
+ fdevent* fde = nullptr;
int fd = -1;
std::string local_name;
@@ -60,7 +60,7 @@
alistener::~alistener() {
// Closes the corresponding fd.
- fdevent_remove(&fde);
+ fdevent_destroy(fde);
if (transport) {
transport->RemoveDisconnect(&disconnect);
@@ -222,11 +222,11 @@
close_on_exec(listener->fd);
if (listener->connect_to == "*smartsocket*") {
- fdevent_install(&listener->fde, listener->fd, ss_listener_event_func, listener.get());
+ listener->fde = fdevent_create(listener->fd, ss_listener_event_func, listener.get());
} else {
- fdevent_install(&listener->fde, listener->fd, listener_event_func, listener.get());
+ listener->fde = fdevent_create(listener->fd, listener_event_func, listener.get());
}
- fdevent_set(&listener->fde, FDE_READ);
+ fdevent_set(listener->fde, FDE_READ);
listener->transport = transport;
diff --git a/adb/adb_unique_fd.cpp b/adb/adb_unique_fd.cpp
new file mode 100644
index 0000000..2079be1
--- /dev/null
+++ b/adb/adb_unique_fd.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2018 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 "adb_unique_fd.h"
+
+#include <errno.h>
+#include <unistd.h>
+
+#include "sysdeps.h"
+
+#if !defined(_WIN32)
+bool Pipe(unique_fd* read, unique_fd* write, int flags) {
+ int pipefd[2];
+#if !defined(__APPLE__)
+ if (pipe2(pipefd, flags) != 0) {
+ return false;
+ }
+#else
+ // Darwin doesn't have pipe2. Implement it ourselves.
+ if (flags != 0 && (flags & ~(O_CLOEXEC | O_NONBLOCK)) != 0) {
+ errno = EINVAL;
+ return false;
+ }
+
+ if (pipe(pipefd) != 0) {
+ return false;
+ }
+
+ if (flags & O_CLOEXEC) {
+ if (fcntl(pipefd[0], F_SETFD, FD_CLOEXEC) != 0 ||
+ fcntl(pipefd[1], F_SETFD, FD_CLOEXEC) != 0) {
+ adb_close(pipefd[0]);
+ adb_close(pipefd[1]);
+ return false;
+ }
+ }
+
+ if (flags & O_NONBLOCK) {
+ if (fcntl(pipefd[0], F_SETFL, O_NONBLOCK) != 0 ||
+ fcntl(pipefd[1], F_SETFL, O_NONBLOCK) != 0) {
+ adb_close(pipefd[0]);
+ adb_close(pipefd[1]);
+ return false;
+ }
+ }
+#endif
+
+ read->reset(pipefd[0]);
+ write->reset(pipefd[1]);
+ return true;
+}
+#endif
diff --git a/adb/adb_unique_fd.h b/adb/adb_unique_fd.h
index 34c1bbc..be63262 100644
--- a/adb/adb_unique_fd.h
+++ b/adb/adb_unique_fd.h
@@ -16,6 +16,9 @@
#pragma once
+#include <errno.h>
+#include <unistd.h>
+
#include <android-base/unique_fd.h>
// Helper to automatically close an FD when it goes out of scope.
@@ -24,3 +27,7 @@
};
using unique_fd = android::base::unique_fd_impl<AdbCloser>;
+
+#if !defined(_WIN32)
+bool Pipe(unique_fd* read, unique_fd* write, int flags = 0);
+#endif
diff --git a/adb/benchmark_device.py b/adb/benchmark_device.py
new file mode 100755
index 0000000..00c2315
--- /dev/null
+++ b/adb/benchmark_device.py
@@ -0,0 +1,120 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2018 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.
+#
+
+import os
+import statistics
+import time
+
+import adb
+
+def lock_min(device):
+ device.shell_nocheck(["""
+ for x in /sys/devices/system/cpu/cpu?/cpufreq; do
+ echo userspace > $x/scaling_governor
+ cat $x/scaling_min_freq > $x/scaling_setspeed
+ done
+ """])
+
+def lock_max(device):
+ device.shell_nocheck(["""
+ for x in /sys/devices/system/cpu/cpu?/cpufreq; do
+ echo userspace > $x/scaling_governor
+ cat $x/scaling_max_freq > $x/scaling_setspeed
+ done
+ """])
+
+def unlock(device):
+ device.shell_nocheck(["""
+ for x in /sys/devices/system/cpu/cpu?/cpufreq; do
+ echo ondemand > $x/scaling_governor
+ echo sched > $x/scaling_governor
+ echo schedutil > $x/scaling_governor
+ done
+ """])
+
+def harmonic_mean(xs):
+ return 1.0 / statistics.mean([1.0 / x for x in xs])
+
+def analyze(name, speeds):
+ median = statistics.median(speeds)
+ mean = harmonic_mean(speeds)
+ stddev = statistics.stdev(speeds)
+ msg = "%s: %d runs: median %.2f MiB/s, mean %.2f MiB/s, stddev: %.2f MiB/s"
+ print(msg % (name, len(speeds), median, mean, stddev))
+
+def benchmark_push(device=None, file_size_mb=100):
+ if device == None:
+ device = adb.get_device()
+
+ lock_max(device)
+
+ remote_path = "/dev/null"
+ local_path = "/tmp/adb_benchmark_temp"
+
+ with open(local_path, "wb") as f:
+ f.truncate(file_size_mb * 1024 * 1024)
+
+ speeds = list()
+ for _ in range(0, 5):
+ begin = time.time()
+ device.push(local=local_path, remote=remote_path)
+ end = time.time()
+ speeds.append(file_size_mb / float(end - begin))
+
+ analyze("push %dMiB" % file_size_mb, speeds)
+
+def benchmark_pull(device=None, file_size_mb=100):
+ if device == None:
+ device = adb.get_device()
+
+ lock_max(device)
+
+ remote_path = "/data/local/tmp/adb_benchmark_temp"
+ local_path = "/tmp/adb_benchmark_temp"
+
+ device.shell(["dd", "if=/dev/zero", "of=" + remote_path, "bs=1m",
+ "count=" + str(file_size_mb)])
+ speeds = list()
+ for _ in range(0, 5):
+ begin = time.time()
+ device.pull(remote=remote_path, local=local_path)
+ end = time.time()
+ speeds.append(file_size_mb / float(end - begin))
+
+ analyze("pull %dMiB" % file_size_mb, speeds)
+
+def benchmark_shell(device=None, file_size_mb=100):
+ if device == None:
+ device = adb.get_device()
+
+ lock_max(device)
+
+ speeds = list()
+ for _ in range(0, 5):
+ begin = time.time()
+ device.shell(["dd", "if=/dev/zero", "bs=1m",
+ "count=" + str(file_size_mb)])
+ end = time.time()
+ speeds.append(file_size_mb / float(end - begin))
+
+ analyze("shell %dMiB" % file_size_mb, speeds)
+
+def main():
+ benchmark_pull()
+
+if __name__ == "__main__":
+ main()
diff --git a/adb/client/main.cpp b/adb/client/main.cpp
index 31cb853..44ed3a2 100644
--- a/adb/client/main.cpp
+++ b/adb/client/main.cpp
@@ -117,6 +117,7 @@
atexit(adb_server_cleanup);
init_transport_registration();
+ init_reconnect_handler();
init_mdns_transport_discovery();
usb_init();
diff --git a/adb/client/transport_mdns.cpp b/adb/client/transport_mdns.cpp
index 3603f09..283fac5 100644
--- a/adb/client/transport_mdns.cpp
+++ b/adb/client/transport_mdns.cpp
@@ -35,7 +35,7 @@
#include "sysdeps.h"
static DNSServiceRef service_ref;
-static fdevent service_ref_fde;
+static fdevent* service_ref_fde;
// Use adb_DNSServiceRefSockFD() instead of calling DNSServiceRefSockFD()
// directly so that the socket is put through the appropriate compatibility
@@ -68,27 +68,26 @@
}
virtual ~AsyncServiceRef() {
- if (! initialized_) {
+ if (!initialized_) {
return;
}
DNSServiceRefDeallocate(sdRef_);
- fdevent_remove(&fde_);
+ fdevent_destroy(fde_);
}
protected:
DNSServiceRef sdRef_;
void Initialize() {
- fdevent_install(&fde_, adb_DNSServiceRefSockFD(sdRef_),
- pump_service_ref, &sdRef_);
- fdevent_set(&fde_, FDE_READ);
+ fde_ = fdevent_create(adb_DNSServiceRefSockFD(sdRef_), pump_service_ref, &sdRef_);
+ fdevent_set(fde_, FDE_READ);
initialized_ = true;
}
private:
- bool initialized_;
- fdevent fde_;
+ bool initialized_ = false;
+ fdevent* fde_;
};
class ResolvedService : public AsyncServiceRef {
@@ -252,14 +251,12 @@
if (errorCode != kDNSServiceErr_NoError) {
D("Got error %d during mDNS browse.", errorCode);
DNSServiceRefDeallocate(sdRef);
- fdevent_remove(&service_ref_fde);
+ fdevent_destroy(service_ref_fde);
return;
}
- auto discovered = new DiscoveredService(interfaceIndex, serviceName,
- regtype, domain);
-
- if (! discovered->Initialized()) {
+ auto discovered = new DiscoveredService(interfaceIndex, serviceName, regtype, domain);
+ if (!discovered->Initialized()) {
delete discovered;
}
}
@@ -274,9 +271,9 @@
}
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);
+ service_ref_fde =
+ fdevent_create(adb_DNSServiceRefSockFD(service_ref), pump_service_ref, &service_ref);
+ fdevent_set(service_ref_fde, FDE_READ);
});
}
diff --git a/adb/daemon/auth.cpp b/adb/daemon/auth.cpp
index 3fd2b31..f0c3629 100644
--- a/adb/daemon/auth.cpp
+++ b/adb/daemon/auth.cpp
@@ -35,8 +35,8 @@
#include <openssl/rsa.h>
#include <openssl/sha.h>
-static fdevent listener_fde;
-static fdevent framework_fde;
+static fdevent* listener_fde = nullptr;
+static fdevent* framework_fde = nullptr;
static int framework_fd = -1;
static void usb_disconnected(void* unused, atransport* t);
@@ -106,8 +106,10 @@
static void framework_disconnected() {
LOG(INFO) << "Framework disconnect";
- fdevent_remove(&framework_fde);
- framework_fd = -1;
+ if (framework_fde) {
+ fdevent_destroy(framework_fde);
+ framework_fd = -1;
+ }
}
static void adbd_auth_event(int fd, unsigned events, void*) {
@@ -168,8 +170,8 @@
}
framework_fd = s;
- fdevent_install(&framework_fde, framework_fd, adbd_auth_event, nullptr);
- fdevent_add(&framework_fde, FDE_READ);
+ framework_fde = fdevent_create(framework_fd, adbd_auth_event, nullptr);
+ fdevent_add(framework_fde, FDE_READ);
if (needs_retry) {
needs_retry = false;
@@ -198,8 +200,8 @@
return;
}
- fdevent_install(&listener_fde, fd, adbd_auth_listener, NULL);
- fdevent_add(&listener_fde, FDE_READ);
+ listener_fde = fdevent_create(fd, adbd_auth_listener, NULL);
+ fdevent_add(listener_fde, FDE_READ);
}
void send_auth_request(atransport* t) {
diff --git a/adb/daemon/jdwp_service.cpp b/adb/daemon/jdwp_service.cpp
index 3a3f399..367695d 100644
--- a/adb/daemon/jdwp_service.cpp
+++ b/adb/daemon/jdwp_service.cpp
@@ -139,8 +139,6 @@
fatal("could not create fdevent for new JDWP process");
}
- this->fde->state |= FDE_DONT_CLOSE;
-
/* start by waiting for the PID */
fdevent_add(this->fde, FDE_READ);
}
@@ -148,7 +146,6 @@
~JdwpProcess() {
if (this->socket >= 0) {
adb_shutdown(this->socket);
- adb_close(this->socket);
this->socket = -1;
}
diff --git a/adb/daemon/remount_service.cpp b/adb/daemon/remount_service.cpp
index 7876368..eb46903 100644
--- a/adb/daemon/remount_service.cpp
+++ b/adb/daemon/remount_service.cpp
@@ -21,6 +21,7 @@
#include <errno.h>
#include <fcntl.h>
#include <mntent.h>
+#include <spawn.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -106,6 +107,41 @@
return (info.feat_ro_compat & EXT4_FEATURE_RO_COMPAT_SHARED_BLOCKS) != 0;
}
+static bool can_unshare_blocks(int fd, const char* dev) {
+ const char* E2FSCK_BIN = "/system/bin/e2fsck";
+ if (access(E2FSCK_BIN, X_OK)) {
+ WriteFdFmt(fd, "e2fsck is not available, cannot undo deduplication on %s\n", dev);
+ return false;
+ }
+
+ pid_t child;
+ char* env[] = {nullptr};
+ const char* argv[] = {E2FSCK_BIN, "-n", "-E", "unshare_blocks", dev, nullptr};
+ if (posix_spawn(&child, E2FSCK_BIN, nullptr, nullptr, const_cast<char**>(argv), env)) {
+ WriteFdFmt(fd, "failed to e2fsck to check deduplication: %s\n", strerror(errno));
+ return false;
+ }
+ int status = 0;
+ int ret = TEMP_FAILURE_RETRY(waitpid(child, &status, 0));
+ if (ret < 0) {
+ WriteFdFmt(fd, "failed to get e2fsck status: %s\n", strerror(errno));
+ return false;
+ }
+ if (!WIFEXITED(status)) {
+ WriteFdFmt(fd, "e2fsck exited abnormally with status %d\n", status);
+ return false;
+ }
+ int rc = WEXITSTATUS(status);
+ if (rc != 0) {
+ WriteFdFmt(fd,
+ "%s is deduplicated, and an e2fsck check failed. It might not "
+ "have enough free-space to be remounted as writable.\n",
+ dev);
+ return false;
+ }
+ return true;
+}
+
static bool remount_partition(int fd, const char* dir, std::vector<std::string>& dedup) {
if (!directory_exists(dir)) {
return true;
@@ -133,6 +169,9 @@
}
if (mount(dev.c_str(), dir, "none", MS_REMOUNT, nullptr) == -1) {
if (errno == EROFS && fs_has_shared_blocks(dev.c_str())) {
+ if (!can_unshare_blocks(fd, dev.c_str())) {
+ return false;
+ }
// We return true so remount_service() can detect that the only
// failure was deduplicated filesystems.
dedup.push_back(dev);
diff --git a/adb/daemon/usb.cpp b/adb/daemon/usb.cpp
index ab11bdd..c724b11 100644
--- a/adb/daemon/usb.cpp
+++ b/adb/daemon/usb.cpp
@@ -234,6 +234,10 @@
for (unsigned i = 0; i < USB_FFS_NUM_BUFS; i++) {
aiob->iocbs[i] = &aiob->iocb[i];
}
+ memset(&aiob->ctx, 0, sizeof(aiob->ctx));
+ if (io_setup(USB_FFS_NUM_BUFS, &aiob->ctx)) {
+ D("[ aio: got error on io_setup (%d) ]", errno);
+ }
}
static int getMaxPacketSize(int ffs_fd) {
@@ -312,13 +316,6 @@
goto err;
}
- memset(&h->read_aiob.ctx, 0, sizeof(h->read_aiob.ctx));
- memset(&h->write_aiob.ctx, 0, sizeof(h->write_aiob.ctx));
- if (io_setup(USB_FFS_NUM_BUFS, &h->read_aiob.ctx) ||
- io_setup(USB_FFS_NUM_BUFS, &h->write_aiob.ctx)) {
- D("[ aio: got error on io_setup (%d) ]", errno);
- }
-
h->read_aiob.fd = h->bulk_out;
h->write_aiob.fd = h->bulk_in;
return true;
@@ -439,23 +436,29 @@
num_bufs += 1;
}
- if (io_submit(aiob->ctx, num_bufs, aiob->iocbs.data()) < num_bufs) {
- D("[ aio: got error submitting %s (%d) ]", read ? "read" : "write", errno);
- return -1;
- }
- if (TEMP_FAILURE_RETRY(
- io_getevents(aiob->ctx, num_bufs, num_bufs, aiob->events.data(), nullptr)) < num_bufs) {
- D("[ aio: got error waiting %s (%d) ]", read ? "read" : "write", errno);
- return -1;
- }
- for (int i = 0; i < num_bufs; i++) {
- if (aiob->events[i].res < 0) {
- errno = aiob->events[i].res;
- D("[ aio: got error event on %s (%d) ]", read ? "read" : "write", errno);
+ while (true) {
+ if (TEMP_FAILURE_RETRY(io_submit(aiob->ctx, num_bufs, aiob->iocbs.data())) < num_bufs) {
+ PLOG(ERROR) << "aio: got error submitting " << (read ? "read" : "write");
return -1;
}
+ if (TEMP_FAILURE_RETRY(io_getevents(aiob->ctx, num_bufs, num_bufs, aiob->events.data(),
+ nullptr)) < num_bufs) {
+ PLOG(ERROR) << "aio: got error waiting " << (read ? "read" : "write");
+ return -1;
+ }
+ if (num_bufs == 1 && aiob->events[0].res == -EINTR) {
+ continue;
+ }
+ for (int i = 0; i < num_bufs; i++) {
+ if (aiob->events[i].res < 0) {
+ errno = -aiob->events[i].res;
+ PLOG(ERROR) << "aio: got error event on " << (read ? "read" : "write")
+ << " total bufs " << num_bufs;
+ return -1;
+ }
+ }
+ return 0;
}
- return 0;
}
static int usb_ffs_aio_read(usb_handle* h, void* data, int len) {
@@ -494,8 +497,6 @@
h->kicked = false;
adb_close(h->bulk_out);
adb_close(h->bulk_in);
- io_destroy(h->read_aiob.ctx);
- io_destroy(h->write_aiob.ctx);
// Notify usb_adb_open_thread to open a new connection.
h->lock.lock();
diff --git a/adb/fdevent.cpp b/adb/fdevent.cpp
index cf441cf..1b7758c 100644
--- a/adb/fdevent.cpp
+++ b/adb/fdevent.cpp
@@ -57,7 +57,7 @@
explicit PollNode(fdevent* fde) : fde(fde) {
memset(&pollfd, 0, sizeof(pollfd));
- pollfd.fd = fde->fd;
+ pollfd.fd = fde->fd.get();
#if defined(__linux__)
// Always enable POLLRDHUP, so the host server can take action when some clients disconnect.
@@ -111,37 +111,22 @@
if (fde->state & FDE_ERROR) {
state += "E";
}
- if (fde->state & FDE_DONT_CLOSE) {
- state += "D";
- }
- return android::base::StringPrintf("(fdevent %d %s)", fde->fd, state.c_str());
-}
-
-fdevent* fdevent_create(int fd, fd_func func, void* arg) {
- check_main_thread();
- fdevent *fde = (fdevent*) malloc(sizeof(fdevent));
- if(fde == 0) return 0;
- fdevent_install(fde, fd, func, arg);
- fde->state |= FDE_CREATED;
- return fde;
-}
-
-void fdevent_destroy(fdevent* fde) {
- check_main_thread();
- if(fde == 0) return;
- if(!(fde->state & FDE_CREATED)) {
- LOG(FATAL) << "destroying fde not created by fdevent_create(): " << dump_fde(fde);
- }
- fdevent_remove(fde);
- free(fde);
+ return android::base::StringPrintf("(fdevent %d %s)", fde->fd.get(), state.c_str());
}
void fdevent_install(fdevent* fde, int fd, fd_func func, void* arg) {
check_main_thread();
CHECK_GE(fd, 0);
memset(fde, 0, sizeof(fdevent));
+}
+
+fdevent* fdevent_create(int fd, fd_func func, void* arg) {
+ check_main_thread();
+ CHECK_GE(fd, 0);
+
+ fdevent* fde = new fdevent();
fde->state = FDE_ACTIVE;
- fde->fd = fd;
+ fde->fd.reset(fd);
fde->func = func;
fde->arg = arg;
if (!set_file_block_mode(fd, false)) {
@@ -150,30 +135,35 @@
// to handle it.
LOG(ERROR) << "failed to set non-blocking mode for fd " << fd;
}
- auto pair = g_poll_node_map.emplace(fde->fd, PollNode(fde));
+ auto pair = g_poll_node_map.emplace(fde->fd.get(), PollNode(fde));
CHECK(pair.second) << "install existing fd " << fd;
- D("fdevent_install %s", dump_fde(fde).c_str());
+
+ fde->state |= FDE_CREATED;
+ return fde;
}
-void fdevent_remove(fdevent* fde) {
+void fdevent_destroy(fdevent* fde) {
check_main_thread();
- D("fdevent_remove %s", dump_fde(fde).c_str());
+ if (fde == 0) return;
+ if (!(fde->state & FDE_CREATED)) {
+ LOG(FATAL) << "destroying fde not created by fdevent_create(): " << dump_fde(fde);
+ }
+
if (fde->state & FDE_ACTIVE) {
- g_poll_node_map.erase(fde->fd);
+ g_poll_node_map.erase(fde->fd.get());
if (fde->state & FDE_PENDING) {
g_pending_list.remove(fde);
}
- if (!(fde->state & FDE_DONT_CLOSE)) {
- adb_close(fde->fd);
- fde->fd = -1;
- }
+ fde->fd.reset();
fde->state = 0;
fde->events = 0;
}
+
+ delete fde;
}
static void fdevent_update(fdevent* fde, unsigned events) {
- auto it = g_poll_node_map.find(fde->fd);
+ auto it = g_poll_node_map.find(fde->fd.get());
CHECK(it != g_poll_node_map.end());
PollNode& node = it->second;
if (events & FDE_READ) {
@@ -272,7 +262,7 @@
auto it = g_poll_node_map.find(pollfd.fd);
CHECK(it != g_poll_node_map.end());
fdevent* fde = it->second.fde;
- CHECK_EQ(fde->fd, pollfd.fd);
+ CHECK_EQ(fde->fd.get(), pollfd.fd);
fde->events |= events;
D("%s got events %x", dump_fde(fde).c_str(), events);
fde->state |= FDE_PENDING;
@@ -287,7 +277,7 @@
CHECK(fde->state & FDE_PENDING);
fde->state &= (~FDE_PENDING);
D("fdevent_call_fdfunc %s", dump_fde(fde).c_str());
- fde->func(fde->fd, events, fde->arg);
+ fde->func(fde->fd.get(), events, fde->arg);
}
static void fdevent_run_flush() EXCLUDES(run_queue_mutex) {
diff --git a/adb/fdevent.h b/adb/fdevent.h
index 896400a..69c4072 100644
--- a/adb/fdevent.h
+++ b/adb/fdevent.h
@@ -22,28 +22,27 @@
#include <functional>
+#include "adb_unique_fd.h"
+
/* events that may be observed */
#define FDE_READ 0x0001
#define FDE_WRITE 0x0002
#define FDE_ERROR 0x0004
-/* features that may be set (via the events set/add/del interface) */
-#define FDE_DONT_CLOSE 0x0080
-
typedef void (*fd_func)(int fd, unsigned events, void *userdata);
struct fdevent {
- fdevent *next;
- fdevent *prev;
+ fdevent* next = nullptr;
+ fdevent* prev = nullptr;
- int fd;
- int force_eof;
+ unique_fd fd;
+ int force_eof = 0;
- uint16_t state;
- uint16_t events;
+ uint16_t state = 0;
+ uint16_t events = 0;
- fd_func func;
- void *arg;
+ fd_func func = nullptr;
+ void* arg = nullptr;
};
/* Allocate and initialize a new fdevent object
@@ -57,15 +56,6 @@
*/
void fdevent_destroy(fdevent *fde);
-/* Initialize an fdevent object that was externally allocated
-*/
-void fdevent_install(fdevent *fde, int fd, fd_func func, void *arg);
-
-/* Uninitialize an fdevent object that was initialized by
-** fdevent_install()
-*/
-void fdevent_remove(fdevent *item);
-
/* Change which events should cause notifications
*/
void fdevent_set(fdevent *fde, unsigned events);
diff --git a/adb/fdevent_test.cpp b/adb/fdevent_test.cpp
index 2f0ff18..0cb2439 100644
--- a/adb/fdevent_test.cpp
+++ b/adb/fdevent_test.cpp
@@ -31,14 +31,14 @@
class FdHandler {
public:
FdHandler(int read_fd, int write_fd) : read_fd_(read_fd), write_fd_(write_fd) {
- fdevent_install(&read_fde_, read_fd_, FdEventCallback, this);
- fdevent_add(&read_fde_, FDE_READ);
- fdevent_install(&write_fde_, write_fd_, FdEventCallback, this);
+ read_fde_ = fdevent_create(read_fd_, FdEventCallback, this);
+ fdevent_add(read_fde_, FDE_READ);
+ write_fde_ = fdevent_create(write_fd_, FdEventCallback, this);
}
~FdHandler() {
- fdevent_remove(&read_fde_);
- fdevent_remove(&write_fde_);
+ fdevent_destroy(read_fde_);
+ fdevent_destroy(write_fde_);
}
private:
@@ -50,7 +50,7 @@
char c;
ASSERT_EQ(1, adb_read(fd, &c, 1));
handler->queue_.push(c);
- fdevent_add(&handler->write_fde_, FDE_WRITE);
+ fdevent_add(handler->write_fde_, FDE_WRITE);
}
if (events & FDE_WRITE) {
ASSERT_EQ(fd, handler->write_fd_);
@@ -59,7 +59,7 @@
handler->queue_.pop();
ASSERT_EQ(1, adb_write(fd, &c, 1));
if (handler->queue_.empty()) {
- fdevent_del(&handler->write_fde_, FDE_WRITE);
+ fdevent_del(handler->write_fde_, FDE_WRITE);
}
}
}
@@ -67,8 +67,8 @@
private:
const int read_fd_;
const int write_fd_;
- fdevent read_fde_;
- fdevent write_fde_;
+ fdevent* read_fde_;
+ fdevent* write_fde_;
std::queue<char> queue_;
};
@@ -137,7 +137,7 @@
}
struct InvalidFdArg {
- fdevent fde;
+ fdevent* fde;
unsigned expected_events;
size_t* happened_event_count;
};
@@ -145,7 +145,7 @@
static void InvalidFdEventCallback(int, unsigned events, void* userdata) {
InvalidFdArg* arg = reinterpret_cast<InvalidFdArg*>(userdata);
ASSERT_EQ(arg->expected_events, events);
- fdevent_remove(&arg->fde);
+ fdevent_destroy(arg->fde);
if (++*(arg->happened_event_count) == 2) {
fdevent_terminate_loop();
}
@@ -157,15 +157,15 @@
InvalidFdArg read_arg;
read_arg.expected_events = FDE_READ | FDE_ERROR;
read_arg.happened_event_count = &happened_event_count;
- fdevent_install(&read_arg.fde, INVALID_READ_FD, InvalidFdEventCallback, &read_arg);
- fdevent_add(&read_arg.fde, FDE_READ);
+ read_arg.fde = fdevent_create(INVALID_READ_FD, InvalidFdEventCallback, &read_arg);
+ fdevent_add(read_arg.fde, FDE_READ);
const int INVALID_WRITE_FD = std::numeric_limits<int>::max();
InvalidFdArg write_arg;
write_arg.expected_events = FDE_READ | FDE_ERROR;
write_arg.happened_event_count = &happened_event_count;
- fdevent_install(&write_arg.fde, INVALID_WRITE_FD, InvalidFdEventCallback, &write_arg);
- fdevent_add(&write_arg.fde, FDE_WRITE);
+ write_arg.fde = fdevent_create(INVALID_WRITE_FD, InvalidFdEventCallback, &write_arg);
+ fdevent_add(write_arg.fde, FDE_WRITE);
fdevent_loop();
}
diff --git a/adb/socket.h b/adb/socket.h
index e0fd87f..0905aab 100644
--- a/adb/socket.h
+++ b/adb/socket.h
@@ -58,11 +58,11 @@
* us to our fd event system. For remote asockets
* these fields are not used.
*/
- fdevent fde = {};
- int fd = 0;
+ fdevent* fde = nullptr;
+ int fd = -1;
// queue of data waiting to be written
- std::deque<Range> packet_queue;
+ IOVector packet_queue;
std::string smart_socket_data;
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index e567ff4..de3215d 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -113,24 +113,23 @@
};
static SocketFlushResult local_socket_flush_incoming(asocket* s) {
- while (!s->packet_queue.empty()) {
- Range& r = s->packet_queue.front();
-
- int rc = adb_write(s->fd, r.data(), r.size());
- if (rc == static_cast<int>(r.size())) {
- s->packet_queue.pop_front();
+ if (!s->packet_queue.empty()) {
+ std::vector<adb_iovec> iov = s->packet_queue.iovecs();
+ ssize_t rc = adb_writev(s->fd, iov.data(), iov.size());
+ if (rc > 0 && static_cast<size_t>(rc) == s->packet_queue.size()) {
+ s->packet_queue.clear();
} else if (rc > 0) {
- r.drop_front(rc);
- fdevent_add(&s->fde, FDE_WRITE);
+ // TODO: Implement a faster drop_front?
+ s->packet_queue.take_front(rc);
+ fdevent_add(s->fde, FDE_WRITE);
return SocketFlushResult::TryAgain;
} else if (rc == -1 && errno == EAGAIN) {
- fdevent_add(&s->fde, FDE_WRITE);
+ fdevent_add(s->fde, FDE_WRITE);
return SocketFlushResult::TryAgain;
} else {
// We failed to write, but it's possible that we can still read from the socket.
// Give that a try before giving up.
s->has_write_error = true;
- break;
}
}
@@ -140,7 +139,7 @@
return SocketFlushResult::Destroyed;
}
- fdevent_del(&s->fde, FDE_WRITE);
+ fdevent_del(s->fde, FDE_WRITE);
return SocketFlushResult::Completed;
}
@@ -173,7 +172,7 @@
break;
}
D("LS(%d): fd=%d post avail loop. r=%d is_eof=%d forced_eof=%d", s->id, s->fd, r, is_eof,
- s->fde.force_eof);
+ s->fde->force_eof);
if (avail != max_payload && s->peer) {
data.resize(max_payload - avail);
@@ -200,13 +199,13 @@
** we disable notification of READs. They'll
** be enabled again when we get a call to ready()
*/
- fdevent_del(&s->fde, FDE_READ);
+ fdevent_del(s->fde, FDE_READ);
}
}
// Don't allow a forced eof if data is still there.
- if ((s->fde.force_eof && !r) || is_eof) {
- D(" closing because is_eof=%d r=%d s->fde.force_eof=%d", is_eof, r, s->fde.force_eof);
+ if ((s->fde->force_eof && !r) || is_eof) {
+ D(" closing because is_eof=%d r=%d s->fde.force_eof=%d", is_eof, r, s->fde->force_eof);
s->close(s);
return false;
}
@@ -217,8 +216,7 @@
static int local_socket_enqueue(asocket* s, apacket::payload_type data) {
D("LS(%d): enqueue %zu", s->id, data.size());
- Range r(std::move(data));
- s->packet_queue.push_back(std::move(r));
+ s->packet_queue.append(std::move(data));
switch (local_socket_flush_incoming(s)) {
case SocketFlushResult::Destroyed:
return -1;
@@ -236,19 +234,19 @@
static void local_socket_ready(asocket* s) {
/* far side is ready for data, pay attention to
readable events */
- fdevent_add(&s->fde, FDE_READ);
+ fdevent_add(s->fde, FDE_READ);
}
// be sure to hold the socket list lock when calling this
static void local_socket_destroy(asocket* s) {
int exit_on_close = s->exit_on_close;
- D("LS(%d): destroying fde.fd=%d", s->id, s->fde.fd);
+ D("LS(%d): destroying fde.fd=%d", s->id, s->fd);
/* IMPORTANT: the remove closes the fd
** that belongs to this socket
*/
- fdevent_remove(&s->fde);
+ fdevent_destroy(s->fde);
remove_socket(s);
delete s;
@@ -290,11 +288,11 @@
*/
D("LS(%d): closing", s->id);
s->closing = 1;
- fdevent_del(&s->fde, FDE_READ);
+ fdevent_del(s->fde, FDE_READ);
remove_socket(s);
D("LS(%d): put on socket_closing_list fd=%d", s->id, s->fd);
local_socket_closing_list.push_back(s);
- CHECK_EQ(FDE_WRITE, s->fde.state & FDE_WRITE);
+ CHECK_EQ(FDE_WRITE, s->fde->state & FDE_WRITE);
}
static void local_socket_event_func(int fd, unsigned ev, void* _s) {
@@ -343,7 +341,7 @@
s->close = local_socket_close;
install_local_socket(s);
- fdevent_install(&s->fde, fd, local_socket_event_func, s);
+ s->fde = fdevent_create(fd, local_socket_event_func, s);
D("LS(%d): created (fd=%d)", s->id, s->fd);
return s;
}
@@ -622,7 +620,7 @@
D("SS(%d): enqueue %zu", s->id, data.size());
if (s->smart_socket_data.empty()) {
- // TODO: Make this a BlockChain?
+ // TODO: Make this an IOVector?
s->smart_socket_data.assign(data.begin(), data.end());
} else {
std::copy(data.begin(), data.end(), std::back_inserter(s->smart_socket_data));
diff --git a/adb/test_adb.py b/adb/test_adb.py
index 32bf029..ddd3ff0 100644
--- a/adb/test_adb.py
+++ b/adb/test_adb.py
@@ -36,10 +36,11 @@
@contextlib.contextmanager
-def fake_adb_server(protocol=socket.AF_INET, port=0):
- """Creates a fake ADB server that just replies with a CNXN packet."""
+def fake_adbd(protocol=socket.AF_INET, port=0):
+ """Creates a fake ADB daemon that just replies with a CNXN packet."""
serversock = socket.socket(protocol, socket.SOCK_STREAM)
+ serversock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
if protocol == socket.AF_INET:
serversock.bind(('127.0.0.1', port))
else:
@@ -60,31 +61,33 @@
rlist = [readpipe, serversock]
cnxn_sent = {}
while True:
- ready, _, _ = select.select(rlist, [], [])
- for r in ready:
- if r == readpipe:
+ read_ready, _, _ = select.select(rlist, [], [])
+ for ready in read_ready:
+ if ready == readpipe:
# Closure pipe
- os.close(r)
+ os.close(ready)
serversock.shutdown(socket.SHUT_RDWR)
serversock.close()
return
- elif r == serversock:
+ elif ready == serversock:
# Server socket
- conn, _ = r.accept()
+ conn, _ = ready.accept()
rlist.append(conn)
else:
# Client socket
- data = r.recv(1024)
- if not data:
- if r in cnxn_sent:
- del cnxn_sent[r]
- rlist.remove(r)
+ data = ready.recv(1024)
+ if not data or data.startswith('OPEN'):
+ if ready in cnxn_sent:
+ del cnxn_sent[ready]
+ ready.shutdown(socket.SHUT_RDWR)
+ ready.close()
+ rlist.remove(ready)
continue
- if r in cnxn_sent:
+ if ready in cnxn_sent:
continue
- cnxn_sent[r] = True
- r.sendall(_adb_packet('CNXN', 0x01000001, 1024 * 1024,
- 'device::ro.product.name=fakeadb'))
+ cnxn_sent[ready] = True
+ ready.sendall(_adb_packet('CNXN', 0x01000001, 1024 * 1024,
+ 'device::ro.product.name=fakeadb'))
port = serversock.getsockname()[1]
server_thread = threading.Thread(target=_handle)
@@ -97,8 +100,52 @@
server_thread.join()
-class NonApiTest(unittest.TestCase):
- """Tests for ADB that aren't a part of the AndroidDevice API."""
+@contextlib.contextmanager
+def adb_connect(unittest, serial):
+ """Context manager for an ADB connection.
+
+ This automatically disconnects when done with the connection.
+ """
+
+ output = subprocess.check_output(['adb', 'connect', serial])
+ unittest.assertEqual(output.strip(), 'connected to {}'.format(serial))
+
+ try:
+ yield
+ finally:
+ # Perform best-effort disconnection. Discard the output.
+ subprocess.Popen(['adb', 'disconnect', serial],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE).communicate()
+
+
+@contextlib.contextmanager
+def adb_server():
+ """Context manager for an ADB server.
+
+ This creates an ADB server and returns the port it's listening on.
+ """
+
+ port = 5038
+ # Kill any existing server on this non-default port.
+ subprocess.check_output(['adb', '-P', str(port), 'kill-server'],
+ stderr=subprocess.STDOUT)
+ read_pipe, write_pipe = os.pipe()
+ proc = subprocess.Popen(['adb', '-L', 'tcp:localhost:{}'.format(port),
+ 'fork-server', 'server',
+ '--reply-fd', str(write_pipe)])
+ try:
+ os.close(write_pipe)
+ greeting = os.read(read_pipe, 1024)
+ assert greeting == 'OK\n', repr(greeting)
+ yield port
+ finally:
+ proc.terminate()
+ proc.wait()
+
+
+class CommandlineTest(unittest.TestCase):
+ """Tests for the ADB commandline."""
def test_help(self):
"""Make sure we get _something_ out of help."""
@@ -120,28 +167,37 @@
revision_line, r'^Revision [0-9a-f]{12}-android$')
def test_tcpip_error_messages(self):
- p = subprocess.Popen(['adb', 'tcpip'], stdout=subprocess.PIPE,
- stderr=subprocess.STDOUT)
- out, _ = p.communicate()
- self.assertEqual(1, p.returncode)
+ """Make sure 'adb tcpip' parsing is sane."""
+ proc = subprocess.Popen(['adb', 'tcpip'], stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT)
+ out, _ = proc.communicate()
+ self.assertEqual(1, proc.returncode)
self.assertIn('requires an argument', out)
- p = subprocess.Popen(['adb', 'tcpip', 'foo'], stdout=subprocess.PIPE,
- stderr=subprocess.STDOUT)
- out, _ = p.communicate()
- self.assertEqual(1, p.returncode)
+ proc = subprocess.Popen(['adb', 'tcpip', 'foo'], stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT)
+ out, _ = proc.communicate()
+ self.assertEqual(1, proc.returncode)
self.assertIn('invalid port', out)
- # Helper method that reads a pipe until it is closed, then sets the event.
- def _read_pipe_and_set_event(self, pipe, event):
- x = pipe.read()
+
+class ServerTest(unittest.TestCase):
+ """Tests for the ADB server."""
+
+ @staticmethod
+ def _read_pipe_and_set_event(pipe, event):
+ """Reads a pipe until it is closed, then sets the event."""
+ pipe.read()
event.set()
- # Test that launch_server() does not let the adb server inherit
- # stdin/stdout/stderr handles which can cause callers of adb.exe to hang.
- # This test also runs fine on unix even though the impetus is an issue
- # unique to Windows.
def test_handle_inheritance(self):
+ """Test that launch_server() does not inherit handles.
+
+ launch_server() should not let the adb server inherit
+ stdin/stdout/stderr handles, which can cause callers of adb.exe to hang.
+ This test also runs fine on unix even though the impetus is an issue
+ unique to Windows.
+ """
# This test takes 5 seconds to run on Windows: if there is no adb server
# running on the the port used below, adb kill-server tries to make a
# TCP connection to a closed port and that takes 1 second on Windows;
@@ -163,29 +219,30 @@
try:
# Run the adb client and have it start the adb server.
- p = subprocess.Popen(['adb', '-P', str(port), 'start-server'],
- stdin=subprocess.PIPE, stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)
+ proc = subprocess.Popen(['adb', '-P', str(port), 'start-server'],
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
# Start threads that set events when stdout/stderr are closed.
stdout_event = threading.Event()
stdout_thread = threading.Thread(
- target=self._read_pipe_and_set_event,
- args=(p.stdout, stdout_event))
+ target=ServerTest._read_pipe_and_set_event,
+ args=(proc.stdout, stdout_event))
stdout_thread.daemon = True
stdout_thread.start()
stderr_event = threading.Event()
stderr_thread = threading.Thread(
- target=self._read_pipe_and_set_event,
- args=(p.stderr, stderr_event))
+ target=ServerTest._read_pipe_and_set_event,
+ args=(proc.stderr, stderr_event))
stderr_thread.daemon = True
stderr_thread.start()
# Wait for the adb client to finish. Once that has occurred, if
# stdin/stderr/stdout are still open, it must be open in the adb
# server.
- p.wait()
+ proc.wait()
# Try to write to stdin which we expect is closed. If it isn't
# closed, we should get an IOError. If we don't get an IOError,
@@ -193,7 +250,7 @@
# probably letting the adb server inherit stdin which would be
# wrong.
with self.assertRaises(IOError):
- p.stdin.write('x')
+ proc.stdin.write('x')
# Wait a few seconds for stdout/stderr to be closed (in the success
# case, this won't wait at all). If there is a timeout, that means
@@ -207,8 +264,12 @@
subprocess.check_output(['adb', '-P', str(port), 'kill-server'],
stderr=subprocess.STDOUT)
- # Use SO_LINGER to cause TCP RST segment to be sent on socket close.
+
+class EmulatorTest(unittest.TestCase):
+ """Tests for the emulator connection."""
+
def _reset_socket_on_close(self, sock):
+ """Use SO_LINGER to cause TCP RST segment to be sent on socket close."""
# The linger structure is two shorts on Windows, but two ints on Unix.
linger_format = 'hh' if os.name == 'nt' else 'ii'
l_onoff = 1
@@ -227,7 +288,7 @@
Bug: https://code.google.com/p/android/issues/detail?id=21021
"""
with contextlib.closing(
- socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as listener:
+ socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as listener:
# Use SO_REUSEADDR so subsequent runs of the test can grab the port
# even if it is in TIME_WAIT.
listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
@@ -237,7 +298,7 @@
# Now that listening has started, start adb emu kill, telling it to
# connect to our mock emulator.
- p = subprocess.Popen(
+ proc = subprocess.Popen(
['adb', '-s', 'emulator-' + str(port), 'emu', 'kill'],
stderr=subprocess.STDOUT)
@@ -246,12 +307,16 @@
# If WSAECONNABORTED (10053) is raised by any socket calls,
# then adb probably isn't reading the data that we sent it.
conn.sendall('Android Console: type \'help\' for a list ' +
- 'of commands\r\n')
+ 'of commands\r\n')
conn.sendall('OK\r\n')
- with contextlib.closing(conn.makefile()) as f:
- self.assertEqual('kill\n', f.readline())
- self.assertEqual('quit\n', f.readline())
+ with contextlib.closing(conn.makefile()) as connf:
+ line = connf.readline()
+ if line.startswith('auth'):
+ # Ignore the first auth line.
+ line = connf.readline()
+ self.assertEqual('kill\n', line)
+ self.assertEqual('quit\n', connf.readline())
conn.sendall('OK: killing emulator, bye bye\r\n')
@@ -264,11 +329,48 @@
self._reset_socket_on_close(conn)
# Wait for adb to finish, so we can check return code.
- p.communicate()
+ proc.communicate()
# If this fails, adb probably isn't ignoring WSAECONNRESET when
# reading the response from the adb emu kill command (on Windows).
- self.assertEqual(0, p.returncode)
+ self.assertEqual(0, proc.returncode)
+
+ def test_emulator_connect(self):
+ """Ensure that the emulator can connect.
+
+ Bug: http://b/78991667
+ """
+ with adb_server() as server_port:
+ with fake_adbd() as port:
+ serial = 'emulator-{}'.format(port - 1)
+ # Ensure that the emulator is not there.
+ try:
+ subprocess.check_output(['adb', '-P', str(server_port),
+ '-s', serial, 'get-state'],
+ stderr=subprocess.STDOUT)
+ self.fail('Device should not be available')
+ except subprocess.CalledProcessError as err:
+ self.assertEqual(
+ err.output.strip(),
+ 'error: device \'{}\' not found'.format(serial))
+
+ # Let the ADB server know that the emulator has started.
+ with contextlib.closing(
+ socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sock:
+ sock.connect(('localhost', server_port))
+ command = 'host:emulator:{}'.format(port)
+ sock.sendall('%04x%s' % (len(command), command))
+
+ # Ensure the emulator is there.
+ subprocess.check_call(['adb', '-P', str(server_port),
+ '-s', serial, 'wait-for-device'])
+ output = subprocess.check_output(['adb', '-P', str(server_port),
+ '-s', serial, 'get-state'])
+ self.assertEqual(output.strip(), 'device')
+
+
+class ConnectionTest(unittest.TestCase):
+ """Tests for adb connect."""
def test_connect_ipv4_ipv6(self):
"""Ensure that `adb connect localhost:1234` will try both IPv4 and IPv6.
@@ -277,38 +379,67 @@
"""
for protocol in (socket.AF_INET, socket.AF_INET6):
try:
- with fake_adb_server(protocol=protocol) as port:
- output = subprocess.check_output(
- ['adb', 'connect', 'localhost:{}'.format(port)])
-
- self.assertEqual(
- output.strip(), 'connected to localhost:{}'.format(port))
+ with fake_adbd(protocol=protocol) as port:
+ serial = 'localhost:{}'.format(port)
+ with adb_connect(self, serial):
+ pass
except socket.error:
print("IPv6 not available, skipping")
continue
def test_already_connected(self):
- with fake_adb_server() as port:
- output = subprocess.check_output(
- ['adb', 'connect', 'localhost:{}'.format(port)])
+ """Ensure that an already-connected device stays connected."""
- self.assertEqual(
- output.strip(), 'connected to localhost:{}'.format(port))
+ with fake_adbd() as port:
+ serial = 'localhost:{}'.format(port)
+ with adb_connect(self, serial):
+ # b/31250450: this always returns 0 but probably shouldn't.
+ output = subprocess.check_output(['adb', 'connect', serial])
+ self.assertEqual(
+ output.strip(), 'already connected to {}'.format(serial))
- # b/31250450: this always returns 0 but probably shouldn't.
- output = subprocess.check_output(
- ['adb', 'connect', 'localhost:{}'.format(port)])
+ def test_reconnect(self):
+ """Ensure that a disconnected device reconnects."""
- self.assertEqual(
- output.strip(), 'already connected to localhost:{}'.format(port))
+ with fake_adbd() as port:
+ serial = 'localhost:{}'.format(port)
+ with adb_connect(self, serial):
+ output = subprocess.check_output(['adb', '-s', serial,
+ 'get-state'])
+ self.assertEqual(output.strip(), 'device')
+
+ # This will fail.
+ proc = subprocess.Popen(['adb', '-s', serial, 'shell', 'true'],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT)
+ output, _ = proc.communicate()
+ self.assertEqual(output.strip(), 'error: closed')
+
+ subprocess.check_call(['adb', '-s', serial, 'wait-for-device'])
+
+ output = subprocess.check_output(['adb', '-s', serial,
+ 'get-state'])
+ self.assertEqual(output.strip(), 'device')
+
+ # Once we explicitly kick a device, it won't attempt to
+ # reconnect.
+ output = subprocess.check_output(['adb', 'disconnect', serial])
+ self.assertEqual(
+ output.strip(), 'disconnected {}'.format(serial))
+ try:
+ subprocess.check_output(['adb', '-s', serial, 'get-state'],
+ stderr=subprocess.STDOUT)
+ self.fail('Device should not be available')
+ except subprocess.CalledProcessError as err:
+ self.assertEqual(
+ err.output.strip(),
+ 'error: device \'{}\' not found'.format(serial))
+
def main():
+ """Main entrypoint."""
random.seed(0)
- if len(adb.get_devices()) > 0:
- suite = unittest.TestLoader().loadTestsFromName(__name__)
- unittest.TextTestRunner(verbosity=3).run(suite)
- else:
- print('Test suite must be run with attached devices')
+ unittest.main(verbosity=3)
if __name__ == '__main__':
diff --git a/adb/transport.cpp b/adb/transport.cpp
index be7f8fe..7db9bf2 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -33,6 +33,7 @@
#include <deque>
#include <list>
#include <mutex>
+#include <queue>
#include <thread>
#include <android-base/logging.h>
@@ -50,7 +51,9 @@
#include "adb_utils.h"
#include "fdevent.h"
-static void transport_unref(atransport *t);
+static void register_transport(atransport* transport);
+static void remove_transport(atransport* transport);
+static void transport_unref(atransport* transport);
// TODO: unordered_map<TransportId, atransport*>
static auto& transport_list = *new std::list<atransport*>();
@@ -77,6 +80,130 @@
~ScopedAssumeLocked() RELEASE() {}
};
+// Tracks and handles atransport*s that are attempting reconnection.
+class ReconnectHandler {
+ public:
+ ReconnectHandler() = default;
+ ~ReconnectHandler() = default;
+
+ // Starts the ReconnectHandler thread.
+ void Start();
+
+ // Requests the ReconnectHandler thread to stop.
+ void Stop();
+
+ // Adds the atransport* to the queue of reconnect attempts.
+ void TrackTransport(atransport* transport);
+
+ private:
+ // The main thread loop.
+ void Run();
+
+ // Tracks a reconnection attempt.
+ struct ReconnectAttempt {
+ atransport* transport;
+ std::chrono::system_clock::time_point deadline;
+ size_t attempts_left;
+ };
+
+ // Only retry for up to one minute.
+ static constexpr const std::chrono::seconds kDefaultTimeout = std::chrono::seconds(10);
+ static constexpr const size_t kMaxAttempts = 6;
+
+ // Protects all members.
+ std::mutex reconnect_mutex_;
+ bool running_ GUARDED_BY(reconnect_mutex_) = true;
+ std::thread handler_thread_;
+ std::condition_variable reconnect_cv_;
+ std::queue<ReconnectAttempt> reconnect_queue_ GUARDED_BY(reconnect_mutex_);
+
+ DISALLOW_COPY_AND_ASSIGN(ReconnectHandler);
+};
+
+void ReconnectHandler::Start() {
+ check_main_thread();
+ handler_thread_ = std::thread(&ReconnectHandler::Run, this);
+}
+
+void ReconnectHandler::Stop() {
+ check_main_thread();
+ {
+ std::lock_guard<std::mutex> lock(reconnect_mutex_);
+ running_ = false;
+ }
+ reconnect_cv_.notify_one();
+ handler_thread_.join();
+
+ // Drain the queue to free all resources.
+ std::lock_guard<std::mutex> lock(reconnect_mutex_);
+ while (!reconnect_queue_.empty()) {
+ ReconnectAttempt attempt = reconnect_queue_.front();
+ reconnect_queue_.pop();
+ remove_transport(attempt.transport);
+ }
+}
+
+void ReconnectHandler::TrackTransport(atransport* transport) {
+ check_main_thread();
+ {
+ std::lock_guard<std::mutex> lock(reconnect_mutex_);
+ if (!running_) return;
+ reconnect_queue_.emplace(ReconnectAttempt{
+ transport, std::chrono::system_clock::now() + ReconnectHandler::kDefaultTimeout,
+ ReconnectHandler::kMaxAttempts});
+ }
+ reconnect_cv_.notify_one();
+}
+
+void ReconnectHandler::Run() {
+ while (true) {
+ ReconnectAttempt attempt;
+ {
+ std::unique_lock<std::mutex> lock(reconnect_mutex_);
+ ScopedAssumeLocked assume_lock(reconnect_mutex_);
+
+ auto deadline = std::chrono::time_point<std::chrono::system_clock>::max();
+ if (!reconnect_queue_.empty()) deadline = reconnect_queue_.front().deadline;
+ reconnect_cv_.wait_until(lock, deadline, [&]() REQUIRES(reconnect_mutex_) {
+ return !running_ ||
+ (!reconnect_queue_.empty() && reconnect_queue_.front().deadline < deadline);
+ });
+
+ if (!running_) return;
+ attempt = reconnect_queue_.front();
+ reconnect_queue_.pop();
+ if (attempt.transport->kicked()) {
+ D("transport %s was kicked. giving up on it.", attempt.transport->serial);
+ remove_transport(attempt.transport);
+ continue;
+ }
+ }
+ D("attempting to reconnect %s", attempt.transport->serial);
+
+ if (!attempt.transport->Reconnect()) {
+ D("attempting to reconnect %s failed.", attempt.transport->serial);
+ if (attempt.attempts_left == 0) {
+ D("transport %s exceeded the number of retry attempts. giving up on it.",
+ attempt.transport->serial);
+ remove_transport(attempt.transport);
+ continue;
+ }
+
+ std::lock_guard<std::mutex> lock(reconnect_mutex_);
+ reconnect_queue_.emplace(ReconnectAttempt{
+ attempt.transport,
+ std::chrono::system_clock::now() + ReconnectHandler::kDefaultTimeout,
+ attempt.attempts_left - 1});
+ continue;
+ }
+
+ D("reconnection to %s succeeded.", attempt.transport->serial);
+ register_transport(attempt.transport);
+ }
+}
+
+static auto& reconnect_handler = *new ReconnectHandler();
+
} // namespace
TransportId NextTransportId() {
@@ -300,7 +427,7 @@
static int transport_registration_send = -1;
static int transport_registration_recv = -1;
-static fdevent transport_registration_fde;
+static fdevent* transport_registration_fde;
#if ADB_HOST
@@ -477,8 +604,6 @@
return 0;
}
-static void remove_transport(atransport*);
-
static void transport_registration_func(int _fd, unsigned ev, void*) {
tmsg m;
atransport* t;
@@ -515,8 +640,9 @@
/* don't create transport threads for inaccessible devices */
if (t->GetConnectionState() != kCsNoPerm) {
- /* initial references are the two threads */
- t->ref_count = 1;
+ // The connection gets a reference to the atransport. It will release it
+ // upon a read/write error.
+ t->ref_count++;
t->connection()->SetTransportName(t->serial_name());
t->connection()->SetReadCallback([t](Connection*, std::unique_ptr<apacket> p) {
if (!check_header(p.get(), t)) {
@@ -547,13 +673,20 @@
{
std::lock_guard<std::recursive_mutex> lock(transport_lock);
- pending_list.remove(t);
- transport_list.push_front(t);
+ auto it = std::find(pending_list.begin(), pending_list.end(), t);
+ if (it != pending_list.end()) {
+ pending_list.remove(t);
+ transport_list.push_front(t);
+ }
}
update_transports();
}
+void init_reconnect_handler(void) {
+ reconnect_handler.Start();
+}
+
void init_transport_registration(void) {
int s[2];
@@ -565,13 +698,13 @@
transport_registration_send = s[0];
transport_registration_recv = s[1];
- fdevent_install(&transport_registration_fde, transport_registration_recv,
- transport_registration_func, 0);
-
- fdevent_set(&transport_registration_fde, FDE_READ);
+ transport_registration_fde =
+ fdevent_create(transport_registration_recv, transport_registration_func, 0);
+ fdevent_set(transport_registration_fde, FDE_READ);
}
void kick_all_transports() {
+ reconnect_handler.Stop();
// To avoid only writing part of a packet to a transport after exit, kick all transports.
std::lock_guard<std::recursive_mutex> lock(transport_lock);
for (auto t : transport_list) {
@@ -601,15 +734,21 @@
}
static void transport_unref(atransport* t) {
+ check_main_thread();
CHECK(t != nullptr);
std::lock_guard<std::recursive_mutex> lock(transport_lock);
CHECK_GT(t->ref_count, 0u);
t->ref_count--;
if (t->ref_count == 0) {
- D("transport: %s unref (kicking and closing)", t->serial);
t->connection()->Stop();
- remove_transport(t);
+ if (t->IsTcpDevice() && !t->kicked()) {
+ D("transport: %s unref (attempting reconnection) %d", t->serial, t->kicked());
+ reconnect_handler.TrackTransport(t);
+ } else {
+ D("transport: %s unref (kicking and closing)", t->serial);
+ remove_transport(t);
+ }
} else {
D("transport: %s unref (count=%zu)", t->serial, t->ref_count);
}
@@ -781,9 +920,8 @@
}
void atransport::Kick() {
- if (!kicked_) {
- D("kicking transport %s", this->serial);
- kicked_ = true;
+ if (!kicked_.exchange(true)) {
+ D("kicking transport %p %s", this, this->serial);
this->connection()->Stop();
}
}
@@ -941,6 +1079,10 @@
connection_waitable_->SetConnectionEstablished(success);
}
+bool atransport::Reconnect() {
+ return reconnect_(this);
+}
+
#if ADB_HOST
// We use newline as our delimiter, make sure to never output it.
@@ -1021,8 +1163,9 @@
}
#endif // ADB_HOST
-int register_socket_transport(int s, const char* serial, int port, int local) {
- atransport* t = new atransport();
+int register_socket_transport(int s, const char* serial, int port, int local,
+ atransport::ReconnectCallback reconnect) {
+ atransport* t = new atransport(std::move(reconnect), kCsOffline);
if (!serial) {
char buf[32];
@@ -1103,7 +1246,7 @@
void register_usb_transport(usb_handle* usb, const char* serial, const char* devpath,
unsigned writeable) {
- atransport* t = new atransport((writeable ? kCsConnecting : kCsNoPerm));
+ 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);
diff --git a/adb/transport.h b/adb/transport.h
index e1cbc09..ae9cc02 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -198,20 +198,27 @@
// class in one go is a very large change. Given how bad our testing is,
// it's better to do this piece by piece.
- atransport(ConnectionState state = kCsConnecting)
+ using ReconnectCallback = std::function<bool(atransport*)>;
+
+ atransport(ReconnectCallback reconnect, ConnectionState state)
: id(NextTransportId()),
+ kicked_(false),
connection_state_(state),
connection_waitable_(std::make_shared<ConnectionWaitable>()),
- connection_(nullptr) {
+ connection_(nullptr),
+ reconnect_(std::move(reconnect)) {
// Initialize protocol to min version for compatibility with older versions.
// Version will be updated post-connect.
protocol_version = A_VERSION_MIN;
max_payload = MAX_PAYLOAD;
}
+ atransport(ConnectionState state = kCsOffline)
+ : atransport([](atransport*) { return false; }, state) {}
virtual ~atransport();
int Write(apacket* p);
void Kick();
+ bool kicked() const { return kicked_; }
// ConnectionState can be read by all threads, but can only be written in the main thread.
ConnectionState GetConnectionState() const;
@@ -286,8 +293,12 @@
// Gets a shared reference to the ConnectionWaitable.
std::shared_ptr<ConnectionWaitable> connection_waitable() { return connection_waitable_; }
+ // Attempts to reconnect with the underlying Connection. Returns true if the
+ // reconnection attempt succeeded.
+ bool Reconnect();
+
private:
- bool kicked_ = false;
+ std::atomic<bool> kicked_;
// A set of features transmitted in the banner with the initial connection.
// This is stored in the banner as 'features=feature0,feature1,etc'.
@@ -310,6 +321,9 @@
// The underlying connection object.
std::shared_ptr<Connection> connection_ GUARDED_BY(mutex_);
+ // A callback that will be invoked when the atransport needs to reconnect.
+ ReconnectCallback reconnect_;
+
std::mutex mutex_;
DISALLOW_COPY_AND_ASSIGN(atransport);
@@ -333,6 +347,7 @@
// Stops iteration and returns false if fn returns false, otherwise returns true.
bool iterate_transports(std::function<bool(const atransport*)> fn);
+void init_reconnect_handler(void);
void init_transport_registration(void);
void init_mdns_transport_discovery(void);
std::string list_transports(bool long_listing);
@@ -347,7 +362,8 @@
void connect_device(const std::string& address, std::string* response);
/* cause new transports to be init'd and added to the list */
-int register_socket_transport(int s, const char* serial, int port, int local);
+int register_socket_transport(int s, const char* serial, int port, int local,
+ atransport::ReconnectCallback reconnect);
// This should only be used for transports with connection_state == kCsNoPerm.
void unregister_usb_transport(usb_handle* usb);
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index e81f27c..181d666 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -68,28 +68,24 @@
return local_connect_arbitrary_ports(port - 1, port, &dummy) == 0;
}
-void connect_device(const std::string& address, std::string* response) {
- if (address.empty()) {
- *response = "empty address";
- return;
- }
-
+std::tuple<unique_fd, int, std::string> tcp_connect(const std::string& address,
+ std::string* response) {
std::string serial;
std::string host;
int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
if (!android::base::ParseNetAddress(address, &host, &port, &serial, response)) {
- return;
+ return std::make_tuple(unique_fd(), port, serial);
}
std::string error;
- int fd = network_connect(host.c_str(), port, SOCK_STREAM, 10, &error);
+ unique_fd fd(network_connect(host.c_str(), port, SOCK_STREAM, 10, &error));
if (fd == -1) {
*response = android::base::StringPrintf("unable to connect to %s: %s",
serial.c_str(), error.c_str());
- return;
+ return std::make_tuple(std::move(fd), port, serial);
}
- D("client: connected %s remote on fd %d", serial.c_str(), fd);
+ D("client: connected %s remote on fd %d", serial.c_str(), fd.get());
close_on_exec(fd);
disable_tcp_nagle(fd);
@@ -98,7 +94,38 @@
D("warning: failed to configure TCP keepalives (%s)", strerror(errno));
}
- int ret = register_socket_transport(fd, serial.c_str(), port, 0);
+ return std::make_tuple(std::move(fd), port, serial);
+}
+
+void connect_device(const std::string& address, std::string* response) {
+ if (address.empty()) {
+ *response = "empty address";
+ return;
+ }
+
+ unique_fd fd;
+ int port;
+ std::string serial;
+ std::tie(fd, port, serial) = tcp_connect(address, response);
+ auto reconnect = [address](atransport* t) {
+ std::string response;
+ unique_fd fd;
+ int port;
+ std::string serial;
+ std::tie(fd, port, serial) = tcp_connect(address, &response);
+ if (fd == -1) {
+ D("reconnect failed: %s", response.c_str());
+ return false;
+ }
+
+ // This invokes the part of register_socket_transport() that needs to be
+ // invoked if the atransport* has already been setup. This eventually
+ // calls atransport->SetConnection() with a newly created Connection*
+ // that will in turn send the CNXN packet.
+ return init_socket_transport(t, fd.release(), port, 0) >= 0;
+ };
+
+ int ret = register_socket_transport(fd.release(), serial.c_str(), port, 0, std::move(reconnect));
if (ret < 0) {
adb_close(fd);
if (ret == -EALREADY) {
@@ -135,7 +162,8 @@
close_on_exec(fd);
disable_tcp_nagle(fd);
std::string serial = getEmulatorSerialString(console_port);
- if (register_socket_transport(fd, serial.c_str(), adb_port, 1) == 0) {
+ if (register_socket_transport(fd, serial.c_str(), adb_port, 1,
+ [](atransport*) { return false; }) == 0) {
return 0;
}
adb_close(fd);
@@ -239,7 +267,8 @@
close_on_exec(fd);
disable_tcp_nagle(fd);
std::string serial = android::base::StringPrintf("host-%d", fd);
- if (register_socket_transport(fd, serial.c_str(), port, 1) != 0) {
+ if (register_socket_transport(fd, serial.c_str(), port, 1,
+ [](atransport*) { return false; }) != 0) {
adb_close(fd);
}
}
@@ -338,7 +367,8 @@
/* Host is connected. Register the transport, and start the
* exchange. */
std::string serial = android::base::StringPrintf("host-%d", fd);
- if (register_socket_transport(fd, serial.c_str(), port, 1) != 0 ||
+ if (register_socket_transport(fd, serial.c_str(), port, 1,
+ [](atransport*) { return false; }) != 0 ||
!WriteFdExactly(fd, _start_req, strlen(_start_req))) {
adb_close(fd);
}
diff --git a/adb/types.h b/adb/types.h
index dd3e063..c6b3f07 100644
--- a/adb/types.h
+++ b/adb/types.h
@@ -17,11 +17,15 @@
#pragma once
#include <algorithm>
+#include <deque>
+#include <type_traits>
#include <utility>
+#include <vector>
#include <android-base/logging.h>
#include "sysdeps/memory.h"
+#include "sysdeps/uio.h"
// Essentially std::vector<char>, except without zero initialization or reallocation.
struct Block {
@@ -130,34 +134,205 @@
payload_type payload;
};
-struct Range {
- explicit Range(apacket::payload_type data) : data_(std::move(data)) {}
+struct IOVector {
+ using value_type = char;
+ using block_type = Block;
+ using size_type = size_t;
- Range(const Range& copy) = delete;
- Range& operator=(const Range& copy) = delete;
+ IOVector() {}
- Range(Range&& move) = default;
- Range& operator=(Range&& move) = default;
+ explicit IOVector(std::unique_ptr<block_type> block) {
+ append(std::move(block));
+ }
- size_t size() const { return data_.size() - begin_offset_ - end_offset_; };
+ IOVector(const IOVector& copy) = delete;
+ IOVector(IOVector&& move) : IOVector() {
+ *this = std::move(move);
+ }
+
+ IOVector& operator=(const IOVector& copy) = delete;
+ IOVector& operator=(IOVector&& move) {
+ chain_ = std::move(move.chain_);
+ chain_length_ = move.chain_length_;
+ begin_offset_ = move.begin_offset_;
+ end_offset_ = move.end_offset_;
+
+ move.chain_.clear();
+ move.chain_length_ = 0;
+ move.begin_offset_ = 0;
+ move.end_offset_ = 0;
+
+ return *this;
+ }
+
+ size_type size() const { return chain_length_ - begin_offset_ - end_offset_; }
bool empty() const { return size() == 0; }
- void drop_front(size_t n) {
- CHECK_GE(size(), n);
- begin_offset_ += n;
+ void clear() {
+ chain_length_ = 0;
+ begin_offset_ = 0;
+ end_offset_ = 0;
+ chain_.clear();
}
- void drop_end(size_t n) {
- CHECK_GE(size(), n);
- end_offset_ += n;
+ // Split the first |len| bytes out of this chain into its own.
+ IOVector take_front(size_type len) {
+ IOVector head;
+
+ if (len == 0) {
+ return head;
+ }
+ CHECK_GE(size(), len);
+
+ std::shared_ptr<const block_type> first_block = chain_.front();
+ CHECK_GE(first_block->size(), begin_offset_);
+ head.append_shared(std::move(first_block));
+ head.begin_offset_ = begin_offset_;
+
+ while (head.size() < len) {
+ pop_front_block();
+ CHECK(!chain_.empty());
+
+ head.append_shared(chain_.front());
+ }
+
+ if (head.size() == len) {
+ // Head takes full ownership of the last block it took.
+ head.end_offset_ = 0;
+ begin_offset_ = 0;
+ pop_front_block();
+ } else {
+ // Head takes partial ownership of the last block it took.
+ size_t bytes_taken = head.size() - len;
+ head.end_offset_ = bytes_taken;
+ CHECK_GE(chain_.front()->size(), bytes_taken);
+ begin_offset_ = chain_.front()->size() - bytes_taken;
+ }
+
+ return head;
}
- char* data() { return &data_[0] + begin_offset_; }
+ // Add a nonempty block to the chain.
+ // The end of the chain must be a complete block (i.e. end_offset_ == 0).
+ void append(std::unique_ptr<const block_type> block) {
+ CHECK_NE(0ULL, block->size());
+ CHECK_EQ(0ULL, end_offset_);
+ chain_length_ += block->size();
+ chain_.emplace_back(std::move(block));
+ }
- apacket::payload_type::iterator begin() { return data_.begin() + begin_offset_; }
- apacket::payload_type::iterator end() { return data_.end() - end_offset_; }
+ void append(block_type&& block) { append(std::make_unique<block_type>(std::move(block))); }
- apacket::payload_type data_;
+ void trim_front() {
+ if (begin_offset_ == 0) {
+ return;
+ }
+
+ const block_type* first_block = chain_.front().get();
+ auto copy = std::make_unique<block_type>(first_block->size() - begin_offset_);
+ memcpy(copy->data(), first_block->data() + begin_offset_, copy->size());
+ chain_.front() = std::move(copy);
+
+ chain_length_ -= begin_offset_;
+ begin_offset_ = 0;
+ }
+
+ private:
+ // append, except takes a shared_ptr.
+ // Private to prevent exterior mutation of blocks.
+ void append_shared(std::shared_ptr<const block_type> block) {
+ CHECK_NE(0ULL, block->size());
+ CHECK_EQ(0ULL, end_offset_);
+ chain_length_ += block->size();
+ chain_.emplace_back(std::move(block));
+ }
+
+ // Drop the front block from the chain, and update chain_length_ appropriately.
+ void pop_front_block() {
+ chain_length_ -= chain_.front()->size();
+ begin_offset_ = 0;
+ chain_.pop_front();
+ }
+
+ // Iterate over the blocks with a callback with an operator()(const char*, size_t).
+ template <typename Fn>
+ void iterate_blocks(Fn&& callback) const {
+ if (chain_.size() == 0) {
+ return;
+ }
+
+ for (size_t i = 0; i < chain_.size(); ++i) {
+ const std::shared_ptr<const block_type>& block = chain_.at(i);
+ const char* begin = block->data();
+ size_t length = block->size();
+
+ // Note that both of these conditions can be true if there's only one block.
+ if (i == 0) {
+ CHECK_GE(block->size(), begin_offset_);
+ begin += begin_offset_;
+ length -= begin_offset_;
+ }
+
+ if (i == chain_.size() - 1) {
+ CHECK_GE(length, end_offset_);
+ length -= end_offset_;
+ }
+
+ callback(begin, length);
+ }
+ }
+
+ public:
+ // Copy all of the blocks into a single block.
+ template <typename CollectionType = block_type>
+ CollectionType coalesce() const {
+ CollectionType result;
+ if (size() == 0) {
+ return result;
+ }
+
+ result.resize(size());
+
+ size_t offset = 0;
+ iterate_blocks([&offset, &result](const char* data, size_t len) {
+ memcpy(&result[offset], data, len);
+ offset += len;
+ });
+
+ return result;
+ }
+
+ template <typename FunctionType>
+ auto coalesced(FunctionType&& f) const ->
+ typename std::result_of<FunctionType(const char*, size_t)>::type {
+ if (chain_.size() == 1) {
+ // If we only have one block, we can use it directly.
+ return f(chain_.front()->data() + begin_offset_, size());
+ } else {
+ // Otherwise, copy to a single block.
+ auto data = coalesce();
+ return f(data.data(), data.size());
+ }
+ }
+
+ // Get a list of iovecs that can be used to write out all of the blocks.
+ std::vector<adb_iovec> iovecs() const {
+ std::vector<adb_iovec> result;
+ iterate_blocks([&result](const char* data, size_t len) {
+ adb_iovec iov;
+ iov.iov_base = const_cast<char*>(data);
+ iov.iov_len = len;
+ result.emplace_back(iov);
+ });
+
+ return result;
+ }
+
+ private:
+ // Total length of all of the blocks in the chain.
+ size_t chain_length_ = 0;
+
size_t begin_offset_ = 0;
size_t end_offset_ = 0;
+ std::deque<std::shared_ptr<const block_type>> chain_;
};
diff --git a/adb/types_test.cpp b/adb/types_test.cpp
new file mode 100644
index 0000000..31ab90a
--- /dev/null
+++ b/adb/types_test.cpp
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2018 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 "sysdeps/memory.h"
+#include "types.h"
+
+static std::unique_ptr<IOVector::block_type> create_block(const std::string& string) {
+ return std::make_unique<IOVector::block_type>(string.begin(), string.end());
+}
+
+static std::unique_ptr<IOVector::block_type> create_block(char value, size_t len) {
+ auto block = std::make_unique<IOVector::block_type>();
+ block->resize(len);
+ memset(&(*block)[0], value, len);
+ return block;
+}
+
+template <typename T>
+static std::unique_ptr<IOVector::block_type> copy_block(T&& block) {
+ auto copy = std::make_unique<IOVector::block_type>();
+ copy->assign(block->begin(), block->end());
+ return copy;
+}
+
+TEST(IOVector, empty) {
+ // Empty IOVector.
+ IOVector bc;
+ CHECK_EQ(0ULL, bc.coalesce().size());
+}
+
+TEST(IOVector, single_block) {
+ // A single block.
+ auto block = create_block('x', 100);
+ IOVector bc;
+ bc.append(copy_block(block));
+ ASSERT_EQ(100ULL, bc.size());
+ auto coalesced = bc.coalesce();
+ ASSERT_EQ(*block, coalesced);
+}
+
+TEST(IOVector, single_block_split) {
+ // One block split.
+ IOVector bc;
+ bc.append(create_block("foobar"));
+ IOVector foo = bc.take_front(3);
+ ASSERT_EQ(3ULL, foo.size());
+ ASSERT_EQ(3ULL, bc.size());
+ ASSERT_EQ(*create_block("foo"), foo.coalesce());
+ ASSERT_EQ(*create_block("bar"), bc.coalesce());
+}
+
+TEST(IOVector, aligned_split) {
+ IOVector bc;
+ bc.append(create_block("foo"));
+ bc.append(create_block("bar"));
+ bc.append(create_block("baz"));
+ ASSERT_EQ(9ULL, bc.size());
+
+ IOVector foo = bc.take_front(3);
+ ASSERT_EQ(3ULL, foo.size());
+ ASSERT_EQ(*create_block("foo"), foo.coalesce());
+
+ IOVector bar = bc.take_front(3);
+ ASSERT_EQ(3ULL, bar.size());
+ ASSERT_EQ(*create_block("bar"), bar.coalesce());
+
+ IOVector baz = bc.take_front(3);
+ ASSERT_EQ(3ULL, baz.size());
+ ASSERT_EQ(*create_block("baz"), baz.coalesce());
+
+ ASSERT_EQ(0ULL, bc.size());
+}
+
+TEST(IOVector, misaligned_split) {
+ IOVector bc;
+ bc.append(create_block("foo"));
+ bc.append(create_block("bar"));
+ bc.append(create_block("baz"));
+ bc.append(create_block("qux"));
+ bc.append(create_block("quux"));
+
+ // Aligned left, misaligned right, across multiple blocks.
+ IOVector foob = bc.take_front(4);
+ ASSERT_EQ(4ULL, foob.size());
+ ASSERT_EQ(*create_block("foob"), foob.coalesce());
+
+ // Misaligned left, misaligned right, in one block.
+ IOVector a = bc.take_front(1);
+ ASSERT_EQ(1ULL, a.size());
+ ASSERT_EQ(*create_block("a"), a.coalesce());
+
+ // Misaligned left, misaligned right, across two blocks.
+ IOVector rba = bc.take_front(3);
+ ASSERT_EQ(3ULL, rba.size());
+ ASSERT_EQ(*create_block("rba"), rba.coalesce());
+
+ // Misaligned left, misaligned right, across three blocks.
+ IOVector zquxquu = bc.take_front(7);
+ ASSERT_EQ(7ULL, zquxquu.size());
+ ASSERT_EQ(*create_block("zquxquu"), zquxquu.coalesce());
+
+ ASSERT_EQ(1ULL, bc.size());
+ ASSERT_EQ(*create_block("x"), bc.coalesce());
+}
diff --git a/base/Android.bp b/base/Android.bp
index ec81f61..3d80d97 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -26,6 +26,7 @@
cc_library_headers {
name: "libbase_headers",
vendor_available: true,
+ recovery_available: true,
host_supported: true,
export_include_dirs: ["include"],
@@ -47,6 +48,7 @@
"file.cpp",
"logging.cpp",
"parsenetaddress.cpp",
+ "properties.cpp",
"quick_exit.cpp",
"stringprintf.cpp",
"strings.cpp",
@@ -57,9 +59,6 @@
shared_libs: ["liblog"],
target: {
android: {
- srcs: [
- "properties.cpp",
- ],
sanitize: {
misc_undefined: ["integer"],
},
@@ -94,6 +93,7 @@
name: "libbase",
defaults: ["libbase_defaults"],
vendor_available: true,
+ recovery_available: true,
host_supported: true,
vndk: {
enabled: true,
@@ -128,6 +128,7 @@
"parsedouble_test.cpp",
"parseint_test.cpp",
"parsenetaddress_test.cpp",
+ "properties_test.cpp",
"quick_exit_test.cpp",
"scopeguard_test.cpp",
"stringprintf_test.cpp",
@@ -137,7 +138,6 @@
],
target: {
android: {
- srcs: ["properties_test.cpp"],
sanitize: {
misc_undefined: ["integer"],
},
diff --git a/base/include/android-base/logging.h b/base/include/android-base/logging.h
index cc7aaf6..05a12e7 100644
--- a/base/include/android-base/logging.h
+++ b/base/include/android-base/logging.h
@@ -100,8 +100,17 @@
unsigned int, const char*)>;
using AbortFunction = std::function<void(const char*)>;
+// Loggers for use with InitLogging/SetLogger.
+
+// Log to the kernel log (dmesg).
void KernelLogger(LogId, LogSeverity, const char*, const char*, unsigned int, const char*);
+// Log to stderr in the full logcat format (with pid/tid/time/tag details).
void StderrLogger(LogId, LogSeverity, const char*, const char*, unsigned int, const char*);
+// Log just the message to stdout/stderr (without pid/tid/time/tag details).
+// The choice of stdout versus stderr is based on the severity.
+// Errors are also prefixed by the program name (as with err(3)/error(3)).
+// Useful for replacing printf(3)/perror(3)/err(3)/error(3) in command-line tools.
+void StdioLogger(LogId, LogSeverity, const char*, const char*, unsigned int, const char*);
void DefaultAborter(const char* abort_message);
diff --git a/base/include/android-base/properties.h b/base/include/android-base/properties.h
index 041586c..3a05143 100644
--- a/base/include/android-base/properties.h
+++ b/base/include/android-base/properties.h
@@ -19,10 +19,6 @@
#include <sys/cdefs.h>
-#if !defined(__BIONIC__)
-#error Only bionic supports system properties.
-#endif
-
#include <chrono>
#include <limits>
#include <string>
@@ -62,14 +58,18 @@
// Waits for the system property `key` to have the value `expected_value`.
// Times out after `relative_timeout`.
// Returns true on success, false on timeout.
+#if defined(__BIONIC__)
bool WaitForProperty(const std::string& key, const std::string& expected_value,
std::chrono::milliseconds relative_timeout = std::chrono::milliseconds::max());
+#endif
// Waits for the system property `key` to be created.
// Times out after `relative_timeout`.
// Returns true on success, false on timeout.
+#if defined(__BIONIC__)
bool WaitForPropertyCreation(const std::string& key, std::chrono::milliseconds relative_timeout =
std::chrono::milliseconds::max());
+#endif
} // namespace base
} // namespace android
diff --git a/base/include/android-base/test_utils.h b/base/include/android-base/test_utils.h
index b95fa07..b29676f 100644
--- a/base/include/android-base/test_utils.h
+++ b/base/include/android-base/test_utils.h
@@ -58,21 +58,33 @@
DISALLOW_COPY_AND_ASSIGN(TemporaryDir);
};
-class CapturedStderr {
+class CapturedStdFd {
public:
- CapturedStderr();
- ~CapturedStderr();
+ CapturedStdFd(int std_fd);
+ ~CapturedStdFd();
int fd() const;
+ std::string str();
private:
- void init();
- void reset();
+ void Init();
+ void Reset();
TemporaryFile temp_file_;
- int old_stderr_;
+ int std_fd_;
+ int old_fd_;
- DISALLOW_COPY_AND_ASSIGN(CapturedStderr);
+ DISALLOW_COPY_AND_ASSIGN(CapturedStdFd);
+};
+
+class CapturedStderr : public CapturedStdFd {
+ public:
+ CapturedStderr() : CapturedStdFd(STDERR_FILENO) {}
+};
+
+class CapturedStdout : public CapturedStdFd {
+ public:
+ CapturedStdout() : CapturedStdFd(STDOUT_FILENO) {}
};
#define ASSERT_MATCH(str, pattern) \
diff --git a/base/logging.cpp b/base/logging.cpp
index a33da22..978d56d 100644
--- a/base/logging.cpp
+++ b/base/logging.cpp
@@ -212,6 +212,16 @@
timestamp, getpid(), GetThreadId(), file, line, message);
}
+void StdioLogger(LogId, LogSeverity severity, const char* /*tag*/, const char* /*file*/,
+ unsigned int /*line*/, const char* message) {
+ if (severity >= WARNING) {
+ fflush(stdout);
+ fprintf(stderr, "%s: %s\n", getprogname(), message);
+ } else {
+ fprintf(stdout, "%s\n", message);
+ }
+}
+
void DefaultAborter(const char* abort_message) {
#ifdef __ANDROID__
android_set_abort_message(abort_message);
diff --git a/base/logging_test.cpp b/base/logging_test.cpp
index 5f689fa..75b4ea0 100644
--- a/base/logging_test.cpp
+++ b/base/logging_test.cpp
@@ -206,11 +206,9 @@
}
#endif
-static void CheckMessage(const CapturedStderr& cap, android::base::LogSeverity severity,
+static void CheckMessage(CapturedStderr& cap, android::base::LogSeverity severity,
const char* expected, const char* expected_tag = nullptr) {
- std::string output;
- ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET));
- android::base::ReadFdToString(cap.fd(), &output);
+ std::string output = cap.str();
// We can't usefully check the output of any of these on Windows because we
// don't have std::regex, but we can at least make sure we printed at least as
@@ -623,3 +621,23 @@
}
CheckMessage(cap, android::base::LogSeverity::INFO, expected_msg, expected_tag);
}
+
+TEST(logging, StdioLogger) {
+ std::string err_str;
+ std::string out_str;
+ {
+ CapturedStderr cap_err;
+ CapturedStdout cap_out;
+ android::base::SetLogger(android::base::StdioLogger);
+ LOG(INFO) << "out";
+ LOG(ERROR) << "err";
+ err_str = cap_err.str();
+ out_str = cap_out.str();
+ }
+
+ // For INFO we expect just the literal "out\n".
+ ASSERT_EQ("out\n", out_str) << out_str;
+ // Whereas ERROR logging includes the program name.
+ ASSERT_EQ(android::base::Basename(android::base::GetExecutablePath()) + ": err\n", err_str)
+ << err_str;
+}
diff --git a/base/properties.cpp b/base/properties.cpp
index 6cf43f9..d5a5918 100644
--- a/base/properties.cpp
+++ b/base/properties.cpp
@@ -14,16 +14,18 @@
* limitations under the License.
*/
-#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
-
#include "android-base/properties.h"
+#if defined(__BIONIC__)
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
#include <sys/system_properties.h>
#include <sys/_system_properties.h>
+#endif
#include <algorithm>
#include <chrono>
#include <limits>
+#include <map>
#include <string>
#include <android-base/parseint.h>
@@ -31,24 +33,6 @@
namespace android {
namespace base {
-std::string GetProperty(const std::string& key, const std::string& default_value) {
- const prop_info* pi = __system_property_find(key.c_str());
- if (pi == nullptr) return default_value;
-
- std::string property_value;
- __system_property_read_callback(pi,
- [](void* cookie, const char*, const char* value, unsigned) {
- auto property_value = reinterpret_cast<std::string*>(cookie);
- *property_value = value;
- },
- &property_value);
-
- // If the property exists but is empty, also return the default value.
- // Since we can't remove system properties, "empty" is traditionally
- // the same as "missing" (this was true for cutils' property_get).
- return property_value.empty() ? default_value : property_value;
-}
-
bool GetBoolProperty(const std::string& key, bool default_value) {
std::string value = GetProperty(key, "");
if (value == "1" || value == "y" || value == "yes" || value == "on" || value == "true") {
@@ -85,10 +69,43 @@
template uint32_t GetUintProperty(const std::string&, uint32_t, uint32_t);
template uint64_t GetUintProperty(const std::string&, uint64_t, uint64_t);
+#if !defined(__BIONIC__)
+static std::map<std::string, std::string>& g_properties = *new std::map<std::string, std::string>;
+static int __system_property_set(const char* key, const char* value) {
+ g_properties[key] = value;
+ return 0;
+}
+#endif
+
+std::string GetProperty(const std::string& key, const std::string& default_value) {
+ std::string property_value;
+#if defined(__BIONIC__)
+ const prop_info* pi = __system_property_find(key.c_str());
+ if (pi == nullptr) return default_value;
+
+ __system_property_read_callback(pi,
+ [](void* cookie, const char*, const char* value, unsigned) {
+ auto property_value = reinterpret_cast<std::string*>(cookie);
+ *property_value = value;
+ },
+ &property_value);
+#else
+ auto it = g_properties.find(key);
+ if (it == g_properties.end()) return default_value;
+ property_value = it->second;
+#endif
+ // If the property exists but is empty, also return the default value.
+ // Since we can't remove system properties, "empty" is traditionally
+ // the same as "missing" (this was true for cutils' property_get).
+ return property_value.empty() ? default_value : property_value;
+}
+
bool SetProperty(const std::string& key, const std::string& value) {
return (__system_property_set(key.c_str(), value.c_str()) == 0);
}
+#if defined(__BIONIC__)
+
struct WaitForPropertyData {
bool done;
const std::string* expected_value;
@@ -175,5 +192,7 @@
return (WaitForPropertyCreation(key, relative_timeout, start_time) != nullptr);
}
+#endif
+
} // namespace base
} // namespace android
diff --git a/base/properties_test.cpp b/base/properties_test.cpp
index de5f3dc..e7d4880 100644
--- a/base/properties_test.cpp
+++ b/base/properties_test.cpp
@@ -23,7 +23,9 @@
#include <string>
#include <thread>
-using namespace std::chrono_literals;
+#if !defined(_WIN32)
+using namespace std::literals;
+#endif
TEST(properties, smoke) {
android::base::SetProperty("debug.libbase.property_test", "hello");
@@ -126,6 +128,7 @@
TEST(properties, GetUintProperty_uint64_t) { CheckGetUintProperty<uint64_t>(); }
TEST(properties, WaitForProperty) {
+#if defined(__BIONIC__)
std::atomic<bool> flag{false};
std::thread thread([&]() {
std::this_thread::sleep_for(100ms);
@@ -138,9 +141,13 @@
flag = true;
ASSERT_TRUE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "b", 1s));
thread.join();
+#else
+ GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
+#endif
}
TEST(properties, WaitForProperty_timeout) {
+#if defined(__BIONIC__)
auto t0 = std::chrono::steady_clock::now();
ASSERT_FALSE(android::base::WaitForProperty("debug.libbase.WaitForProperty_timeout_test", "a",
200ms));
@@ -149,9 +156,13 @@
ASSERT_GE(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 200ms);
// Upper bounds on timing are inherently flaky, but let's try...
ASSERT_LT(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 600ms);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
+#endif
}
TEST(properties, WaitForProperty_MaxTimeout) {
+#if defined(__BIONIC__)
std::atomic<bool> flag{false};
std::thread thread([&]() {
android::base::SetProperty("debug.libbase.WaitForProperty_test", "a");
@@ -165,9 +176,13 @@
// Test that this does not immediately return false due to overflow issues with the timeout.
ASSERT_TRUE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "b"));
thread.join();
+#else
+ GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
+#endif
}
TEST(properties, WaitForProperty_NegativeTimeout) {
+#if defined(__BIONIC__)
std::atomic<bool> flag{false};
std::thread thread([&]() {
android::base::SetProperty("debug.libbase.WaitForProperty_test", "a");
@@ -181,9 +196,13 @@
// Assert that this immediately returns with a negative timeout
ASSERT_FALSE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "b", -100ms));
thread.join();
+#else
+ GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
+#endif
}
TEST(properties, WaitForPropertyCreation) {
+#if defined(__BIONIC__)
std::thread thread([&]() {
std::this_thread::sleep_for(100ms);
android::base::SetProperty("debug.libbase.WaitForPropertyCreation_test", "a");
@@ -192,9 +211,13 @@
ASSERT_TRUE(android::base::WaitForPropertyCreation(
"debug.libbase.WaitForPropertyCreation_test", 1s));
thread.join();
+#else
+ GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
+#endif
}
TEST(properties, WaitForPropertyCreation_timeout) {
+#if defined(__BIONIC__)
auto t0 = std::chrono::steady_clock::now();
ASSERT_FALSE(android::base::WaitForPropertyCreation(
"debug.libbase.WaitForPropertyCreation_timeout_test", 200ms));
@@ -203,4 +226,7 @@
ASSERT_GE(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 200ms);
// Upper bounds on timing are inherently flaky, but let's try...
ASSERT_LT(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 600ms);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
+#endif
}
diff --git a/base/test_utils.cpp b/base/test_utils.cpp
index 1619c21..5096369 100644
--- a/base/test_utils.cpp
+++ b/base/test_utils.cpp
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-#include "android-base/logging.h"
#include "android-base/test_utils.h"
#include <fcntl.h>
@@ -33,6 +32,9 @@
#include <string>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+
#ifdef _WIN32
int mkstemp(char* template_name) {
if (_mktemp(template_name) == nullptr) {
@@ -123,31 +125,38 @@
return (mkdtemp(path) != nullptr);
}
-CapturedStderr::CapturedStderr() : old_stderr_(-1) {
- init();
+CapturedStdFd::CapturedStdFd(int std_fd) : std_fd_(std_fd), old_fd_(-1) {
+ Init();
}
-CapturedStderr::~CapturedStderr() {
- reset();
+CapturedStdFd::~CapturedStdFd() {
+ Reset();
}
-int CapturedStderr::fd() const {
+int CapturedStdFd::fd() const {
return temp_file_.fd;
}
-void CapturedStderr::init() {
+std::string CapturedStdFd::str() {
+ std::string result;
+ CHECK_EQ(0, TEMP_FAILURE_RETRY(lseek(fd(), 0, SEEK_SET)));
+ android::base::ReadFdToString(fd(), &result);
+ return result;
+}
+
+void CapturedStdFd::Init() {
#if defined(_WIN32)
// On Windows, stderr is often buffered, so make sure it is unbuffered so
// that we can immediately read back what was written to stderr.
- CHECK_EQ(0, setvbuf(stderr, NULL, _IONBF, 0));
+ if (std_fd_ == STDERR_FILENO) CHECK_EQ(0, setvbuf(stderr, NULL, _IONBF, 0));
#endif
- old_stderr_ = dup(STDERR_FILENO);
- CHECK_NE(-1, old_stderr_);
- CHECK_NE(-1, dup2(fd(), STDERR_FILENO));
+ old_fd_ = dup(std_fd_);
+ CHECK_NE(-1, old_fd_);
+ CHECK_NE(-1, dup2(fd(), std_fd_));
}
-void CapturedStderr::reset() {
- CHECK_NE(-1, dup2(old_stderr_, STDERR_FILENO));
- CHECK_EQ(0, close(old_stderr_));
+void CapturedStdFd::Reset() {
+ CHECK_NE(-1, dup2(old_fd_, std_fd_));
+ CHECK_EQ(0, close(old_fd_));
// Note: cannot restore prior setvbuf() setting.
}
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 7c28b28..0b13662 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -17,6 +17,7 @@
cc_library_headers {
name: "libdebuggerd_common_headers",
export_include_dirs: ["common/include"],
+ recovery_available: true,
}
cc_library_shared {
@@ -67,6 +68,7 @@
cc_library_static {
name: "libdebuggerd_handler_core",
defaults: ["debuggerd_defaults"],
+ recovery_available: true,
srcs: ["handler/debuggerd_handler.cpp"],
header_libs: [
@@ -88,6 +90,7 @@
cc_library_static {
name: "libdebuggerd_handler",
defaults: ["debuggerd_defaults"],
+ recovery_available: true,
srcs: ["handler/debuggerd_fallback_nop.cpp"],
whole_static_libs: [
@@ -143,6 +146,7 @@
cc_library_static {
name: "libdebuggerd",
defaults: ["debuggerd_defaults"],
+ recovery_available: true,
srcs: [
"libdebuggerd/backtrace.cpp",
diff --git a/debuggerd/handler/debuggerd_fallback.cpp b/debuggerd/handler/debuggerd_fallback.cpp
index dea2e17..079a574 100644
--- a/debuggerd/handler/debuggerd_fallback.cpp
+++ b/debuggerd/handler/debuggerd_fallback.cpp
@@ -304,7 +304,16 @@
crash_mutex.lock();
if (lock_count++ > 0) {
- async_safe_format_log(ANDROID_LOG_ERROR, "libc", "recursed signal handler call, exiting");
+ async_safe_format_log(ANDROID_LOG_ERROR, "libc", "recursed signal handler call, aborting");
+ signal(SIGABRT, SIG_DFL);
+ raise(SIGABRT);
+ sigset_t sigset;
+ sigemptyset(&sigset);
+ sigaddset(&sigset, SIGABRT);
+ sigprocmask(SIG_UNBLOCK, &sigset, nullptr);
+
+ // Just in case...
+ async_safe_format_log(ANDROID_LOG_ERROR, "libc", "abort didn't exit, exiting");
_exit(1);
}
diff --git a/demangle/Android.bp b/demangle/Android.bp
index cf6abfd..fd79cf8 100644
--- a/demangle/Android.bp
+++ b/demangle/Android.bp
@@ -36,6 +36,7 @@
name: "libdemangle",
defaults: ["libdemangle_defaults"],
vendor_available: true,
+ recovery_available: true,
srcs: [
"Demangler.cpp",
diff --git a/diagnose_usb/Android.bp b/diagnose_usb/Android.bp
index a7ecf37..6bee28c 100644
--- a/diagnose_usb/Android.bp
+++ b/diagnose_usb/Android.bp
@@ -2,6 +2,7 @@
name: "libdiagnose_usb",
cflags: ["-Wall", "-Wextra", "-Werror"],
host_supported: true,
+ recovery_available: true,
target: {
windows: {
enabled: true,
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 9463cc9..5aa87d9 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -316,7 +316,8 @@
static int show_help() {
// clang-format off
fprintf(stdout,
-/* 1234567890123456789012345678901234567890123456789012345678901234567890123456 */
+// 1 2 3 4 5 6 7 8
+// 12345678901234567890123456789012345678901234567890123456789012345678901234567890
"usage: fastboot [OPTION...] COMMAND...\n"
"\n"
"flashing:\n"
@@ -324,8 +325,8 @@
" flashall Flash all partitions from $ANDROID_PRODUCT_OUT.\n"
" On A/B devices, flashed slot is set as active.\n"
" Secondary images may be flashed to inactive slot.\n"
- " flash PARTITION [FILENAME]\n"
- " Flash given partition only.\n"
+ " flash PARTITION [FILENAME] Flash given partition, using the image from\n"
+ " $ANDROID_PRODUCT_OUT if no filename is given.\n"
"\n"
"basics:\n"
" devices [-l] List devices in bootloader (-l: with device paths).\n"
@@ -507,7 +508,8 @@
static std::string make_temporary_directory() {
std::string result(make_temporary_template());
if (mkdtemp(&result[0]) == nullptr) {
- die("unable to create temporary directory: %s", strerror(errno));
+ die("unable to create temporary directory with template %s: %s",
+ result.c_str(), strerror(errno));
}
return result;
}
@@ -516,7 +518,8 @@
std::string path_template(make_temporary_template());
int fd = mkstemp(&path_template[0]);
if (fd == -1) {
- die("failed to create temporary file for %s: %s\n", what, strerror(errno));
+ die("failed to create temporary file for %s with template %s: %s\n",
+ path_template.c_str(), what, strerror(errno));
}
unlink(path_template.c_str());
return fd;
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index 05dba15..bc3b04b 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -33,6 +33,7 @@
cc_library_static {
name: "libfs_mgr",
defaults: ["fs_mgr_defaults"],
+ recovery_available: true,
export_include_dirs: ["include"],
include_dirs: ["system/vold"],
srcs: [
@@ -79,6 +80,7 @@
cc_library_static {
name: "libfstab",
vendor_available: true,
+ recovery_available: true,
defaults: ["fs_mgr_defaults"],
srcs: [
"fs_mgr_fstab.cpp",
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 472ab59..af4d6c1 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -686,6 +686,49 @@
return a;
}
+/* Extracts <device>s from the by-name symlinks specified in a fstab:
+ * /dev/block/<type>/<device>/by-name/<partition>
+ *
+ * <type> can be: platform, pci or vbd.
+ *
+ * For example, given the following entries in the input fstab:
+ * /dev/block/platform/soc/1da4000.ufshc/by-name/system
+ * /dev/block/pci/soc.0/f9824900.sdhci/by-name/vendor
+ * it returns a set { "soc/1da4000.ufshc", "soc.0/f9824900.sdhci" }.
+ */
+static std::set<std::string> extract_boot_devices(const fstab& fstab) {
+ std::set<std::string> boot_devices;
+
+ for (int i = 0; i < fstab.num_entries; i++) {
+ std::string blk_device(fstab.recs[i].blk_device);
+ // Skips blk_device that doesn't conform to the format.
+ if (!android::base::StartsWith(blk_device, "/dev/block") ||
+ android::base::StartsWith(blk_device, "/dev/block/by-name") ||
+ android::base::StartsWith(blk_device, "/dev/block/bootdevice/by-name")) {
+ continue;
+ }
+ // Skips non-by_name blk_device.
+ // /dev/block/<type>/<device>/by-name/<partition>
+ // ^ slash_by_name
+ auto slash_by_name = blk_device.find("/by-name");
+ if (slash_by_name == std::string::npos) continue;
+ blk_device.erase(slash_by_name); // erases /by-name/<partition>
+
+ // Erases /dev/block/, now we have <type>/<device>
+ blk_device.erase(0, std::string("/dev/block/").size());
+
+ // <type>/<device>
+ // ^ first_slash
+ auto first_slash = blk_device.find('/');
+ if (first_slash == std::string::npos) continue;
+
+ auto boot_device = blk_device.substr(first_slash + 1);
+ if (!boot_device.empty()) boot_devices.insert(std::move(boot_device));
+ }
+
+ return boot_devices;
+}
+
struct fstab *fs_mgr_read_fstab(const char *fstab_path)
{
FILE *fstab_file;
@@ -863,6 +906,23 @@
return nullptr;
}
+std::set<std::string> fs_mgr_get_boot_devices() {
+ // boot_devices can be specified in device tree.
+ std::string dt_value;
+ std::string file_name = get_android_dt_dir() + "/boot_devices";
+ if (read_dt_file(file_name, &dt_value)) {
+ auto boot_devices = android::base::Split(dt_value, ",");
+ return std::set<std::string>(boot_devices.begin(), boot_devices.end());
+ }
+
+ // Fallback to extract boot devices from fstab.
+ std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
+ fs_mgr_free_fstab);
+ if (fstab) return extract_boot_devices(*fstab);
+
+ return {};
+}
+
int fs_mgr_is_voldmanaged(const struct fstab_rec *fstab)
{
return fstab->fs_mgr_flags & MF_VOLDMANAGED;
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index 04ccfc5..d232cca 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -22,6 +22,7 @@
#include <stdint.h>
#include <stdio.h>
+#include <set>
#include <string>
/*
@@ -89,5 +90,6 @@
int fs_mgr_has_sysfs_path(const struct fstab_rec* fstab);
std::string fs_mgr_get_slot_suffix();
+std::set<std::string> fs_mgr_get_boot_devices();
#endif /* __CORE_FS_TAB_H */
diff --git a/init/Android.bp b/init/Android.bp
index a31c5a5..25877c0 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -100,6 +100,7 @@
"capabilities.cpp",
"descriptors.cpp",
"devices.cpp",
+ "epoll.cpp",
"firmware_handler.cpp",
"import_parser.cpp",
"init.cpp",
@@ -169,6 +170,7 @@
srcs: [
"devices_test.cpp",
"init_test.cpp",
+ "keychords_test.cpp",
"persistent_properties_test.cpp",
"property_service_test.cpp",
"property_type_test.cpp",
@@ -231,8 +233,11 @@
"action_parser.cpp",
"capabilities.cpp",
"descriptors.cpp",
+ "epoll.cpp",
+ "keychords.cpp",
"import_parser.cpp",
- "host_init_parser.cpp",
+ "host_import_parser.cpp",
+ "host_init_verifier.cpp",
"host_init_stubs.cpp",
"parser.cpp",
"rlimit_parser.cpp",
@@ -245,7 +250,10 @@
proto: {
type: "lite",
},
- generated_headers: ["generated_stub_builtin_function_map"],
+ generated_headers: [
+ "generated_stub_builtin_function_map",
+ "generated_android_ids"
+ ],
target: {
android: {
enabled: false,
diff --git a/init/README.md b/init/README.md
index c08b07a..550ef05 100644
--- a/init/README.md
+++ b/init/README.md
@@ -195,6 +195,10 @@
> This service will not automatically start with its class.
It must be explicitly started by name or by interface name.
+`enter_namespace <type> <path>`
+> Enters the namespace of type _type_ located at _path_. Only network namespaces are supported with
+ _type_ set to "net". Note that only one namespace of a given _type_ may be entered.
+
`file <path> <type>`
> Open a file path and pass its fd to the launched process. _type_ must be
"r", "w" or "rw". For native executables see libcutils
diff --git a/init/action.cpp b/init/action.cpp
index f782b51..11335ca 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -18,16 +18,11 @@
#include <android-base/chrono_utils.h>
#include <android-base/logging.h>
+#include <android-base/properties.h>
#include <android-base/strings.h>
#include "util.h"
-#if defined(__ANDROID__)
-#include <android-base/properties.h>
-#else
-#include "host_init_stubs.h"
-#endif
-
using android::base::Join;
namespace android {
diff --git a/init/action_parser.cpp b/init/action_parser.cpp
index a2c9671..8a4b518 100644
--- a/init/action_parser.cpp
+++ b/init/action_parser.cpp
@@ -16,16 +16,11 @@
#include "action_parser.h"
+#include <android-base/properties.h>
#include <android-base/strings.h>
#include "stable_properties.h"
-#if defined(__ANDROID__)
-#include <android-base/properties.h>
-#else
-#include "host_init_stubs.h"
-#endif
-
using android::base::GetBoolProperty;
using android::base::StartsWith;
diff --git a/init/devices.cpp b/init/devices.cpp
index 8d27f4f..ada1e28 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -329,10 +329,10 @@
<< partition_name_sanitized << "'";
}
links.emplace_back(link_path + "/by-name/" + partition_name_sanitized);
- }
-
- if (uevent.partition_num >= 0) {
- links.emplace_back(link_path + "/by-num/p" + std::to_string(uevent.partition_num));
+ // Adds symlink: /dev/block/by-name/<partition_name>.
+ if (boot_devices_.find(device) != boot_devices_.end()) {
+ links.emplace_back("/dev/block/by-name/" + partition_name_sanitized);
+ }
}
auto last_slash = uevent.path.rfind('/');
@@ -350,8 +350,14 @@
PLOG(ERROR) << "Failed to create directory " << Dirname(link);
}
- if (symlink(devpath.c_str(), link.c_str()) && errno != EEXIST) {
- PLOG(ERROR) << "Failed to symlink " << devpath << " to " << link;
+ if (symlink(devpath.c_str(), link.c_str())) {
+ if (errno != EEXIST) {
+ PLOG(ERROR) << "Failed to symlink " << devpath << " to " << link;
+ } else if (std::string link_path;
+ Readlink(link, &link_path) && link_path != devpath) {
+ PLOG(ERROR) << "Failed to symlink " << devpath << " to " << link
+ << ", which already links to: " << link_path;
+ }
}
}
}
@@ -415,16 +421,18 @@
DeviceHandler::DeviceHandler(std::vector<Permissions> dev_permissions,
std::vector<SysfsPermissions> sysfs_permissions,
- std::vector<Subsystem> subsystems, bool skip_restorecon)
+ std::vector<Subsystem> subsystems, std::set<std::string> boot_devices,
+ bool skip_restorecon)
: dev_permissions_(std::move(dev_permissions)),
sysfs_permissions_(std::move(sysfs_permissions)),
subsystems_(std::move(subsystems)),
+ boot_devices_(std::move(boot_devices)),
skip_restorecon_(skip_restorecon),
sysfs_mount_point_("/sys") {}
DeviceHandler::DeviceHandler()
: DeviceHandler(std::vector<Permissions>{}, std::vector<SysfsPermissions>{},
- std::vector<Subsystem>{}, false) {}
+ std::vector<Subsystem>{}, std::set<std::string>{}, false) {}
} // namespace init
} // namespace android
diff --git a/init/devices.h b/init/devices.h
index 1f8f1e8..f9035da 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -21,6 +21,7 @@
#include <sys/types.h>
#include <algorithm>
+#include <set>
#include <string>
#include <vector>
@@ -103,8 +104,8 @@
DeviceHandler();
DeviceHandler(std::vector<Permissions> dev_permissions,
- std::vector<SysfsPermissions> sysfs_permissions,
- std::vector<Subsystem> subsystems, bool skip_restorecon);
+ std::vector<SysfsPermissions> sysfs_permissions, std::vector<Subsystem> subsystems,
+ std::set<std::string> boot_devices, bool skip_restorecon);
~DeviceHandler(){};
void HandleDeviceEvent(const Uevent& uevent);
@@ -125,6 +126,7 @@
std::vector<Permissions> dev_permissions_;
std::vector<SysfsPermissions> sysfs_permissions_;
std::vector<Subsystem> subsystems_;
+ std::set<std::string> boot_devices_;
bool skip_restorecon_;
std::string sysfs_mount_point_;
};
diff --git a/init/devices_test.cpp b/init/devices_test.cpp
index eba00cb..d658f4d 100644
--- a/init/devices_test.cpp
+++ b/init/devices_test.cpp
@@ -84,7 +84,6 @@
};
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",
};
@@ -100,7 +99,6 @@
.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",
};
diff --git a/init/epoll.cpp b/init/epoll.cpp
new file mode 100644
index 0000000..4bca09e
--- /dev/null
+++ b/init/epoll.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2018 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 "epoll.h"
+
+#include <sys/epoll.h>
+
+#include <chrono>
+#include <functional>
+#include <map>
+
+namespace android {
+namespace init {
+
+Epoll::Epoll() {}
+
+Result<Success> Epoll::Open() {
+ if (epoll_fd_ >= 0) return Success();
+ epoll_fd_.reset(epoll_create1(EPOLL_CLOEXEC));
+
+ if (epoll_fd_ == -1) {
+ return ErrnoError() << "epoll_create1 failed";
+ }
+ return Success();
+}
+
+Result<Success> Epoll::RegisterHandler(int fd, std::function<void()> handler) {
+ auto [it, inserted] = epoll_handlers_.emplace(fd, std::move(handler));
+ if (!inserted) {
+ return Error() << "Cannot specify two epoll handlers for a given FD";
+ }
+ epoll_event ev;
+ ev.events = EPOLLIN;
+ // std::map's iterators do not get invalidated until erased, so we use the
+ // pointer to the std::function in the map directly for epoll_ctl.
+ ev.data.ptr = reinterpret_cast<void*>(&it->second);
+ if (epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, fd, &ev) == -1) {
+ Result<Success> result = ErrnoError() << "epoll_ctl failed to add fd";
+ epoll_handlers_.erase(fd);
+ return result;
+ }
+ return Success();
+}
+
+Result<Success> Epoll::UnregisterHandler(int fd) {
+ if (epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, fd, nullptr) == -1) {
+ return ErrnoError() << "epoll_ctl failed to remove fd";
+ }
+ if (epoll_handlers_.erase(fd) != 1) {
+ return Error() << "Attempting to remove epoll handler for FD without an existing handler";
+ }
+ return Success();
+}
+
+Result<Success> Epoll::Wait(std::optional<std::chrono::milliseconds> timeout) {
+ int timeout_ms = -1;
+ if (timeout && timeout->count() < INT_MAX) {
+ timeout_ms = timeout->count();
+ }
+ epoll_event ev;
+ auto nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd_, &ev, 1, timeout_ms));
+ if (nr == -1) {
+ return ErrnoError() << "epoll_wait failed";
+ } else if (nr == 1) {
+ std::invoke(*reinterpret_cast<std::function<void()>*>(ev.data.ptr));
+ }
+ return Success();
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/epoll.h b/init/epoll.h
new file mode 100644
index 0000000..85a791c
--- /dev/null
+++ b/init/epoll.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2018 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_EPOLL_H
+#define _INIT_EPOLL_H
+
+#include <chrono>
+#include <functional>
+#include <map>
+#include <optional>
+
+#include <android-base/unique_fd.h>
+
+#include "result.h"
+
+namespace android {
+namespace init {
+
+class Epoll {
+ public:
+ Epoll();
+
+ Result<Success> Open();
+ Result<Success> RegisterHandler(int fd, std::function<void()> handler);
+ Result<Success> UnregisterHandler(int fd);
+ Result<Success> Wait(std::optional<std::chrono::milliseconds> timeout);
+
+ private:
+ android::base::unique_fd epoll_fd_;
+ std::map<int, std::function<void()>> epoll_handlers_;
+};
+
+} // namespace init
+} // namespace android
+
+#endif
diff --git a/init/host_import_parser.cpp b/init/host_import_parser.cpp
new file mode 100644
index 0000000..faf6fc1
--- /dev/null
+++ b/init/host_import_parser.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2018 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 "host_import_parser.h"
+
+#include <android-base/strings.h>
+
+using android::base::StartsWith;
+
+namespace android {
+namespace init {
+
+Result<Success> HostImportParser::ParseSection(std::vector<std::string>&& args,
+ const std::string& filename, int line) {
+ if (args.size() != 2) {
+ return Error() << "single argument needed for import\n";
+ }
+
+ auto import_path = args[1];
+
+ if (StartsWith(import_path, "/system") || StartsWith(import_path, "/product") ||
+ StartsWith(import_path, "/odm") || StartsWith(import_path, "/vendor")) {
+ import_path = out_dir_ + "/" + import_path;
+ } else {
+ import_path = out_dir_ + "/root/" + import_path;
+ }
+
+ return ImportParser::ParseSection({"import", import_path}, filename, line);
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/host_import_parser.h b/init/host_import_parser.h
new file mode 100644
index 0000000..e2980b2
--- /dev/null
+++ b/init/host_import_parser.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include "import_parser.h"
+#include "parser.h"
+
+namespace android {
+namespace init {
+
+class HostImportParser : public ImportParser {
+ public:
+ HostImportParser(const std::string& out_dir, Parser* parser)
+ : ImportParser(parser), out_dir_(out_dir) {}
+ Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
+ int line) override;
+
+ private:
+ std::string out_dir_;
+};
+
+} // namespace init
+} // namespace android
diff --git a/init/host_init_parser.cpp b/init/host_init_parser.cpp
deleted file mode 100644
index 5232b7e..0000000
--- a/init/host_init_parser.cpp
+++ /dev/null
@@ -1,82 +0,0 @@
-//
-// Copyright (C) 2018 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 <pwd.h>
-
-#include <android-base/logging.h>
-
-#include "action.h"
-#include "action_manager.h"
-#include "action_parser.h"
-#include "parser.h"
-#include "result.h"
-#include "service.h"
-
-// The host passwd file won't have the Android entries, so we fake success here.
-passwd* getpwnam(const char* login) { // NOLINT: implementing bad function.
- char dummy_buf[] = "dummy";
- static passwd dummy_passwd = {
- .pw_name = dummy_buf,
- .pw_dir = dummy_buf,
- .pw_shell = dummy_buf,
- .pw_uid = 123,
- .pw_gid = 123,
- };
- return &dummy_passwd;
-}
-
-namespace android {
-namespace init {
-
-static Result<Success> do_stub(const BuiltinArguments& args) {
- return Success();
-}
-
-#include "generated_stub_builtin_function_map.h"
-
-int main(int argc, char** argv) {
- android::base::InitLogging(argv, &android::base::StderrLogger);
- if (argc != 2) {
- LOG(ERROR) << "Usage: " << argv[0] << " <init file to parse>";
- return -1;
- }
- const BuiltinFunctionMap function_map;
- Action::set_function_map(&function_map);
- ActionManager& am = ActionManager::GetInstance();
- ServiceList& sl = ServiceList::GetInstance();
- Parser parser;
- parser.AddSectionParser("service", std::make_unique<ServiceParser>(&sl, nullptr));
- parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, nullptr));
-
- size_t num_errors = 0;
- if (!parser.ParseConfig(argv[1], &num_errors)) {
- LOG(ERROR) << "Failed to find script";
- return -1;
- }
- if (num_errors > 0) {
- LOG(ERROR) << "Parse failed with " << num_errors << " errors";
- return -1;
- }
- LOG(INFO) << "Parse success!";
- return 0;
-}
-
-} // namespace init
-} // namespace android
-
-int main(int argc, char** argv) {
- android::init::main(argc, argv);
-}
diff --git a/init/host_init_stubs.cpp b/init/host_init_stubs.cpp
index 4451ac8..2352fc7 100644
--- a/init/host_init_stubs.cpp
+++ b/init/host_init_stubs.cpp
@@ -16,33 +16,25 @@
#include "host_init_stubs.h"
+#include <android-base/properties.h>
+
// unistd.h
int setgroups(size_t __size, const gid_t* __list) {
return 0;
}
namespace android {
-namespace base {
-
-std::string GetProperty(const std::string&, const std::string& default_value) {
- return default_value;
-}
-
-bool GetBoolProperty(const std::string&, bool default_value) {
- return default_value;
-}
-
-} // namespace base
-} // namespace android
-
-namespace android {
namespace init {
// init.h
std::string default_console = "/dev/console";
// property_service.h
-uint32_t (*property_set)(const std::string& name, const std::string& value) = nullptr;
+uint32_t SetProperty(const std::string& key, const std::string& value) {
+ android::base::SetProperty(key, value);
+ return 0;
+}
+uint32_t (*property_set)(const std::string& name, const std::string& value) = SetProperty;
uint32_t HandlePropertySet(const std::string&, const std::string&, const std::string&, const ucred&,
std::string*) {
return 0;
diff --git a/init/host_init_stubs.h b/init/host_init_stubs.h
index bb241af..f0e1f07 100644
--- a/init/host_init_stubs.h
+++ b/init/host_init_stubs.h
@@ -29,16 +29,6 @@
// unistd.h
int setgroups(size_t __size, const gid_t* __list);
-// android-base/properties.h
-namespace android {
-namespace base {
-
-std::string GetProperty(const std::string& key, const std::string& default_value);
-bool GetBoolProperty(const std::string& key, bool default_value);
-
-} // namespace base
-} // namespace android
-
namespace android {
namespace init {
diff --git a/init/host_init_verifier.cpp b/init/host_init_verifier.cpp
new file mode 100644
index 0000000..d6884af
--- /dev/null
+++ b/init/host_init_verifier.cpp
@@ -0,0 +1,162 @@
+//
+// Copyright (C) 2018 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 <pwd.h>
+#include <stdio.h>
+
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
+
+#include "action.h"
+#include "action_manager.h"
+#include "action_parser.h"
+#include "host_import_parser.h"
+#include "host_init_stubs.h"
+#include "parser.h"
+#include "result.h"
+#include "service.h"
+
+#define EXCLUDE_FS_CONFIG_STRUCTURES
+#include "generated_android_ids.h"
+
+using namespace std::literals;
+
+using android::base::ParseInt;
+using android::base::ReadFileToString;
+using android::base::Split;
+
+static std::string out_dir;
+
+static std::vector<std::pair<std::string, int>> GetVendorPasswd() {
+ std::string passwd;
+ if (!ReadFileToString(out_dir + "/vendor/etc/passwd", &passwd)) {
+ return {};
+ }
+
+ std::vector<std::pair<std::string, int>> result;
+ auto passwd_lines = Split(passwd, "\n");
+ for (const auto& line : passwd_lines) {
+ auto split_line = Split(line, ":");
+ if (split_line.size() < 3) {
+ continue;
+ }
+ int uid = 0;
+ if (!ParseInt(split_line[2], &uid)) {
+ continue;
+ }
+ result.emplace_back(split_line[0], uid);
+ }
+ return result;
+}
+
+passwd* getpwnam(const char* login) { // NOLINT: implementing bad function.
+ // This isn't thread safe, but that's okay for our purposes.
+ static char static_name[32] = "";
+ static char static_dir[32] = "/";
+ static char static_shell[32] = "/system/bin/sh";
+ static passwd static_passwd = {
+ .pw_name = static_name,
+ .pw_dir = static_dir,
+ .pw_shell = static_shell,
+ .pw_uid = 0,
+ .pw_gid = 0,
+ };
+
+ for (size_t n = 0; n < android_id_count; ++n) {
+ if (!strcmp(android_ids[n].name, login)) {
+ snprintf(static_name, sizeof(static_name), "%s", android_ids[n].name);
+ static_passwd.pw_uid = android_ids[n].aid;
+ static_passwd.pw_gid = android_ids[n].aid;
+ return &static_passwd;
+ }
+ }
+
+ static const auto vendor_passwd = GetVendorPasswd();
+
+ for (const auto& [name, uid] : vendor_passwd) {
+ if (name == login) {
+ snprintf(static_name, sizeof(static_name), "%s", name.c_str());
+ static_passwd.pw_uid = uid;
+ static_passwd.pw_gid = uid;
+ return &static_passwd;
+ }
+ }
+
+ errno = ENOENT;
+ return nullptr;
+}
+
+namespace android {
+namespace init {
+
+static Result<Success> do_stub(const BuiltinArguments& args) {
+ return Success();
+}
+
+#include "generated_stub_builtin_function_map.h"
+
+int main(int argc, char** argv) {
+ android::base::InitLogging(argv, &android::base::StdioLogger);
+ android::base::SetMinimumLogSeverity(android::base::ERROR);
+ if (argc != 3) {
+ LOG(ERROR) << "Usage: " << argv[0] << " <out directory> <properties>";
+ return -1;
+ }
+
+ out_dir = argv[1];
+
+ auto properties = Split(argv[2], ",");
+ for (const auto& property : properties) {
+ auto split_property = Split(property, "=");
+ if (split_property.size() != 2) {
+ continue;
+ }
+ property_set(split_property[0], split_property[1]);
+ }
+
+ const BuiltinFunctionMap function_map;
+ Action::set_function_map(&function_map);
+ ActionManager& am = ActionManager::GetInstance();
+ ServiceList& sl = ServiceList::GetInstance();
+ Parser parser;
+ parser.AddSectionParser("service", std::make_unique<ServiceParser>(&sl, nullptr));
+ parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, nullptr));
+ parser.AddSectionParser("import", std::make_unique<HostImportParser>(out_dir, &parser));
+
+ if (!parser.ParseConfig(argv[1] + "/root/init.rc"s)) {
+ LOG(ERROR) << "Failed to find root init.rc script";
+ return -1;
+ }
+ if (parser.parse_error_count() > 0) {
+ LOG(ERROR) << "Init script parsing failed with " << parser.parse_error_count() << " errors";
+ return -1;
+ }
+ return 0;
+}
+
+} // namespace init
+} // namespace android
+
+int main(int argc, char** argv) {
+ android::init::main(argc, argv);
+}
diff --git a/init/import_parser.cpp b/init/import_parser.cpp
index e335fd1..fb3185e 100644
--- a/init/import_parser.cpp
+++ b/init/import_parser.cpp
@@ -41,14 +41,15 @@
return Success();
}
+Result<Success> ImportParser::ParseLineSection(std::vector<std::string>&&, int) {
+ return Error() << "Unexpected line found after import statement";
+}
+
void ImportParser::EndFile() {
auto current_imports = std::move(imports_);
imports_.clear();
for (const auto& [import, line_num] : current_imports) {
- if (!parser_->ParseConfig(import)) {
- PLOG(ERROR) << filename_ << ": " << line_num << ": Could not import file '" << import
- << "'";
- }
+ parser_->ParseConfig(import);
}
}
diff --git a/init/import_parser.h b/init/import_parser.h
index 5a2f894..7bc72e6 100644
--- a/init/import_parser.h
+++ b/init/import_parser.h
@@ -30,6 +30,7 @@
ImportParser(Parser* parser) : parser_(parser) {}
Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
int line) override;
+ Result<Success> ParseLineSection(std::vector<std::string>&&, int) override;
void EndFile() override;
private:
diff --git a/init/init.cpp b/init/init.cpp
index 645184b..82648d9 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -24,7 +24,6 @@
#include <signal.h>
#include <stdlib.h>
#include <string.h>
-#include <sys/epoll.h>
#include <sys/mount.h>
#include <sys/signalfd.h>
#include <sys/sysmacros.h>
@@ -48,6 +47,7 @@
#include <selinux/android.h>
#include "action_parser.h"
+#include "epoll.h"
#include "import_parser.h"
#include "init_first_stage.h"
#include "keychords.h"
@@ -61,6 +61,7 @@
#include "util.h"
#include "watchdogd.h"
+using namespace std::chrono_literals;
using namespace std::string_literals;
using android::base::boot_clock;
@@ -79,7 +80,6 @@
std::string default_console = "/dev/console";
-static int epoll_fd = -1;
static int signal_fd = -1;
static std::unique_ptr<Timer> waiting_for_prop(nullptr);
@@ -131,34 +131,6 @@
}
}
-static std::map<int, std::function<void()>> epoll_handlers;
-
-void register_epoll_handler(int fd, std::function<void()> handler) {
- auto[it, inserted] = epoll_handlers.emplace(fd, std::move(handler));
- if (!inserted) {
- LOG(ERROR) << "Cannot specify two epoll handlers for a given FD";
- return;
- }
- epoll_event ev;
- ev.events = EPOLLIN;
- // std::map's iterators do not get invalidated until erased, so we use the pointer to the
- // std::function in the map directly for epoll_ctl.
- ev.data.ptr = reinterpret_cast<void*>(&it->second);
- if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) {
- PLOG(ERROR) << "epoll_ctl failed to add fd";
- epoll_handlers.erase(fd);
- }
-}
-
-void unregister_epoll_handler(int fd) {
- if (epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, nullptr) == -1) {
- PLOG(ERROR) << "epoll_ctl failed to remove fd";
- }
- if (epoll_handlers.erase(fd) != 1) {
- LOG(ERROR) << "Attempting to remove epoll handler for FD without an existing handler";
- }
-}
-
bool start_waiting_for_property(const char *name, const char *value)
{
if (waiting_for_prop) {
@@ -343,11 +315,6 @@
return Success();
}
-static Result<Success> KeychordInitAction(const BuiltinArguments& args) {
- KeychordInit();
- return Success();
-}
-
static Result<Success> console_init_action(const BuiltinArguments& args) {
std::string console = GetProperty("ro.boot.console", "");
if (!console.empty()) {
@@ -550,7 +517,7 @@
}
}
-static void InstallSignalFdHandler() {
+static void InstallSignalFdHandler(Epoll* epoll) {
// Applying SA_NOCLDSTOP to a defaulted SIGCHLD handler prevents the signalfd from receiving
// SIGCHLD when a child process stops or continues (b/77867680#comment9).
const struct sigaction act { .sa_handler = SIG_DFL, .sa_flags = SA_NOCLDSTOP };
@@ -581,7 +548,36 @@
PLOG(FATAL) << "failed to create signalfd";
}
- register_epoll_handler(signal_fd, HandleSignalFd);
+ if (auto result = epoll->RegisterHandler(signal_fd, HandleSignalFd); !result) {
+ LOG(FATAL) << result.error();
+ }
+}
+
+void HandleKeychord(const std::vector<int>& keycodes) {
+ // Only handle keychords if adb is enabled.
+ std::string adb_enabled = android::base::GetProperty("init.svc.adbd", "");
+ if (adb_enabled != "running") {
+ LOG(WARNING) << "Not starting service for keychord " << android::base::Join(keycodes, ' ')
+ << " because ADB is disabled";
+ return;
+ }
+
+ auto found = false;
+ for (const auto& service : ServiceList::GetInstance()) {
+ auto svc = service.get();
+ if (svc->keycodes() == keycodes) {
+ found = true;
+ LOG(INFO) << "Starting service '" << svc->name() << "' from keychord "
+ << android::base::Join(keycodes, ' ');
+ if (auto result = svc->Start(); !result) {
+ LOG(ERROR) << "Could not start service '" << svc->name() << "' from keychord "
+ << android::base::Join(keycodes, ' ') << ": " << result.error();
+ }
+ }
+ }
+ if (!found) {
+ LOG(ERROR) << "Service for keychord " << android::base::Join(keycodes, ' ') << " not found";
+ }
}
int main(int argc, char** argv) {
@@ -727,16 +723,16 @@
SelabelInitialize();
SelinuxRestoreContext();
- epoll_fd = epoll_create1(EPOLL_CLOEXEC);
- if (epoll_fd == -1) {
- PLOG(FATAL) << "epoll_create1 failed";
+ Epoll epoll;
+ if (auto result = epoll.Open(); !result) {
+ PLOG(FATAL) << result.error();
}
- InstallSignalFdHandler();
+ InstallSignalFdHandler(&epoll);
property_load_boot_defaults();
export_oem_lock_status();
- start_property_service();
+ StartPropertyService(&epoll);
set_usb_controller();
const BuiltinFunctionMap function_map;
@@ -761,7 +757,16 @@
am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
am.QueueBuiltinAction(SetMmapRndBitsAction, "SetMmapRndBits");
am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
- am.QueueBuiltinAction(KeychordInitAction, "KeychordInit");
+ Keychords keychords;
+ am.QueueBuiltinAction(
+ [&epoll, &keychords](const BuiltinArguments& args) -> Result<Success> {
+ for (const auto& svc : ServiceList::GetInstance()) {
+ keychords.Register(svc->keycodes());
+ }
+ keychords.Start(&epoll, HandleKeychord);
+ return Success();
+ },
+ "KeychordInit");
am.QueueBuiltinAction(console_init_action, "console_init");
// Trigger all the boot actions to get us started.
@@ -784,7 +789,7 @@
while (true) {
// By default, sleep until something happens.
- int epoll_timeout_ms = -1;
+ auto epoll_timeout = std::optional<std::chrono::milliseconds>{};
if (do_shutdown && !shutting_down) {
do_shutdown = false;
@@ -802,23 +807,18 @@
// If there's a process that needs restarting, wake up in time for that.
if (next_process_restart_time) {
- epoll_timeout_ms = std::chrono::ceil<std::chrono::milliseconds>(
- *next_process_restart_time - boot_clock::now())
- .count();
- if (epoll_timeout_ms < 0) epoll_timeout_ms = 0;
+ epoll_timeout = std::chrono::ceil<std::chrono::milliseconds>(
+ *next_process_restart_time - boot_clock::now());
+ if (*epoll_timeout < 0ms) epoll_timeout = 0ms;
}
}
// If there's more work to do, wake up again immediately.
- if (am.HasMoreCommands()) epoll_timeout_ms = 0;
+ if (am.HasMoreCommands()) epoll_timeout = 0ms;
}
- epoll_event ev;
- int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, epoll_timeout_ms));
- if (nr == -1) {
- PLOG(ERROR) << "epoll_wait failed";
- } else if (nr == 1) {
- std::invoke(*reinterpret_cast<std::function<void()>*>(ev.data.ptr));
+ if (auto result = epoll.Wait(epoll_timeout); !result) {
+ LOG(ERROR) << result.error();
}
}
diff --git a/init/init.h b/init/init.h
index e7c4d8d..6c82fa1 100644
--- a/init/init.h
+++ b/init/init.h
@@ -43,9 +43,6 @@
void property_changed(const std::string& name, const std::string& value);
-void register_epoll_handler(int fd, std::function<void()> handler);
-void unregister_epoll_handler(int fd);
-
bool start_waiting_for_property(const char *name, const char *value);
void DumpState();
diff --git a/init/init_first_stage.cpp b/init/init_first_stage.cpp
index 2221740..db60ce1 100644
--- a/init/init_first_stage.cpp
+++ b/init/init_first_stage.cpp
@@ -77,7 +77,7 @@
std::unique_ptr<LogicalPartitionTable> dm_linear_table_;
std::vector<fstab_rec*> mount_fstab_recs_;
std::set<std::string> required_devices_partition_names_;
- DeviceHandler device_handler_;
+ std::unique_ptr<DeviceHandler> device_handler_;
UeventListener uevent_listener_;
};
@@ -147,6 +147,11 @@
if (IsDmLinearEnabled()) {
dm_linear_table_ = android::fs_mgr::LoadPartitionsFromDeviceTree();
}
+
+ auto boot_devices = fs_mgr_get_boot_devices();
+ device_handler_ =
+ std::make_unique<DeviceHandler>(std::vector<Permissions>{}, std::vector<SysfsPermissions>{},
+ std::vector<Subsystem>{}, std::move(boot_devices), false);
}
std::unique_ptr<FirstStageMount> FirstStageMount::Create() {
@@ -205,7 +210,7 @@
bool found = false;
auto dm_callback = [this, &dm_path, &found](const Uevent& uevent) {
if (uevent.path == dm_path) {
- device_handler_.HandleDeviceEvent(uevent);
+ device_handler_->HandleDeviceEvent(uevent);
found = true;
return ListenerAction::kStop;
}
@@ -262,7 +267,7 @@
if (iter != required_devices_partition_names_.end()) {
LOG(VERBOSE) << __PRETTY_FUNCTION__ << ": found partition: " << *iter;
required_devices_partition_names_.erase(iter);
- device_handler_.HandleDeviceEvent(uevent);
+ device_handler_->HandleDeviceEvent(uevent);
if (required_devices_partition_names_.empty()) {
return ListenerAction::kStop;
} else {
@@ -299,7 +304,7 @@
auto verity_callback = [&device_name, &dm_device, this, &found](const Uevent& uevent) {
if (uevent.device_name == device_name) {
LOG(VERBOSE) << "Creating device-mapper device : " << dm_device;
- device_handler_.HandleDeviceEvent(uevent);
+ device_handler_->HandleDeviceEvent(uevent);
found = true;
return ListenerAction::kStop;
}
@@ -468,9 +473,8 @@
// 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);
+ 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) {
diff --git a/init/keychords.cpp b/init/keychords.cpp
index 293736d..1af06dd 100644
--- a/init/keychords.cpp
+++ b/init/keychords.cpp
@@ -33,148 +33,117 @@
#include <vector>
#include <android-base/logging.h>
-#include <android-base/properties.h>
-
-#include "init.h"
namespace android {
namespace init {
-namespace {
+Keychords::Keychords() : epoll_(nullptr), inotify_fd_(-1) {}
-int keychords_count;
-
-struct KeychordEntry {
- const std::vector<int> keycodes;
- bool notified;
- int id;
-
- KeychordEntry(const std::vector<int>& keycodes, int id)
- : keycodes(keycodes), notified(false), id(id) {}
-};
-
-std::vector<KeychordEntry> keychord_entries;
-
-// Bit management
-class KeychordMask {
- private:
- typedef unsigned int mask_t;
- std::vector<mask_t> bits;
- static constexpr size_t bits_per_byte = 8;
-
- public:
- explicit KeychordMask(size_t bit = 0) : bits((bit + sizeof(mask_t) - 1) / sizeof(mask_t), 0) {}
-
- void SetBit(size_t bit, bool value = true) {
- auto idx = bit / (bits_per_byte * sizeof(mask_t));
- if (idx >= bits.size()) return;
- if (value) {
- bits[idx] |= mask_t(1) << (bit % (bits_per_byte * sizeof(mask_t)));
- } else {
- bits[idx] &= ~(mask_t(1) << (bit % (bits_per_byte * sizeof(mask_t))));
- }
+Keychords::~Keychords() noexcept {
+ if (inotify_fd_ >= 0) {
+ epoll_->UnregisterHandler(inotify_fd_);
+ ::close(inotify_fd_);
}
+ while (!registration_.empty()) GeteventCloseDevice(registration_.begin()->first);
+}
- bool GetBit(size_t bit) const {
- auto idx = bit / (bits_per_byte * sizeof(mask_t));
- return bits[idx] & (mask_t(1) << (bit % (bits_per_byte * sizeof(mask_t))));
- }
+Keychords::Mask::Mask(size_t bit) : bits_((bit + sizeof(mask_t) - 1) / sizeof(mask_t), 0) {}
- size_t bytesize() const { return bits.size() * sizeof(mask_t); }
- void* data() { return bits.data(); }
- size_t size() const { return bits.size() * sizeof(mask_t) * bits_per_byte; }
- void resize(size_t bit) {
- auto idx = bit / (bits_per_byte * sizeof(mask_t));
- if (idx >= bits.size()) {
- bits.resize(idx + 1, 0);
- }
- }
-
- operator bool() const {
- for (size_t i = 0; i < bits.size(); ++i) {
- if (bits[i]) return true;
- }
- return false;
- }
-
- KeychordMask operator&(const KeychordMask& rval) const {
- auto len = std::min(bits.size(), rval.bits.size());
- KeychordMask ret;
- ret.bits.resize(len);
- for (size_t i = 0; i < len; ++i) {
- ret.bits[i] = bits[i] & rval.bits[i];
- }
- return ret;
- }
-
- void operator|=(const KeychordMask& rval) {
- size_t len = rval.bits.size();
- bits.resize(len);
- for (size_t i = 0; i < len; ++i) {
- bits[i] |= rval.bits[i];
- }
- }
-};
-
-KeychordMask keychord_current;
-
-constexpr char kDevicePath[] = "/dev/input";
-
-std::map<std::string, int> keychord_registration;
-
-void HandleKeychord(int id) {
- // Only handle keychords if adb is enabled.
- std::string adb_enabled = android::base::GetProperty("init.svc.adbd", "");
- if (adb_enabled == "running") {
- Service* svc = ServiceList::GetInstance().FindService(id, &Service::keychord_id);
- if (svc) {
- LOG(INFO) << "Starting service '" << svc->name() << "' from keychord " << id;
- if (auto result = svc->Start(); !result) {
- LOG(ERROR) << "Could not start service '" << svc->name() << "' from keychord " << id
- << ": " << result.error();
- }
- } else {
- LOG(ERROR) << "Service for keychord " << id << " not found";
- }
+void Keychords::Mask::SetBit(size_t bit, bool value) {
+ auto idx = bit / (kBitsPerByte * sizeof(mask_t));
+ if (idx >= bits_.size()) return;
+ if (value) {
+ bits_[idx] |= mask_t(1) << (bit % (kBitsPerByte * sizeof(mask_t)));
} else {
- LOG(WARNING) << "Not starting service for keychord " << id << " because ADB is disabled";
+ bits_[idx] &= ~(mask_t(1) << (bit % (kBitsPerByte * sizeof(mask_t))));
}
}
-void KeychordLambdaCheck() {
- for (auto& e : keychord_entries) {
- bool found = true;
- for (auto& code : e.keycodes) {
- if (!keychord_current.GetBit(code)) {
- e.notified = false;
+bool Keychords::Mask::GetBit(size_t bit) const {
+ auto idx = bit / (kBitsPerByte * sizeof(mask_t));
+ return bits_[idx] & (mask_t(1) << (bit % (kBitsPerByte * sizeof(mask_t))));
+}
+
+size_t Keychords::Mask::bytesize() const {
+ return bits_.size() * sizeof(mask_t);
+}
+
+void* Keychords::Mask::data() {
+ return bits_.data();
+}
+
+size_t Keychords::Mask::size() const {
+ return bits_.size() * sizeof(mask_t) * kBitsPerByte;
+}
+
+void Keychords::Mask::resize(size_t bit) {
+ auto idx = bit / (kBitsPerByte * sizeof(mask_t));
+ if (idx >= bits_.size()) {
+ bits_.resize(idx + 1, 0);
+ }
+}
+
+Keychords::Mask::operator bool() const {
+ for (size_t i = 0; i < bits_.size(); ++i) {
+ if (bits_[i]) return true;
+ }
+ return false;
+}
+
+Keychords::Mask Keychords::Mask::operator&(const Keychords::Mask& rval) const {
+ auto len = std::min(bits_.size(), rval.bits_.size());
+ Keychords::Mask ret;
+ ret.bits_.resize(len);
+ for (size_t i = 0; i < len; ++i) {
+ ret.bits_[i] = bits_[i] & rval.bits_[i];
+ }
+ return ret;
+}
+
+void Keychords::Mask::operator|=(const Keychords::Mask& rval) {
+ auto len = rval.bits_.size();
+ bits_.resize(len);
+ for (size_t i = 0; i < len; ++i) {
+ bits_[i] |= rval.bits_[i];
+ }
+}
+
+Keychords::Entry::Entry() : notified(false) {}
+
+void Keychords::LambdaCheck() {
+ for (auto& [keycodes, entry] : entries_) {
+ auto found = true;
+ for (auto& code : keycodes) {
+ if (!current_.GetBit(code)) {
+ entry.notified = false;
found = false;
break;
}
}
if (!found) continue;
- if (e.notified) continue;
- e.notified = true;
- HandleKeychord(e.id);
+ if (entry.notified) continue;
+ entry.notified = true;
+ handler_(keycodes);
}
}
-void KeychordLambdaHandler(int fd) {
+void Keychords::LambdaHandler(int fd) {
input_event event;
auto res = TEMP_FAILURE_RETRY(::read(fd, &event, sizeof(event)));
if ((res != sizeof(event)) || (event.type != EV_KEY)) return;
- keychord_current.SetBit(event.code, event.value);
- KeychordLambdaCheck();
+ current_.SetBit(event.code, event.value);
+ LambdaCheck();
}
-bool KeychordGeteventEnable(int fd) {
- static bool EviocsmaskSupported = true;
-
+bool Keychords::GeteventEnable(int fd) {
// Make sure it is an event channel, should pass this ioctl call
int version;
if (::ioctl(fd, EVIOCGVERSION, &version)) return false;
+#ifdef EVIOCSMASK
+ static auto EviocsmaskSupported = true;
if (EviocsmaskSupported) {
- KeychordMask mask(EV_KEY);
+ Keychords::Mask mask(EV_KEY);
mask.SetBit(EV_KEY);
input_mask msg = {};
msg.type = EV_SYN;
@@ -185,21 +154,23 @@
EviocsmaskSupported = false;
}
}
+#endif
- KeychordMask mask;
- for (auto& e : keychord_entries) {
- for (auto& code : e.keycodes) {
+ Keychords::Mask mask;
+ for (auto& [keycodes, entry] : entries_) {
+ for (auto& code : keycodes) {
mask.resize(code);
mask.SetBit(code);
}
}
- keychord_current.resize(mask.size());
- KeychordMask available(mask.size());
+ current_.resize(mask.size());
+ Keychords::Mask available(mask.size());
auto res = ::ioctl(fd, EVIOCGBIT(EV_KEY, available.bytesize()), available.data());
if (res == -1) return false;
if (!(available & mask)) return false;
+#ifdef EVIOCSMASK
if (EviocsmaskSupported) {
input_mask msg = {};
msg.type = EV_KEY;
@@ -207,46 +178,45 @@
msg.codes_ptr = reinterpret_cast<uintptr_t>(mask.data());
::ioctl(fd, EVIOCSMASK, &msg);
}
+#endif
- KeychordMask set(mask.size());
+ Keychords::Mask set(mask.size());
res = ::ioctl(fd, EVIOCGKEY(res), set.data());
if (res > 0) {
- keychord_current |= mask & available & set;
- KeychordLambdaCheck();
+ current_ |= mask & available & set;
+ LambdaCheck();
}
- register_epoll_handler(fd, [fd]() { KeychordLambdaHandler(fd); });
+ epoll_->RegisterHandler(fd, [this, fd]() { this->LambdaHandler(fd); });
return true;
}
-void GeteventOpenDevice(const std::string& device) {
- if (keychord_registration.count(device)) return;
+void Keychords::GeteventOpenDevice(const std::string& device) {
+ if (registration_.count(device)) return;
auto fd = TEMP_FAILURE_RETRY(::open(device.c_str(), O_RDWR | O_CLOEXEC));
if (fd == -1) {
PLOG(ERROR) << "Can not open " << device;
return;
}
- if (!KeychordGeteventEnable(fd)) {
+ if (!GeteventEnable(fd)) {
::close(fd);
} else {
- keychord_registration.emplace(device, fd);
+ registration_.emplace(device, fd);
}
}
-void GeteventCloseDevice(const std::string& device) {
- auto it = keychord_registration.find(device);
- if (it == keychord_registration.end()) return;
+void Keychords::GeteventCloseDevice(const std::string& device) {
+ auto it = registration_.find(device);
+ if (it == registration_.end()) return;
auto fd = (*it).second;
- unregister_epoll_handler(fd);
- keychord_registration.erase(it);
+ epoll_->UnregisterHandler(fd);
+ registration_.erase(it);
::close(fd);
}
-int inotify_fd = -1;
+void Keychords::InotifyHandler() {
+ unsigned char buf[512]; // History shows 32-64 bytes typical
-void InotifyHandler() {
- unsigned char buf[512];
-
- auto res = TEMP_FAILURE_RETRY(::read(inotify_fd, buf, sizeof(buf)));
+ auto res = TEMP_FAILURE_RETRY(::read(inotify_fd_, buf, sizeof(buf)));
if (res < 0) {
PLOG(WARNING) << "could not get event";
return;
@@ -272,14 +242,15 @@
}
}
-void GeteventOpenDevice() {
- inotify_fd = ::inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
- if (inotify_fd < 0) {
+void Keychords::GeteventOpenDevice() {
+ inotify_fd_ = ::inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
+ if (inotify_fd_ < 0) {
PLOG(WARNING) << "Could not instantiate inotify for " << kDevicePath;
- } else if (::inotify_add_watch(inotify_fd, kDevicePath, IN_DELETE | IN_CREATE | IN_ONLYDIR) < 0) {
+ } else if (::inotify_add_watch(inotify_fd_, kDevicePath, IN_DELETE | IN_CREATE | IN_ONLYDIR) <
+ 0) {
PLOG(WARNING) << "Could not add watch for " << kDevicePath;
- ::close(inotify_fd);
- inotify_fd = -1;
+ ::close(inotify_fd_);
+ inotify_fd_ = -1;
}
std::unique_ptr<DIR, decltype(&closedir)> device(opendir(kDevicePath), closedir);
@@ -294,26 +265,20 @@
}
}
- if (inotify_fd >= 0) register_epoll_handler(inotify_fd, InotifyHandler);
+ if (inotify_fd_ >= 0) {
+ epoll_->RegisterHandler(inotify_fd_, [this]() { this->InotifyHandler(); });
+ }
}
-void AddServiceKeycodes(Service* svc) {
- if (svc->keycodes().empty()) return;
- for (auto& code : svc->keycodes()) {
- if ((code < 0) || (code >= KEY_MAX)) return;
- }
- ++keychords_count;
- keychord_entries.emplace_back(KeychordEntry(svc->keycodes(), keychords_count));
- svc->set_keychord_id(keychords_count);
+void Keychords::Register(const std::vector<int>& keycodes) {
+ if (keycodes.empty()) return;
+ entries_.try_emplace(keycodes, Entry());
}
-} // namespace
-
-void KeychordInit() {
- for (const auto& service : ServiceList::GetInstance()) {
- AddServiceKeycodes(service.get());
- }
- if (keychords_count) GeteventOpenDevice();
+void Keychords::Start(Epoll* epoll, std::function<void(const std::vector<int>&)> handler) {
+ epoll_ = epoll;
+ handler_ = handler;
+ if (entries_.size()) GeteventOpenDevice();
}
} // namespace init
diff --git a/init/keychords.h b/init/keychords.h
index 689a3b5..00ed205 100644
--- a/init/keychords.h
+++ b/init/keychords.h
@@ -17,12 +17,81 @@
#ifndef _INIT_KEYCHORDS_H_
#define _INIT_KEYCHORDS_H_
-#include "service.h"
+#include <functional>
+#include <map>
+#include <string>
+#include <vector>
+
+#include "epoll.h"
namespace android {
namespace init {
-void KeychordInit();
+class Keychords {
+ public:
+ Keychords();
+ Keychords(const Keychords&) = delete;
+ Keychords(Keychords&&) = delete;
+ Keychords& operator=(const Keychords&) = delete;
+ Keychords& operator=(Keychords&&) = delete;
+ ~Keychords() noexcept;
+
+ void Register(const std::vector<int>& keycodes);
+ void Start(Epoll* epoll, std::function<void(const std::vector<int>&)> handler);
+
+ private:
+ // Bit management
+ class Mask {
+ public:
+ explicit Mask(size_t bit = 0);
+
+ void SetBit(size_t bit, bool value = true);
+ bool GetBit(size_t bit) const;
+
+ size_t bytesize() const;
+ void* data();
+ size_t size() const;
+ void resize(size_t bit);
+
+ operator bool() const;
+ Mask operator&(const Mask& rval) const;
+ void operator|=(const Mask& rval);
+
+ private:
+ typedef unsigned int mask_t;
+ static constexpr size_t kBitsPerByte = 8;
+
+ std::vector<mask_t> bits_;
+ };
+
+ struct Entry {
+ Entry();
+
+ bool notified;
+ };
+
+ static constexpr char kDevicePath[] = "/dev/input";
+
+ void LambdaCheck();
+ void LambdaHandler(int fd);
+ void InotifyHandler();
+
+ bool GeteventEnable(int fd);
+ void GeteventOpenDevice(const std::string& device);
+ void GeteventOpenDevice();
+ void GeteventCloseDevice(const std::string& device);
+
+ Epoll* epoll_;
+ std::function<void(const std::vector<int>&)> handler_;
+
+ std::map<std::string, int> registration_;
+
+ std::map<const std::vector<int>, Entry> entries_;
+
+ Mask current_;
+
+ int inotify_fd_;
+};
} // namespace init
} // namespace android
diff --git a/init/keychords_test.cpp b/init/keychords_test.cpp
new file mode 100644
index 0000000..c8c47a8
--- /dev/null
+++ b/init/keychords_test.cpp
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) 2018 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 "keychords.h"
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <linux/input.h>
+#include <linux/uinput.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <chrono>
+#include <set>
+#include <string>
+#include <vector>
+
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <gtest/gtest.h>
+
+#include "epoll.h"
+
+using namespace std::chrono_literals;
+
+namespace android {
+namespace init {
+
+namespace {
+
+// This class is used to inject keys.
+class EventHandler {
+ public:
+ EventHandler();
+ EventHandler(const EventHandler&) = delete;
+ EventHandler(EventHandler&&);
+ EventHandler& operator=(const EventHandler&) = delete;
+ EventHandler& operator=(EventHandler&&);
+ ~EventHandler() noexcept;
+
+ bool init();
+
+ bool send(struct input_event& e);
+ bool send(uint16_t type, uint16_t code, uint16_t value);
+ bool send(uint16_t code, bool value);
+
+ private:
+ int fd_;
+};
+
+EventHandler::EventHandler() : fd_(-1) {}
+
+EventHandler::EventHandler(EventHandler&& rval) : fd_(rval.fd_) {
+ rval.fd_ = -1;
+}
+
+EventHandler& EventHandler::operator=(EventHandler&& rval) {
+ fd_ = rval.fd_;
+ rval.fd_ = -1;
+ return *this;
+}
+
+EventHandler::~EventHandler() {
+ if (fd_ == -1) return;
+ ::ioctl(fd_, UI_DEV_DESTROY);
+ ::close(fd_);
+}
+
+bool EventHandler::init() {
+ if (fd_ != -1) return true;
+ auto fd = TEMP_FAILURE_RETRY(::open("/dev/uinput", O_WRONLY | O_NONBLOCK | O_CLOEXEC));
+ if (fd == -1) return false;
+ if (::ioctl(fd, UI_SET_EVBIT, EV_KEY) == -1) {
+ ::close(fd);
+ return false;
+ }
+
+ static const struct uinput_user_dev u = {
+ .name = "com.google.android.init.test",
+ .id.bustype = BUS_VIRTUAL,
+ .id.vendor = 0x1AE0, // Google
+ .id.product = 0x494E, // IN
+ .id.version = 1,
+ };
+ if (TEMP_FAILURE_RETRY(::write(fd, &u, sizeof(u))) != sizeof(u)) {
+ ::close(fd);
+ return false;
+ }
+
+ // all keys
+ for (uint16_t i = 0; i < KEY_MAX; ++i) {
+ if (::ioctl(fd, UI_SET_KEYBIT, i) == -1) {
+ ::close(fd);
+ return false;
+ }
+ }
+ if (::ioctl(fd, UI_DEV_CREATE) == -1) {
+ ::close(fd);
+ return false;
+ }
+ fd_ = fd;
+ return true;
+}
+
+bool EventHandler::send(struct input_event& e) {
+ gettimeofday(&e.time, nullptr);
+ return TEMP_FAILURE_RETRY(::write(fd_, &e, sizeof(e))) == sizeof(e);
+}
+
+bool EventHandler::send(uint16_t type, uint16_t code, uint16_t value) {
+ struct input_event e = {.type = type, .code = code, .value = value};
+ return send(e);
+}
+
+bool EventHandler::send(uint16_t code, bool value) {
+ return (code < KEY_MAX) && init() && send(EV_KEY, code, value) && send(EV_SYN, SYN_REPORT, 0);
+}
+
+std::string InitFds(const char* prefix, pid_t pid = getpid()) {
+ std::string ret;
+
+ std::string init_fds("/proc/");
+ init_fds += std::to_string(pid) + "/fd";
+ std::unique_ptr<DIR, decltype(&closedir)> fds(opendir(init_fds.c_str()), closedir);
+ if (!fds) return ret;
+
+ dirent* entry;
+ while ((entry = readdir(fds.get()))) {
+ if (entry->d_name[0] == '.') continue;
+ std::string devname = init_fds + '/' + entry->d_name;
+ char buf[256];
+ auto retval = readlink(devname.c_str(), buf, sizeof(buf) - 1);
+ if ((retval < 0) || (size_t(retval) >= (sizeof(buf) - 1))) continue;
+ buf[retval] = '\0';
+ if (!android::base::StartsWith(buf, prefix)) continue;
+ if (ret.size() != 0) ret += ",";
+ ret += buf;
+ }
+ return ret;
+}
+
+std::string InitInputFds() {
+ return InitFds("/dev/input/");
+}
+
+std::string InitInotifyFds() {
+ return InitFds("anon_inode:inotify");
+}
+
+// NB: caller (this series of tests, or conversely the service parser in init)
+// is responsible for validation, sorting and uniqueness of the chords, so no
+// fuzzing is advised.
+
+const std::vector<int> escape_chord = {KEY_ESC};
+const std::vector<int> triple1_chord = {KEY_BACKSPACE, KEY_VOLUMEDOWN, KEY_VOLUMEUP};
+const std::vector<int> triple2_chord = {KEY_VOLUMEDOWN, KEY_VOLUMEUP, KEY_BACK};
+
+const std::vector<const std::vector<int>> empty_chords;
+const std::vector<const std::vector<int>> chords = {
+ escape_chord,
+ triple1_chord,
+ triple2_chord,
+};
+
+class TestFrame {
+ public:
+ TestFrame(const std::vector<const std::vector<int>>& chords, EventHandler* ev = nullptr);
+
+ void RelaxForMs(std::chrono::milliseconds wait = 1ms);
+
+ void SetChord(int key, bool value = true);
+ void SetChords(const std::vector<int>& chord, bool value = true);
+ void ClrChord(int key);
+ void ClrChords(const std::vector<int>& chord);
+
+ bool IsOnlyChord(const std::vector<int>& chord) const;
+ bool IsNoChord() const;
+ bool IsChord(const std::vector<int>& chord) const;
+ void WaitForChord(const std::vector<int>& chord);
+
+ std::string Format() const;
+
+ private:
+ static std::string Format(const std::vector<const std::vector<int>>& chords);
+
+ Epoll epoll_;
+ Keychords keychords_;
+ std::vector<const std::vector<int>> keycodes_;
+ EventHandler* ev_;
+};
+
+TestFrame::TestFrame(const std::vector<const std::vector<int>>& chords, EventHandler* ev)
+ : ev_(ev) {
+ if (!epoll_.Open()) return;
+ for (const auto& keycodes : chords) keychords_.Register(keycodes);
+ keychords_.Start(&epoll_, [this](const std::vector<int>& keycodes) {
+ this->keycodes_.emplace_back(keycodes);
+ });
+}
+
+void TestFrame::RelaxForMs(std::chrono::milliseconds wait) {
+ epoll_.Wait(wait);
+}
+
+void TestFrame::SetChord(int key, bool value) {
+ ASSERT_TRUE(!!ev_);
+ RelaxForMs();
+ EXPECT_TRUE(ev_->send(key, value));
+}
+
+void TestFrame::SetChords(const std::vector<int>& chord, bool value) {
+ ASSERT_TRUE(!!ev_);
+ for (auto& key : chord) SetChord(key, value);
+ RelaxForMs();
+}
+
+void TestFrame::ClrChord(int key) {
+ ASSERT_TRUE(!!ev_);
+ SetChord(key, false);
+}
+
+void TestFrame::ClrChords(const std::vector<int>& chord) {
+ ASSERT_TRUE(!!ev_);
+ SetChords(chord, false);
+}
+
+bool TestFrame::IsOnlyChord(const std::vector<int>& chord) const {
+ auto ret = false;
+ for (const auto& keycode : keycodes_) {
+ if (keycode != chord) return false;
+ ret = true;
+ }
+ return ret;
+}
+
+bool TestFrame::IsNoChord() const {
+ return keycodes_.empty();
+}
+
+bool TestFrame::IsChord(const std::vector<int>& chord) const {
+ for (const auto& keycode : keycodes_) {
+ if (keycode == chord) return true;
+ }
+ return false;
+}
+
+void TestFrame::WaitForChord(const std::vector<int>& chord) {
+ for (int retry = 1000; retry && !IsChord(chord); --retry) RelaxForMs();
+}
+
+std::string TestFrame::Format(const std::vector<const std::vector<int>>& chords) {
+ std::string ret("{");
+ if (!chords.empty()) {
+ ret += android::base::Join(chords.front(), ' ');
+ for (auto it = std::next(chords.begin()); it != chords.end(); ++it) {
+ ret += ',';
+ ret += android::base::Join(*it, ' ');
+ }
+ }
+ return ret + '}';
+}
+
+std::string TestFrame::Format() const {
+ return Format(keycodes_);
+}
+
+} // namespace
+
+TEST(keychords, not_instantiated) {
+ TestFrame test_frame(empty_chords);
+ EXPECT_TRUE(InitInotifyFds().size() == 0);
+}
+
+TEST(keychords, instantiated) {
+ // Test if a valid set of chords results in proper instantiation of the
+ // underlying mechanisms for /dev/input/ attachment.
+ TestFrame test_frame(chords);
+ EXPECT_TRUE(InitInotifyFds().size() != 0);
+}
+
+TEST(keychords, init_inotify) {
+ std::string before(InitInputFds());
+
+ TestFrame test_frame(chords);
+
+ EventHandler ev;
+ EXPECT_TRUE(ev.init());
+
+ for (int retry = 1000; retry && before == InitInputFds(); --retry) test_frame.RelaxForMs();
+ std::string after(InitInputFds());
+ EXPECT_NE(before, after);
+}
+
+TEST(keychords, key) {
+ EventHandler ev;
+ EXPECT_TRUE(ev.init());
+ TestFrame test_frame(chords, &ev);
+
+ test_frame.SetChords(escape_chord);
+ test_frame.WaitForChord(escape_chord);
+ test_frame.ClrChords(escape_chord);
+ EXPECT_TRUE(test_frame.IsOnlyChord(escape_chord))
+ << "expected only " << android::base::Join(escape_chord, ' ') << " got "
+ << test_frame.Format();
+}
+
+TEST(keychords, keys_in_series) {
+ EventHandler ev;
+ EXPECT_TRUE(ev.init());
+ TestFrame test_frame(chords, &ev);
+
+ for (auto& key : triple1_chord) {
+ test_frame.SetChord(key);
+ test_frame.ClrChord(key);
+ }
+ test_frame.WaitForChord(triple1_chord);
+ EXPECT_TRUE(test_frame.IsNoChord()) << "expected nothing got " << test_frame.Format();
+}
+
+TEST(keychords, keys_in_parallel) {
+ EventHandler ev;
+ EXPECT_TRUE(ev.init());
+ TestFrame test_frame(chords, &ev);
+
+ test_frame.SetChords(triple2_chord);
+ test_frame.WaitForChord(triple2_chord);
+ test_frame.ClrChords(triple2_chord);
+ EXPECT_TRUE(test_frame.IsOnlyChord(triple2_chord))
+ << "expected only " << android::base::Join(triple2_chord, ' ') << " got "
+ << test_frame.Format();
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/parser.cpp b/init/parser.cpp
index 4453aaa..ee6ee06 100644
--- a/init/parser.cpp
+++ b/init/parser.cpp
@@ -39,7 +39,7 @@
line_callbacks_.emplace_back(prefix, callback);
}
-void Parser::ParseData(const std::string& filename, const std::string& data, size_t* parse_errors) {
+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');
@@ -57,7 +57,7 @@
if (section_parser == nullptr) return;
if (auto result = section_parser->EndSection(); !result) {
- (*parse_errors)++;
+ parse_error_count_++;
LOG(ERROR) << filename << ": " << section_start_line << ": " << result.error();
}
@@ -81,7 +81,7 @@
end_section();
if (auto result = callback(std::move(args)); !result) {
- (*parse_errors)++;
+ parse_error_count_++;
LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
}
break;
@@ -94,16 +94,20 @@
if (auto result =
section_parser->ParseSection(std::move(args), filename, state.line);
!result) {
- (*parse_errors)++;
+ parse_error_count_++;
LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
section_parser = nullptr;
}
} else if (section_parser) {
if (auto result = section_parser->ParseLineSection(std::move(args), state.line);
!result) {
- (*parse_errors)++;
+ parse_error_count_++;
LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
}
+ } else {
+ parse_error_count_++;
+ LOG(ERROR) << filename << ": " << state.line
+ << ": Invalid section keyword found";
}
args.clear();
break;
@@ -114,17 +118,17 @@
}
}
-bool Parser::ParseConfigFile(const std::string& path, size_t* parse_errors) {
+bool Parser::ParseConfigFile(const std::string& path) {
LOG(INFO) << "Parsing file " << path << "...";
android::base::Timer t;
auto config_contents = ReadFile(path);
if (!config_contents) {
- LOG(ERROR) << "Unable to read config file '" << path << "': " << config_contents.error();
+ LOG(INFO) << "Unable to read config file '" << path << "': " << config_contents.error();
return false;
}
config_contents->push_back('\n'); // TODO: fix parse_config.
- ParseData(path, *config_contents, parse_errors);
+ ParseData(path, *config_contents);
for (const auto& [section_name, section_parser] : section_parsers_) {
section_parser->EndFile();
}
@@ -133,11 +137,11 @@
return true;
}
-bool Parser::ParseConfigDir(const std::string& path, size_t* parse_errors) {
+bool Parser::ParseConfigDir(const std::string& path) {
LOG(INFO) << "Parsing directory " << path << "...";
std::unique_ptr<DIR, decltype(&closedir)> config_dir(opendir(path.c_str()), closedir);
if (!config_dir) {
- PLOG(ERROR) << "Could not import directory '" << path << "'";
+ PLOG(INFO) << "Could not import directory '" << path << "'";
return false;
}
dirent* current_file;
@@ -153,7 +157,7 @@
// Sort first so we load files in a consistent order (bug 31996208)
std::sort(files.begin(), files.end());
for (const auto& file : files) {
- if (!ParseConfigFile(file, parse_errors)) {
+ if (!ParseConfigFile(file)) {
LOG(ERROR) << "could not import file '" << file << "'";
}
}
@@ -161,16 +165,10 @@
}
bool Parser::ParseConfig(const std::string& path) {
- size_t parse_errors;
- return ParseConfig(path, &parse_errors);
-}
-
-bool Parser::ParseConfig(const std::string& path, size_t* parse_errors) {
- *parse_errors = 0;
if (is_dir(path.c_str())) {
- return ParseConfigDir(path, parse_errors);
+ return ParseConfigDir(path);
}
- return ParseConfigFile(path, parse_errors);
+ return ParseConfigFile(path);
}
} // namespace init
diff --git a/init/parser.h b/init/parser.h
index f6e237f..3501d8c 100644
--- a/init/parser.h
+++ b/init/parser.h
@@ -72,17 +72,19 @@
Parser();
bool ParseConfig(const std::string& path);
- bool ParseConfig(const std::string& path, size_t* parse_errors);
void AddSectionParser(const std::string& name, std::unique_ptr<SectionParser> parser);
void AddSingleLineParser(const std::string& prefix, LineCallback callback);
+ size_t parse_error_count() const { return parse_error_count_; }
+
private:
- void ParseData(const std::string& filename, const std::string& data, size_t* parse_errors);
- bool ParseConfigFile(const std::string& path, size_t* parse_errors);
- bool ParseConfigDir(const std::string& path, size_t* parse_errors);
+ 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_;
+ size_t parse_error_count_ = 0;
};
} // namespace init
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 47e45ef..d1c427d 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -56,6 +56,7 @@
#include <selinux/label.h>
#include <selinux/selinux.h>
+#include "epoll.h"
#include "init.h"
#include "persistent_properties.h"
#include "property_type.h"
@@ -94,6 +95,11 @@
void CreateSerializedPropertyInfo();
+struct PropertyAuditData {
+ const ucred* cr;
+ const char* name;
+};
+
void property_init() {
mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH);
CreateSerializedPropertyInfo();
@@ -110,7 +116,7 @@
return false;
}
- property_audit_data audit_data;
+ PropertyAuditData audit_data;
audit_data.name = name.c_str();
audit_data.cr = &cr;
@@ -371,6 +377,7 @@
int result = TEMP_FAILURE_RETRY(recv(socket_, data, bytes_left, MSG_DONTWAIT));
if (result <= 0) {
+ PLOG(ERROR) << "sys_prop: recv error";
return false;
}
@@ -378,6 +385,10 @@
data += result;
}
+ if (bytes_left != 0) {
+ LOG(ERROR) << "sys_prop: recv data is not properly obtained.";
+ }
+
return bytes_left == 0;
}
@@ -387,6 +398,35 @@
DISALLOW_IMPLICIT_CONSTRUCTORS(SocketConnection);
};
+bool CheckControlPropertyPerms(const std::string& name, const std::string& value,
+ const std::string& source_context, const ucred& cr) {
+ // We check the legacy method first but these properties are dontaudit, so we only log an audit
+ // if the newer method fails as well. We only do this with the legacy ctl. properties.
+ if (name == "ctl.start" || name == "ctl.stop" || name == "ctl.restart") {
+ // The legacy permissions model is that ctl. properties have their name ctl.<action> and
+ // their value is the name of the service to apply that action to. Permissions for these
+ // actions are based on the service, so we must create a fake name of ctl.<service> to
+ // check permissions.
+ auto control_string_legacy = "ctl." + value;
+ const char* target_context_legacy = nullptr;
+ const char* type_legacy = nullptr;
+ property_info_area->GetPropertyInfo(control_string_legacy.c_str(), &target_context_legacy,
+ &type_legacy);
+
+ if (CheckMacPerms(control_string_legacy, target_context_legacy, source_context.c_str(), cr)) {
+ return true;
+ }
+ }
+
+ auto control_string_full = name + "$" + value;
+ const char* target_context_full = nullptr;
+ const char* type_full = nullptr;
+ property_info_area->GetPropertyInfo(control_string_full.c_str(), &target_context_full,
+ &type_full);
+
+ return CheckMacPerms(control_string_full, target_context_full, source_context.c_str(), cr);
+}
+
// This returns one of the enum of PROP_SUCCESS or PROP_ERROR*.
uint32_t HandlePropertySet(const std::string& name, const std::string& value,
const std::string& source_context, const ucred& cr, std::string* error) {
@@ -396,15 +436,9 @@
}
if (StartsWith(name, "ctl.")) {
- // ctl. properties have their name ctl.<action> and their value is the name of the service
- // to apply that action to. Permissions for these actions are based on the service, so we
- // must create a fake name of ctl.<service> to check permissions.
- auto control_string = "ctl." + value;
- const char* target_context = nullptr;
- const char* type = nullptr;
- property_info_area->GetPropertyInfo(control_string.c_str(), &target_context, &type);
- if (!CheckMacPerms(control_string, target_context, source_context.c_str(), cr)) {
- *error = StringPrintf("Unable to '%s' service %s", name.c_str() + 4, value.c_str());
+ if (!CheckControlPropertyPerms(name, value, source_context, cr)) {
+ *error = StringPrintf("Invalid permissions to perform '%s' on '%s'", name.c_str() + 4,
+ value.c_str());
return PROP_ERROR_HANDLE_CONTROL_MESSAGE;
}
@@ -736,7 +770,7 @@
}
static int SelinuxAuditCallback(void* data, security_class_t /*cls*/, char* buf, size_t len) {
- property_audit_data* d = reinterpret_cast<property_audit_data*>(data);
+ auto* d = reinterpret_cast<PropertyAuditData*>(data);
if (!d || !d->name || !d->cr) {
LOG(ERROR) << "AuditCallback invoked with null data arguments!";
@@ -808,7 +842,7 @@
selinux_android_restorecon(kPropertyInfosPath, 0);
}
-void start_property_service() {
+void StartPropertyService(Epoll* epoll) {
selinux_callback cb;
cb.func_audit = SelinuxAuditCallback;
selinux_set_callback(SELINUX_CB_AUDIT, cb);
@@ -823,7 +857,9 @@
listen(property_set_fd, 8);
- register_epoll_handler(property_set_fd, handle_property_set_fd);
+ if (auto result = epoll->RegisterHandler(property_set_fd, handle_property_set_fd); !result) {
+ PLOG(FATAL) << result.error();
+ }
}
} // namespace init
diff --git a/init/property_service.h b/init/property_service.h
index 29eaaa9..cacd987 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -21,14 +21,11 @@
#include <string>
+#include "epoll.h"
+
namespace android {
namespace init {
-struct property_audit_data {
- const ucred* cr;
- const char* name;
-};
-
extern uint32_t (*property_set)(const std::string& name, const std::string& value);
uint32_t HandlePropertySet(const std::string& name, const std::string& value,
@@ -40,7 +37,7 @@
void property_load_boot_defaults(void);
void load_persist_props(void);
void load_system_props(void);
-void start_property_service(void);
+void StartPropertyService(Epoll* epoll);
} // namespace init
} // namespace android
diff --git a/init/service.cpp b/init/service.cpp
index 03c2cee..565cae7 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -18,6 +18,7 @@
#include <fcntl.h>
#include <inttypes.h>
+#include <linux/input.h>
#include <linux/securebits.h>
#include <sched.h>
#include <sys/mount.h>
@@ -32,8 +33,10 @@
#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 <android-base/unique_fd.h>
#include <hidl-util/FQName.h>
#include <processgroup/processgroup.h>
#include <selinux/selinux.h>
@@ -45,8 +48,6 @@
#if defined(__ANDROID__)
#include <sys/system_properties.h>
-#include <android-base/properties.h>
-
#include "init.h"
#include "property_service.h"
#else
@@ -59,13 +60,13 @@
using android::base::ParseInt;
using android::base::StartsWith;
using android::base::StringPrintf;
+using android::base::unique_fd;
using android::base::WriteStringToFile;
namespace android {
namespace init {
-static Result<std::string> ComputeContextFromExecutable(std::string& service_name,
- const std::string& service_path) {
+static Result<std::string> ComputeContextFromExecutable(const std::string& service_path) {
std::string computed_context;
char* raw_con = nullptr;
@@ -101,36 +102,49 @@
return computed_context;
}
-static void SetUpPidNamespace(const std::string& service_name) {
+Result<Success> Service::SetUpMountNamespace() const {
constexpr unsigned int kSafeFlags = MS_NODEV | MS_NOEXEC | MS_NOSUID;
- // It's OK to LOG(FATAL) in this function since it's running in the first
- // child process.
-
// Recursively remount / as slave like zygote does so unmounting and mounting /proc
// doesn't interfere with the parent namespace's /proc mount. This will also
// prevent any other mounts/unmounts initiated by the service from interfering
// with the parent namespace but will still allow mount events from the parent
// namespace to propagate to the child.
if (mount("rootfs", "/", nullptr, (MS_SLAVE | MS_REC), nullptr) == -1) {
- PLOG(FATAL) << "couldn't remount(/) recursively as slave for " << service_name;
- }
- // umount() then mount() /proc.
- // Note that it is not sufficient to mount with MS_REMOUNT.
- if (umount("/proc") == -1) {
- PLOG(FATAL) << "couldn't umount(/proc) for " << service_name;
- }
- if (mount("", "/proc", "proc", kSafeFlags, "") == -1) {
- PLOG(FATAL) << "couldn't mount(/proc) for " << service_name;
+ return ErrnoError() << "Could not remount(/) recursively as slave";
}
- if (prctl(PR_SET_NAME, service_name.c_str()) == -1) {
- PLOG(FATAL) << "couldn't set name for " << service_name;
+ // umount() then mount() /proc and/or /sys
+ // Note that it is not sufficient to mount with MS_REMOUNT.
+ if (namespace_flags_ & CLONE_NEWPID) {
+ if (umount("/proc") == -1) {
+ return ErrnoError() << "Could not umount(/proc)";
+ }
+ if (mount("", "/proc", "proc", kSafeFlags, "") == -1) {
+ return ErrnoError() << "Could not mount(/proc)";
+ }
+ }
+ bool remount_sys = std::any_of(namespaces_to_enter_.begin(), namespaces_to_enter_.end(),
+ [](const auto& entry) { return entry.first == CLONE_NEWNET; });
+ if (remount_sys) {
+ if (umount2("/sys", MNT_DETACH) == -1) {
+ return ErrnoError() << "Could not umount(/sys)";
+ }
+ if (mount("", "/sys", "sys", kSafeFlags, "") == -1) {
+ return ErrnoError() << "Could not mount(/sys)";
+ }
+ }
+ return Success();
+}
+
+Result<Success> Service::SetUpPidNamespace() const {
+ if (prctl(PR_SET_NAME, name_.c_str()) == -1) {
+ return ErrnoError() << "Could not set name";
}
pid_t child_pid = fork();
if (child_pid == -1) {
- PLOG(FATAL) << "couldn't fork init inside the PID namespace for " << service_name;
+ return ErrnoError() << "Could not fork init inside the PID namespace";
}
if (child_pid > 0) {
@@ -153,6 +167,20 @@
}
_exit(WEXITSTATUS(init_exitstatus));
}
+ return Success();
+}
+
+Result<Success> Service::EnterNamespaces() const {
+ for (const auto& [nstype, path] : namespaces_to_enter_) {
+ auto fd = unique_fd{open(path.c_str(), O_RDONLY | O_CLOEXEC)};
+ if (!fd) {
+ return ErrnoError() << "Could not open namespace at " << path;
+ }
+ if (setns(fd, nstype) == -1) {
+ return ErrnoError() << "Could not setns() namespace at " << path;
+ }
+ }
+ return Success();
}
static bool ExpandArgsAndExecv(const std::vector<std::string>& args, bool sigstop) {
@@ -200,7 +228,6 @@
seclabel_(seclabel),
onrestart_(false, subcontext_for_restart_commands, "<Service '" + name + "' onrestart>", 0,
"onrestart", {}),
- keychord_id_(0),
ioprio_class_(IoSchedClass_NONE),
ioprio_pri_(0),
priority_(0),
@@ -422,6 +449,20 @@
return Success();
}
+Result<Success> Service::ParseEnterNamespace(const std::vector<std::string>& args) {
+ if (args[1] != "net") {
+ return Error() << "Init only supports entering network namespaces";
+ }
+ if (!namespaces_to_enter_.empty()) {
+ return Error() << "Only one network namespace may be entered";
+ }
+ // Network namespaces require that /sys is remounted, otherwise the old adapters will still be
+ // present. Therefore, they also require mount namespaces.
+ namespace_flags_ |= CLONE_NEWNS;
+ namespaces_to_enter_.emplace_back(CLONE_NEWNET, args[2]);
+ return Success();
+}
+
Result<Success> Service::ParseGroup(const std::vector<std::string>& args) {
auto gid = DecodeUid(args[1]);
if (!gid) {
@@ -502,10 +543,13 @@
Result<Success> Service::ParseKeycodes(const std::vector<std::string>& args) {
for (std::size_t i = 1; i < args.size(); i++) {
int code;
- if (ParseInt(args[i], &code)) {
- keycodes_.emplace_back(code);
+ if (ParseInt(args[i], &code, 0, KEY_MAX)) {
+ for (auto& key : keycodes_) {
+ if (key == code) return Error() << "duplicate keycode: " << args[i];
+ }
+ keycodes_.insert(std::upper_bound(keycodes_.begin(), keycodes_.end(), code), code);
} else {
- LOG(WARNING) << "ignoring invalid keycode: " << args[i];
+ return Error() << "invalid keycode: " << args[i];
}
}
return Success();
@@ -691,6 +735,8 @@
{"console", {0, 1, &Service::ParseConsole}},
{"critical", {0, 0, &Service::ParseCritical}},
{"disabled", {0, 0, &Service::ParseDisabled}},
+ {"enter_namespace",
+ {2, 2, &Service::ParseEnterNamespace}},
{"file", {2, 2, &Service::ParseFile}},
{"group", {1, NR_SVC_SUPP_GIDS + 1, &Service::ParseGroup}},
{"interface", {2, 2, &Service::ParseInterface}},
@@ -793,7 +839,7 @@
if (!seclabel_.empty()) {
scon = seclabel_;
} else {
- auto result = ComputeContextFromExecutable(name_, args_[0]);
+ auto result = ComputeContextFromExecutable(args_[0]);
if (!result) {
return result.error();
}
@@ -812,10 +858,24 @@
if (pid == 0) {
umask(077);
+ if (auto result = EnterNamespaces(); !result) {
+ LOG(FATAL) << "Service '" << name_ << "' could not enter namespaces: " << result.error();
+ }
+
+ if (namespace_flags_ & CLONE_NEWNS) {
+ if (auto result = SetUpMountNamespace(); !result) {
+ LOG(FATAL) << "Service '" << name_
+ << "' could not set up mount namespace: " << result.error();
+ }
+ }
+
if (namespace_flags_ & CLONE_NEWPID) {
// This will fork again to run an init process inside the PID
// namespace.
- SetUpPidNamespace(name_);
+ if (auto result = SetUpPidNamespace(); !result) {
+ LOG(FATAL) << "Service '" << name_
+ << "' could not set up PID namespace: " << result.error();
+ }
}
for (const auto& [key, value] : environment_vars_) {
diff --git a/init/service.h b/init/service.h
index 9cb35b8..ea79a07 100644
--- a/init/service.h
+++ b/init/service.h
@@ -108,8 +108,6 @@
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_; }
const std::set<std::string>& interfaces() const { return interfaces_; }
@@ -125,6 +123,9 @@
using OptionParser = Result<Success> (Service::*)(const std::vector<std::string>& args);
class OptionParserMap;
+ Result<Success> SetUpMountNamespace() const;
+ Result<Success> SetUpPidNamespace() const;
+ Result<Success> EnterNamespaces() const;
void NotifyStateChange(const std::string& new_state) const;
void StopOrReset(int how);
void ZapStdio() const;
@@ -137,6 +138,7 @@
Result<Success> ParseConsole(const std::vector<std::string>& args);
Result<Success> ParseCritical(const std::vector<std::string>& args);
Result<Success> ParseDisabled(const std::vector<std::string>& args);
+ Result<Success> ParseEnterNamespace(const std::vector<std::string>& args);
Result<Success> ParseGroup(const std::vector<std::string>& args);
Result<Success> ParsePriority(const std::vector<std::string>& args);
Result<Success> ParseInterface(const std::vector<std::string>& args);
@@ -181,6 +183,8 @@
std::vector<gid_t> supp_gids_;
CapSet capabilities_;
unsigned namespace_flags_;
+ // Pair of namespace type, path to namespace.
+ std::vector<std::pair<int, std::string>> namespaces_to_enter_;
std::string seclabel_;
@@ -193,9 +197,8 @@
std::set<std::string> interfaces_; // e.g. some.package.foo@1.0::IBaz/instance-name
- // keycodes for triggering this service via /dev/keychord
+ // keycodes for triggering this service via /dev/input/input*
std::vector<int> keycodes_;
- int keychord_id_;
IoSchedClass ioprio_class_;
int ioprio_pri_;
diff --git a/init/service_test.cpp b/init/service_test.cpp
index b43c2e9..194aa2b 100644
--- a/init/service_test.cpp
+++ b/init/service_test.cpp
@@ -46,7 +46,6 @@
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());
@@ -66,7 +65,6 @@
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());
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index 1435d82..a284203 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -30,6 +30,7 @@
#include <android-base/chrono_utils.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
+#include <fstab/fstab.h>
#include <selinux/android.h>
#include <selinux/selinux.h>
@@ -242,8 +243,9 @@
std::string hardware = android::base::GetProperty("ro.hardware", "");
parser.ParseConfig("/ueventd." + hardware + ".rc");
+ auto boot_devices = fs_mgr_get_boot_devices();
return DeviceHandler(std::move(dev_permissions), std::move(sysfs_permissions),
- std::move(subsystems), true);
+ std::move(subsystems), std::move(boot_devices), true);
}
int ueventd_main(int argc, char** argv) {
diff --git a/init/util.cpp b/init/util.cpp
index 4455b2e..5f2b87d 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -33,6 +33,7 @@
#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>
@@ -43,8 +44,6 @@
#include "reboot.h"
#if defined(__ANDROID__)
-#include <android-base/properties.h>
-
#include "selinux.h"
#else
#include "host_init_stubs.h"
diff --git a/libasyncio/Android.bp b/libasyncio/Android.bp
index 8a2afea..4ab439d 100644
--- a/libasyncio/Android.bp
+++ b/libasyncio/Android.bp
@@ -27,6 +27,7 @@
name: "libasyncio",
defaults: ["libasyncio_defaults"],
vendor_available: true,
+ recovery_available: true,
host_supported: true,
srcs: [
"AsyncIO.cpp",
diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp
index 4987ba1..b4bf35f 100644
--- a/libbacktrace/Android.bp
+++ b/libbacktrace/Android.bp
@@ -51,12 +51,14 @@
cc_library_headers {
name: "libbacktrace_headers",
vendor_available: true,
+ recovery_available: true,
export_include_dirs: ["include"],
}
cc_library {
name: "libbacktrace",
vendor_available: false,
+ recovery_available: true,
vndk: {
enabled: true,
support_system_process: true,
@@ -101,8 +103,6 @@
include_dirs: [
"art/runtime",
],
-
- header_libs: ["jni_headers"],
},
android: {
static_libs: ["libasync_safe"],
@@ -111,6 +111,10 @@
cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
exclude_shared_libs: ["libdexfile"],
},
+ recovery: {
+ cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
+ exclude_shared_libs: ["libdexfile"],
+ },
},
whole_static_libs: ["libdemangle"],
}
diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp
index 1e3d379..f78a31f 100644
--- a/libbacktrace/backtrace_test.cpp
+++ b/libbacktrace/backtrace_test.cpp
@@ -46,6 +46,7 @@
#include <android-base/macros.h>
#include <android-base/stringprintf.h>
+#include <android-base/test_utils.h>
#include <android-base/unique_fd.h>
#include <cutils/atomic.h>
#include <cutils/threads.h>
@@ -1186,49 +1187,45 @@
ASSERT_TRUE(expected_functions.empty()) << "Not all functions found in shared library.";
}
-static const char* CopySharedLibrary() {
-#if defined(__LP64__)
- const char* lib_name = "lib64";
-#else
- const char* lib_name = "lib";
-#endif
+static void CopySharedLibrary(const char* tmp_dir, std::string* tmp_so_name) {
+ std::string system_dir;
#if defined(__BIONIC__)
- const char* tmp_so_name = "/data/local/tmp/libbacktrace_test.so";
- std::string cp_cmd = android::base::StringPrintf("cp /system/%s/libbacktrace_test.so %s",
- lib_name, tmp_so_name);
+ system_dir = "/system/lib";
#else
- const char* tmp_so_name = "/tmp/libbacktrace_test.so";
- if (getenv("ANDROID_HOST_OUT") == NULL) {
- fprintf(stderr, "ANDROID_HOST_OUT not set, make sure you run lunch.");
- return nullptr;
- }
- std::string cp_cmd = android::base::StringPrintf("cp %s/%s/libbacktrace_test.so %s",
- getenv("ANDROID_HOST_OUT"), lib_name,
- tmp_so_name);
+ const char* host_out_env = getenv("ANDROID_HOST_OUT");
+ ASSERT_TRUE(host_out_env != nullptr);
+ system_dir = std::string(host_out_env) + "/lib";
#endif
- // Copy the shared so to a tempory directory.
- system(cp_cmd.c_str());
+#if defined(__LP64__)
+ system_dir += "64";
+#endif
- return tmp_so_name;
+ *tmp_so_name = std::string(tmp_dir) + "/libbacktrace_test.so";
+ std::string cp_cmd =
+ android::base::StringPrintf("cp %s/libbacktrace_test.so %s", system_dir.c_str(), tmp_dir);
+
+ // Copy the shared so to a tempory directory.
+ ASSERT_EQ(0, system(cp_cmd.c_str()));
}
TEST(libbacktrace, check_unreadable_elf_local) {
- const char* tmp_so_name = CopySharedLibrary();
- ASSERT_TRUE(tmp_so_name != nullptr);
+ TemporaryDir td;
+ std::string tmp_so_name;
+ ASSERT_NO_FATAL_FAILURE(CopySharedLibrary(td.path, &tmp_so_name));
struct stat buf;
- ASSERT_TRUE(stat(tmp_so_name, &buf) != -1);
+ ASSERT_TRUE(stat(tmp_so_name.c_str(), &buf) != -1);
uint64_t map_size = buf.st_size;
- int fd = open(tmp_so_name, O_RDONLY);
+ int fd = open(tmp_so_name.c_str(), O_RDONLY);
ASSERT_TRUE(fd != -1);
void* map = mmap(nullptr, map_size, PROT_READ | PROT_EXEC, MAP_PRIVATE, fd, 0);
ASSERT_TRUE(map != MAP_FAILED);
close(fd);
- ASSERT_TRUE(unlink(tmp_so_name) != -1);
+ ASSERT_TRUE(unlink(tmp_so_name.c_str()) != -1);
std::vector<std::string> found_functions;
std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS,
@@ -1256,32 +1253,33 @@
}
TEST(libbacktrace, check_unreadable_elf_remote) {
- const char* tmp_so_name = CopySharedLibrary();
- ASSERT_TRUE(tmp_so_name != nullptr);
+ TemporaryDir td;
+ std::string tmp_so_name;
+ ASSERT_NO_FATAL_FAILURE(CopySharedLibrary(td.path, &tmp_so_name));
g_ready = 0;
struct stat buf;
- ASSERT_TRUE(stat(tmp_so_name, &buf) != -1);
+ ASSERT_TRUE(stat(tmp_so_name.c_str(), &buf) != -1);
uint64_t map_size = buf.st_size;
pid_t pid;
if ((pid = fork()) == 0) {
- int fd = open(tmp_so_name, O_RDONLY);
+ int fd = open(tmp_so_name.c_str(), O_RDONLY);
if (fd == -1) {
- fprintf(stderr, "Failed to open file %s: %s\n", tmp_so_name, strerror(errno));
- unlink(tmp_so_name);
+ fprintf(stderr, "Failed to open file %s: %s\n", tmp_so_name.c_str(), strerror(errno));
+ unlink(tmp_so_name.c_str());
exit(0);
}
void* map = mmap(nullptr, map_size, PROT_READ | PROT_EXEC, MAP_PRIVATE, fd, 0);
if (map == MAP_FAILED) {
fprintf(stderr, "Failed to map in memory: %s\n", strerror(errno));
- unlink(tmp_so_name);
+ unlink(tmp_so_name.c_str());
exit(0);
}
close(fd);
- if (unlink(tmp_so_name) == -1) {
+ if (unlink(tmp_so_name.c_str()) == -1) {
fprintf(stderr, "Failed to unlink: %s\n", strerror(errno));
exit(0);
}
@@ -1394,11 +1392,13 @@
typedef int (*test_func_t)(int, int, int, int, void (*)(void*), void*);
TEST(libbacktrace, unwind_through_unreadable_elf_local) {
- const char* tmp_so_name = CopySharedLibrary();
- ASSERT_TRUE(tmp_so_name != nullptr);
- void* lib_handle = dlopen(tmp_so_name, RTLD_NOW);
+ TemporaryDir td;
+ std::string tmp_so_name;
+ ASSERT_NO_FATAL_FAILURE(CopySharedLibrary(td.path, &tmp_so_name));
+
+ void* lib_handle = dlopen(tmp_so_name.c_str(), RTLD_NOW);
ASSERT_TRUE(lib_handle != nullptr);
- ASSERT_TRUE(unlink(tmp_so_name) != -1);
+ ASSERT_TRUE(unlink(tmp_so_name.c_str()) != -1);
test_func_t test_func;
test_func = reinterpret_cast<test_func_t>(dlsym(lib_handle, "test_level_one"));
@@ -1411,11 +1411,13 @@
}
TEST(libbacktrace, unwind_through_unreadable_elf_remote) {
- const char* tmp_so_name = CopySharedLibrary();
- ASSERT_TRUE(tmp_so_name != nullptr);
- void* lib_handle = dlopen(tmp_so_name, RTLD_NOW);
+ TemporaryDir td;
+ std::string tmp_so_name;
+ ASSERT_NO_FATAL_FAILURE(CopySharedLibrary(td.path, &tmp_so_name));
+
+ void* lib_handle = dlopen(tmp_so_name.c_str(), RTLD_NOW);
ASSERT_TRUE(lib_handle != nullptr);
- ASSERT_TRUE(unlink(tmp_so_name) != -1);
+ ASSERT_TRUE(unlink(tmp_so_name.c_str()) != -1);
test_func_t test_func;
test_func = reinterpret_cast<test_func_t>(dlsym(lib_handle, "test_level_one"));
@@ -1444,7 +1446,8 @@
size_t frame_num;
if (FindFuncFrameInBacktrace(backtrace.get(), reinterpret_cast<uint64_t>(test_func),
- &frame_num)) {
+ &frame_num) &&
+ frame_num != 0) {
VerifyUnreadableElfFrame(backtrace.get(), reinterpret_cast<uint64_t>(test_func), frame_num);
done = true;
}
diff --git a/libcrypto_utils/Android.bp b/libcrypto_utils/Android.bp
index 47de12a..e47560f 100644
--- a/libcrypto_utils/Android.bp
+++ b/libcrypto_utils/Android.bp
@@ -17,6 +17,7 @@
cc_library {
name: "libcrypto_utils",
vendor_available: true,
+ recovery_available: true,
vndk: {
enabled: true,
},
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index e823257..cdbb65f 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -32,6 +32,7 @@
cc_library_headers {
name: "libcutils_headers",
vendor_available: true,
+ recovery_available: true,
host_supported: true,
export_include_dirs: ["include"],
target: {
@@ -54,6 +55,7 @@
enabled: true,
support_system_process: true,
},
+ recovery_available: true,
host_supported: true,
srcs: [
"config_utils.cpp",
diff --git a/libkeyutils/Android.bp b/libkeyutils/Android.bp
index f3593ff..b388e95 100644
--- a/libkeyutils/Android.bp
+++ b/libkeyutils/Android.bp
@@ -2,6 +2,7 @@
name: "libkeyutils",
cflags: ["-Werror"],
defaults: ["linux_bionic_supported"],
+ recovery_available: true,
export_include_dirs: ["include/"],
local_include_dirs: ["include/"],
srcs: ["keyutils.cpp"],
diff --git a/liblog/Android.bp b/liblog/Android.bp
index 7d9e306..4a165a0 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -46,6 +46,7 @@
name: "liblog_headers",
host_supported: true,
vendor_available: true,
+ recovery_available: true,
export_include_dirs: ["include"],
target: {
windows: {
@@ -65,7 +66,7 @@
cc_library {
name: "liblog",
host_supported: true,
-
+ recovery_available: true,
srcs: liblog_sources,
target: {
@@ -82,6 +83,7 @@
},
android_arm: {
// TODO: This is to work around b/24465209. Remove after root cause is fixed
+ pack_relocations: false,
ldflags: ["-Wl,--hash-style=both"],
},
windows: {
diff --git a/liblog/logprint.c b/liblog/logprint.c
index a2839bf..7937cb1 100644
--- a/liblog/logprint.c
+++ b/liblog/logprint.c
@@ -1632,8 +1632,10 @@
prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), "\x1B[38;5;%dm",
colorFromPri(entry->priority));
prefixLen = MIN(prefixLen, sizeof(prefixBuf));
- suffixLen = snprintf(suffixBuf, sizeof(suffixBuf), "\x1B[0m");
- suffixLen = MIN(suffixLen, sizeof(suffixBuf));
+
+ const char suffixContents[] = "\x1B[0m";
+ strcpy(suffixBuf, suffixContents);
+ suffixLen = strlen(suffixContents);
}
char uid[16];
diff --git a/libmemunreachable/README.md b/libmemunreachable/README.md
index ae8fa94..9cc0c9b 100644
--- a/libmemunreachable/README.md
+++ b/libmemunreachable/README.md
@@ -12,6 +12,27 @@
Usage
-------
+### In Android apps ###
+
+libmemunreachble is loaded by zygote and can be triggered with `dumpsys -t 600 meminfo --unreachable [process]`.
+
+To enable malloc\_debug backtraces on allocations for a single app process on a userdebug device, use:
+```
+adb root
+adb shell setprop libc.debug.malloc.program app_process
+adb shell setprop wrap.[process] "\$\@"
+adb shell setprop libc.debug.malloc.options backtrace=4
+```
+
+Kill and restart the app, trigger the leak, and then run `dumpsys -t 600 meminfo --unreachable [process]`.
+
+To disable malloc\_debug:
+```
+adb shell setprop libc.debug.malloc.options "''"
+adb shell setprop libc.debug.malloc.program "''"
+adb shell setprop wrap.[process] "''"
+```
+
### C interface ###
#### `bool LogUnreachableMemory(bool log_contents, size_t limit)` ####
@@ -23,7 +44,7 @@
### C++ interface ###
-####`bool GetUnreachableMemory(UnreachableMemoryInfo& info, size_t limit = 100)`####
+#### `bool GetUnreachableMemory(UnreachableMemoryInfo& info, size_t limit = 100)` ####
Updates an `UnreachableMemoryInfo` object with information on leaks, including details on up to `limit` leaks. Returns true if leak detection succeeded.
#### `std::string GetUnreachableMemoryString(bool log_contents = false, size_t limit = 100)` ####
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index 0ebb226..7fef106 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -46,6 +46,8 @@
"%s:%d: %s CHECK '" #predicate "' failed.",\
__FILE__, __LINE__, __FUNCTION__)
+using namespace std::string_literals;
+
namespace android {
#if defined(__ANDROID__)
@@ -236,10 +238,15 @@
// Different name is useful for debugging
namespace_name = kVendorClassloaderNamespaceName;
ALOGD("classloader namespace configured for unbundled vendor apk. library_path=%s", library_path.c_str());
- } else if (!oem_public_libraries_.empty()) {
- // oem_public_libraries are NOT available to vendor apks, otherwise it
+ } else {
+ // oem and product public libraries are NOT available to vendor apks, otherwise it
// would be system->vendor violation.
- system_exposed_libraries = system_exposed_libraries + ":" + oem_public_libraries_.c_str();
+ if (!oem_public_libraries_.empty()) {
+ system_exposed_libraries = system_exposed_libraries + ':' + oem_public_libraries_;
+ }
+ if (!product_public_libraries_.empty()) {
+ system_exposed_libraries = system_exposed_libraries + ':' + product_public_libraries_;
+ }
}
NativeLoaderNamespace native_loader_ns;
@@ -351,6 +358,8 @@
std::string vndksp_native_libraries_system_config =
root_dir + kVndkspNativeLibrariesSystemConfigPathFromRoot;
+ std::string product_public_native_libraries_dir = "/product/etc";
+
std::string error_msg;
LOG_ALWAYS_FATAL_IF(
!ReadConfig(public_native_libraries_system_config, &sonames, always_true, &error_msg),
@@ -373,7 +382,7 @@
//
// TODO(dimitry): this is a bit misleading since we do not know
// if the vendor public library is going to be opened from /vendor/lib
- // we might as well end up loading them from /system/lib
+ // we might as well end up loading them from /system/lib or /product/lib
// For now we rely on CTS test to catch things like this but
// it should probably be addressed in the future.
for (const auto& soname : sonames) {
@@ -387,48 +396,15 @@
// system libs that are exposed to apps. The libs in the txt files must be
// named as lib<name>.<companyname>.so.
sonames.clear();
- std::string dirname = base::Dirname(public_native_libraries_system_config);
- std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(dirname.c_str()), closedir);
- if (dir != nullptr) {
- // Failing to opening the dir is not an error, which can happen in
- // webview_zygote.
- struct dirent* ent;
- while ((ent = readdir(dir.get())) != nullptr) {
- if (ent->d_type != DT_REG && ent->d_type != DT_LNK) {
- continue;
- }
- const std::string filename(ent->d_name);
- if (android::base::StartsWith(filename, kPublicNativeLibrariesExtensionConfigPrefix) &&
- android::base::EndsWith(filename, kPublicNativeLibrariesExtensionConfigSuffix)) {
- const size_t start = kPublicNativeLibrariesExtensionConfigPrefixLen;
- const size_t end = filename.size() - kPublicNativeLibrariesExtensionConfigSuffixLen;
- const std::string company_name = filename.substr(start, end - start);
- const std::string config_file_path = dirname + "/" + filename;
- LOG_ALWAYS_FATAL_IF(
- company_name.empty(),
- "Error extracting company name from public native library list file path \"%s\"",
- config_file_path.c_str());
- LOG_ALWAYS_FATAL_IF(
- !ReadConfig(
- config_file_path, &sonames,
- [&company_name](const std::string& soname, std::string* error_msg) {
- if (android::base::StartsWith(soname, "lib") &&
- android::base::EndsWith(soname, "." + company_name + ".so")) {
- return true;
- } else {
- *error_msg = "Library name \"" + soname +
- "\" does not end with the company name: " + company_name + ".";
- return false;
- }
- },
- &error_msg),
- "Error reading public native library list from \"%s\": %s", config_file_path.c_str(),
- error_msg.c_str());
- }
- }
- }
+ ReadExtensionLibraries(base::Dirname(public_native_libraries_system_config).c_str(), &sonames);
oem_public_libraries_ = base::Join(sonames, ':');
+ // read /product/etc/public.libraries-<companyname>.txt which contain partner defined
+ // product libs that are exposed to apps.
+ sonames.clear();
+ ReadExtensionLibraries(product_public_native_libraries_dir.c_str(), &sonames);
+ product_public_libraries_ = base::Join(sonames, ':');
+
// Insert VNDK version to llndk and vndksp config file names.
insert_vndk_version_str(&llndk_native_libraries_system_config);
insert_vndk_version_str(&vndksp_native_libraries_system_config);
@@ -448,11 +424,54 @@
vendor_public_libraries_ = base::Join(sonames, ':');
}
- void Reset() {
- namespaces_.clear();
- }
+ void Reset() { namespaces_.clear(); }
private:
+ void ReadExtensionLibraries(const char* dirname, std::vector<std::string>* sonames) {
+ std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(dirname), closedir);
+ if (dir != nullptr) {
+ // Failing to opening the dir is not an error, which can happen in
+ // webview_zygote.
+ while (struct dirent* ent = readdir(dir.get())) {
+ if (ent->d_type != DT_REG && ent->d_type != DT_LNK) {
+ continue;
+ }
+ const std::string filename(ent->d_name);
+ if (android::base::StartsWith(filename, kPublicNativeLibrariesExtensionConfigPrefix) &&
+ android::base::EndsWith(filename, kPublicNativeLibrariesExtensionConfigSuffix)) {
+ const size_t start = kPublicNativeLibrariesExtensionConfigPrefixLen;
+ const size_t end = filename.size() - kPublicNativeLibrariesExtensionConfigSuffixLen;
+ const std::string company_name = filename.substr(start, end - start);
+ const std::string config_file_path = dirname + "/"s + filename;
+ LOG_ALWAYS_FATAL_IF(
+ company_name.empty(),
+ "Error extracting company name from public native library list file path \"%s\"",
+ config_file_path.c_str());
+
+ std::string error_msg;
+
+ LOG_ALWAYS_FATAL_IF(
+ !ReadConfig(
+ config_file_path, sonames,
+ [&company_name](const std::string& soname, std::string* error_msg) {
+ if (android::base::StartsWith(soname, "lib") &&
+ android::base::EndsWith(soname, "." + company_name + ".so")) {
+ return true;
+ } else {
+ *error_msg = "Library name \"" + soname +
+ "\" does not end with the company name: " + company_name + ".";
+ return false;
+ }
+ },
+ &error_msg),
+ "Error reading public native library list from \"%s\": %s", config_file_path.c_str(),
+ error_msg.c_str());
+ }
+ }
+ }
+ }
+
+
bool ReadConfig(const std::string& configFile, std::vector<std::string>* sonames,
const std::function<bool(const std::string& /* soname */,
std::string* /* error_msg */)>& check_soname,
@@ -559,6 +578,7 @@
std::string system_public_libraries_;
std::string vendor_public_libraries_;
std::string oem_public_libraries_;
+ std::string product_public_libraries_;
std::string system_llndk_libraries_;
std::string system_vndksp_libraries_;
diff --git a/libnativeloader/test/Android.bp b/libnativeloader/test/Android.bp
index 5cf88b0..d528f30 100644
--- a/libnativeloader/test/Android.bp
+++ b/libnativeloader/test/Android.bp
@@ -49,3 +49,23 @@
"libbase",
],
}
+
+cc_library {
+ name: "libfoo.product1",
+ srcs: ["test.cpp"],
+ cflags: ["-DLIBNAME=\"libfoo.product1.so\""],
+ product_specific: true,
+ shared_libs: [
+ "libbase",
+ ],
+}
+
+cc_library {
+ name: "libbar.product1",
+ srcs: ["test.cpp"],
+ cflags: ["-DLIBNAME=\"libbar.product1.so\""],
+ product_specific: true,
+ shared_libs: [
+ "libbase",
+ ],
+}
diff --git a/libnativeloader/test/Android.mk b/libnativeloader/test/Android.mk
index e625454..65e7b09 100644
--- a/libnativeloader/test/Android.mk
+++ b/libnativeloader/test/Android.mk
@@ -30,6 +30,13 @@
include $(BUILD_PREBUILT)
include $(CLEAR_VARS)
+LOCAL_MODULE := public.libraries-product1.txt
+LOCAL_SRC_FILES:= $(LOCAL_MODULE)
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_PRODUCT_ETC)
+include $(BUILD_PREBUILT)
+
+include $(CLEAR_VARS)
LOCAL_PACKAGE_NAME := oemlibrarytest-system
LOCAL_MODULE_TAGS := tests
LOCAL_MANIFEST_FILE := system/AndroidManifest.xml
diff --git a/libnativeloader/test/public.libraries-product1.txt b/libnativeloader/test/public.libraries-product1.txt
new file mode 100644
index 0000000..358154c
--- /dev/null
+++ b/libnativeloader/test/public.libraries-product1.txt
@@ -0,0 +1,2 @@
+libfoo.product1.so
+libbar.product1.so
diff --git a/libnativeloader/test/src/android/test/app/TestActivity.java b/libnativeloader/test/src/android/test/app/TestActivity.java
index 214892d..a7a455d 100644
--- a/libnativeloader/test/src/android/test/app/TestActivity.java
+++ b/libnativeloader/test/src/android/test/app/TestActivity.java
@@ -29,6 +29,8 @@
tryLoadingLib("bar.oem1");
tryLoadingLib("foo.oem2");
tryLoadingLib("bar.oem2");
+ tryLoadingLib("foo.product1");
+ tryLoadingLib("bar.product1");
}
private void tryLoadingLib(String name) {
diff --git a/libpackagelistparser/Android.bp b/libpackagelistparser/Android.bp
index 27693b3..c38594a 100644
--- a/libpackagelistparser/Android.bp
+++ b/libpackagelistparser/Android.bp
@@ -1,6 +1,7 @@
cc_library {
name: "libpackagelistparser",
+ recovery_available: true,
srcs: ["packagelistparser.c"],
cflags: [
"-Wall",
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 6dfa697..1cebb5d 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -39,12 +39,14 @@
#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 <private/android_filesystem_config.h>
#include <processgroup/processgroup.h>
+using android::base::GetBoolProperty;
using android::base::StartsWith;
using android::base::StringPrintf;
using android::base::WriteStringToFile;
@@ -62,12 +64,20 @@
static const std::string& GetCgroupRootPath() {
static std::string cgroup_root_path;
std::call_once(init_path_flag, [&]() {
- // Check if mem cgroup is mounted, only then check for write-access to avoid
- // SELinux denials
+ // low-ram devices use per-app memcg by default, unlike high-end ones
+ bool low_ram_device = GetBoolProperty("ro.config.low_ram", false);
+ bool per_app_memcg =
+ GetBoolProperty("ro.config.per_app_memcg", low_ram_device);
+ if (per_app_memcg) {
+ // Check if mem cgroup is mounted, only then check for
+ // write-access to avoid SELinux denials
cgroup_root_path =
- (access(MEM_CGROUP_TASKS, F_OK) || access(MEM_CGROUP_PATH, W_OK) ? ACCT_CGROUP_PATH
- : MEM_CGROUP_PATH);
- });
+ (access(MEM_CGROUP_TASKS, F_OK) || access(MEM_CGROUP_PATH, W_OK) ?
+ ACCT_CGROUP_PATH : MEM_CGROUP_PATH);
+ } else {
+ cgroup_root_path = ACCT_CGROUP_PATH;
+ }
+ });
return cgroup_root_path;
}
diff --git a/libprocinfo/Android.bp b/libprocinfo/Android.bp
index d776b3d..15f03d0 100644
--- a/libprocinfo/Android.bp
+++ b/libprocinfo/Android.bp
@@ -27,6 +27,7 @@
name: "libprocinfo",
defaults: ["libprocinfo_defaults"],
vendor_available: true,
+ recovery_available: true,
vndk: {
enabled: true,
},
diff --git a/libsparse/Android.bp b/libsparse/Android.bp
index b894656..c7c089f 100644
--- a/libsparse/Android.bp
+++ b/libsparse/Android.bp
@@ -3,6 +3,7 @@
cc_library {
name: "libsparse",
host_supported: true,
+ recovery_available: true,
unique_host_soname: true,
srcs: [
"backed_block.c",
diff --git a/libstats/Android.bp b/libstats/Android.bp
new file mode 100644
index 0000000..d58f294
--- /dev/null
+++ b/libstats/Android.bp
@@ -0,0 +1,37 @@
+//
+// Copyright (C) 2018 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.
+//
+
+// ==========================================================
+// Native library to write stats log to statsd socket
+// ==========================================================
+cc_library_static {
+ name: "libstatssocket",
+ srcs: [
+ "stats_event_list.c",
+ "statsd_writer.c",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-DLIBLOG_LOG_TAG=1006",
+ "-DWRITE_TO_STATSD=1",
+ "-DWRITE_TO_LOGD=0",
+ ],
+ export_include_dirs: ["include"],
+ shared_libs: [
+ "liblog",
+ ],
+}
diff --git a/libstats/OWNERS b/libstats/OWNERS
new file mode 100644
index 0000000..ed06fbc
--- /dev/null
+++ b/libstats/OWNERS
@@ -0,0 +1,4 @@
+bookatz@google.com
+joeo@google.com
+yaochen@google.com
+yanglu@google.com
diff --git a/libstats/include/stats_event_list.h b/libstats/include/stats_event_list.h
new file mode 100644
index 0000000..5d174ae
--- /dev/null
+++ b/libstats/include/stats_event_list.h
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2018, 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_STATS_LOG_STATS_EVENT_LIST_H
+#define ANDROID_STATS_LOG_STATS_EVENT_LIST_H
+
+#include <log/log_event_list.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+void reset_log_context(android_log_context ctx);
+int write_to_logger(android_log_context context, log_id_t id);
+
+#ifdef __cplusplus
+}
+#endif
+
+#ifdef __cplusplus
+/**
+ * A copy of android_log_event_list class.
+ *
+ * android_log_event_list is going to be deprecated soon, so copy it here to
+ * avoid creating dependency on upstream code. TODO(b/78304629): Rewrite this
+ * code.
+ */
+class stats_event_list {
+ private:
+ android_log_context ctx;
+ int ret;
+
+ stats_event_list(const stats_event_list&) = delete;
+ void operator=(const stats_event_list&) = delete;
+
+ public:
+ explicit stats_event_list(int tag) : ret(0) {
+ ctx = create_android_logger(static_cast<uint32_t>(tag));
+ }
+ explicit stats_event_list(log_msg& log_msg) : ret(0) {
+ ctx = create_android_log_parser(log_msg.msg() + sizeof(uint32_t),
+ log_msg.entry.len - sizeof(uint32_t));
+ }
+ ~stats_event_list() { android_log_destroy(&ctx); }
+
+ int close() {
+ int retval = android_log_destroy(&ctx);
+ if (retval < 0) {
+ ret = retval;
+ }
+ return retval;
+ }
+
+ /* To allow above C calls to use this class as parameter */
+ operator android_log_context() const { return ctx; }
+
+ /* return errors or transmit status */
+ int status() const { return ret; }
+
+ int begin() {
+ int retval = android_log_write_list_begin(ctx);
+ if (retval < 0) {
+ ret = retval;
+ }
+ return ret;
+ }
+ int end() {
+ int retval = android_log_write_list_end(ctx);
+ if (retval < 0) {
+ ret = retval;
+ }
+ return ret;
+ }
+
+ stats_event_list& operator<<(int32_t value) {
+ int retval = android_log_write_int32(ctx, value);
+ if (retval < 0) {
+ ret = retval;
+ }
+ return *this;
+ }
+
+ stats_event_list& operator<<(uint32_t value) {
+ int retval = android_log_write_int32(ctx, static_cast<int32_t>(value));
+ if (retval < 0) {
+ ret = retval;
+ }
+ return *this;
+ }
+
+ stats_event_list& operator<<(bool value) {
+ int retval = android_log_write_int32(ctx, value ? 1 : 0);
+ if (retval < 0) {
+ ret = retval;
+ }
+ return *this;
+ }
+
+ stats_event_list& operator<<(int64_t value) {
+ int retval = android_log_write_int64(ctx, value);
+ if (retval < 0) {
+ ret = retval;
+ }
+ return *this;
+ }
+
+ stats_event_list& operator<<(uint64_t value) {
+ int retval = android_log_write_int64(ctx, static_cast<int64_t>(value));
+ if (retval < 0) {
+ ret = retval;
+ }
+ return *this;
+ }
+
+ stats_event_list& operator<<(const char* value) {
+ int retval = android_log_write_string8(ctx, value);
+ if (retval < 0) {
+ ret = retval;
+ }
+ return *this;
+ }
+
+#if defined(_USING_LIBCXX)
+ stats_event_list& operator<<(const std::string& value) {
+ int retval = android_log_write_string8_len(ctx, value.data(), value.length());
+ if (retval < 0) {
+ ret = retval;
+ }
+ return *this;
+ }
+#endif
+
+ stats_event_list& operator<<(float value) {
+ int retval = android_log_write_float32(ctx, value);
+ if (retval < 0) {
+ ret = retval;
+ }
+ return *this;
+ }
+
+ int write(log_id_t id = LOG_ID_EVENTS) {
+ /* facilitate -EBUSY retry */
+ if ((ret == -EBUSY) || (ret > 0)) {
+ ret = 0;
+ }
+ int retval = write_to_logger(ctx, id);
+ /* existing errors trump transmission errors */
+ if (!ret) {
+ ret = retval;
+ }
+ return ret;
+ }
+
+ /*
+ * Append<Type> methods removes any integer promotion
+ * confusion, and adds access to string with length.
+ * Append methods are also added for all types for
+ * convenience.
+ */
+
+ bool AppendInt(int32_t value) {
+ int retval = android_log_write_int32(ctx, value);
+ if (retval < 0) {
+ ret = retval;
+ }
+ return ret >= 0;
+ }
+
+ bool AppendLong(int64_t value) {
+ int retval = android_log_write_int64(ctx, value);
+ if (retval < 0) {
+ ret = retval;
+ }
+ return ret >= 0;
+ }
+
+ bool AppendString(const char* value) {
+ int retval = android_log_write_string8(ctx, value);
+ if (retval < 0) {
+ ret = retval;
+ }
+ return ret >= 0;
+ }
+
+ bool AppendString(const char* value, size_t len) {
+ int retval = android_log_write_string8_len(ctx, value, len);
+ if (retval < 0) {
+ ret = retval;
+ }
+ return ret >= 0;
+ }
+
+#if defined(_USING_LIBCXX)
+ bool AppendString(const std::string& value) {
+ int retval = android_log_write_string8_len(ctx, value.data(), value.length());
+ if (retval < 0) {
+ ret = retval;
+ }
+ return ret;
+ }
+
+ bool Append(const std::string& value) {
+ int retval = android_log_write_string8_len(ctx, value.data(), value.length());
+ if (retval < 0) {
+ ret = retval;
+ }
+ return ret;
+ }
+#endif
+
+ bool AppendFloat(float value) {
+ int retval = android_log_write_float32(ctx, value);
+ if (retval < 0) {
+ ret = retval;
+ }
+ return ret >= 0;
+ }
+
+ template <typename Tvalue>
+ bool Append(Tvalue value) {
+ *this << value;
+ return ret >= 0;
+ }
+
+ bool Append(const char* value, size_t len) {
+ int retval = android_log_write_string8_len(ctx, value, len);
+ if (retval < 0) {
+ ret = retval;
+ }
+ return ret >= 0;
+ }
+
+ android_log_list_element read() { return android_log_read_next(ctx); }
+ android_log_list_element peek() { return android_log_peek_next(ctx); }
+};
+
+#endif
+#endif // ANDROID_STATS_LOG_STATS_EVENT_LIST_H
diff --git a/libstats/stats_event_list.c b/libstats/stats_event_list.c
new file mode 100644
index 0000000..3d746db
--- /dev/null
+++ b/libstats/stats_event_list.c
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2018, 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 "include/stats_event_list.h"
+
+#include <string.h>
+#include "statsd_writer.h"
+
+#define MAX_EVENT_PAYLOAD (LOGGER_ENTRY_MAX_PAYLOAD - sizeof(int32_t))
+
+typedef struct {
+ uint32_t tag;
+ unsigned pos; /* Read/write position into buffer */
+ unsigned count[ANDROID_MAX_LIST_NEST_DEPTH + 1]; /* Number of elements */
+ unsigned list[ANDROID_MAX_LIST_NEST_DEPTH + 1]; /* pos for list counter */
+ unsigned list_nest_depth;
+ unsigned len; /* Length or raw buffer. */
+ bool overflow;
+ bool list_stop; /* next call decrement list_nest_depth and issue a stop */
+ enum {
+ kAndroidLoggerRead = 1,
+ kAndroidLoggerWrite = 2,
+ } read_write_flag;
+ uint8_t storage[LOGGER_ENTRY_MAX_PAYLOAD];
+} android_log_context_internal;
+
+extern struct android_log_transport_write statsdLoggerWrite;
+
+static int __write_to_statsd_init(struct iovec* vec, size_t nr);
+static int (*write_to_statsd)(struct iovec* vec, size_t nr) = __write_to_statsd_init;
+
+// Similar to create_android_logger(), but instead of allocation a new buffer,
+// this function resets the buffer for resuse.
+void reset_log_context(android_log_context ctx) {
+ if (!ctx) {
+ return;
+ }
+ android_log_context_internal* context = (android_log_context_internal*)(ctx);
+ uint32_t tag = context->tag;
+ memset(context, 0, sizeof(android_log_context_internal));
+
+ context->tag = tag;
+ context->read_write_flag = kAndroidLoggerWrite;
+ size_t needed = sizeof(uint8_t) + sizeof(uint8_t);
+ if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
+ context->overflow = true;
+ }
+ /* Everything is a list */
+ context->storage[context->pos + 0] = EVENT_TYPE_LIST;
+ context->list[0] = context->pos + 1;
+ context->pos += needed;
+}
+
+int stats_write_list(android_log_context ctx) {
+ android_log_context_internal* context;
+ const char* msg;
+ ssize_t len;
+
+ context = (android_log_context_internal*)(ctx);
+ if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+ return -EBADF;
+ }
+
+ if (context->list_nest_depth) {
+ return -EIO;
+ }
+
+ /* NB: if there was overflow, then log is truncated. Nothing reported */
+ context->storage[1] = context->count[0];
+ len = context->len = context->pos;
+ msg = (const char*)context->storage;
+ /* it's not a list */
+ if (context->count[0] <= 1) {
+ len -= sizeof(uint8_t) + sizeof(uint8_t);
+ if (len < 0) {
+ len = 0;
+ }
+ msg += sizeof(uint8_t) + sizeof(uint8_t);
+ }
+
+ struct iovec vec[2];
+ vec[0].iov_base = &context->tag;
+ vec[0].iov_len = sizeof(context->tag);
+ vec[1].iov_base = (void*)msg;
+ vec[1].iov_len = len;
+ return write_to_statsd(vec, 2);
+}
+
+int write_to_logger(android_log_context ctx, log_id_t id) {
+ int retValue = 0;
+
+ if (WRITE_TO_LOGD) {
+ retValue = android_log_write_list(ctx, id);
+ }
+
+ if (WRITE_TO_STATSD) {
+ // log_event_list's cast operator is overloaded.
+ int ret = stats_write_list(ctx);
+ // In debugging phase, we may write to both logd and statsd. Prefer to
+ // return statsd socket write error code here.
+ if (ret < 0) {
+ retValue = ret;
+ }
+ }
+
+ return retValue;
+}
+
+/* log_init_lock assumed */
+static int __write_to_statsd_initialize_locked() {
+ if (!statsdLoggerWrite.open || ((*statsdLoggerWrite.open)() < 0)) {
+ if (statsdLoggerWrite.close) {
+ (*statsdLoggerWrite.close)();
+ return -ENODEV;
+ }
+ }
+ return 1;
+}
+
+static int __write_to_stats_daemon(struct iovec* vec, size_t nr) {
+ int save_errno;
+ struct timespec ts;
+ size_t len, i;
+
+ for (len = i = 0; i < nr; ++i) {
+ len += vec[i].iov_len;
+ }
+ if (!len) {
+ return -EINVAL;
+ }
+
+ save_errno = errno;
+ clock_gettime(CLOCK_REALTIME, &ts);
+
+ int ret = (int)(*statsdLoggerWrite.write)(&ts, vec, nr);
+ errno = save_errno;
+ return ret;
+}
+
+static int __write_to_statsd_init(struct iovec* vec, size_t nr) {
+ int ret, save_errno = errno;
+
+ statsd_writer_init_lock();
+
+ if (write_to_statsd == __write_to_statsd_init) {
+ ret = __write_to_statsd_initialize_locked();
+ if (ret < 0) {
+ statsd_writer_init_unlock();
+ errno = save_errno;
+ return ret;
+ }
+
+ write_to_statsd = __write_to_stats_daemon;
+ }
+
+ statsd_writer_init_unlock();
+
+ ret = write_to_statsd(vec, nr);
+ errno = save_errno;
+ return ret;
+}
diff --git a/libstats/statsd_writer.c b/libstats/statsd_writer.c
new file mode 100644
index 0000000..9953bba
--- /dev/null
+++ b/libstats/statsd_writer.c
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2018, 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 "statsd_writer.h"
+
+#include <cutils/sockets.h>
+#include <endian.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <poll.h>
+#include <private/android_filesystem_config.h>
+#include <private/android_logger.h>
+#include <stdarg.h>
+#include <stdatomic.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <time.h>
+#include <unistd.h>
+
+/* branchless on many architectures. */
+#define min(x, y) ((y) ^ (((x) ^ (y)) & -((x) < (y))))
+
+static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER;
+
+void statsd_writer_init_lock() {
+ /*
+ * If we trigger a signal handler in the middle of locked activity and the
+ * signal handler logs a message, we could get into a deadlock state.
+ */
+ pthread_mutex_lock(&log_init_lock);
+}
+
+int statd_writer_trylock() {
+ return pthread_mutex_trylock(&log_init_lock);
+}
+
+void statsd_writer_init_unlock() {
+ pthread_mutex_unlock(&log_init_lock);
+}
+
+static int statsdAvailable();
+static int statsdOpen();
+static void statsdClose();
+static int statsdWrite(struct timespec* ts, struct iovec* vec, size_t nr);
+
+struct android_log_transport_write statsdLoggerWrite = {
+ .name = "statsd",
+ .sock = -EBADF,
+ .available = statsdAvailable,
+ .open = statsdOpen,
+ .close = statsdClose,
+ .write = statsdWrite,
+};
+
+/* log_init_lock assumed */
+static int statsdOpen() {
+ int i, ret = 0;
+
+ i = atomic_load(&statsdLoggerWrite.sock);
+ if (i < 0) {
+ int sock = TEMP_FAILURE_RETRY(socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0));
+ if (sock < 0) {
+ ret = -errno;
+ } else {
+ struct sockaddr_un un;
+ memset(&un, 0, sizeof(struct sockaddr_un));
+ un.sun_family = AF_UNIX;
+ strcpy(un.sun_path, "/dev/socket/statsdw");
+
+ if (TEMP_FAILURE_RETRY(
+ connect(sock, (struct sockaddr*)&un, sizeof(struct sockaddr_un))) < 0) {
+ ret = -errno;
+ switch (ret) {
+ case -ENOTCONN:
+ case -ECONNREFUSED:
+ case -ENOENT:
+ i = atomic_exchange(&statsdLoggerWrite.sock, ret);
+ /* FALLTHRU */
+ default:
+ break;
+ }
+ close(sock);
+ } else {
+ ret = atomic_exchange(&statsdLoggerWrite.sock, sock);
+ if ((ret >= 0) && (ret != sock)) {
+ close(ret);
+ }
+ ret = 0;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static void __statsdClose(int negative_errno) {
+ int sock = atomic_exchange(&statsdLoggerWrite.sock, negative_errno);
+ if (sock >= 0) {
+ close(sock);
+ }
+}
+
+static void statsdClose() {
+ __statsdClose(-EBADF);
+}
+
+static int statsdAvailable() {
+ if (atomic_load(&statsdLoggerWrite.sock) < 0) {
+ if (access("/dev/socket/statsdw", W_OK) == 0) {
+ return 0;
+ }
+ return -EBADF;
+ }
+ return 1;
+}
+
+static int statsdWrite(struct timespec* ts, struct iovec* vec, size_t nr) {
+ ssize_t ret;
+ int sock;
+ static const unsigned headerLength = 1;
+ struct iovec newVec[nr + headerLength];
+ android_log_header_t header;
+ size_t i, payloadSize;
+ static atomic_int dropped;
+
+ sock = atomic_load(&statsdLoggerWrite.sock);
+ if (sock < 0) switch (sock) {
+ case -ENOTCONN:
+ case -ECONNREFUSED:
+ case -ENOENT:
+ break;
+ default:
+ return -EBADF;
+ }
+ /*
+ * struct {
+ * // what we provide to socket
+ * android_log_header_t header;
+ * // caller provides
+ * union {
+ * struct {
+ * char prio;
+ * char payload[];
+ * } string;
+ * struct {
+ * uint32_t tag
+ * char payload[];
+ * } binary;
+ * };
+ * };
+ */
+
+ header.tid = gettid();
+ header.realtime.tv_sec = ts->tv_sec;
+ header.realtime.tv_nsec = ts->tv_nsec;
+
+ newVec[0].iov_base = (unsigned char*)&header;
+ newVec[0].iov_len = sizeof(header);
+
+ // If we dropped events before, try to tell statsd.
+ if (sock >= 0) {
+ int32_t snapshot = atomic_exchange_explicit(&dropped, 0, memory_order_relaxed);
+ if (snapshot) {
+ android_log_event_int_t buffer;
+ header.id = LOG_ID_STATS;
+ buffer.header.tag = htole32(LIBLOG_LOG_TAG);
+ buffer.payload.type = EVENT_TYPE_INT;
+ buffer.payload.data = htole32(snapshot);
+
+ newVec[headerLength].iov_base = &buffer;
+ newVec[headerLength].iov_len = sizeof(buffer);
+
+ ret = TEMP_FAILURE_RETRY(writev(sock, newVec, 2));
+ if (ret != (ssize_t)(sizeof(header) + sizeof(buffer))) {
+ atomic_fetch_add_explicit(&dropped, snapshot, memory_order_relaxed);
+ }
+ }
+ }
+
+ header.id = LOG_ID_STATS;
+
+ for (payloadSize = 0, i = headerLength; i < nr + headerLength; i++) {
+ newVec[i].iov_base = vec[i - headerLength].iov_base;
+ payloadSize += newVec[i].iov_len = vec[i - headerLength].iov_len;
+
+ if (payloadSize > LOGGER_ENTRY_MAX_PAYLOAD) {
+ newVec[i].iov_len -= payloadSize - LOGGER_ENTRY_MAX_PAYLOAD;
+ if (newVec[i].iov_len) {
+ ++i;
+ }
+ break;
+ }
+ }
+
+ /*
+ * The write below could be lost, but will never block.
+ *
+ * ENOTCONN occurs if statsd has died.
+ * ENOENT occurs if statsd is not running and socket is missing.
+ * ECONNREFUSED occurs if we can not reconnect to statsd.
+ * EAGAIN occurs if statsd is overloaded.
+ */
+ if (sock < 0) {
+ ret = sock;
+ } else {
+ ret = TEMP_FAILURE_RETRY(writev(sock, newVec, i));
+ if (ret < 0) {
+ ret = -errno;
+ }
+ }
+ switch (ret) {
+ case -ENOTCONN:
+ case -ECONNREFUSED:
+ case -ENOENT:
+ if (statd_writer_trylock()) {
+ return ret; /* in a signal handler? try again when less stressed
+ */
+ }
+ __statsdClose(ret);
+ ret = statsdOpen();
+ statsd_writer_init_unlock();
+
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = TEMP_FAILURE_RETRY(writev(atomic_load(&statsdLoggerWrite.sock), newVec, i));
+ if (ret < 0) {
+ ret = -errno;
+ }
+ /* FALLTHRU */
+ default:
+ break;
+ }
+
+ if (ret > (ssize_t)sizeof(header)) {
+ ret -= sizeof(header);
+ } else if (ret == -EAGAIN) {
+ atomic_fetch_add_explicit(&dropped, 1, memory_order_relaxed);
+ }
+
+ return ret;
+}
diff --git a/libstats/statsd_writer.h b/libstats/statsd_writer.h
new file mode 100644
index 0000000..82e14e0
--- /dev/null
+++ b/libstats/statsd_writer.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2018, 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_STATS_LOG_STATS_WRITER_H
+#define ANDROID_STATS_LOG_STATS_WRITER_H
+
+#include <pthread.h>
+#include <stdatomic.h>
+#include <sys/socket.h>
+
+/**
+ * Internal lock should not be exposed. This is bad design.
+ * TODO: rewrite it in c++ code and encapsulate the functionality in a
+ * StatsdWriter class.
+ */
+void statsd_writer_init_lock();
+int statsd_writer_init_trylock();
+void statsd_writer_init_unlock();
+
+struct android_log_transport_write {
+ const char* name; /* human name to describe the transport */
+ atomic_int sock;
+ int (*available)(); /* Does not cause resources to be taken */
+ int (*open)(); /* can be called multiple times, reusing current resources */
+ void (*close)(); /* free up resources */
+ /* write log to transport, returns number of bytes propagated, or -errno */
+ int (*write)(struct timespec* ts, struct iovec* vec, size_t nr);
+};
+
+#endif // ANDROID_STATS_LOG_STATS_WRITER_H
diff --git a/libsystem/Android.bp b/libsystem/Android.bp
index 82bf1bc..2e22b43 100644
--- a/libsystem/Android.bp
+++ b/libsystem/Android.bp
@@ -1,6 +1,7 @@
cc_library_headers {
name: "libsystem_headers",
vendor_available: true,
+ recovery_available: true,
host_supported: true,
export_include_dirs: ["include"],
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 5d996e9..26be64d 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -38,6 +38,7 @@
cc_library {
name: "libunwindstack",
vendor_available: true,
+ recovery_available: true,
vndk: {
enabled: true,
support_system_process: true,
@@ -62,6 +63,7 @@
"MapInfo.cpp",
"Maps.cpp",
"Memory.cpp",
+ "LocalUnwinder.cpp",
"Regs.cpp",
"RegsArm.cpp",
"RegsArm64.cpp",
@@ -93,6 +95,14 @@
],
exclude_shared_libs: ["libdexfile"],
},
+ recovery: {
+ cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
+ exclude_srcs: [
+ "DexFile.cpp",
+ "DexFiles.cpp",
+ ],
+ exclude_shared_libs: ["libdexfile"],
+ },
},
arch: {
@@ -125,6 +135,21 @@
//-------------------------------------------------------------------------
// Unit Tests
//-------------------------------------------------------------------------
+cc_test_library {
+ name: "libunwindstack_local",
+ defaults: ["libunwindstack_flags"],
+ srcs: ["tests/TestLocal.cpp"],
+
+ cflags: [
+ "-O0",
+ "-g",
+ ],
+
+ shared_libs: [
+ "libunwindstack",
+ ],
+}
+
cc_test {
name: "libunwindstack_test",
defaults: ["libunwindstack_flags"],
@@ -151,6 +176,7 @@
"tests/ElfTest.cpp",
"tests/ElfTestUtils.cpp",
"tests/JitDebugTest.cpp",
+ "tests/LocalUnwinderTest.cpp",
"tests/LogFake.cpp",
"tests/MapInfoGetElfTest.cpp",
"tests/MapInfoGetLoadBiasTest.cpp",
@@ -202,6 +228,7 @@
"tests/files/offline/jit_debug_x86/*",
"tests/files/offline/jit_map_arm/*",
"tests/files/offline/gnu_debugdata_arm/*",
+ "tests/files/offline/offset_arm/*",
"tests/files/offline/straddle_arm/*",
"tests/files/offline/straddle_arm64/*",
],
@@ -219,6 +246,15 @@
"libbase",
"liblzma",
],
+ target: {
+ // Always disable optimizations for host to make it easier to debug.
+ host: {
+ cflags: [
+ "-O0",
+ "-g",
+ ],
+ },
+ },
}
cc_binary {
diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp
index 02f8a9a..3762107 100644
--- a/libunwindstack/Elf.cpp
+++ b/libunwindstack/Elf.cpp
@@ -160,14 +160,14 @@
}
// The relative pc is always relative to the start of the map from which it comes.
-bool Elf::Step(uint64_t rel_pc, uint64_t adjusted_rel_pc, uint64_t elf_offset, Regs* regs,
- Memory* process_memory, bool* finished) {
+bool Elf::Step(uint64_t rel_pc, uint64_t adjusted_rel_pc, Regs* regs, Memory* process_memory,
+ bool* finished) {
if (!valid_) {
return false;
}
// The relative pc expectd by StepIfSignalHandler is relative to the start of the elf.
- if (regs->StepIfSignalHandler(rel_pc + elf_offset, this, process_memory)) {
+ if (regs->StepIfSignalHandler(rel_pc, this, process_memory)) {
*finished = false;
return true;
}
diff --git a/libunwindstack/LocalUnwinder.cpp b/libunwindstack/LocalUnwinder.cpp
new file mode 100644
index 0000000..952b332
--- /dev/null
+++ b/libunwindstack/LocalUnwinder.cpp
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2018 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 <pthread.h>
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/LocalUnwinder.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/RegsGetLocal.h>
+
+namespace unwindstack {
+
+bool LocalUnwinder::Init() {
+ pthread_rwlock_init(&maps_rwlock_, nullptr);
+
+ // Create the maps.
+ maps_.reset(new unwindstack::LocalUpdatableMaps());
+ if (!maps_->Parse()) {
+ maps_.reset();
+ return false;
+ }
+
+ process_memory_ = unwindstack::Memory::CreateProcessMemory(getpid());
+
+ return true;
+}
+
+bool LocalUnwinder::ShouldSkipLibrary(const std::string& map_name) {
+ for (const std::string& skip_library : skip_libraries_) {
+ if (skip_library == map_name) {
+ return true;
+ }
+ }
+ return false;
+}
+
+MapInfo* LocalUnwinder::GetMapInfo(uint64_t pc) {
+ pthread_rwlock_rdlock(&maps_rwlock_);
+ MapInfo* map_info = maps_->Find(pc);
+ pthread_rwlock_unlock(&maps_rwlock_);
+
+ if (map_info == nullptr) {
+ pthread_rwlock_wrlock(&maps_rwlock_);
+ // This is guaranteed not to invalidate any previous MapInfo objects so
+ // we don't need to worry about any MapInfo* values already in use.
+ if (maps_->Reparse()) {
+ map_info = maps_->Find(pc);
+ }
+ pthread_rwlock_unlock(&maps_rwlock_);
+ }
+
+ return map_info;
+}
+
+bool LocalUnwinder::Unwind(std::vector<LocalFrameData>* frame_info, size_t max_frames) {
+ std::unique_ptr<unwindstack::Regs> regs(unwindstack::Regs::CreateFromLocal());
+ unwindstack::RegsGetLocal(regs.get());
+
+ size_t num_frames = 0;
+ bool adjust_pc = false;
+ while (true) {
+ uint64_t cur_pc = regs->pc();
+ uint64_t cur_sp = regs->sp();
+
+ MapInfo* map_info = GetMapInfo(cur_pc);
+ if (map_info == nullptr) {
+ break;
+ }
+
+ Elf* elf = map_info->GetElf(process_memory_, true);
+ uint64_t rel_pc = elf->GetRelPc(cur_pc, map_info);
+ uint64_t step_pc = rel_pc;
+ uint64_t pc_adjustment;
+ if (adjust_pc) {
+ pc_adjustment = regs->GetPcAdjustment(rel_pc, elf);
+ } else {
+ pc_adjustment = 0;
+ }
+ step_pc -= pc_adjustment;
+ // Skip any locations that are within this library.
+ if (num_frames != 0 || !ShouldSkipLibrary(map_info->name)) {
+ // Add frame information.
+ std::string func_name;
+ uint64_t func_offset;
+ if (elf->GetFunctionName(rel_pc, &func_name, &func_offset)) {
+ frame_info->emplace_back(map_info, cur_pc - pc_adjustment, rel_pc - pc_adjustment,
+ func_name, func_offset);
+ } else {
+ frame_info->emplace_back(map_info, cur_pc - pc_adjustment, rel_pc - pc_adjustment, "", 0);
+ }
+ num_frames++;
+ }
+ if (!elf->valid()) {
+ break;
+ }
+ if (frame_info->size() == max_frames) {
+ break;
+ }
+
+ adjust_pc = true;
+ bool finished;
+ if (!elf->Step(rel_pc, step_pc, regs.get(), process_memory_.get(), &finished) || finished) {
+ break;
+ }
+ // pc and sp are the same, terminate the unwind.
+ if (cur_pc == regs->pc() && cur_sp == regs->sp()) {
+ break;
+ }
+ }
+ return num_frames != 0;
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/Maps.cpp b/libunwindstack/Maps.cpp
index bb682ea..e676a5a 100644
--- a/libunwindstack/Maps.cpp
+++ b/libunwindstack/Maps.cpp
@@ -105,4 +105,83 @@
return "/proc/" + std::to_string(pid_) + "/maps";
}
+const std::string LocalUpdatableMaps::GetMapsFile() const {
+ return "/proc/self/maps";
+}
+
+bool LocalUpdatableMaps::Reparse() {
+ // New maps will be added at the end without deleting the old ones.
+ size_t last_map_idx = maps_.size();
+ if (!Parse()) {
+ // Delete any maps added by the Parse call.
+ for (size_t i = last_map_idx; i < maps_.size(); i++) {
+ delete maps_[i];
+ }
+ maps_.resize(last_map_idx);
+ return false;
+ }
+
+ size_t total_entries = maps_.size();
+ size_t search_map_idx = 0;
+ for (size_t new_map_idx = last_map_idx; new_map_idx < maps_.size(); new_map_idx++) {
+ MapInfo* new_map_info = maps_[new_map_idx];
+ uint64_t start = new_map_info->start;
+ uint64_t end = new_map_info->end;
+ uint64_t flags = new_map_info->flags;
+ std::string* name = &new_map_info->name;
+ for (size_t old_map_idx = search_map_idx; old_map_idx < last_map_idx; old_map_idx++) {
+ MapInfo* info = maps_[old_map_idx];
+ if (start == info->start && end == info->end && flags == info->flags && *name == info->name) {
+ // No need to check
+ search_map_idx = old_map_idx + 1;
+ delete new_map_info;
+ maps_[new_map_idx] = nullptr;
+ total_entries--;
+ break;
+ } else if (info->start > start) {
+ // Stop, there isn't going to be a match.
+ search_map_idx = old_map_idx;
+ break;
+ }
+
+ // Never delete these maps, they may be in use. The assumption is
+ // that there will only every be a handfull of these so waiting
+ // to destroy them is not too expensive.
+ saved_maps_.push_back(info);
+ maps_[old_map_idx] = nullptr;
+ total_entries--;
+ }
+ if (search_map_idx >= last_map_idx) {
+ break;
+ }
+ }
+
+ // Now move out any of the maps that never were found.
+ for (size_t i = search_map_idx; i < last_map_idx; i++) {
+ saved_maps_.push_back(maps_[i]);
+ maps_[i] = nullptr;
+ total_entries--;
+ }
+
+ // Sort all of the values such that the nullptrs wind up at the end, then
+ // resize them away.
+ std::sort(maps_.begin(), maps_.end(), [](const auto* a, const auto* b) {
+ if (a == nullptr) {
+ return false;
+ } else if (b == nullptr) {
+ return true;
+ }
+ return a->start < b->start;
+ });
+ maps_.resize(total_entries);
+
+ return true;
+}
+
+LocalUpdatableMaps::~LocalUpdatableMaps() {
+ for (auto map_info : saved_maps_) {
+ delete map_info;
+ }
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index 9a6c6df..099cc9e 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -220,8 +220,7 @@
in_device_map = true;
} else {
bool finished;
- stepped = elf->Step(rel_pc, step_pc, map_info->elf_offset, regs_, process_memory_.get(),
- &finished);
+ stepped = elf->Step(rel_pc, step_pc, regs_, process_memory_.get(), &finished);
elf->GetLastError(&last_error_);
if (stepped && finished) {
break;
diff --git a/libunwindstack/include/unwindstack/Elf.h b/libunwindstack/include/unwindstack/Elf.h
index 385973e..f4cdbda 100644
--- a/libunwindstack/include/unwindstack/Elf.h
+++ b/libunwindstack/include/unwindstack/Elf.h
@@ -65,8 +65,8 @@
uint64_t GetRelPc(uint64_t pc, const MapInfo* map_info);
- bool Step(uint64_t rel_pc, uint64_t adjusted_rel_pc, uint64_t elf_offset, Regs* regs,
- Memory* process_memory, bool* finished);
+ bool Step(uint64_t rel_pc, uint64_t adjusted_rel_pc, Regs* regs, Memory* process_memory,
+ bool* finished);
ElfInterface* CreateInterfaceFromMemory(Memory* memory);
diff --git a/libunwindstack/include/unwindstack/LocalUnwinder.h b/libunwindstack/include/unwindstack/LocalUnwinder.h
new file mode 100644
index 0000000..80bb53e
--- /dev/null
+++ b/libunwindstack/include/unwindstack/LocalUnwinder.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2018 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_LOCAL_UNWINDER_H
+#define _LIBUNWINDSTACK_LOCAL_UNWINDER_H
+
+#include <pthread.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <unwindstack/Error.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
+
+// Forward declarations.
+class Elf;
+struct MapInfo;
+
+struct LocalFrameData {
+ LocalFrameData(MapInfo* map_info, uint64_t pc, uint64_t rel_pc, const std::string& function_name,
+ uint64_t function_offset)
+ : map_info(map_info),
+ pc(pc),
+ rel_pc(rel_pc),
+ function_name(function_name),
+ function_offset(function_offset) {}
+
+ MapInfo* map_info;
+ uint64_t pc;
+ uint64_t rel_pc;
+ std::string function_name;
+ uint64_t function_offset;
+};
+
+// This is a specialized class that should only be used for doing local unwinds.
+// The Unwind call can be made as multiple times on the same object, and it can
+// be called by multiple threads at the same time.
+// It is designed to be used in debugging circumstances to get a stack trace
+// as fast as possible.
+class LocalUnwinder {
+ public:
+ LocalUnwinder() = default;
+ LocalUnwinder(const std::vector<std::string>& skip_libraries) : skip_libraries_(skip_libraries) {}
+ ~LocalUnwinder() = default;
+
+ bool Init();
+
+ bool Unwind(std::vector<LocalFrameData>* frame_info, size_t max_frames);
+
+ bool ShouldSkipLibrary(const std::string& map_name);
+
+ MapInfo* GetMapInfo(uint64_t pc);
+
+ ErrorCode LastErrorCode() { return last_error_.code; }
+ uint64_t LastErrorAddress() { return last_error_.address; }
+
+ private:
+ pthread_rwlock_t maps_rwlock_;
+ std::unique_ptr<LocalUpdatableMaps> maps_ = nullptr;
+ std::shared_ptr<Memory> process_memory_;
+ std::vector<std::string> skip_libraries_;
+ ErrorData last_error_;
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_LOCAL_UNWINDER_H
diff --git a/libunwindstack/include/unwindstack/Maps.h b/libunwindstack/include/unwindstack/Maps.h
index 74e5c47..67fbed2 100644
--- a/libunwindstack/include/unwindstack/Maps.h
+++ b/libunwindstack/include/unwindstack/Maps.h
@@ -87,6 +87,19 @@
virtual ~LocalMaps() = default;
};
+class LocalUpdatableMaps : public Maps {
+ public:
+ LocalUpdatableMaps() : Maps() {}
+ virtual ~LocalUpdatableMaps();
+
+ bool Reparse();
+
+ const std::string GetMapsFile() const override;
+
+ private:
+ std::vector<MapInfo*> saved_maps_;
+};
+
class BufferMaps : public Maps {
public:
BufferMaps(const char* buffer) : buffer_(buffer) {}
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index f9028c4..aecbf6d 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -133,7 +133,7 @@
ASSERT_FALSE(elf.GetFunctionName(0, &name, &func_offset));
bool finished;
- ASSERT_FALSE(elf.Step(0, 0, 0, nullptr, nullptr, &finished));
+ ASSERT_FALSE(elf.Step(0, 0, nullptr, nullptr, &finished));
}
TEST_F(ElfTest, elf32_invalid_machine) {
@@ -330,7 +330,7 @@
elf.FakeSetValid(true);
elf.FakeSetLoadBias(0);
bool finished;
- ASSERT_TRUE(elf.Step(0x1000, 0x1000, 0x2000, ®s, &process_memory, &finished));
+ ASSERT_TRUE(elf.Step(0x3000, 0x1000, ®s, &process_memory, &finished));
EXPECT_FALSE(finished);
EXPECT_EQ(15U, regs.pc());
EXPECT_EQ(13U, regs.sp());
@@ -370,7 +370,7 @@
EXPECT_CALL(*interface, Step(0x1000, 0, ®s, &process_memory, &finished))
.WillOnce(::testing::Return(true));
- ASSERT_TRUE(elf.Step(0x1004, 0x1000, 0x2000, ®s, &process_memory, &finished));
+ ASSERT_TRUE(elf.Step(0x1004, 0x1000, ®s, &process_memory, &finished));
}
TEST_F(ElfTest, step_in_interface_non_zero_load_bias) {
@@ -388,7 +388,7 @@
EXPECT_CALL(*interface, Step(0x7300, 0x4000, ®s, &process_memory, &finished))
.WillOnce(::testing::Return(true));
- ASSERT_TRUE(elf.Step(0x7304, 0x7300, 0x2000, ®s, &process_memory, &finished));
+ ASSERT_TRUE(elf.Step(0x7304, 0x7300, ®s, &process_memory, &finished));
}
TEST_F(ElfTest, get_global_invalid_elf) {
diff --git a/libunwindstack/tests/LocalUnwinderTest.cpp b/libunwindstack/tests/LocalUnwinderTest.cpp
new file mode 100644
index 0000000..56a18cd
--- /dev/null
+++ b/libunwindstack/tests/LocalUnwinderTest.cpp
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2018 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 <dlfcn.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <android-base/stringprintf.h>
+
+#include <unwindstack/LocalUnwinder.h>
+
+namespace unwindstack {
+
+static std::vector<LocalFrameData>* g_frame_info;
+static LocalUnwinder* g_unwinder;
+
+extern "C" void SignalLocalInnerFunction() {
+ g_unwinder->Unwind(g_frame_info, 256);
+}
+
+extern "C" void SignalLocalMiddleFunction() {
+ SignalLocalInnerFunction();
+}
+
+extern "C" void SignalLocalOuterFunction() {
+ SignalLocalMiddleFunction();
+}
+
+static void SignalLocalCallerHandler(int, siginfo_t*, void*) {
+ SignalLocalOuterFunction();
+}
+
+static std::string ErrorMsg(const std::vector<const char*>& function_names,
+ const std::vector<LocalFrameData>& frame_info) {
+ std::string unwind;
+ size_t i = 0;
+ for (const auto& frame : frame_info) {
+ unwind += android::base::StringPrintf("#%02zu pc 0x%" PRIx64 " rel_pc 0x%" PRIx64, i++,
+ frame.pc, frame.rel_pc);
+ if (frame.map_info != nullptr) {
+ if (!frame.map_info->name.empty()) {
+ unwind += " " + frame.map_info->name;
+ } else {
+ unwind += android::base::StringPrintf(" 0x%" PRIx64 "-0x%" PRIx64, frame.map_info->start,
+ frame.map_info->end);
+ }
+ if (frame.map_info->offset != 0) {
+ unwind += android::base::StringPrintf(" offset 0x%" PRIx64, frame.map_info->offset);
+ }
+ }
+ if (!frame.function_name.empty()) {
+ unwind += " " + frame.function_name;
+ if (frame.function_offset != 0) {
+ unwind += android::base::StringPrintf("+%" PRId64, frame.function_offset);
+ }
+ }
+ unwind += '\n';
+ }
+
+ return std::string(
+ "Unwind completed without finding all frames\n"
+ " Looking for function: ") +
+ function_names.front() + "\n" + "Unwind data:\n" + unwind;
+}
+
+// This test assumes that this code is compiled with optimizations turned
+// off. If this doesn't happen, then all of the calls will be optimized
+// away.
+extern "C" void LocalInnerFunction(LocalUnwinder* unwinder, bool unwind_through_signal) {
+ std::vector<LocalFrameData> frame_info;
+ g_frame_info = &frame_info;
+ g_unwinder = unwinder;
+ std::vector<const char*> expected_function_names;
+
+ if (unwind_through_signal) {
+ struct sigaction act, oldact;
+ memset(&act, 0, sizeof(act));
+ act.sa_sigaction = SignalLocalCallerHandler;
+ act.sa_flags = SA_RESTART | SA_ONSTACK;
+ ASSERT_EQ(0, sigaction(SIGUSR1, &act, &oldact));
+
+ raise(SIGUSR1);
+
+ ASSERT_EQ(0, sigaction(SIGUSR1, &oldact, nullptr));
+
+ expected_function_names = {"LocalOuterFunction", "LocalMiddleFunction",
+ "LocalInnerFunction", "SignalLocalOuterFunction",
+ "SignalLocalMiddleFunction", "SignalLocalInnerFunction"};
+ } else {
+ ASSERT_TRUE(unwinder->Unwind(&frame_info, 256));
+
+ expected_function_names = {"LocalOuterFunction", "LocalMiddleFunction", "LocalInnerFunction"};
+ }
+
+ for (auto& frame : frame_info) {
+ if (frame.function_name == expected_function_names.back()) {
+ expected_function_names.pop_back();
+ if (expected_function_names.empty()) {
+ break;
+ }
+ }
+ }
+
+ ASSERT_TRUE(expected_function_names.empty()) << ErrorMsg(expected_function_names, frame_info);
+}
+
+extern "C" void LocalMiddleFunction(LocalUnwinder* unwinder, bool unwind_through_signal) {
+ LocalInnerFunction(unwinder, unwind_through_signal);
+}
+
+extern "C" void LocalOuterFunction(LocalUnwinder* unwinder, bool unwind_through_signal) {
+ LocalMiddleFunction(unwinder, unwind_through_signal);
+}
+
+class LocalUnwinderTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ unwinder_.reset(new LocalUnwinder);
+ ASSERT_TRUE(unwinder_->Init());
+ }
+
+ std::unique_ptr<LocalUnwinder> unwinder_;
+};
+
+TEST_F(LocalUnwinderTest, local) {
+ LocalOuterFunction(unwinder_.get(), false);
+}
+
+TEST_F(LocalUnwinderTest, local_signal) {
+ LocalOuterFunction(unwinder_.get(), true);
+}
+
+TEST_F(LocalUnwinderTest, local_multiple) {
+ ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), false));
+
+ ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), true));
+
+ ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), false));
+
+ ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), true));
+}
+
+// This test verifies that doing an unwind before and after a dlopen
+// works. It's verifying that the maps read during the first unwind
+// do not cause a problem when doing the unwind using the code in
+// the dlopen'd code.
+TEST_F(LocalUnwinderTest, unwind_after_dlopen) {
+ // Prime the maps data.
+ ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), false));
+
+ std::string testlib(testing::internal::GetArgvs()[0]);
+ auto const value = testlib.find_last_of('/');
+ if (value == std::string::npos) {
+ testlib = "../";
+ } else {
+ testlib = testlib.substr(0, value + 1) + "../";
+ }
+ testlib += "libunwindstack_local.so";
+
+ void* handle = dlopen(testlib.c_str(), RTLD_NOW);
+ ASSERT_TRUE(handle != nullptr);
+
+ void (*unwind_function)(void*, void*) =
+ reinterpret_cast<void (*)(void*, void*)>(dlsym(handle, "TestlibLevel1"));
+ ASSERT_TRUE(unwind_function != nullptr);
+
+ std::vector<LocalFrameData> frame_info;
+ unwind_function(unwinder_.get(), &frame_info);
+
+ ASSERT_EQ(0, dlclose(handle));
+
+ std::vector<const char*> expected_function_names{"TestlibLevel1", "TestlibLevel2",
+ "TestlibLevel3", "TestlibLevel4"};
+
+ for (auto& frame : frame_info) {
+ if (frame.function_name == expected_function_names.back()) {
+ expected_function_names.pop_back();
+ if (expected_function_names.empty()) {
+ break;
+ }
+ }
+ }
+
+ ASSERT_TRUE(expected_function_names.empty()) << ErrorMsg(expected_function_names, frame_info);
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/TestLocal.cpp b/libunwindstack/tests/TestLocal.cpp
new file mode 100644
index 0000000..fa0baff
--- /dev/null
+++ b/libunwindstack/tests/TestLocal.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2018 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 <unwindstack/LocalUnwinder.h>
+
+#include <vector>
+
+extern "C" void TestlibLevel4(void* unwinder_data, void* frame_data) {
+ unwindstack::LocalUnwinder* unwinder =
+ reinterpret_cast<unwindstack::LocalUnwinder*>(unwinder_data);
+ std::vector<unwindstack::LocalFrameData>* frame_info =
+ reinterpret_cast<std::vector<unwindstack::LocalFrameData>*>(frame_data);
+ unwinder->Unwind(frame_info, 256);
+}
+
+extern "C" void TestlibLevel3(void* unwinder_data, void* frame_data) {
+ TestlibLevel4(unwinder_data, frame_data);
+}
+
+extern "C" void TestlibLevel2(void* unwinder_data, void* frame_data) {
+ TestlibLevel3(unwinder_data, frame_data);
+}
+
+extern "C" void TestlibLevel1(void* unwinder_data, void* frame_data) {
+ TestlibLevel2(unwinder_data, frame_data);
+}
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
index 2b8f0c2..285fc9e 100644
--- a/libunwindstack/tests/UnwindOfflineTest.cpp
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -46,6 +46,12 @@
namespace unwindstack {
+static void AddMemory(std::string file_name, MemoryOfflineParts* parts) {
+ MemoryOffline* memory = new MemoryOffline;
+ ASSERT_TRUE(memory->Init(file_name.c_str(), 0));
+ parts->Add(memory);
+}
+
class UnwindOfflineTest : public ::testing::Test {
protected:
void TearDown() override {
@@ -64,9 +70,24 @@
maps_.reset(new BufferMaps(data.c_str()));
ASSERT_TRUE(maps_->Parse());
- std::unique_ptr<MemoryOffline> stack_memory(new MemoryOffline);
- ASSERT_TRUE(stack_memory->Init((dir_ + "stack.data").c_str(), 0));
- process_memory_.reset(stack_memory.release());
+ std::string stack_name(dir_ + "stack.data");
+ struct stat st;
+ if (stat(stack_name.c_str(), &st) == 0 && S_ISREG(st.st_mode)) {
+ std::unique_ptr<MemoryOffline> stack_memory(new MemoryOffline);
+ ASSERT_TRUE(stack_memory->Init((dir_ + "stack.data").c_str(), 0));
+ process_memory_.reset(stack_memory.release());
+ } else {
+ std::unique_ptr<MemoryOfflineParts> stack_memory(new MemoryOfflineParts);
+ for (size_t i = 0;; i++) {
+ stack_name = dir_ + "stack" + std::to_string(i) + ".data";
+ if (stat(stack_name.c_str(), &st) == -1 || !S_ISREG(st.st_mode)) {
+ ASSERT_TRUE(i != 0) << "No stack data files found.";
+ break;
+ }
+ AddMemory(stack_name, stack_memory.get());
+ }
+ process_memory_.reset(stack_memory.release());
+ }
switch (arch) {
case ARCH_ARM: {
@@ -180,7 +201,7 @@
}
TEST_F(UnwindOfflineTest, pc_straddle_arm) {
- Init("straddle_arm/", ARCH_ARM);
+ ASSERT_NO_FATAL_FAILURE(Init("straddle_arm/", ARCH_ARM));
Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
unwinder.Unwind();
@@ -204,7 +225,7 @@
}
TEST_F(UnwindOfflineTest, pc_in_gnu_debugdata_arm) {
- Init("gnu_debugdata_arm/", ARCH_ARM);
+ ASSERT_NO_FATAL_FAILURE(Init("gnu_debugdata_arm/", ARCH_ARM));
Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
unwinder.Unwind();
@@ -224,7 +245,7 @@
}
TEST_F(UnwindOfflineTest, pc_straddle_arm64) {
- Init("straddle_arm64/", ARCH_ARM64);
+ ASSERT_NO_FATAL_FAILURE(Init("straddle_arm64/", ARCH_ARM64));
Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
unwinder.Unwind();
@@ -255,14 +276,8 @@
EXPECT_EQ(0x7fe0d84110U, unwinder.frames()[5].sp);
}
-static void AddMemory(std::string file_name, MemoryOfflineParts* parts) {
- MemoryOffline* memory = new MemoryOffline;
- ASSERT_TRUE(memory->Init(file_name.c_str(), 0));
- parts->Add(memory);
-}
-
TEST_F(UnwindOfflineTest, jit_debug_x86) {
- Init("jit_debug_x86/", ARCH_X86);
+ ASSERT_NO_FATAL_FAILURE(Init("jit_debug_x86/", ARCH_X86));
MemoryOfflineParts* memory = new MemoryOfflineParts;
AddMemory(dir_ + "descriptor.data", memory);
@@ -555,7 +570,7 @@
}
TEST_F(UnwindOfflineTest, jit_debug_arm) {
- Init("jit_debug_arm/", ARCH_ARM);
+ ASSERT_NO_FATAL_FAILURE(Init("jit_debug_arm/", ARCH_ARM));
MemoryOfflineParts* memory = new MemoryOfflineParts;
AddMemory(dir_ + "descriptor.data", memory);
@@ -873,7 +888,7 @@
// fallback to iterating over the cies/fdes and ignore the eh_frame_hdr.
// No .gnu_debugdata section in the elf file, so no symbols.
TEST_F(UnwindOfflineTest, bad_eh_frame_hdr_arm64) {
- Init("bad_eh_frame_hdr_arm64/", ARCH_ARM64);
+ ASSERT_NO_FATAL_FAILURE(Init("bad_eh_frame_hdr_arm64/", ARCH_ARM64));
Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
unwinder.Unwind();
@@ -902,7 +917,7 @@
// The elf has bad eh_frame unwind information for the pcs. If eh_frame
// is used first, the unwind will not match the expected output.
TEST_F(UnwindOfflineTest, debug_frame_first_x86) {
- Init("debug_frame_first_x86/", ARCH_X86);
+ ASSERT_NO_FATAL_FAILURE(Init("debug_frame_first_x86/", ARCH_X86));
Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
unwinder.Unwind();
@@ -930,7 +945,7 @@
// Make sure that a pc that is at the beginning of an fde unwinds correctly.
TEST_F(UnwindOfflineTest, eh_frame_hdr_begin_x86_64) {
- Init("eh_frame_hdr_begin_x86_64/", ARCH_X86_64);
+ ASSERT_NO_FATAL_FAILURE(Init("eh_frame_hdr_begin_x86_64/", ARCH_X86_64));
Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
unwinder.Unwind();
@@ -957,7 +972,7 @@
}
TEST_F(UnwindOfflineTest, art_quick_osr_stub_arm) {
- Init("art_quick_osr_stub_arm/", ARCH_ARM);
+ ASSERT_NO_FATAL_FAILURE(Init("art_quick_osr_stub_arm/", ARCH_ARM));
MemoryOfflineParts* memory = new MemoryOfflineParts;
AddMemory(dir_ + "descriptor.data", memory);
@@ -1072,7 +1087,7 @@
}
TEST_F(UnwindOfflineTest, jit_map_arm) {
- Init("jit_map_arm/", ARCH_ARM);
+ ASSERT_NO_FATAL_FAILURE(Init("jit_map_arm/", ARCH_ARM));
maps_->Add(0xd025c788, 0xd025c9f0, 0, PROT_READ | PROT_EXEC | MAPS_FLAGS_JIT_SYMFILE_MAP,
"jit_map0.so", 0);
@@ -1111,4 +1126,78 @@
EXPECT_EQ(0xcd4ff960U, unwinder.frames()[5].sp);
}
+TEST_F(UnwindOfflineTest, offset_arm) {
+ ASSERT_NO_FATAL_FAILURE(Init("offset_arm/", ARCH_ARM));
+
+ Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ unwinder.Unwind();
+
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(19U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(
+ " #00 pc 0032bfa0 (offset 0x42000) libunwindstack_test (SignalInnerFunction+40)\n"
+ " #01 pc 0032bfeb (offset 0x42000) libunwindstack_test (SignalMiddleFunction+2)\n"
+ " #02 pc 0032bff3 (offset 0x42000) libunwindstack_test (SignalOuterFunction+2)\n"
+ " #03 pc 0032fed3 (offset 0x42000) libunwindstack_test "
+ "(_ZN11unwindstackL19SignalCallerHandlerEiP7siginfoPv+26)\n"
+ " #04 pc 00026528 (offset 0x25000) libc.so\n"
+ " #05 pc 00000000 <unknown>\n"
+ " #06 pc 0032c2d9 (offset 0x42000) libunwindstack_test (InnerFunction+736)\n"
+ " #07 pc 0032cc4f (offset 0x42000) libunwindstack_test (MiddleFunction+42)\n"
+ " #08 pc 0032cc81 (offset 0x42000) libunwindstack_test (OuterFunction+42)\n"
+ " #09 pc 0032e547 (offset 0x42000) libunwindstack_test "
+ "(_ZN11unwindstackL19RemoteThroughSignalEij+270)\n"
+ " #10 pc 0032ed99 (offset 0x42000) libunwindstack_test "
+ "(_ZN11unwindstack55UnwindTest_remote_through_signal_with_invalid_func_Test8TestBodyEv+16)\n"
+ " #11 pc 00354453 (offset 0x42000) libunwindstack_test (_ZN7testing4Test3RunEv+154)\n"
+ " #12 pc 00354de7 (offset 0x42000) libunwindstack_test (_ZN7testing8TestInfo3RunEv+194)\n"
+ " #13 pc 00355105 (offset 0x42000) libunwindstack_test (_ZN7testing8TestCase3RunEv+180)\n"
+ " #14 pc 0035a215 (offset 0x42000) libunwindstack_test "
+ "(_ZN7testing8internal12UnitTestImpl11RunAllTestsEv+664)\n"
+ " #15 pc 00359f4f (offset 0x42000) libunwindstack_test (_ZN7testing8UnitTest3RunEv+110)\n"
+ " #16 pc 0034d3db (offset 0x42000) libunwindstack_test (main+38)\n"
+ " #17 pc 00092c0d (offset 0x25000) libc.so (__libc_init+48)\n"
+ " #18 pc 0004202f (offset 0x42000) libunwindstack_test (_start_main+38)\n",
+ frame_info);
+
+ EXPECT_EQ(0x2e55fa0U, unwinder.frames()[0].pc);
+ EXPECT_EQ(0xf43d2cccU, unwinder.frames()[0].sp);
+ EXPECT_EQ(0x2e55febU, unwinder.frames()[1].pc);
+ EXPECT_EQ(0xf43d2ce0U, unwinder.frames()[1].sp);
+ EXPECT_EQ(0x2e55ff3U, unwinder.frames()[2].pc);
+ EXPECT_EQ(0xf43d2ce8U, unwinder.frames()[2].sp);
+ EXPECT_EQ(0x2e59ed3U, unwinder.frames()[3].pc);
+ EXPECT_EQ(0xf43d2cf0U, unwinder.frames()[3].sp);
+ EXPECT_EQ(0xf4136528U, unwinder.frames()[4].pc);
+ EXPECT_EQ(0xf43d2d10U, unwinder.frames()[4].sp);
+ EXPECT_EQ(0U, unwinder.frames()[5].pc);
+ EXPECT_EQ(0xffcc0ee0U, unwinder.frames()[5].sp);
+ EXPECT_EQ(0x2e562d9U, unwinder.frames()[6].pc);
+ EXPECT_EQ(0xffcc0ee0U, unwinder.frames()[6].sp);
+ EXPECT_EQ(0x2e56c4fU, unwinder.frames()[7].pc);
+ EXPECT_EQ(0xffcc1060U, unwinder.frames()[7].sp);
+ EXPECT_EQ(0x2e56c81U, unwinder.frames()[8].pc);
+ EXPECT_EQ(0xffcc1078U, unwinder.frames()[8].sp);
+ EXPECT_EQ(0x2e58547U, unwinder.frames()[9].pc);
+ EXPECT_EQ(0xffcc1090U, unwinder.frames()[9].sp);
+ EXPECT_EQ(0x2e58d99U, unwinder.frames()[10].pc);
+ EXPECT_EQ(0xffcc1438U, unwinder.frames()[10].sp);
+ EXPECT_EQ(0x2e7e453U, unwinder.frames()[11].pc);
+ EXPECT_EQ(0xffcc1448U, unwinder.frames()[11].sp);
+ EXPECT_EQ(0x2e7ede7U, unwinder.frames()[12].pc);
+ EXPECT_EQ(0xffcc1458U, unwinder.frames()[12].sp);
+ EXPECT_EQ(0x2e7f105U, unwinder.frames()[13].pc);
+ EXPECT_EQ(0xffcc1490U, unwinder.frames()[13].sp);
+ EXPECT_EQ(0x2e84215U, unwinder.frames()[14].pc);
+ EXPECT_EQ(0xffcc14c0U, unwinder.frames()[14].sp);
+ EXPECT_EQ(0x2e83f4fU, unwinder.frames()[15].pc);
+ EXPECT_EQ(0xffcc1510U, unwinder.frames()[15].sp);
+ EXPECT_EQ(0x2e773dbU, unwinder.frames()[16].pc);
+ EXPECT_EQ(0xffcc1528U, unwinder.frames()[16].sp);
+ EXPECT_EQ(0xf41a2c0dU, unwinder.frames()[17].pc);
+ EXPECT_EQ(0xffcc1540U, unwinder.frames()[17].sp);
+ EXPECT_EQ(0x2b6c02fU, unwinder.frames()[18].pc);
+ EXPECT_EQ(0xffcc1558U, unwinder.frames()[18].sp);
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/tests/UnwindTest.cpp b/libunwindstack/tests/UnwindTest.cpp
index 242cc6a..83695bb 100644
--- a/libunwindstack/tests/UnwindTest.cpp
+++ b/libunwindstack/tests/UnwindTest.cpp
@@ -106,15 +106,12 @@
Unwinder unwinder(512, maps, regs, process_memory);
unwinder.Unwind();
- std::string expected_function = expected_function_names.back();
- expected_function_names.pop_back();
for (auto& frame : unwinder.frames()) {
- if (frame.function_name == expected_function) {
+ if (frame.function_name == expected_function_names.back()) {
+ expected_function_names.pop_back();
if (expected_function_names.empty()) {
break;
}
- expected_function = expected_function_names.back();
- expected_function_names.pop_back();
}
}
diff --git a/libunwindstack/tests/files/offline/offset_arm/libc.so b/libunwindstack/tests/files/offline/offset_arm/libc.so
new file mode 100644
index 0000000..9f5c8ca
--- /dev/null
+++ b/libunwindstack/tests/files/offline/offset_arm/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/offset_arm/libunwindstack_test b/libunwindstack/tests/files/offline/offset_arm/libunwindstack_test
new file mode 100644
index 0000000..7a30bfa
--- /dev/null
+++ b/libunwindstack/tests/files/offline/offset_arm/libunwindstack_test
Binary files differ
diff --git a/libunwindstack/tests/files/offline/offset_arm/maps.txt b/libunwindstack/tests/files/offline/offset_arm/maps.txt
new file mode 100644
index 0000000..6224464
--- /dev/null
+++ b/libunwindstack/tests/files/offline/offset_arm/maps.txt
@@ -0,0 +1,2 @@
+2b6c000-2e92000 r-xp 42000 00:00 0 libunwindstack_test
+f4135000-f41a9000 r-xp 25000 00:00 0 libc.so
diff --git a/libunwindstack/tests/files/offline/offset_arm/regs.txt b/libunwindstack/tests/files/offline/offset_arm/regs.txt
new file mode 100644
index 0000000..1f4ac8f
--- /dev/null
+++ b/libunwindstack/tests/files/offline/offset_arm/regs.txt
@@ -0,0 +1,16 @@
+r0: 5
+r1: 5
+r2: 4
+r3: 1
+r4: 73804b6b
+r5: f3c9c000
+r6: 2ea09ac
+r7: 10624dd3
+r8: f41b5d8c
+r9: f3c9c000
+r10: 6f17
+r11: f3c94048
+ip: 2ea0807
+sp: f43d2ccc
+lr: 2e55fef
+pc: 2e55fa0
diff --git a/libunwindstack/tests/files/offline/offset_arm/stack0.data b/libunwindstack/tests/files/offline/offset_arm/stack0.data
new file mode 100644
index 0000000..23a9874
--- /dev/null
+++ b/libunwindstack/tests/files/offline/offset_arm/stack0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/offset_arm/stack1.data b/libunwindstack/tests/files/offline/offset_arm/stack1.data
new file mode 100644
index 0000000..49bdd1e
--- /dev/null
+++ b/libunwindstack/tests/files/offline/offset_arm/stack1.data
Binary files differ
diff --git a/libunwindstack/tools/unwind_for_offline.cpp b/libunwindstack/tools/unwind_for_offline.cpp
index 74868d4..640992f 100644
--- a/libunwindstack/tools/unwind_for_offline.cpp
+++ b/libunwindstack/tools/unwind_for_offline.cpp
@@ -30,6 +30,7 @@
#include <memory>
#include <string>
#include <unordered_map>
+#include <utility>
#include <vector>
#include <unwindstack/Elf.h>
@@ -68,7 +69,7 @@
bool SaveRegs(unwindstack::Regs* regs) {
std::unique_ptr<FILE, decltype(&fclose)> fp(fopen("regs.txt", "w+"), &fclose);
if (fp == nullptr) {
- printf("Failed to create file regs.txt.\n");
+ perror("Failed to create file regs.txt");
return false;
}
regs->IterateRegisters([&fp](const char* name, uint64_t value) {
@@ -78,30 +79,45 @@
return true;
}
-bool SaveStack(pid_t pid, uint64_t sp_start, uint64_t sp_end) {
- std::unique_ptr<FILE, decltype(&fclose)> fp(fopen("stack.data", "w+"), &fclose);
- if (fp == nullptr) {
- printf("Failed to create stack.data.\n");
- return false;
- }
+bool SaveStack(pid_t pid, const std::vector<std::pair<uint64_t, uint64_t>>& stacks) {
+ for (size_t i = 0; i < stacks.size(); i++) {
+ std::string file_name;
+ if (stacks.size() != 1) {
+ file_name = "stack" + std::to_string(i) + ".data";
+ } else {
+ file_name = "stack.data";
+ }
- size_t bytes = fwrite(&sp_start, 1, sizeof(sp_start), fp.get());
- if (bytes != sizeof(sp_start)) {
- perror("Failed to write all data.");
- return false;
- }
+ // Do this first, so if it fails, we don't create the file.
+ uint64_t sp_start = stacks[i].first;
+ uint64_t sp_end = stacks[i].second;
+ std::vector<uint8_t> buffer(sp_end - sp_start);
+ auto process_memory = unwindstack::Memory::CreateProcessMemory(pid);
+ if (!process_memory->Read(sp_start, buffer.data(), buffer.size())) {
+ printf("Unable to read stack data.\n");
+ return false;
+ }
- std::vector<uint8_t> buffer(sp_end - sp_start);
- auto process_memory = unwindstack::Memory::CreateProcessMemory(pid);
- if (!process_memory->Read(sp_start, buffer.data(), buffer.size())) {
- printf("Unable to read stack data.\n");
- return false;
- }
+ printf("Saving the stack 0x%" PRIx64 "-0x%" PRIx64 "\n", sp_start, sp_end);
- bytes = fwrite(buffer.data(), 1, buffer.size(), fp.get());
- if (bytes != buffer.size()) {
- printf("Failed to write all stack data: stack size %zu, written %zu\n", buffer.size(), bytes);
- return 1;
+ std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(file_name.c_str(), "w+"), &fclose);
+ if (fp == nullptr) {
+ perror("Failed to create stack.data");
+ return false;
+ }
+
+ size_t bytes = fwrite(&sp_start, 1, sizeof(sp_start), fp.get());
+ if (bytes != sizeof(sp_start)) {
+ printf("Failed to write sp_start data: sizeof(sp_start) %zu, written %zu\n", sizeof(sp_start),
+ bytes);
+ return false;
+ }
+
+ bytes = fwrite(buffer.data(), 1, buffer.size(), fp.get());
+ if (bytes != buffer.size()) {
+ printf("Failed to write all stack data: stack size %zu, written %zu\n", buffer.size(), bytes);
+ return false;
+ }
}
return true;
@@ -110,17 +126,11 @@
bool CreateElfFromMemory(std::shared_ptr<unwindstack::Memory>& memory, map_info_t* info) {
std::string cur_name;
if (info->name.empty()) {
- cur_name = android::base::StringPrintf("anonymous:%" PRIx64, info->start);
+ cur_name = android::base::StringPrintf("anonymous_%" PRIx64, info->start);
} else {
- cur_name = basename(info->name.c_str());
- cur_name = android::base::StringPrintf("%s:%" PRIx64, basename(info->name.c_str()), info->start);
+ cur_name = android::base::StringPrintf("%s_%" PRIx64, basename(info->name.c_str()), info->start);
}
- std::unique_ptr<FILE, decltype(&fclose)> output(fopen(cur_name.c_str(), "w+"), &fclose);
- if (output == nullptr) {
- printf("Cannot create %s\n", cur_name.c_str());
- return false;
- }
std::vector<uint8_t> buffer(info->end - info->start);
// If this is a mapped in file, it might not be possible to read the entire
// map, so read all that is readable.
@@ -129,6 +139,13 @@
printf("Cannot read data from address %" PRIx64 " length %zu\n", info->start, buffer.size());
return false;
}
+
+ std::unique_ptr<FILE, decltype(&fclose)> output(fopen(cur_name.c_str(), "w+"), &fclose);
+ if (output == nullptr) {
+ perror((std::string("Cannot create ") + cur_name).c_str());
+ return false;
+ }
+
size_t bytes_written = fwrite(buffer.data(), 1, bytes, output.get());
if (bytes_written != bytes) {
printf("Failed to write all data to file: bytes read %zu, written %zu\n", bytes, bytes_written);
@@ -144,13 +161,14 @@
bool CopyElfFromFile(map_info_t* info) {
std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(info->name.c_str(), "r"), &fclose);
if (fp == nullptr) {
+ perror((std::string("Cannot open ") + info->name).c_str());
return false;
}
std::string cur_name = basename(info->name.c_str());
std::unique_ptr<FILE, decltype(&fclose)> output(fopen(cur_name.c_str(), "w+"), &fclose);
if (output == nullptr) {
- printf("Cannot create file %s\n", cur_name.c_str());
+ perror((std::string("Cannot create file " + cur_name)).c_str());
return false;
}
std::vector<uint8_t> buffer(10000);
@@ -198,9 +216,21 @@
unwinder.Unwind();
std::unordered_map<uint64_t, map_info_t> maps_by_start;
- uint64_t last_sp;
+ std::vector<std::pair<uint64_t, uint64_t>> stacks;
+ uint64_t sp_map_start = 0;
+ unwindstack::MapInfo* map_info = maps.Find(sp);
+ if (map_info != nullptr) {
+ stacks.emplace_back(std::make_pair(sp, map_info->end));
+ sp_map_start = map_info->start;
+ }
+
for (auto frame : unwinder.frames()) {
- last_sp = frame.sp;
+ map_info = maps.Find(frame.sp);
+ if (map_info != nullptr && sp_map_start != map_info->start) {
+ stacks.emplace_back(std::make_pair(frame.sp, map_info->end));
+ sp_map_start = map_info->start;
+ }
+
if (maps_by_start.count(frame.map_start) == 0) {
auto info = &maps_by_start[frame.map_start];
info->start = frame.map_start;
@@ -211,7 +241,12 @@
// Try to create the elf from memory, this will handle cases where
// the data only exists in memory such as vdso data on x86.
if (!CreateElfFromMemory(process_memory, info)) {
- return 1;
+ printf("Ignoring map ");
+ if (!info->name.empty()) {
+ printf("%s\n", info->name.c_str());
+ } else {
+ printf("anonymous:%" PRIx64 "\n", info->start);
+ }
}
}
}
@@ -221,7 +256,7 @@
printf("%s\n", unwinder.FormatFrame(i).c_str());
}
- if (!SaveStack(pid, sp, last_sp)) {
+ if (!SaveStack(pid, stacks)) {
return 1;
}
@@ -232,7 +267,7 @@
std::unique_ptr<FILE, decltype(&fclose)> fp(fopen("maps.txt", "w+"), &fclose);
if (fp == nullptr) {
- printf("Failed to create maps.txt.\n");
+ perror("Failed to create maps.txt");
return false;
}
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 0d7925a..9395ef8 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -15,6 +15,7 @@
cc_library_headers {
name: "libutils_headers",
vendor_available: true,
+ recovery_available: true,
host_supported: true,
header_libs: [
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index 80711bc..1cfef34 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -472,47 +472,50 @@
return;
}
- if (params.oomadj >= 900) {
- soft_limit_mult = 0;
- } else if (params.oomadj >= 800) {
- soft_limit_mult = 0;
- } else if (params.oomadj >= 700) {
- soft_limit_mult = 0;
- } else if (params.oomadj >= 600) {
- // Launcher should be perceptible, don't kill it.
- params.oomadj = 200;
- soft_limit_mult = 1;
- } else if (params.oomadj >= 500) {
- soft_limit_mult = 0;
- } else if (params.oomadj >= 400) {
- soft_limit_mult = 0;
- } else if (params.oomadj >= 300) {
- soft_limit_mult = 1;
- } else if (params.oomadj >= 200) {
- soft_limit_mult = 2;
- } else if (params.oomadj >= 100) {
- soft_limit_mult = 10;
- } else if (params.oomadj >= 0) {
- soft_limit_mult = 20;
- } else {
- // Persistent processes will have a large
- // soft limit 512MB.
- soft_limit_mult = 64;
+ if (low_ram_device) {
+ if (params.oomadj >= 900) {
+ soft_limit_mult = 0;
+ } else if (params.oomadj >= 800) {
+ soft_limit_mult = 0;
+ } else if (params.oomadj >= 700) {
+ soft_limit_mult = 0;
+ } else if (params.oomadj >= 600) {
+ // Launcher should be perceptible, don't kill it.
+ params.oomadj = 200;
+ soft_limit_mult = 1;
+ } else if (params.oomadj >= 500) {
+ soft_limit_mult = 0;
+ } else if (params.oomadj >= 400) {
+ soft_limit_mult = 0;
+ } else if (params.oomadj >= 300) {
+ soft_limit_mult = 1;
+ } else if (params.oomadj >= 200) {
+ soft_limit_mult = 2;
+ } else if (params.oomadj >= 100) {
+ soft_limit_mult = 10;
+ } else if (params.oomadj >= 0) {
+ soft_limit_mult = 20;
+ } else {
+ // Persistent processes will have a large
+ // soft limit 512MB.
+ soft_limit_mult = 64;
+ }
+
+ snprintf(path, sizeof(path), MEMCG_SYSFS_PATH
+ "apps/uid_%d/pid_%d/memory.soft_limit_in_bytes",
+ params.uid, params.pid);
+ snprintf(val, sizeof(val), "%d", soft_limit_mult * EIGHT_MEGA);
+
+ /*
+ * system_server process has no memcg under /dev/memcg/apps but should be
+ * registered with lmkd. This is the best way so far to identify it.
+ */
+ is_system_server = (params.oomadj == SYSTEM_ADJ &&
+ (pwdrec = getpwnam("system")) != NULL &&
+ params.uid == pwdrec->pw_uid);
+ writefilestring(path, val, !is_system_server);
}
- snprintf(path, sizeof(path), MEMCG_SYSFS_PATH "apps/uid_%d/pid_%d/memory.soft_limit_in_bytes",
- params.uid, params.pid);
- snprintf(val, sizeof(val), "%d", soft_limit_mult * EIGHT_MEGA);
-
- /*
- * system_server process has no memcg under /dev/memcg/apps but should be
- * registered with lmkd. This is the best way so far to identify it.
- */
- is_system_server = (params.oomadj == SYSTEM_ADJ &&
- (pwdrec = getpwnam("system")) != NULL &&
- params.uid == pwdrec->pw_uid);
- writefilestring(path, val, !is_system_server);
-
procp = pid_lookup(params.pid);
if (!procp) {
procp = malloc(sizeof(struct proc));
@@ -1150,8 +1153,15 @@
}
}
- if (min_score_adj == OOM_SCORE_ADJ_MAX + 1)
+ if (min_score_adj == OOM_SCORE_ADJ_MAX + 1) {
+ if (debug_process_killing) {
+ ALOGI("Ignore %s memory pressure event "
+ "(free memory=%ldkB, cache=%ldkB, limit=%ldkB)",
+ level_name[level], other_free * page_k, other_file * page_k,
+ (long)lowmem_minfree[lowmem_targets_size - 1] * page_k);
+ }
return;
+ }
/* Free up enough pages to push over the highest minfree level */
pages_to_free = lowmem_minfree[lowmem_targets_size - 1] -
diff --git a/logwrapper/Android.bp b/logwrapper/Android.bp
index d4ba4f4..c378646 100644
--- a/logwrapper/Android.bp
+++ b/logwrapper/Android.bp
@@ -12,6 +12,7 @@
cc_library {
name: "liblogwrap",
defaults: ["logwrapper_defaults"],
+ recovery_available: true,
srcs: ["logwrap.c"],
shared_libs: [
"libcutils",
diff --git a/mkbootimg/include/bootimg/bootimg.h b/mkbootimg/include/bootimg/bootimg.h
index 406e208..bce308b 100644
--- a/mkbootimg/include/bootimg/bootimg.h
+++ b/mkbootimg/include/bootimg/bootimg.h
@@ -111,7 +111,7 @@
struct boot_img_hdr_v1 : public boot_img_hdr_v0 {
uint32_t recovery_dtbo_size; /* size in bytes for recovery DTBO image */
- uint64_t recovery_dtbo_offset; /* physical load addr */
+ uint64_t recovery_dtbo_offset; /* offset to recovery dtbo in boot image */
uint32_t header_size;
} __attribute__((packed));
@@ -138,11 +138,14 @@
* 1. kernel and ramdisk are required (size != 0)
* 2. recovery_dtbo is required for recovery.img in non-A/B devices(recovery_dtbo_size != 0)
* 3. second is optional (second_size == 0 -> no second)
- * 4. load each element (kernel, ramdisk, second, recovery_dtbo) at
+ * 4. load each element (kernel, ramdisk, second) at
* the specified physical address (kernel_addr, etc)
- * 5. prepare tags at tag_addr. kernel_args[] is
+ * 5. If booting to recovery mode in a non-A/B device, extract recovery dtbo and
+ * apply the correct set of overlays on the base device tree depending on the
+ * hardware/product revision.
+ * 6. prepare tags at tag_addr. kernel_args[] is
* appended to the kernel commandline in the tags.
- * 6. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr
- * 7. if second_size != 0: jump to second_addr
+ * 7. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr
+ * 8. if second_size != 0: jump to second_addr
* else: jump to kernel_addr
*/
diff --git a/property_service/libpropertyinfoparser/Android.bp b/property_service/libpropertyinfoparser/Android.bp
index ea9b968..70f6faa 100644
--- a/property_service/libpropertyinfoparser/Android.bp
+++ b/property_service/libpropertyinfoparser/Android.bp
@@ -2,6 +2,7 @@
name: "libpropertyinfoparser",
host_supported: true,
vendor_available: true,
+ recovery_available: true,
srcs: ["property_info_parser.cpp"],
cpp_std: "experimental",
diff --git a/qemu_pipe/Android.bp b/qemu_pipe/Android.bp
index 93c347b..c6bda4a 100644
--- a/qemu_pipe/Android.bp
+++ b/qemu_pipe/Android.bp
@@ -3,6 +3,7 @@
cc_library_static {
name: "libqemu_pipe",
vendor_available: true,
+ recovery_available: true,
sanitize: {
misc_undefined: ["integer"],
},
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index f488ed5..3c9e5f3 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -147,13 +147,10 @@
bcp_md5 :=
bcp_dep :=
-# If BOARD_VNDK_VERSION is defined, append PLATFORM_VNDK_VERSION to base name.
+# Append PLATFORM_VNDK_VERSION to base name.
define append_vndk_version
$(strip \
- $(if $(BOARD_VNDK_VERSION), \
- $(basename $(1)).$(PLATFORM_VNDK_VERSION)$(suffix $(1)), \
- $(1) \
- ) \
+ $(basename $(1)).$(PLATFORM_VNDK_VERSION)$(suffix $(1)) \
)
endef
@@ -215,31 +212,46 @@
vndk_version_suffix :=
endef # update_and_install_ld_config
+
+#######################################
+# ld.config.txt selection variables
+#
+_enforce_vndk_at_runtime := false
+ifdef BOARD_VNDK_VERSION
+ ifneq ($(BOARD_VNDK_RUNTIME_DISABLE),true)
+ _enforce_vndk_at_runtime := true
+ endif
+endif
+
+_enforce_vndk_lite_at_runtime := false
+ifeq ($(_enforce_vndk_at_runtime),false)
+ ifeq ($(PRODUCT_TREBLE_LINKER_NAMESPACES)|$(SANITIZE_TARGET),true|)
+ _enforce_vndk_lite_at_runtime := true
+ endif
+endif
+
#######################################
# ld.config.txt
#
# For VNDK enforced devices that have defined BOARD_VNDK_VERSION, use
# "ld.config.txt" as a source file. This configuration includes strict VNDK
# run-time restrictions for vendor process.
+#
# Other treblized devices, that have not defined BOARD_VNDK_VERSION or that
# have set BOARD_VNDK_RUNTIME_DISABLE to true, use "ld.config.vndk_lite.txt"
# as a source file. This configuration does not have strict VNDK run-time
# restrictions.
+#
# If the device is not treblized, use "ld.config.legacy.txt" for legacy
# namespace configuration.
+#
include $(CLEAR_VARS)
LOCAL_MODULE := ld.config.txt
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
-_enforce_vndk_at_runtime := false
-ifdef BOARD_VNDK_VERSION
-ifneq ($(BOARD_VNDK_RUNTIME_DISABLE),true)
- _enforce_vndk_at_runtime := true
-endif
-endif
-
ifeq ($(_enforce_vndk_at_runtime),true)
+
# for VNDK enforced devices
LOCAL_MODULE_STEM := $(call append_vndk_version,$(LOCAL_MODULE))
include $(BUILD_SYSTEM)/base_rules.mk
@@ -248,37 +260,36 @@
$(LOCAL_BUILT_MODULE),\
$(PLATFORM_VNDK_VERSION)))
-else ifeq ($(PRODUCT_TREBLE_LINKER_NAMESPACES)|$(SANITIZE_TARGET),true|)
-# for treblized but VNDK non-enforced devices
-LOCAL_MODULE_STEM := $(call append_vndk_version,$(LOCAL_MODULE))
+else ifeq ($(_enforce_vndk_lite_at_runtime),true)
+
+# for treblized but VNDK lightly enforced devices
+LOCAL_MODULE_STEM := ld.config.vndk_lite.txt
include $(BUILD_SYSTEM)/base_rules.mk
$(eval $(call update_and_install_ld_config,\
$(LOCAL_PATH)/etc/ld.config.vndk_lite.txt,\
$(LOCAL_BUILT_MODULE),\
- $(if $(BOARD_VNDK_VERSION),$(PLATFORM_VNDK_VERSION)),\
+ $(PLATFORM_VNDK_VERSION),\
true))
else
+
# for legacy non-treblized devices
-LOCAL_SRC_FILES := etc/ld.config.legacy.txt
LOCAL_MODULE_STEM := $(LOCAL_MODULE)
+LOCAL_SRC_FILES := etc/ld.config.legacy.txt
include $(BUILD_PREBUILT)
-endif # if _enforce_vndk_at_runtime is true
+endif # ifeq ($(_enforce_vndk_at_runtime),true)
-_enforce_vndk_at_runtime :=
#######################################
-# ld.config.noenforce.txt
+# ld.config.vndk_lite.txt
#
-# This file is a temporary configuration file only for GSI. Originally GSI has
-# BOARD_VNDK_VERSION defined and has strict VNDK enforcing rule based on
-# "ld.config.txt". However for the devices, that have not defined
-# BOARD_VNDK_VERSION, GSI provides this configuration file which is based on
-# "ld.config.vndk_lite.txt".
-# Do not install this file for the devices other than GSI.
+# This module is only for GSI.
+#
+ifeq ($(_enforce_vndk_lite_at_runtime),false)
+
include $(CLEAR_VARS)
-LOCAL_MODULE := ld.config.noenforce.txt
+LOCAL_MODULE := ld.config.vndk_lite.txt
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
LOCAL_MODULE_STEM := $(LOCAL_MODULE)
@@ -289,6 +300,11 @@
$(PLATFORM_VNDK_VERSION),\
true))
+endif # ifeq ($(_enforce_vndk_lite_at_runtime),false)
+
+_enforce_vndk_at_runtime :=
+_enforce_vndk_lite_at_runtime :=
+
#######################################
# llndk.libraries.txt
include $(CLEAR_VARS)
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index ba96cc8..51e3f9e 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -37,7 +37,8 @@
###############################################################################
namespace.default.isolated = true
-namespace.default.search.paths = /system/${LIB}
+namespace.default.search.paths = /system/${LIB}
+namespace.default.search.paths += /product/${LIB}
# We can't have entire /system/${LIB} as permitted paths because doing so
# makes it possible to load libs in /system/${LIB}/vndk* directories by
@@ -49,6 +50,7 @@
namespace.default.permitted.paths = /system/${LIB}/drm
namespace.default.permitted.paths += /system/${LIB}/extractors
namespace.default.permitted.paths += /system/${LIB}/hw
+namespace.default.permitted.paths += /product/${LIB}
# These are where odex files are located. libart has to be able to dlopen the files
namespace.default.permitted.paths += /system/framework
namespace.default.permitted.paths += /system/app
@@ -68,6 +70,8 @@
namespace.default.asan.search.paths = /data/asan/system/${LIB}
namespace.default.asan.search.paths += /system/${LIB}
+namespace.default.asan.search.paths += /data/asan/product/${LIB}
+namespace.default.asan.search.paths += /product/${LIB}
namespace.default.asan.permitted.paths = /data
namespace.default.asan.permitted.paths += /system/${LIB}/drm
@@ -83,6 +87,7 @@
namespace.default.asan.permitted.paths += /odm/app
namespace.default.asan.permitted.paths += /odm/priv-app
namespace.default.asan.permitted.paths += /oem/app
+namespace.default.asan.permitted.paths += /product/${LIB}
namespace.default.asan.permitted.paths += /product/framework
namespace.default.asan.permitted.paths += /product/app
namespace.default.asan.permitted.paths += /product/priv-app
@@ -320,10 +325,13 @@
###############################################################################
namespace.system.isolated = false
-namespace.system.search.paths = /system/${LIB}
+namespace.system.search.paths = /system/${LIB}
+namespace.system.search.paths += /product/${LIB}
namespace.system.asan.search.paths = /data/asan/system/${LIB}
namespace.system.asan.search.paths += /system/${LIB}
+namespace.system.asan.search.paths += /data/asan/product/${LIB}
+namespace.system.asan.search.paths += /product/${LIB}
###############################################################################
# Namespace config for binaries under /postinstall.
@@ -335,4 +343,5 @@
###############################################################################
[postinstall]
namespace.default.isolated = false
-namespace.default.search.paths = /system/${LIB}
+namespace.default.search.paths = /system/${LIB}
+namespace.default.search.paths += /product/${LIB}
diff --git a/rootdir/etc/ld.config.vndk_lite.txt b/rootdir/etc/ld.config.vndk_lite.txt
index 1fd4195..ab03755 100644
--- a/rootdir/etc/ld.config.vndk_lite.txt
+++ b/rootdir/etc/ld.config.vndk_lite.txt
@@ -40,6 +40,7 @@
namespace.default.search.paths = /system/${LIB}
namespace.default.search.paths += /odm/${LIB}
namespace.default.search.paths += /vendor/${LIB}
+namespace.default.search.paths += /product/${LIB}
namespace.default.asan.search.paths = /data/asan/system/${LIB}
namespace.default.asan.search.paths += /system/${LIB}
@@ -47,6 +48,8 @@
namespace.default.asan.search.paths += /odm/${LIB}
namespace.default.asan.search.paths += /data/asan/vendor/${LIB}
namespace.default.asan.search.paths += /vendor/${LIB}
+namespace.default.asan.search.paths += /data/asan/product/${LIB}
+namespace.default.asan.search.paths += /product/${LIB}
###############################################################################
# "sphal" namespace
@@ -205,6 +208,7 @@
namespace.default.search.paths += /system/${LIB}/vndk%VNDK_VER%
namespace.default.search.paths += /system/${LIB}/vndk-sp%VNDK_VER%
namespace.default.search.paths += /system/${LIB}
+namespace.default.search.paths += /product/${LIB}
namespace.default.asan.search.paths = /data/asan/odm/${LIB}
namespace.default.asan.search.paths += /odm/${LIB}
@@ -224,6 +228,8 @@
namespace.default.asan.search.paths += /system/${LIB}/vndk-sp%VNDK_VER%
namespace.default.asan.search.paths += /data/asan/system/${LIB}
namespace.default.asan.search.paths += /system/${LIB}
+namespace.default.asan.search.paths += /data/asan/product/${LIB}
+namespace.default.asan.search.paths += /product/${LIB}
###############################################################################
# Namespace config for binaries under /postinstall.
@@ -235,4 +241,5 @@
###############################################################################
[postinstall]
namespace.default.isolated = false
-namespace.default.search.paths = /system/${LIB}
+namespace.default.search.paths = /system/${LIB}
+namespace.default.search.paths += /product/${LIB}
diff --git a/rootdir/init.rc b/rootdir/init.rc
index d3504ad..197047d 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -419,6 +419,7 @@
mkdir /data/misc/radio 0770 system radio
mkdir /data/misc/sms 0770 system radio
mkdir /data/misc/carrierid 0770 system radio
+ mkdir /data/misc/apns 0770 system radio
mkdir /data/misc/zoneinfo 0775 system system
mkdir /data/misc/textclassifier 0771 system system
mkdir /data/misc/vpn 0770 system vpn