Merge "init: finer grained permissions for ctl. properties"
diff --git a/adb/Android.bp b/adb/Android.bp
index 99de54e..1f41e4f 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -122,6 +122,7 @@
"sysdeps_test.cpp",
"sysdeps/stat_test.cpp",
"transport_test.cpp",
+ "types_test.cpp",
]
cc_library_host_static {
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.h b/adb/adb_unique_fd.h
index 34c1bbc..7d2354d 100644
--- a/adb/adb_unique_fd.h
+++ b/adb/adb_unique_fd.h
@@ -16,6 +16,8 @@
#pragma once
+#include <unistd.h>
+
#include <android-base/unique_fd.h>
// Helper to automatically close an FD when it goes out of scope.
@@ -24,3 +26,42 @@
};
using unique_fd = android::base::unique_fd_impl<AdbCloser>;
+
+#if !defined(_WIN32)
+inline bool Pipe(unique_fd* read, unique_fd* write, int flags = 0) {
+ 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) {
+ PLOG(FATAL) << "failed to set FD_CLOEXEC on newly created pipe";
+ }
+ }
+
+ if (flags & O_NONBLOCK) {
+ if (fcntl(pipefd[0], F_SETFL, O_NONBLOCK) != 0 ||
+ fcntl(pipefd[1], F_SETFL, O_NONBLOCK) != 0) {
+ PLOG(FATAL) << "failed to set O_NONBLOCK on newly created pipe";
+ }
+ }
+#endif
+
+ read->reset(pipefd[0]);
+ write->reset(pipefd[1]);
+ return true;
+}
+#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/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/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/transport.cpp b/adb/transport.cpp
index beec13a..7db9bf2 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -427,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
@@ -698,10 +698,9 @@
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() {
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/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/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/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/init/host_init_parser.cpp b/init/host_init_parser.cpp
index 5232b7e..df497ea 100644
--- a/init/host_init_parser.cpp
+++ b/init/host_init_parser.cpp
@@ -48,7 +48,7 @@
#include "generated_stub_builtin_function_map.h"
int main(int argc, char** argv) {
- android::base::InitLogging(argv, &android::base::StderrLogger);
+ android::base::InitLogging(argv, &android::base::StdioLogger);
if (argc != 2) {
LOG(ERROR) << "Usage: " << argv[0] << " <init file to parse>";
return -1;
diff --git a/liblog/Android.bp b/liblog/Android.bp
index 2d5a5db..1bd796a 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -84,6 +84,7 @@
android_arm: {
// TODO: This is to work around b/24465209. Remove after root cause is fixed
ldflags: ["-Wl,--hash-style=both"],
+ use_clang_lld: false,
},
windows: {
srcs: ["uio.c"],
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/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/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/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 82f2e73..a6bf730 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -220,6 +220,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/tools/unwind_for_offline.cpp b/libunwindstack/tools/unwind_for_offline.cpp
index 589731d..640992f 100644
--- a/libunwindstack/tools/unwind_for_offline.cpp
+++ b/libunwindstack/tools/unwind_for_offline.cpp
@@ -69,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) {
@@ -102,13 +102,14 @@
std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(file_name.c_str(), "w+"), &fclose);
if (fp == nullptr) {
- printf("Failed to create stack.data.\n");
+ 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)) {
- perror("Failed to write all data.");
+ printf("Failed to write sp_start data: sizeof(sp_start) %zu, written %zu\n", sizeof(sp_start),
+ bytes);
return false;
}
@@ -141,7 +142,7 @@
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());
+ perror((std::string("Cannot create ") + cur_name).c_str());
return false;
}
@@ -160,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);
@@ -265,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/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}