Merge "Turn off -Wdate-time for crasher"
diff --git a/adb/.clang-format b/adb/.clang-format
index 0395c8e..6737535 100644
--- a/adb/.clang-format
+++ b/adb/.clang-format
@@ -2,6 +2,7 @@
AllowShortBlocksOnASingleLine: false
AllowShortFunctionsOnASingleLine: false
+ColumnLimit: 100
CommentPragmas: NOLINT:.*
DerivePointerAlignment: false
IndentWidth: 4
diff --git a/adb/Android.mk b/adb/Android.mk
index 903d1e1..9c8ab6d 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -17,10 +17,20 @@
-Wvla \
-DADB_REVISION='"$(adb_version)"' \
+ADB_COMMON_linux_CFLAGS := \
+ -std=c++14 \
+ -Wexit-time-destructors \
+
+ADB_COMMON_darwin_CFLAGS := \
+ -std=c++14 \
+ -Wexit-time-destructors \
+
# Define windows.h and tchar.h Unicode preprocessor symbols so that
# CreateFile(), _tfopen(), etc. map to versions that take wchar_t*, breaking the
# build if you accidentally pass char*. Fix by calling like:
-# CreateFileW(widen(utf8).c_str()).
+# std::wstring path_wide;
+# if (!android::base::UTF8ToWide(path_utf8, &path_wide)) { /* error handling */ }
+# CreateFileW(path_wide.c_str());
ADB_COMMON_windows_CFLAGS := \
-DUNICODE=1 -D_UNICODE=1 \
@@ -55,7 +65,10 @@
-fvisibility=hidden \
LIBADB_linux_CFLAGS := \
- -std=c++14 \
+ $(ADB_COMMON_linux_CFLAGS) \
+
+LIBADB_darwin_CFLAGS := \
+ $(ADB_COMMON_darwin_CFLAGS) \
LIBADB_windows_CFLAGS := \
$(ADB_COMMON_windows_CFLAGS) \
@@ -110,6 +123,7 @@
LOCAL_CFLAGS := $(LIBADB_CFLAGS) -DADB_HOST=1
LOCAL_CFLAGS_windows := $(LIBADB_windows_CFLAGS)
LOCAL_CFLAGS_linux := $(LIBADB_linux_CFLAGS)
+LOCAL_CFLAGS_darwin := $(LIBADB_darwin_CFLAGS)
LOCAL_SRC_FILES := \
$(LIBADB_SRC_FILES) \
adb_auth_host.cpp \
@@ -146,6 +160,19 @@
LOCAL_SHARED_LIBRARIES := libbase libcutils
include $(BUILD_NATIVE_TEST)
+# libdiagnose_usb
+# =========================================================
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libdiagnose_usb
+LOCAL_MODULE_HOST_OS := darwin linux windows
+LOCAL_CFLAGS := $(LIBADB_CFLAGS)
+LOCAL_SRC_FILES := diagnose_usb.cpp
+# Even though we're building a static library (and thus there's no link step for
+# this to take effect), this adds the includes to our path.
+LOCAL_STATIC_LIBRARIES := libbase
+include $(BUILD_HOST_STATIC_LIBRARY)
+
# adb_test
# =========================================================
@@ -155,6 +182,7 @@
LOCAL_CFLAGS := -DADB_HOST=1 $(LIBADB_CFLAGS)
LOCAL_CFLAGS_windows := $(LIBADB_windows_CFLAGS)
LOCAL_CFLAGS_linux := $(LIBADB_linux_CFLAGS)
+LOCAL_CFLAGS_darwin := $(LIBADB_darwin_CFLAGS)
LOCAL_SRC_FILES := \
$(LIBADB_TEST_SRCS) \
services.cpp \
@@ -170,6 +198,7 @@
libadb \
libcrypto_static \
libcutils \
+ libdiagnose_usb \
# Set entrypoint to wmain from sysdeps_win32.cpp instead of main
LOCAL_LDFLAGS_windows := -municode
@@ -178,6 +207,8 @@
LOCAL_LDLIBS_windows := -lws2_32 -luserenv
LOCAL_STATIC_LIBRARIES_windows := AdbWinApi
+LOCAL_MULTILIB := first
+
include $(BUILD_HOST_NATIVE_TEST)
# adb device tracker (used by ddms) test tool
@@ -189,6 +220,7 @@
LOCAL_CFLAGS := -DADB_HOST=1 $(LIBADB_CFLAGS)
LOCAL_CFLAGS_windows := $(LIBADB_windows_CFLAGS)
LOCAL_CFLAGS_linux := $(LIBADB_linux_CFLAGS)
+LOCAL_CFLAGS_darwin := $(LIBADB_darwin_CFLAGS)
LOCAL_SRC_FILES := test_track_devices.cpp
LOCAL_SANITIZE := $(adb_host_sanitize)
LOCAL_SHARED_LIBRARIES := libbase
@@ -204,7 +236,6 @@
LOCAL_LDLIBS_linux := -lrt -ldl -lpthread
LOCAL_LDLIBS_darwin := -lpthread -framework CoreFoundation -framework IOKit -framework Carbon
-LOCAL_CFLAGS_darwin := -Wno-sizeof-pointer-memaccess -Wno-unused-parameter
# Use wmain instead of main
LOCAL_LDFLAGS_windows := -municode
@@ -230,6 +261,13 @@
LOCAL_CFLAGS_windows := \
$(ADB_COMMON_windows_CFLAGS)
+LOCAL_CFLAGS_linux := \
+ $(ADB_COMMON_linux_CFLAGS) \
+
+LOCAL_CFLAGS_darwin := \
+ $(ADB_COMMON_darwin_CFLAGS) \
+ -Wno-sizeof-pointer-memaccess -Wno-unused-parameter \
+
LOCAL_MODULE := adb
LOCAL_MODULE_TAGS := debug
LOCAL_MODULE_HOST_OS := darwin linux windows
@@ -239,9 +277,13 @@
libadb \
libbase \
libcrypto_static \
- libcutils \
+ libdiagnose_usb \
liblog \
+# Don't use libcutils on Windows.
+LOCAL_STATIC_LIBRARIES_darwin := libcutils
+LOCAL_STATIC_LIBRARIES_linux := libcutils
+
LOCAL_CXX_STL := libc++_static
# Don't add anything here, we don't want additional shared dependencies
@@ -273,6 +315,7 @@
LOCAL_CFLAGS := \
$(ADB_COMMON_CFLAGS) \
+ $(ADB_COMMON_linux_CFLAGS) \
-DADB_HOST=0 \
-D_GNU_SOURCE \
-Wno-deprecated-declarations \
@@ -305,6 +348,9 @@
libsquashfs_utils \
libcutils \
libbase \
- libcrypto_static
+ libcrypto_static \
+ libminijail \
+ libminijail_generated \
+ libcap
include $(BUILD_EXECUTABLE)
diff --git a/adb/adb.cpp b/adb/adb.cpp
index c39c178..c03d7db 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -33,10 +33,10 @@
#include <string>
#include <vector>
-#include <base/logging.h>
-#include <base/macros.h>
-#include <base/stringprintf.h>
-#include <base/strings.h>
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include "adb_auth.h"
#include "adb_io.h"
diff --git a/adb/adb.h b/adb/adb.h
index c284c8c..9020fc3 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -22,7 +22,7 @@
#include <string>
-#include <base/macros.h>
+#include <android-base/macros.h>
#include "adb_trace.h"
#include "fdevent.h"
@@ -50,7 +50,7 @@
std::string adb_version();
// Increment this when we want to force users to start a new adb server.
-#define ADB_SERVER_VERSION 34
+#define ADB_SERVER_VERSION 35
class atransport;
struct usb_handle;
@@ -151,7 +151,7 @@
void get_my_path(char *s, size_t maxLen);
int launch_server(int server_port);
-int adb_main(int is_daemon, int server_port, int ack_reply_fd);
+int adb_server_main(int is_daemon, int server_port, int ack_reply_fd);
/* initialize a transport object's func pointers and state */
#if ADB_HOST
@@ -214,14 +214,14 @@
void local_connect(int port);
int local_connect_arbitrary_ports(int console_port, int adb_port, std::string* error);
-/* usb host/client interface */
+// USB host/client interface.
void usb_init();
int usb_write(usb_handle *h, const void *data, int len);
int usb_read(usb_handle *h, void *data, int len);
int usb_close(usb_handle *h);
void usb_kick(usb_handle *h);
-/* used for USB device detection */
+// USB device detection.
#if ADB_HOST
int is_adb_interface(int vid, int pid, int usb_class, int usb_subclass, int usb_protocol);
#endif
diff --git a/adb/adb_auth_client.cpp b/adb/adb_auth_client.cpp
index 463b496..c4ffc85 100644
--- a/adb/adb_auth_client.cpp
+++ b/adb/adb_auth_client.cpp
@@ -228,13 +228,13 @@
static void adb_auth_listener(int fd, unsigned events, void *data)
{
- struct sockaddr addr;
+ sockaddr_storage addr;
socklen_t alen;
int s;
alen = sizeof(addr);
- s = adb_socket_accept(fd, &addr, &alen);
+ s = adb_socket_accept(fd, reinterpret_cast<sockaddr*>(&addr), &alen);
if (s < 0) {
D("Failed to accept: errno=%d", errno);
return;
diff --git a/adb/adb_auth_host.cpp b/adb/adb_auth_host.cpp
index 5309519..facacef 100644
--- a/adb/adb_auth_host.cpp
+++ b/adb/adb_auth_host.cpp
@@ -43,7 +43,7 @@
#include "mincrypt/rsa.h"
#undef RSA_verify
-#include <base/strings.h>
+#include <android-base/strings.h>
#include <cutils/list.h>
#include <openssl/evp.h>
@@ -311,7 +311,9 @@
SystemErrorCodeToString(hr).c_str());
return -1;
}
- home_str = narrow(path);
+ if (!android::base::WideToUTF8(path, &home_str)) {
+ return -1;
+ }
home = home_str.c_str();
}
format = "%s\\%s";
diff --git a/adb/adb_client.cpp b/adb/adb_client.cpp
index ddeb5f1..cb5e488 100644
--- a/adb/adb_client.cpp
+++ b/adb/adb_client.cpp
@@ -31,8 +31,8 @@
#include <string>
#include <vector>
-#include <base/stringprintf.h>
-#include <base/strings.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include <cutils/sockets.h>
#include "adb_io.h"
diff --git a/adb/adb_io.cpp b/adb/adb_io.cpp
index a37fbc0..176b7bd 100644
--- a/adb/adb_io.cpp
+++ b/adb/adb_io.cpp
@@ -20,7 +20,7 @@
#include <unistd.h>
-#include <base/stringprintf.h>
+#include <android-base/stringprintf.h>
#include "adb_trace.h"
#include "adb_utils.h"
diff --git a/adb/adb_io_test.cpp b/adb/adb_io_test.cpp
index 6928a90..21a82e8 100644
--- a/adb/adb_io_test.cpp
+++ b/adb/adb_io_test.cpp
@@ -27,8 +27,8 @@
#include <string>
-#include "base/file.h"
-#include "base/test_utils.h"
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
// All of these tests fail on Windows because they use the C Runtime open(),
// but the adb_io APIs expect file descriptors from adb_open(). This could
diff --git a/adb/adb_listeners.cpp b/adb/adb_listeners.cpp
index d5b1fd5..e8c2338 100644
--- a/adb/adb_listeners.cpp
+++ b/adb/adb_listeners.cpp
@@ -19,7 +19,7 @@
#include <stdio.h>
#include <stdlib.h>
-#include <base/stringprintf.h>
+#include <android-base/stringprintf.h>
#include <cutils/sockets.h>
#include "sysdeps.h"
@@ -33,21 +33,18 @@
};
static void ss_listener_event_func(int _fd, unsigned ev, void *_l) {
- asocket *s;
+ if (ev & FDE_READ) {
+ sockaddr_storage ss;
+ sockaddr* addrp = reinterpret_cast<sockaddr*>(&ss);
+ socklen_t alen = sizeof(ss);
+ int fd = adb_socket_accept(_fd, addrp, &alen);
+ if (fd < 0) return;
- if(ev & FDE_READ) {
- struct sockaddr addr;
- socklen_t alen;
- int fd;
+ int rcv_buf_size = CHUNK_SIZE;
+ adb_setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcv_buf_size, sizeof(rcv_buf_size));
- alen = sizeof(addr);
- fd = adb_socket_accept(_fd, &addr, &alen);
- if(fd < 0) return;
-
- adb_socket_setbufsize(fd, CHUNK_SIZE);
-
- s = create_local_socket(fd);
- if(s) {
+ asocket* s = create_local_socket(fd);
+ if (s) {
connect_to_smartsocket(s);
return;
}
@@ -62,12 +59,13 @@
asocket *s;
if (ev & FDE_READ) {
- struct sockaddr addr;
+ sockaddr_storage ss;
+ sockaddr* addrp = reinterpret_cast<sockaddr*>(&ss);
socklen_t alen;
int fd;
- alen = sizeof(addr);
- fd = adb_socket_accept(_fd, &addr, &alen);
+ alen = sizeof(ss);
+ fd = adb_socket_accept(_fd, addrp, &alen);
if (fd < 0) {
return;
}
@@ -83,7 +81,7 @@
}
}
-static void free_listener(alistener* l)
+static void free_listener(alistener* l)
{
if (l->next) {
l->next->prev = l->prev;
diff --git a/adb/adb_trace.cpp b/adb/adb_trace.cpp
index 04b82f6..62900c0 100644
--- a/adb/adb_trace.cpp
+++ b/adb/adb_trace.cpp
@@ -21,8 +21,8 @@
#include <unordered_map>
#include <vector>
-#include <base/logging.h>
-#include <base/strings.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
#include "adb.h"
@@ -157,13 +157,13 @@
// Don't open log file if no tracing, since this will block
// the crypto unmount of /data
if (!get_trace_setting().empty()) {
- if (isatty(STDOUT_FILENO) == 0) {
+ if (unix_isatty(STDOUT_FILENO) == 0) {
start_device_log();
}
}
#endif
- android::base::InitLogging(argv, AdbLogger);
+ android::base::InitLogging(argv, &AdbLogger);
setup_trace_mask();
VLOG(ADB) << adb_version();
diff --git a/adb/adb_trace.h b/adb/adb_trace.h
index 78b2deb..d50f947 100644
--- a/adb/adb_trace.h
+++ b/adb/adb_trace.h
@@ -17,8 +17,8 @@
#ifndef __ADB_TRACE_H
#define __ADB_TRACE_H
-#include <base/logging.h>
-#include <base/stringprintf.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
/* IMPORTANT: if you change the following list, don't
* forget to update the corresponding 'tags' table in
diff --git a/adb/adb_utils.cpp b/adb/adb_utils.cpp
index f7b2e4e..b132118 100644
--- a/adb/adb_utils.cpp
+++ b/adb/adb_utils.cpp
@@ -26,15 +26,35 @@
#include <algorithm>
-#include <base/logging.h>
-#include <base/stringprintf.h>
-#include <base/strings.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include "adb.h"
#include "adb_trace.h"
#include "sysdeps.h"
+ADB_MUTEX_DEFINE(basename_lock);
ADB_MUTEX_DEFINE(dirname_lock);
+#if defined(_WIN32)
+constexpr char kNullFileName[] = "NUL";
+#else
+constexpr char kNullFileName[] = "/dev/null";
+#endif
+
+void close_stdin() {
+ int fd = unix_open(kNullFileName, O_RDONLY);
+ if (fd == -1) {
+ fatal_errno("failed to open %s", kNullFileName);
+ }
+
+ if (TEMP_FAILURE_RETRY(dup2(fd, STDIN_FILENO)) == -1) {
+ fatal_errno("failed to redirect stdin to %s", kNullFileName);
+ }
+ unix_close(fd);
+}
+
bool getcwd(std::string* s) {
char* cwd = getcwd(nullptr, 0);
if (cwd != nullptr) *s = cwd;
@@ -69,13 +89,31 @@
}
std::string adb_basename(const std::string& path) {
- size_t base = path.find_last_of(OS_PATH_SEPARATORS);
- return (base != std::string::npos) ? path.substr(base + 1) : path;
+ // Copy path because basename may modify the string passed in.
+ std::string result(path);
+
+ // Use lock because basename() may write to a process global and return a
+ // pointer to that. Note that this locking strategy only works if all other
+ // callers to dirname in the process also grab this same lock.
+ adb_mutex_lock(&basename_lock);
+
+ // Note that if std::string uses copy-on-write strings, &str[0] will cause
+ // the copy to be made, so there is no chance of us accidentally writing to
+ // the storage for 'path'.
+ char* name = basename(&result[0]);
+
+ // In case dirname returned a pointer to a process global, copy that string
+ // before leaving the lock.
+ result.assign(name);
+
+ adb_mutex_unlock(&basename_lock);
+
+ return result;
}
std::string adb_dirname(const std::string& path) {
// Copy path because dirname may modify the string passed in.
- std::string parent_storage(path);
+ std::string result(path);
// Use lock because dirname() may write to a process global and return a
// pointer to that. Note that this locking strategy only works if all other
@@ -85,11 +123,11 @@
// Note that if std::string uses copy-on-write strings, &str[0] will cause
// the copy to be made, so there is no chance of us accidentally writing to
// the storage for 'path'.
- char* parent = dirname(&parent_storage[0]);
+ char* parent = dirname(&result[0]);
// In case dirname returned a pointer to a process global, copy that string
// before leaving the lock.
- const std::string result(parent);
+ result.assign(parent);
adb_mutex_unlock(&dirname_lock);
@@ -115,9 +153,7 @@
// - Recursive, so it uses stack space relative to number of directory
// components.
- const std::string parent(adb_dirname(path));
-
- if (directory_exists(parent)) {
+ if (directory_exists(path)) {
return true;
}
@@ -125,19 +161,21 @@
// This can happen on Windows when walking up the directory hierarchy and not
// finding anything that already exists (unlike POSIX that will eventually
// find . or /).
+ const std::string parent(adb_dirname(path));
+
if (parent == path) {
errno = ENOENT;
return false;
}
- // Recursively make parent directories of 'parent'.
+ // Recursively make parent directories of 'path'.
if (!mkdirs(parent)) {
return false;
}
- // Now that the parent directory hierarchy of 'parent' has been ensured,
+ // Now that the parent directory hierarchy of 'path' has been ensured,
// create parent itself.
- if (adb_mkdir(parent, 0775) == -1) {
+ if (adb_mkdir(path, 0775) == -1) {
// Can't just check for errno == EEXIST because it might be a file that
// exists.
const int saved_errno = errno;
@@ -163,11 +201,8 @@
line.push_back(' ');
for (size_t i = 0; i < byte_count; ++i) {
- int c = p[i];
- if (c < 32 || c > 127) {
- c = '.';
- }
- line.push_back(c);
+ int ch = p[i];
+ line.push_back(isprint(ch) ? ch : '.');
}
return line;
diff --git a/adb/adb_utils.h b/adb/adb_utils.h
index 537d0e4..388d7dd 100644
--- a/adb/adb_utils.h
+++ b/adb/adb_utils.h
@@ -19,6 +19,8 @@
#include <string>
+void close_stdin();
+
bool getcwd(std::string* cwd);
bool directory_exists(const std::string& path);
diff --git a/adb/adb_utils_test.cpp b/adb/adb_utils_test.cpp
index 4a2787a..4508bca 100644
--- a/adb/adb_utils_test.cpp
+++ b/adb/adb_utils_test.cpp
@@ -30,8 +30,8 @@
#include "sysdeps.h"
-#include <base/macros.h>
-#include <base/test_utils.h>
+#include <android-base/macros.h>
+#include <android-base/test_utils.h>
#ifdef _WIN32
static std::string subdir(const char* parent, const char* child) {
@@ -102,6 +102,13 @@
TEST(adb_utils, adb_basename) {
EXPECT_EQ("sh", adb_basename("/system/bin/sh"));
EXPECT_EQ("sh", adb_basename("sh"));
+ EXPECT_EQ("sh", adb_basename("/system/bin/sh/"));
+}
+
+TEST(adb_utils, adb_dirname) {
+ EXPECT_EQ("/system/bin", adb_dirname("/system/bin/sh"));
+ EXPECT_EQ(".", adb_dirname("sh"));
+ EXPECT_EQ("/system/bin", adb_dirname("/system/bin/sh/"));
}
TEST(adb_utils, parse_host_and_port) {
diff --git a/adb/client/main.cpp b/adb/client/main.cpp
index a225a53..b37d04d 100644
--- a/adb/client/main.cpp
+++ b/adb/client/main.cpp
@@ -27,18 +27,17 @@
#include <sched.h>
#endif
-#include "base/file.h"
-#include "base/logging.h"
-#include "base/stringprintf.h"
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
#include "adb.h"
#include "adb_auth.h"
#include "adb_listeners.h"
+#include "adb_utils.h"
#include "transport.h"
#if defined(_WIN32)
-static const char kNullFileName[] = "NUL";
-
static BOOL WINAPI ctrlc_handler(DWORD type) {
// TODO: Consider trying to kill a starting up adb server (if we're in
// launch_server) by calling GenerateConsoleCtrlEvent().
@@ -58,31 +57,22 @@
SystemErrorCodeToString(GetLastError()).c_str());
}
- return narrow(temp_path) + log_name;
+ std::string temp_path_utf8;
+ if (!android::base::WideToUTF8(temp_path, &temp_path_utf8)) {
+ fatal_errno("cannot convert temporary file path from UTF-16 to UTF-8");
+ }
+
+ return temp_path_utf8 + log_name;
}
#else
-static const char kNullFileName[] = "/dev/null";
-
static std::string GetLogFilePath() {
return std::string("/tmp/adb.log");
}
#endif
-static void close_stdin() {
- int fd = unix_open(kNullFileName, O_RDONLY);
- if (fd == -1) {
- fatal("cannot open '%s': %s", kNullFileName, strerror(errno));
- }
- if (dup2(fd, STDIN_FILENO) == -1) {
- fatal("cannot redirect stdin: %s", strerror(errno));
- }
- unix_close(fd);
-}
-
static void setup_daemon_logging(void) {
const std::string log_file_path(GetLogFilePath());
- int fd = unix_open(log_file_path.c_str(), O_WRONLY | O_CREAT | O_APPEND,
- 0640);
+ int fd = unix_open(log_file_path.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0640);
if (fd == -1) {
fatal("cannot open '%s': %s", log_file_path.c_str(), strerror(errno));
}
@@ -98,10 +88,10 @@
LOG(INFO) << adb_version();
}
-int adb_main(int is_daemon, int server_port, int ack_reply_fd) {
+int adb_server_main(int is_daemon, int server_port, int ack_reply_fd) {
#if defined(_WIN32)
// adb start-server starts us up with stdout and stderr hooked up to
- // anonymous pipes to. When the C Runtime sees this, it makes stderr and
+ // anonymous pipes. When the C Runtime sees this, it makes stderr and
// stdout buffered, but to improve the chance that error output is seen,
// unbuffer stdout and stderr just like if we were run at the console.
// This also keeps stderr unbuffered when it is redirected to adb.log.
@@ -115,8 +105,6 @@
}
SetConsoleCtrlHandler(ctrlc_handler, TRUE);
-#else
- signal(SIGPIPE, SIG_IGN);
#endif
init_transport_registration();
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index 37bd777..a025ed7 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -33,13 +33,15 @@
#include <memory>
#include <string>
+#include <vector>
-#include <base/logging.h>
-#include <base/stringprintf.h>
-#include <base/strings.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#if !defined(_WIN32)
#include <signal.h>
+#include <sys/ioctl.h>
#include <termios.h>
#include <unistd.h>
#endif
@@ -57,8 +59,10 @@
static int install_app(TransportType t, const char* serial, int argc, const char** argv);
static int install_multiple_app(TransportType t, const char* serial, int argc, const char** argv);
static int uninstall_app(TransportType t, const char* serial, int argc, const char** argv);
+static int install_app_legacy(TransportType t, const char* serial, int argc, const char** argv);
+static int uninstall_app_legacy(TransportType t, const char* serial, int argc, const char** argv);
-static std::string gProductOutPath;
+static auto& gProductOutPath = *new std::string();
extern int gListenAll;
static std::string product_file(const char *extra) {
@@ -101,18 +105,20 @@
" will disconnect from all connected TCP/IP devices.\n"
"\n"
"device commands:\n"
- " adb push <local> <remote>\n"
- " - copy file/dir to device\n"
- " adb pull [-a] <remote> [<local>]\n"
- " - copy file/dir from device\n"
- " ('-a' means copy timestamp and mode)\n"
+ " adb push <local>... <remote>\n"
+ " - copy files/dirs to device\n"
+ " adb pull [-a] <remote>... <local>\n"
+ " - copy files/dirs from device\n"
+ " (-a preserves file timestamp and mode)\n"
" adb sync [ <directory> ] - copy host->device only if changed\n"
" (-l means list but don't copy)\n"
- " adb shell [-Ttx] - run remote shell interactively\n"
- " adb shell [-Ttx] <command> - run remote shell command\n"
- " (-T disables PTY allocation)\n"
- " (-t forces PTY allocation)\n"
- " (-x disables remote exit codes and stdout/stderr separation)\n"
+ " adb shell [-e escape] [-n] [-Tt] [-x] [command]\n"
+ " - run remote shell command (interactive shell if no command given)\n"
+ " (-e: choose escape character, or \"none\"; default '~')\n"
+ " (-n: don't read from stdin)\n"
+ " (-T: disable PTY allocation)\n"
+ " (-t: force PTY allocation)\n"
+ " (-x: disable remote exit codes and stdout/stderr separation)\n"
" adb emu <command> - run emulator console command\n"
" adb logcat [ <filter-spec> ] - View device log\n"
" adb forward --list - list all forward socket connections.\n"
@@ -138,7 +144,7 @@
" localabstract:<unix domain socket name>\n"
" localreserved:<unix domain socket name>\n"
" localfilesystem:<unix domain socket name>\n"
- " adb reverse --norebind <remote> <local>\n"
+ " adb reverse --no-rebind <remote> <local>\n"
" - same as 'adb reverse <remote> <local>' but fails\n"
" if <remote> is already reversed.\n"
" adb reverse --remove <remote>\n"
@@ -198,7 +204,10 @@
" adb version - show version num\n"
"\n"
"scripting:\n"
- " adb wait-for-device - block until device is online\n"
+ " adb wait-for[-<transport>]-<state>\n"
+ " - wait for device to be in the given state:\n"
+ " device, recovery, sideload, or bootloader\n"
+ " Transport is: usb, local or any [default=any]\n"
" adb start-server - ensure that there is a server running\n"
" adb kill-server - kill the server if it is running\n"
" adb get-state - prints: offline | bootloader | device\n"
@@ -246,17 +255,17 @@
#if defined(_WIN32)
// Implemented in sysdeps_win32.cpp.
-void stdin_raw_init(int fd);
-void stdin_raw_restore(int fd);
+void stdin_raw_init();
+void stdin_raw_restore();
#else
static termios g_saved_terminal_state;
-static void stdin_raw_init(int fd) {
- if (tcgetattr(fd, &g_saved_terminal_state)) return;
+static void stdin_raw_init() {
+ if (tcgetattr(STDIN_FILENO, &g_saved_terminal_state)) return;
termios tio;
- if (tcgetattr(fd, &tio)) return;
+ if (tcgetattr(STDIN_FILENO, &tio)) return;
cfmakeraw(&tio);
@@ -264,11 +273,11 @@
tio.c_cc[VTIME] = 0;
tio.c_cc[VMIN] = 1;
- tcsetattr(fd, TCSAFLUSH, &tio);
+ tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio);
}
-static void stdin_raw_restore(int fd) {
- tcsetattr(fd, TCSAFLUSH, &g_saved_terminal_state);
+static void stdin_raw_restore() {
+ tcsetattr(STDIN_FILENO, TCSAFLUSH, &g_saved_terminal_state);
}
#endif
@@ -357,7 +366,7 @@
D("copy_to_file(%d -> %d)", inFd, outFd);
if (inFd == STDIN_FILENO) {
- stdin_raw_init(STDIN_FILENO);
+ stdin_raw_init();
#ifdef _WIN32
old_stdin_mode = _setmode(STDIN_FILENO, _O_BINARY);
if (old_stdin_mode == -1) {
@@ -399,7 +408,7 @@
}
if (inFd == STDIN_FILENO) {
- stdin_raw_restore(STDIN_FILENO);
+ stdin_raw_restore();
#ifdef _WIN32
if (_setmode(STDIN_FILENO, old_stdin_mode) == -1) {
fatal_errno("could not restore stdin mode");
@@ -419,47 +428,110 @@
free(buf);
}
-namespace {
+static std::string format_host_command(const char* command,
+ TransportType type, const char* serial) {
+ if (serial) {
+ return android::base::StringPrintf("host-serial:%s:%s", serial, command);
+ }
+
+ const char* prefix = "host";
+ if (type == kTransportUsb) {
+ prefix = "host-usb";
+ } else if (type == kTransportLocal) {
+ prefix = "host-local";
+ }
+ return android::base::StringPrintf("%s:%s", prefix, command);
+}
+
+// Returns the FeatureSet for the indicated transport.
+static FeatureSet GetFeatureSet(TransportType transport_type, const char* serial) {
+ std::string result, error;
+ if (adb_query(format_host_command("features", transport_type, serial), &result, &error)) {
+ return StringToFeatureSet(result);
+ }
+ return FeatureSet();
+}
+
+static void send_window_size_change(int fd, std::unique_ptr<ShellProtocol>& shell) {
+#if !defined(_WIN32)
+ // Old devices can't handle window size changes.
+ if (shell == nullptr) return;
+
+ winsize ws;
+ if (ioctl(fd, TIOCGWINSZ, &ws) == -1) return;
+
+ // Send the new window size as human-readable ASCII for debugging convenience.
+ size_t l = snprintf(shell->data(), shell->data_capacity(), "%dx%d,%dx%d",
+ ws.ws_row, ws.ws_col, ws.ws_xpixel, ws.ws_ypixel);
+ shell->Write(ShellProtocol::kIdWindowSizeChange, l + 1);
+#endif
+}
// Used to pass multiple values to the stdin read thread.
struct StdinReadArgs {
int stdin_fd, write_fd;
bool raw_stdin;
std::unique_ptr<ShellProtocol> protocol;
+ char escape_char;
};
-} // namespace
-
// Loops to read from stdin and push the data to the given FD.
// The argument should be a pointer to a StdinReadArgs object. This function
// will take ownership of the object and delete it when finished.
-static void* stdin_read_thread(void* x) {
+static void* stdin_read_thread_loop(void* x) {
std::unique_ptr<StdinReadArgs> args(reinterpret_cast<StdinReadArgs*>(x));
- int state = 0;
- adb_thread_setname("stdin reader");
-
-#ifndef _WIN32
- // Mask SIGTTIN in case we're in a backgrounded process
+#if !defined(_WIN32)
+ // Mask SIGTTIN in case we're in a backgrounded process.
sigset_t sigset;
sigemptyset(&sigset);
sigaddset(&sigset, SIGTTIN);
pthread_sigmask(SIG_BLOCK, &sigset, nullptr);
#endif
- char raw_buffer[1024];
+#if !defined(_WIN32)
+ // Unblock SIGWINCH for this thread, so our read(2) below will be
+ // interrupted if the window size changes.
+ sigset_t mask;
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGWINCH);
+ pthread_sigmask(SIG_UNBLOCK, &mask, nullptr);
+#endif
+
+ // Set up the initial window size.
+ send_window_size_change(args->stdin_fd, args->protocol);
+
+ char raw_buffer[BUFSIZ];
char* buffer_ptr = raw_buffer;
size_t buffer_size = sizeof(raw_buffer);
- if (args->protocol) {
+ if (args->protocol != nullptr) {
buffer_ptr = args->protocol->data();
buffer_size = args->protocol->data_capacity();
}
+ // If we need to parse escape sequences, make life easy.
+ if (args->raw_stdin && args->escape_char != '\0') {
+ buffer_size = 1;
+ }
+
+ enum EscapeState { kMidFlow, kStartOfLine, kInEscape };
+ EscapeState state = kStartOfLine;
+
while (true) {
// Use unix_read() rather than adb_read() for stdin.
- D("stdin_read_thread(): pre unix_read(fdi=%d,...)", args->stdin_fd);
+ D("stdin_read_thread_loop(): pre unix_read(fdi=%d,...)", args->stdin_fd);
+#if !defined(_WIN32)
+#undef read
+ int r = read(args->stdin_fd, buffer_ptr, buffer_size);
+ if (r == -1 && errno == EINTR) {
+ send_window_size_change(args->stdin_fd, args->protocol);
+ continue;
+ }
+#define read ___xxx_read
+#else
int r = unix_read(args->stdin_fd, buffer_ptr, buffer_size);
- D("stdin_read_thread(): post unix_read(fdi=%d,...)", args->stdin_fd);
+#endif
+ D("stdin_read_thread_loop(): post unix_read(fdi=%d,...)", args->stdin_fd);
if (r <= 0) {
// Only devices using the shell protocol know to close subprocess
// stdin. For older devices we want to just leave the connection
@@ -470,36 +542,36 @@
}
break;
}
- // If we made stdin raw, check input for the "~." escape sequence. In
+ // If we made stdin raw, check input for escape sequences. In
// this situation signals like Ctrl+C are sent remotely rather than
// interpreted locally so this provides an emergency out if the remote
// process starts ignoring the signal. SSH also does this, see the
// "escape characters" section on the ssh man page for more info.
- if (args->raw_stdin) {
- for (int n = 0; n < r; n++){
- switch(buffer_ptr[n]) {
- case '\n':
- state = 1;
- break;
- case '\r':
- state = 1;
- break;
- case '~':
- if(state == 1) {
- state++;
- } else {
- state = 0;
- }
- break;
- case '.':
- if(state == 2) {
- stdin_raw_restore(args->stdin_fd);
- fprintf(stderr,"\n* disconnect *\n");
- exit(0);
- }
- default:
- state = 0;
+ if (args->raw_stdin && args->escape_char != '\0') {
+ char ch = buffer_ptr[0];
+ if (ch == args->escape_char) {
+ if (state == kStartOfLine) {
+ state = kInEscape;
+ // Swallow the escape character.
+ continue;
+ } else {
+ state = kMidFlow;
}
+ } else {
+ if (state == kInEscape) {
+ if (ch == '.') {
+ fprintf(stderr,"\r\n[ disconnected ]\r\n");
+ stdin_raw_restore();
+ exit(0);
+ } else {
+ // We swallowed an escape character that wasn't part of
+ // a valid escape sequence; time to cough it up.
+ buffer_ptr[0] = args->escape_char;
+ buffer_ptr[1] = ch;
+ ++r;
+ }
+ }
+ state = (ch == '\n' || ch == '\r') ? kStartOfLine : kMidFlow;
}
}
if (args->protocol) {
@@ -523,6 +595,11 @@
std::vector<std::string> args;
if (use_shell_protocol) {
args.push_back(kShellServiceArgShellProtocol);
+
+ const char* terminal_type = getenv("TERM");
+ if (terminal_type != nullptr) {
+ args.push_back(std::string("TERM=") + terminal_type);
+ }
}
if (!type_arg.empty()) {
args.push_back(type_arg);
@@ -544,6 +621,7 @@
// On success returns the remote exit code if |use_shell_protocol| is true,
// 0 otherwise. On failure returns 1.
static int RemoteShell(bool use_shell_protocol, const std::string& type_arg,
+ char escape_char,
const std::string& command) {
std::string service_string = ShellServiceString(use_shell_protocol,
type_arg, command);
@@ -569,57 +647,140 @@
args->stdin_fd = STDIN_FILENO;
args->write_fd = fd;
args->raw_stdin = raw_stdin;
+ args->escape_char = escape_char;
if (use_shell_protocol) {
args->protocol.reset(new ShellProtocol(args->write_fd));
}
- if (raw_stdin) {
- stdin_raw_init(STDIN_FILENO);
- }
+ if (raw_stdin) stdin_raw_init();
- int exit_code = 0;
- if (!adb_thread_create(stdin_read_thread, args)) {
+#if !defined(_WIN32)
+ // Ensure our process is notified if the local window size changes.
+ // We use sigaction(2) to ensure that the SA_RESTART flag is not set,
+ // because the whole reason we're sending signals is to unblock the read(2)!
+ // That also means we don't need to do anything in the signal handler:
+ // the side effect of delivering the signal is all we need.
+ struct sigaction sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = [](int) {};
+ sa.sa_flags = 0;
+ sigaction(SIGWINCH, &sa, nullptr);
+
+ // Now block SIGWINCH in this thread (the main thread) and all threads spawned
+ // from it. The stdin read thread will unblock this signal to ensure that it's
+ // the thread that receives the signal.
+ sigset_t mask;
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGWINCH);
+ pthread_sigmask(SIG_BLOCK, &mask, nullptr);
+#endif
+
+ // TODO: combine read_and_dump with stdin_read_thread to make life simpler?
+ int exit_code = 1;
+ if (!adb_thread_create(stdin_read_thread_loop, args)) {
PLOG(ERROR) << "error starting stdin read thread";
- exit_code = 1;
delete args;
} else {
exit_code = read_and_dump(fd, use_shell_protocol);
}
- if (raw_stdin) {
- stdin_raw_restore(STDIN_FILENO);
- }
+ // TODO: properly exit stdin_read_thread_loop and close |fd|.
- // TODO(dpursell): properly exit stdin_read_thread and close |fd|.
+ // TODO: we should probably install signal handlers for this.
+ // TODO: can we use atexit? even on Windows?
+ if (raw_stdin) stdin_raw_restore();
return exit_code;
}
+static int adb_shell(int argc, const char** argv,
+ TransportType transport_type, const char* serial) {
+ FeatureSet features = GetFeatureSet(transport_type, serial);
-static std::string format_host_command(const char* command, TransportType type, const char* serial) {
- if (serial) {
- return android::base::StringPrintf("host-serial:%s:%s", serial, command);
+ bool use_shell_protocol = CanUseFeature(features, kFeatureShell2);
+ if (!use_shell_protocol) {
+ D("shell protocol not supported, using raw data transfer");
+ } else {
+ D("using shell protocol");
}
- const char* prefix = "host";
- if (type == kTransportUsb) {
- prefix = "host-usb";
- } else if (type == kTransportLocal) {
- prefix = "host-local";
- }
- return android::base::StringPrintf("%s:%s", prefix, command);
-}
+ // Parse shell-specific command-line options.
+ // argv[0] is always "shell".
+ --argc;
+ ++argv;
+ int t_arg_count = 0;
+ char escape_char = '~';
+ while (argc) {
+ if (!strcmp(argv[0], "-e")) {
+ if (argc < 2 || !(strlen(argv[1]) == 1 || strcmp(argv[1], "none") == 0)) {
+ fprintf(stderr, "error: -e requires a single-character argument or 'none'\n");
+ return 1;
+ }
+ escape_char = (strcmp(argv[1], "none") == 0) ? 0 : argv[1][0];
+ argc -= 2;
+ argv += 2;
+ } else if (!strcmp(argv[0], "-T") || !strcmp(argv[0], "-t")) {
+ if (!CanUseFeature(features, kFeatureShell2)) {
+ fprintf(stderr, "error: target doesn't support PTY args -Tt\n");
+ return 1;
+ }
+ // Like ssh, -t arguments are cumulative so that multiple -t's
+ // are needed to force a PTY.
+ if (argv[0][1] == 't') {
+ ++t_arg_count;
+ } else {
+ t_arg_count = -1;
+ }
+ --argc;
+ ++argv;
+ } else if (!strcmp(argv[0], "-x")) {
+ use_shell_protocol = false;
+ --argc;
+ ++argv;
+ } else if (!strcmp(argv[0], "-n")) {
+ close_stdin();
-// Returns the FeatureSet for the indicated transport.
-static FeatureSet GetFeatureSet(TransportType transport_type,
- const char* serial) {
- std::string result, error;
-
- if (adb_query(format_host_command("features", transport_type, serial),
- &result, &error)) {
- return StringToFeatureSet(result);
+ --argc;
+ ++argv;
+ } else {
+ break;
+ }
}
- return FeatureSet();
+
+ std::string shell_type_arg;
+ if (CanUseFeature(features, kFeatureShell2)) {
+ if (t_arg_count < 0) {
+ shell_type_arg = kShellServiceArgRaw;
+ } else if (t_arg_count == 0) {
+ // If stdin isn't a TTY, default to a raw shell; this lets
+ // things like `adb shell < my_script.sh` work as expected.
+ // Otherwise leave |shell_type_arg| blank which uses PTY for
+ // interactive shells and raw for non-interactive.
+ if (!unix_isatty(STDIN_FILENO)) {
+ shell_type_arg = kShellServiceArgRaw;
+ }
+ } else if (t_arg_count == 1) {
+ // A single -t arg isn't enough to override implicit -T.
+ if (!unix_isatty(STDIN_FILENO)) {
+ fprintf(stderr,
+ "Remote PTY will not be allocated because stdin is not a terminal.\n"
+ "Use multiple -t options to force remote PTY allocation.\n");
+ shell_type_arg = kShellServiceArgRaw;
+ } else {
+ shell_type_arg = kShellServiceArgPty;
+ }
+ } else {
+ shell_type_arg = kShellServiceArgPty;
+ }
+ }
+
+ std::string command;
+ if (argc) {
+ // We don't escape here, just like ssh(1). http://b/20564385.
+ command = android::base::Join(std::vector<const char*>(argv, argv + argc), ' ');
+ }
+
+ return RemoteShell(use_shell_protocol, shell_type_arg, escape_char, command);
}
static int adb_download_buffer(const char *service, const char *fn, const void* data, unsigned sz,
@@ -852,19 +1013,49 @@
#endif /* !defined(_WIN32) */
}
+static bool check_wait_for_device_syntax(const char* service) {
+ // TODO: when we have libc++ for Windows, use a regular expression instead.
+ // wait-for-((any|local|usb)-)?(bootloader|device|recovery|sideload)
+
+ char type[20];
+ char state[20];
+ int length = 0;
+ if (sscanf(service, "wait-for-%20[a-z]-%20[a-z]%n", type, state, &length) < 2 ||
+ length != static_cast<int>(strlen(service))) {
+ fprintf(stderr, "adb: couldn't parse 'wait-for' command: %s\n", service);
+ return false;
+ }
+
+ if (strcmp(type, "any") != 0 && strcmp(type, "local") != 0 && strcmp(type, "usb") != 0) {
+ fprintf(stderr, "adb: unknown type %s; expected 'any', 'local', or 'usb'\n", type);
+ return false;
+ }
+ if (strcmp(state, "bootloader") != 0 && strcmp(state, "device") != 0 &&
+ strcmp(state, "recovery") != 0 && strcmp(state, "sideload") != 0) {
+ fprintf(stderr, "adb: unknown state %s; "
+ "expected 'bootloader', 'device', 'recovery', or 'sideload'\n", state);
+ return false;
+ }
+ return true;
+}
+
static bool wait_for_device(const char* service, TransportType t, const char* serial) {
// Was the caller vague about what they'd like us to wait for?
// If so, check they weren't more specific in their choice of transport type.
if (strcmp(service, "wait-for-device") == 0) {
if (t == kTransportUsb) {
- service = "wait-for-usb";
+ service = "wait-for-usb-device";
} else if (t == kTransportLocal) {
- service = "wait-for-local";
+ service = "wait-for-local-device";
} else {
- service = "wait-for-any";
+ service = "wait-for-any-device";
}
}
+ if (!check_wait_for_device_syntax(service)) {
+ return false;
+ }
+
std::string cmd = format_host_command(service, t, serial);
return adb_command(cmd);
}
@@ -882,8 +1073,7 @@
use_shell_protocol = CanUseFeature(features, kFeatureShell2);
}
- std::string service_string = ShellServiceString(use_shell_protocol, "",
- command);
+ std::string service_string = ShellServiceString(use_shell_protocol, "", command);
int fd;
while (true) {
@@ -927,14 +1117,14 @@
}
static int backup(int argc, const char** argv) {
- const char* filename = "./backup.ab";
+ const char* filename = "backup.ab";
/* find, extract, and use any -f argument */
for (int i = 1; i < argc; i++) {
if (!strcmp("-f", argv[i])) {
if (i == argc-1) {
- fprintf(stderr, "adb: -f passed with no filename\n");
- return usage();
+ fprintf(stderr, "adb: backup -f passed with no filename.\n");
+ return EXIT_FAILURE;
}
filename = argv[i+1];
for (int j = i+2; j <= argc; ) {
@@ -945,15 +1135,18 @@
}
}
- /* bare "adb backup" or "adb backup -f filename" are not valid invocations */
- if (argc < 2) return usage();
+ // Bare "adb backup" or "adb backup -f filename" are not valid invocations ---
+ // a list of packages is required.
+ if (argc < 2) {
+ fprintf(stderr, "adb: backup either needs a list of packages or -all/-shared.\n");
+ return EXIT_FAILURE;
+ }
adb_unlink(filename);
- mkdirs(filename);
int outFd = adb_creat(filename, 0640);
if (outFd < 0) {
- fprintf(stderr, "adb: unable to open file %s\n", filename);
- return -1;
+ fprintf(stderr, "adb: backup unable to create file '%s': %s\n", filename, strerror(errno));
+ return EXIT_FAILURE;
}
std::string cmd = "backup:";
@@ -969,15 +1162,17 @@
if (fd < 0) {
fprintf(stderr, "adb: unable to connect for backup: %s\n", error.c_str());
adb_close(outFd);
- return -1;
+ return EXIT_FAILURE;
}
- printf("Now unlock your device and confirm the backup operation.\n");
+ printf("Now unlock your device and confirm the backup operation...\n");
+ fflush(stdout);
+
copy_to_file(fd, outFd);
adb_close(fd);
adb_close(outFd);
- return 0;
+ return EXIT_SUCCESS;
}
static int restore(int argc, const char** argv) {
@@ -1065,31 +1260,35 @@
return path;
}
-static void parse_push_pull_args(const char **arg, int narg,
- char const **path1, char const **path2,
- int *copy_attrs) {
- *copy_attrs = 0;
+static void parse_push_pull_args(const char** arg, int narg,
+ std::vector<const char*>* srcs,
+ const char** dst, bool* copy_attrs) {
+ *copy_attrs = false;
+ srcs->clear();
+ bool ignore_flags = false;
while (narg > 0) {
- if (!strcmp(*arg, "-p")) {
- // Silently ignore for backwards compatibility.
- } else if (!strcmp(*arg, "-a")) {
- *copy_attrs = 1;
+ if (ignore_flags || *arg[0] != '-') {
+ srcs->push_back(*arg);
} else {
- break;
+ if (!strcmp(*arg, "-p")) {
+ // Silently ignore for backwards compatibility.
+ } else if (!strcmp(*arg, "-a")) {
+ *copy_attrs = true;
+ } else if (!strcmp(*arg, "--")) {
+ ignore_flags = true;
+ } else {
+ fprintf(stderr, "adb: unrecognized option '%s'\n", *arg);
+ exit(1);
+ }
}
++arg;
--narg;
}
- if (narg > 0) {
- *path1 = *arg;
- ++arg;
- --narg;
- }
-
- if (narg > 0) {
- *path2 = *arg;
+ if (srcs->size() > 1) {
+ *dst = srcs->back();
+ srcs->pop_back();
}
}
@@ -1136,6 +1335,11 @@
TransportType transport_type = kTransportAny;
int ack_reply_fd = -1;
+#if !defined(_WIN32)
+ // We'd rather have EPIPE than SIGPIPE.
+ signal(SIGPIPE, SIG_IGN);
+#endif
+
// If defined, this should be an absolute path to
// the directory containing all of the various system images
// for a particular product. If not defined, and the adb
@@ -1267,7 +1471,7 @@
fprintf(stderr, "reply fd for adb server to client communication not specified.\n");
return usage();
}
- r = adb_main(is_daemon, server_port, ack_reply_fd);
+ r = adb_server_main(is_daemon, server_port, ack_reply_fd);
} else {
r = launch_server(server_port);
}
@@ -1338,94 +1542,8 @@
else if (!strcmp(argv[0], "emu")) {
return adb_send_emulator_command(argc, argv, serial);
}
- else if (!strcmp(argv[0], "shell") || !strcmp(argv[0], "hell")) {
- char h = (argv[0][0] == 'h');
-
- FeatureSet features = GetFeatureSet(transport_type, serial);
-
- bool use_shell_protocol = CanUseFeature(features, kFeatureShell2);
- if (!use_shell_protocol) {
- D("shell protocol not supported, using raw data transfer");
- } else {
- D("using shell protocol");
- }
-
- // Parse shell-specific command-line options.
- // argv[0] is always "shell".
- --argc;
- ++argv;
- int t_arg_count = 0;
- while (argc) {
- if (!strcmp(argv[0], "-T") || !strcmp(argv[0], "-t")) {
- if (!CanUseFeature(features, kFeatureShell2)) {
- fprintf(stderr, "error: target doesn't support PTY args -Tt\n");
- return 1;
- }
- // Like ssh, -t arguments are cumulative so that multiple -t's
- // are needed to force a PTY.
- if (argv[0][1] == 't') {
- ++t_arg_count;
- } else {
- t_arg_count = -1;
- }
- --argc;
- ++argv;
- } else if (!strcmp(argv[0], "-x")) {
- use_shell_protocol = false;
- --argc;
- ++argv;
- } else {
- break;
- }
- }
-
- std::string shell_type_arg;
- if (CanUseFeature(features, kFeatureShell2)) {
- if (t_arg_count < 0) {
- shell_type_arg = kShellServiceArgRaw;
- } else if (t_arg_count == 0) {
- // If stdin isn't a TTY, default to a raw shell; this lets
- // things like `adb shell < my_script.sh` work as expected.
- // Otherwise leave |shell_type_arg| blank which uses PTY for
- // interactive shells and raw for non-interactive.
- if (!isatty(STDIN_FILENO)) {
- shell_type_arg = kShellServiceArgRaw;
- }
- } else if (t_arg_count == 1) {
- // A single -t arg isn't enough to override implicit -T.
- if (!isatty(STDIN_FILENO)) {
- fprintf(stderr,
- "Remote PTY will not be allocated because stdin is not a terminal.\n"
- "Use multiple -t options to force remote PTY allocation.\n");
- shell_type_arg = kShellServiceArgRaw;
- } else {
- shell_type_arg = kShellServiceArgPty;
- }
- } else {
- shell_type_arg = kShellServiceArgPty;
- }
- }
-
- std::string command;
- if (argc) {
- // We don't escape here, just like ssh(1). http://b/20564385.
- command = android::base::Join(
- std::vector<const char*>(argv, argv + argc), ' ');
- }
-
- if (h) {
- printf("\x1b[41;33m");
- fflush(stdout);
- }
-
- r = RemoteShell(use_shell_protocol, shell_type_arg, command);
-
- if (h) {
- printf("\x1b[0m");
- fflush(stdout);
- }
-
- return r;
+ else if (!strcmp(argv[0], "shell")) {
+ return adb_shell(argc, argv, transport_type, serial);
}
else if (!strcmp(argv[0], "exec-in") || !strcmp(argv[0], "exec-out")) {
int exec_in = !strcmp(argv[0], "exec-in");
@@ -1561,24 +1679,30 @@
return do_sync_ls(argv[1]) ? 0 : 1;
}
else if (!strcmp(argv[0], "push")) {
- int copy_attrs = 0;
- const char* lpath = NULL, *rpath = NULL;
+ bool copy_attrs = false;
+ std::vector<const char*> srcs;
+ const char* dst = nullptr;
- parse_push_pull_args(&argv[1], argc - 1, &lpath, &rpath, ©_attrs);
- if (!lpath || !rpath || copy_attrs != 0) return usage();
- return do_sync_push(lpath, rpath) ? 0 : 1;
+ parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, ©_attrs);
+ if (srcs.empty() || !dst) return usage();
+ return do_sync_push(srcs, dst) ? 0 : 1;
}
else if (!strcmp(argv[0], "pull")) {
- int copy_attrs = 0;
- const char* rpath = NULL, *lpath = ".";
+ bool copy_attrs = false;
+ std::vector<const char*> srcs;
+ const char* dst = ".";
- parse_push_pull_args(&argv[1], argc - 1, &rpath, &lpath, ©_attrs);
- if (!rpath) return usage();
- return do_sync_pull(rpath, lpath, copy_attrs) ? 0 : 1;
+ parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, ©_attrs);
+ if (srcs.empty()) return usage();
+ return do_sync_pull(srcs, dst, copy_attrs) ? 0 : 1;
}
else if (!strcmp(argv[0], "install")) {
if (argc < 2) return usage();
- return install_app(transport_type, serial, argc, argv);
+ FeatureSet features = GetFeatureSet(transport_type, serial);
+ if (CanUseFeature(features, kFeatureCmd)) {
+ return install_app(transport_type, serial, argc, argv);
+ }
+ return install_app_legacy(transport_type, serial, argc, argv);
}
else if (!strcmp(argv[0], "install-multiple")) {
if (argc < 2) return usage();
@@ -1586,7 +1710,11 @@
}
else if (!strcmp(argv[0], "uninstall")) {
if (argc < 2) return usage();
- return uninstall_app(transport_type, serial, argc, argv);
+ FeatureSet features = GetFeatureSet(transport_type, serial);
+ if (CanUseFeature(features, kFeatureCmd)) {
+ return uninstall_app(transport_type, serial, argc, argv);
+ }
+ return uninstall_app_legacy(transport_type, serial, argc, argv);
}
else if (!strcmp(argv[0], "sync")) {
std::string src;
@@ -1694,85 +1822,82 @@
return 1;
}
-static int pm_command(TransportType transport, const char* serial, int argc, const char** argv) {
- std::string cmd = "pm";
-
+static int uninstall_app(TransportType transport, const char* serial, int argc, const char** argv) {
+ // 'adb uninstall' takes the same arguments as 'cmd package uninstall' on device
+ std::string cmd = "cmd package";
while (argc-- > 0) {
+ // deny the '-k' option until the remaining data/cache can be removed with adb/UI
+ if (strcmp(*argv, "-k") == 0) {
+ printf(
+ "The -k option uninstalls the application while retaining the data/cache.\n"
+ "At the moment, there is no way to remove the remaining data.\n"
+ "You will have to reinstall the application with the same signature, and fully uninstall it.\n"
+ "If you truly wish to continue, execute 'adb shell cmd package uninstall -k'.\n");
+ return EXIT_FAILURE;
+ }
cmd += " " + escape_arg(*argv++);
}
- // TODO(dpursell): add command-line arguments to install/uninstall to
- // manually disable shell protocol if needed.
- return send_shell_command(transport, serial, cmd, false);
-}
-
-static int uninstall_app(TransportType transport, const char* serial, int argc, const char** argv) {
- /* if the user choose the -k option, we refuse to do it until devices are
- out with the option to uninstall the remaining data somehow (adb/ui) */
- if (argc == 3 && strcmp(argv[1], "-k") == 0)
- {
- printf(
- "The -k option uninstalls the application while retaining the data/cache.\n"
- "At the moment, there is no way to remove the remaining data.\n"
- "You will have to reinstall the application with the same signature, and fully uninstall it.\n"
- "If you truly wish to continue, execute 'adb shell pm uninstall -k %s'\n", argv[2]);
- return -1;
- }
-
- /* 'adb uninstall' takes the same arguments as 'pm uninstall' on device */
- return pm_command(transport, serial, argc, argv);
-}
-
-static int delete_file(TransportType transport, const char* serial, const std::string& filename) {
- std::string cmd = "rm -f " + escape_arg(filename);
return send_shell_command(transport, serial, cmd, false);
}
static int install_app(TransportType transport, const char* serial, int argc, const char** argv) {
- static const char *const DATA_DEST = "/data/local/tmp/%s";
- static const char *const SD_DEST = "/sdcard/tmp/%s";
- const char* where = DATA_DEST;
- int i;
+ // The last argument must be the APK file
+ const char* file = argv[argc - 1];
+ const char* dot = strrchr(file, '.');
+ bool found_apk = false;
struct stat sb;
-
- for (i = 1; i < argc; i++) {
- if (!strcmp(argv[i], "-s")) {
- where = SD_DEST;
+ if (dot && !strcasecmp(dot, ".apk")) {
+ if (stat(file, &sb) == -1 || !S_ISREG(sb.st_mode)) {
+ fprintf(stderr, "Invalid APK file: %s\n", file);
+ return EXIT_FAILURE;
}
+ found_apk = true;
}
- // Find last APK argument.
- // All other arguments passed through verbatim.
- int last_apk = -1;
- for (i = argc - 1; i >= 0; i--) {
- const char* file = argv[i];
- const char* dot = strrchr(file, '.');
- if (dot && !strcasecmp(dot, ".apk")) {
- if (stat(file, &sb) == -1 || !S_ISREG(sb.st_mode)) {
- fprintf(stderr, "Invalid APK file: %s\n", file);
- return -1;
- }
-
- last_apk = i;
- break;
- }
- }
-
- if (last_apk == -1) {
+ if (!found_apk) {
fprintf(stderr, "Missing APK file\n");
- return -1;
+ return EXIT_FAILURE;
}
- int result = -1;
- const char* apk_file = argv[last_apk];
- std::string apk_dest = android::base::StringPrintf(where, adb_basename(apk_file).c_str());
- if (!do_sync_push(apk_file, apk_dest.c_str())) goto cleanup_apk;
- argv[last_apk] = apk_dest.c_str(); /* destination name, not source location */
- result = pm_command(transport, serial, argc, argv);
+ int localFd = adb_open(file, O_RDONLY);
+ if (localFd < 0) {
+ fprintf(stderr, "Failed to open %s: %s\n", file, strerror(errno));
+ return 1;
+ }
-cleanup_apk:
- delete_file(transport, serial, apk_dest);
- return result;
+ std::string error;
+ std::string cmd = "exec:cmd package";
+
+ // don't copy the APK name, but, copy the rest of the arguments as-is
+ while (argc-- > 1) {
+ cmd += " " + escape_arg(std::string(*argv++));
+ }
+
+ // add size parameter [required for streaming installs]
+ // do last to override any user specified value
+ cmd += " " + android::base::StringPrintf("-S %" PRIu64, static_cast<uint64_t>(sb.st_size));
+
+ int remoteFd = adb_connect(cmd, &error);
+ if (remoteFd < 0) {
+ fprintf(stderr, "Connect error for write: %s\n", error.c_str());
+ adb_close(localFd);
+ return 1;
+ }
+
+ char buf[BUFSIZ];
+ copy_to_file(localFd, remoteFd);
+ read_status_line(remoteFd, buf, sizeof(buf));
+
+ adb_close(localFd);
+ adb_close(remoteFd);
+
+ if (strncmp("Success", buf, 7)) {
+ fprintf(stderr, "Failed to install %s: %s", file, buf);
+ return 1;
+ }
+ fputs(buf, stderr);
+ return 0;
}
static int install_multiple_app(TransportType transport, const char* serial, int argc,
@@ -1791,7 +1916,7 @@
if (dot && !strcasecmp(dot, ".apk")) {
if (stat(file, &sb) == -1 || !S_ISREG(sb.st_mode)) {
fprintf(stderr, "Invalid APK file: %s\n", file);
- return -1;
+ return EXIT_FAILURE;
}
total_size += sb.st_size;
@@ -1816,7 +1941,7 @@
int fd = adb_connect(cmd, &error);
if (fd < 0) {
fprintf(stderr, "Connect error for create: %s\n", error.c_str());
- return -1;
+ return EXIT_FAILURE;
}
char buf[BUFSIZ];
read_status_line(fd, buf, sizeof(buf));
@@ -1834,7 +1959,7 @@
if (session_id < 0) {
fprintf(stderr, "Failed to create session\n");
fputs(buf, stderr);
- return -1;
+ return EXIT_FAILURE;
}
// Valid session, now stream the APKs
@@ -1889,7 +2014,7 @@
fd = adb_connect(service, &error);
if (fd < 0) {
fprintf(stderr, "Connect error for finalize: %s\n", error.c_str());
- return -1;
+ return EXIT_FAILURE;
}
read_status_line(fd, buf, sizeof(buf));
adb_close(fd);
@@ -1900,6 +2025,88 @@
} else {
fprintf(stderr, "Failed to finalize session\n");
fputs(buf, stderr);
- return -1;
+ return EXIT_FAILURE;
}
}
+
+static int pm_command(TransportType transport, const char* serial, int argc, const char** argv) {
+ std::string cmd = "pm";
+
+ while (argc-- > 0) {
+ cmd += " " + escape_arg(*argv++);
+ }
+
+ return send_shell_command(transport, serial, cmd, false);
+}
+
+static int uninstall_app_legacy(TransportType transport, const char* serial, int argc, const char** argv) {
+ /* if the user choose the -k option, we refuse to do it until devices are
+ out with the option to uninstall the remaining data somehow (adb/ui) */
+ int i;
+ for (i = 1; i < argc; i++) {
+ if (!strcmp(argv[i], "-k")) {
+ printf(
+ "The -k option uninstalls the application while retaining the data/cache.\n"
+ "At the moment, there is no way to remove the remaining data.\n"
+ "You will have to reinstall the application with the same signature, and fully uninstall it.\n"
+ "If you truly wish to continue, execute 'adb shell pm uninstall -k'\n.");
+ return EXIT_FAILURE;
+ }
+ }
+
+ /* 'adb uninstall' takes the same arguments as 'pm uninstall' on device */
+ return pm_command(transport, serial, argc, argv);
+}
+
+static int delete_file(TransportType transport, const char* serial, const std::string& filename) {
+ std::string cmd = "rm -f " + escape_arg(filename);
+ return send_shell_command(transport, serial, cmd, false);
+}
+
+static int install_app_legacy(TransportType transport, const char* serial, int argc, const char** argv) {
+ static const char *const DATA_DEST = "/data/local/tmp/%s";
+ static const char *const SD_DEST = "/sdcard/tmp/%s";
+ const char* where = DATA_DEST;
+ int i;
+ struct stat sb;
+
+ for (i = 1; i < argc; i++) {
+ if (!strcmp(argv[i], "-s")) {
+ where = SD_DEST;
+ }
+ }
+
+ // Find last APK argument.
+ // All other arguments passed through verbatim.
+ int last_apk = -1;
+ for (i = argc - 1; i >= 0; i--) {
+ const char* file = argv[i];
+ const char* dot = strrchr(file, '.');
+ if (dot && !strcasecmp(dot, ".apk")) {
+ if (stat(file, &sb) == -1 || !S_ISREG(sb.st_mode)) {
+ fprintf(stderr, "Invalid APK file: %s\n", file);
+ return EXIT_FAILURE;
+ }
+
+ last_apk = i;
+ break;
+ }
+ }
+
+ if (last_apk == -1) {
+ fprintf(stderr, "Missing APK file\n");
+ return EXIT_FAILURE;
+ }
+
+ int result = -1;
+ std::vector<const char*> apk_file = {argv[last_apk]};
+ std::string apk_dest = android::base::StringPrintf(
+ where, adb_basename(argv[last_apk]).c_str());
+ if (!do_sync_push(apk_file, apk_dest.c_str())) goto cleanup_apk;
+ argv[last_apk] = apk_dest.c_str(); /* destination name, not source location */
+ result = pm_command(transport, serial, argc, argv);
+
+cleanup_apk:
+ delete_file(transport, serial, apk_dest);
+ return result;
+}
diff --git a/adb/console.cpp b/adb/console.cpp
index 5a9c6ab..15c6abd 100644
--- a/adb/console.cpp
+++ b/adb/console.cpp
@@ -18,9 +18,9 @@
#include <stdio.h>
-#include <base/file.h>
-#include <base/logging.h>
-#include <base/strings.h>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
#include <cutils/sockets.h>
#include "adb.h"
diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp
index 8c3ca63..78db69d 100644
--- a/adb/daemon/main.cpp
+++ b/adb/daemon/main.cpp
@@ -25,15 +25,20 @@
#include <getopt.h>
#include <sys/prctl.h>
-#include "base/logging.h"
-#include "base/stringprintf.h"
+#include <memory>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <libminijail.h>
+
#include "cutils/properties.h"
#include "private/android_filesystem_config.h"
-#include "selinux/selinux.h"
+#include "selinux/android.h"
#include "adb.h"
#include "adb_auth.h"
#include "adb_listeners.h"
+#include "adb_utils.h"
#include "transport.h"
static const char* root_seclabel = nullptr;
@@ -52,12 +57,7 @@
continue;
}
- int err = prctl(PR_CAPBSET_DROP, i, 0, 0, 0);
-
- // Some kernels don't have file capabilities compiled in, and
- // prctl(PR_CAPBSET_DROP) returns EINVAL. Don't automatically
- // die when we see such misconfigured kernels.
- if ((err < 0) && (errno != EINVAL)) {
+ if (prctl(PR_CAPBSET_DROP, i, 0, 0, 0) == -1) {
PLOG(FATAL) << "Could not drop capabilities";
}
}
@@ -90,12 +90,12 @@
bool adb_root = (strcmp(value, "1") == 0);
bool adb_unroot = (strcmp(value, "0") == 0);
- // ...except "adb root" lets you keep privileges in a debuggable build.
+ // ... except "adb root" lets you keep privileges in a debuggable build.
if (ro_debuggable && adb_root) {
drop = false;
}
- // ...and "adb unroot" lets you explicitly drop privileges.
+ // ... and "adb unroot" lets you explicitly drop privileges.
if (adb_unroot) {
drop = true;
}
@@ -106,6 +106,62 @@
#endif // ALLOW_ADBD_ROOT
}
+static void drop_privileges(int server_port) {
+ std::unique_ptr<minijail, void (*)(minijail*)> jail(minijail_new(),
+ &minijail_destroy);
+
+ // Add extra groups:
+ // AID_ADB to access the USB driver
+ // AID_LOG to read system logs (adb logcat)
+ // AID_INPUT to diagnose input issues (getevent)
+ // AID_INET to diagnose network issues (ping)
+ // AID_NET_BT and AID_NET_BT_ADMIN to diagnose bluetooth (hcidump)
+ // AID_SDCARD_R to allow reading from the SD card
+ // AID_SDCARD_RW to allow writing to the SD card
+ // AID_NET_BW_STATS to read out qtaguid statistics
+ // AID_READPROC for reading /proc entries across UID boundaries
+ gid_t groups[] = {AID_ADB, AID_LOG, AID_INPUT,
+ AID_INET, AID_NET_BT, AID_NET_BT_ADMIN,
+ AID_SDCARD_R, AID_SDCARD_RW, AID_NET_BW_STATS,
+ AID_READPROC};
+ if (minijail_set_supplementary_gids(
+ jail.get(),
+ sizeof(groups) / sizeof(groups[0]),
+ groups) != 0) {
+ LOG(FATAL) << "Could not configure supplementary groups";
+ }
+
+ // Don't listen on a port (default 5037) if running in secure mode.
+ // Don't run as root if running in secure mode.
+ if (should_drop_privileges()) {
+ drop_capabilities_bounding_set_if_needed();
+
+ minijail_change_gid(jail.get(), AID_SHELL);
+ minijail_change_uid(jail.get(), AID_SHELL);
+ // minijail_enter() will abort if any priv-dropping step fails.
+ minijail_enter(jail.get());
+
+ D("Local port disabled");
+ } else {
+ // minijail_enter() will abort if any priv-dropping step fails.
+ minijail_enter(jail.get());
+
+ if (root_seclabel != nullptr) {
+ if (selinux_android_setcon(root_seclabel) < 0) {
+ LOG(FATAL) << "Could not set SELinux context";
+ }
+ }
+ std::string error;
+ std::string local_name =
+ android::base::StringPrintf("tcp:%d", server_port);
+ if (install_listener(local_name, "*smartsocket*", nullptr, 0,
+ &error)) {
+ LOG(FATAL) << "Could not install *smartsocket* listener: "
+ << error;
+ }
+ }
+}
+
int adbd_main(int server_port) {
umask(0);
@@ -133,51 +189,7 @@
" unchanged.\n");
}
- // Add extra groups:
- // AID_ADB to access the USB driver
- // AID_LOG to read system logs (adb logcat)
- // AID_INPUT to diagnose input issues (getevent)
- // AID_INET to diagnose network issues (ping)
- // AID_NET_BT and AID_NET_BT_ADMIN to diagnose bluetooth (hcidump)
- // AID_SDCARD_R to allow reading from the SD card
- // AID_SDCARD_RW to allow writing to the SD card
- // AID_NET_BW_STATS to read out qtaguid statistics
- gid_t groups[] = {AID_ADB, AID_LOG, AID_INPUT,
- AID_INET, AID_NET_BT, AID_NET_BT_ADMIN,
- AID_SDCARD_R, AID_SDCARD_RW, AID_NET_BW_STATS};
- if (setgroups(sizeof(groups) / sizeof(groups[0]), groups) != 0) {
- PLOG(FATAL) << "Could not set supplental groups";
- }
-
- /* don't listen on a port (default 5037) if running in secure mode */
- /* don't run as root if we are running in secure mode */
- if (should_drop_privileges()) {
- drop_capabilities_bounding_set_if_needed();
-
- /* then switch user and group to "shell" */
- if (setgid(AID_SHELL) != 0) {
- PLOG(FATAL) << "Could not setgid";
- }
- if (setuid(AID_SHELL) != 0) {
- PLOG(FATAL) << "Could not setuid";
- }
-
- D("Local port disabled");
- } else {
- if (root_seclabel != nullptr) {
- if (setcon(root_seclabel) < 0) {
- LOG(FATAL) << "Could not set selinux context";
- }
- }
- std::string error;
- std::string local_name =
- android::base::StringPrintf("tcp:%d", server_port);
- if (install_listener(local_name, "*smartsocket*", nullptr, 0,
- &error)) {
- LOG(FATAL) << "Could not install *smartsocket* listener: "
- << error;
- }
- }
+ drop_privileges(server_port);
bool is_usb = false;
if (access(USB_ADB_PATH, F_OK) == 0 || access(USB_FFS_ADB_EP0, F_OK) == 0) {
@@ -215,16 +227,6 @@
return 0;
}
-static void close_stdin() {
- int fd = unix_open("/dev/null", O_RDONLY);
- if (fd == -1) {
- perror("failed to open /dev/null, stdin will remain open");
- return;
- }
- dup2(fd, STDIN_FILENO);
- unix_close(fd);
-}
-
int main(int argc, char** argv) {
while (true) {
static struct option opts[] = {
diff --git a/adb/diagnose_usb.cpp b/adb/diagnose_usb.cpp
new file mode 100644
index 0000000..0f067b0
--- /dev/null
+++ b/adb/diagnose_usb.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "diagnose_usb.h"
+
+#include <errno.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/stringprintf.h>
+
+#if defined(__linux__)
+#include <grp.h>
+#endif
+
+static const char kPermissionsHelpUrl[] = "http://developer.android.com/tools/device.html";
+
+// Returns a message describing any potential problems we find with udev, or nullptr if we can't
+// find plugdev information (i.e. udev is not installed).
+static const char* GetUdevProblem() {
+#if defined(__linux__)
+ errno = 0;
+ group* plugdev_group = getgrnam("plugdev");
+
+ if (plugdev_group == nullptr) {
+ if (errno != 0) {
+ perror("failed to read plugdev group info");
+ }
+ // We can't give any generally useful advice here, just let the caller print the help URL.
+ return nullptr;
+ }
+
+ // getgroups(2) indicates that the group_member() may not check the egid so we check it
+ // additionally just to be sure.
+ if (group_member(plugdev_group->gr_gid) || getegid() == plugdev_group->gr_gid) {
+ // The user is in plugdev so the problem is likely with the udev rules.
+ return "verify udev rules";
+ }
+ return "udev requires plugdev group membership";
+#else
+ return nullptr;
+#endif
+}
+
+// Short help text must be a single line, and will look something like:
+// no permissions (reason); see <URL>
+std::string UsbNoPermissionsShortHelpText() {
+ std::string help_text = "no permissions";
+
+ const char* problem = GetUdevProblem();
+ if (problem != nullptr) {
+ help_text += android::base::StringPrintf(" (%s)", problem);
+ }
+
+ return android::base::StringPrintf("%s; see [%s]", help_text.c_str(), kPermissionsHelpUrl);
+}
+
+// Long help text can span multiple lines and should provide more detailed information.
+std::string UsbNoPermissionsLongHelpText() {
+ std::string header = "insufficient permissions for device";
+
+ const char* problem = GetUdevProblem();
+ if (problem != nullptr) {
+ header += android::base::StringPrintf(": %s", problem);
+ }
+
+ return android::base::StringPrintf("%s.\nSee [%s] for more information.",
+ header.c_str(), kPermissionsHelpUrl);
+}
diff --git a/adb/diagnose_usb.h b/adb/diagnose_usb.h
new file mode 100644
index 0000000..325b2e3
--- /dev/null
+++ b/adb/diagnose_usb.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __DIAGNOSE_LINUX_USB_H
+#define __DIAGNOSE_LINUX_USB_H
+
+#include <string>
+
+// USB permission error help text. The short version will be one line, long may be multi-line.
+// Returns a string message to print, or an empty string if no problems could be found.
+std::string UsbNoPermissionsShortHelpText();
+std::string UsbNoPermissionsLongHelpText();
+
+#endif
diff --git a/adb/fdevent.cpp b/adb/fdevent.cpp
index 4458c85..25e8376 100644
--- a/adb/fdevent.cpp
+++ b/adb/fdevent.cpp
@@ -31,8 +31,8 @@
#include <unordered_map>
#include <vector>
-#include <base/logging.h>
-#include <base/stringprintf.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
#include "adb_io.h"
#include "adb_trace.h"
@@ -54,7 +54,7 @@
struct PollNode {
fdevent* fde;
- pollfd pollfd;
+ ::pollfd pollfd;
PollNode(fdevent* fde) : fde(fde) {
memset(&pollfd, 0, sizeof(pollfd));
@@ -70,8 +70,8 @@
// All operations to fdevent should happen only in the main thread.
// That's why we don't need a lock for fdevent.
-static std::unordered_map<int, PollNode> g_poll_node_map;
-static std::list<fdevent*> g_pending_list;
+static auto& g_poll_node_map = *new std::unordered_map<int, PollNode>();
+static auto& g_pending_list = *new std::list<fdevent*>();
static bool main_thread_valid;
static pthread_t main_thread;
diff --git a/adb/file_sync_client.cpp b/adb/file_sync_client.cpp
index 8c9f7a6..0fa5917 100644
--- a/adb/file_sync_client.cpp
+++ b/adb/file_sync_client.cpp
@@ -28,6 +28,7 @@
#include <unistd.h>
#include <utime.h>
+#include <functional>
#include <memory>
#include <vector>
@@ -40,9 +41,9 @@
#include "file_sync_service.h"
#include "line_printer.h"
-#include <base/file.h>
-#include <base/strings.h>
-#include <base/stringprintf.h>
+#include <android-base/file.h>
+#include <android-base/strings.h>
+#include <android-base/stringprintf.h>
struct syncsendbuf {
unsigned id;
@@ -50,9 +51,44 @@
char data[SYNC_DATA_MAX];
};
+static void ensure_trailing_separators(std::string& local_path, std::string& remote_path) {
+ if (!adb_is_separator(local_path.back())) {
+ local_path.push_back(OS_PATH_SEPARATOR);
+ }
+ if (remote_path.back() != '/') {
+ remote_path.push_back('/');
+ }
+}
+
+struct copyinfo {
+ std::string lpath;
+ std::string rpath;
+ unsigned int time = 0;
+ unsigned int mode;
+ uint64_t size = 0;
+ bool skip = false;
+
+ copyinfo(const std::string& local_path,
+ const std::string& remote_path,
+ const std::string& name,
+ unsigned int mode)
+ : lpath(local_path), rpath(remote_path), mode(mode) {
+ ensure_trailing_separators(lpath, rpath);
+ lpath.append(name);
+ rpath.append(name);
+ if (S_ISDIR(mode)) {
+ ensure_trailing_separators(lpath, rpath);
+ }
+ }
+};
+
class SyncConnection {
public:
- SyncConnection() : total_bytes(0), start_time_ms_(CurrentTimeMs()) {
+ SyncConnection()
+ : total_bytes_(0),
+ start_time_ms_(CurrentTimeMs()),
+ expected_total_bytes_(0),
+ expect_multiple_files_(false) {
max = SYNC_DATA_MAX; // TODO: decide at runtime.
std::string error;
@@ -75,6 +111,8 @@
ReadOrderlyShutdown(fd);
}
adb_close(fd);
+
+ line_printer_.KeepInfoLine();
}
bool IsValid() { return fd >= 0; }
@@ -102,11 +140,9 @@
// Sending header, payload, and footer in a single write makes a huge
// difference to "adb sync" performance.
bool SendSmallFile(const char* path_and_mode,
- const char* rpath,
- const char* data, size_t data_length,
- unsigned mtime) {
- Print(rpath);
-
+ const char* lpath, const char* rpath,
+ unsigned mtime,
+ const char* data, size_t data_length) {
size_t path_length = strlen(path_and_mode);
if (path_length > 1024) {
Error("SendSmallFile failed: path too long: %zu", path_length);
@@ -115,8 +151,8 @@
}
std::vector<char> buf(sizeof(SyncRequest) + path_length +
- sizeof(SyncRequest) + data_length +
- sizeof(SyncRequest));
+ sizeof(SyncRequest) + data_length +
+ sizeof(SyncRequest));
char* p = &buf[0];
SyncRequest* req_send = reinterpret_cast<SyncRequest*>(p);
@@ -138,12 +174,63 @@
req_done->path_length = mtime;
p += sizeof(SyncRequest);
- if (!WriteFdExactly(fd, &buf[0], (p - &buf[0]))) return false;
-
- total_bytes += data_length;
+ WriteOrDie(lpath, rpath, &buf[0], (p - &buf[0]));
+ total_bytes_ += data_length;
return true;
}
+ bool SendLargeFile(const char* path_and_mode,
+ const char* lpath, const char* rpath,
+ unsigned mtime) {
+ if (!SendRequest(ID_SEND, path_and_mode)) {
+ Error("failed to send ID_SEND message '%s': %s", path_and_mode, strerror(errno));
+ return false;
+ }
+
+ struct stat st;
+ if (stat(lpath, &st) == -1) {
+ Error("cannot stat '%s': %s", lpath, strerror(errno));
+ return false;
+ }
+
+ uint64_t total_size = st.st_size;
+ uint64_t bytes_copied = 0;
+
+ int lfd = adb_open(lpath, O_RDONLY);
+ if (lfd < 0) {
+ Error("opening '%s' locally failed: %s", lpath, strerror(errno));
+ return false;
+ }
+
+ syncsendbuf sbuf;
+ sbuf.id = ID_DATA;
+ while (true) {
+ int bytes_read = adb_read(lfd, sbuf.data, max);
+ if (bytes_read == -1) {
+ Error("reading '%s' locally failed: %s", lpath, strerror(errno));
+ adb_close(lfd);
+ return false;
+ } else if (bytes_read == 0) {
+ break;
+ }
+
+ sbuf.size = bytes_read;
+ WriteOrDie(lpath, rpath, &sbuf, sizeof(SyncRequest) + bytes_read);
+
+ total_bytes_ += bytes_read;
+ bytes_copied += bytes_read;
+
+ ReportProgress(rpath, bytes_copied, total_size);
+ }
+
+ adb_close(lfd);
+
+ syncmsg msg;
+ msg.data.id = ID_DONE;
+ msg.data.size = mtime;
+ return WriteOrDie(lpath, rpath, &msg.data, sizeof(msg.data));
+ }
+
bool CopyDone(const char* from, const char* to) {
syncmsg msg;
if (!ReadFdExactly(fd, &msg.status, sizeof(msg.status))) {
@@ -174,17 +261,52 @@
std::string TransferRate() {
uint64_t ms = CurrentTimeMs() - start_time_ms_;
- if (total_bytes == 0 || ms == 0) return "";
+ if (total_bytes_ == 0 || ms == 0) return "";
double s = static_cast<double>(ms) / 1000LL;
- double rate = (static_cast<double>(total_bytes) / s) / (1024*1024);
+ double rate = (static_cast<double>(total_bytes_) / s) / (1024*1024);
return android::base::StringPrintf(" %.1f MB/s (%" PRId64 " bytes in %.3fs)",
- rate, total_bytes, s);
+ rate, total_bytes_, s);
}
- void Print(const std::string& s) {
- // TODO: we actually don't want ELIDE; we want "ELIDE if smart, FULL if dumb".
- line_printer_.Print(s, LinePrinter::ELIDE);
+ void ReportProgress(const char* file, uint64_t file_copied_bytes, uint64_t file_total_bytes) {
+ char overall_percentage_str[5] = "?";
+ if (expected_total_bytes_ != 0) {
+ int overall_percentage = static_cast<int>(total_bytes_ * 100 / expected_total_bytes_);
+ // If we're pulling symbolic links, we'll pull the target of the link rather than
+ // just create a local link, and that will cause us to go over 100%.
+ if (overall_percentage <= 100) {
+ snprintf(overall_percentage_str, sizeof(overall_percentage_str), "%d%%",
+ overall_percentage);
+ }
+ }
+
+ if (file_copied_bytes > file_total_bytes || file_total_bytes == 0) {
+ // This case can happen if we're racing against something that wrote to the file
+ // between our stat and our read, or if we're reading a magic file that lies about
+ // its size. Just show how much we've copied.
+ Printf("[%4s] %s: %" PRId64 "/?", overall_percentage_str, file, file_copied_bytes);
+ } else {
+ // If we're transferring multiple files, we want to know how far through the current
+ // file we are, as well as the overall percentage.
+ if (expect_multiple_files_) {
+ int file_percentage = static_cast<int>(file_copied_bytes * 100 / file_total_bytes);
+ Printf("[%4s] %s: %d%%", overall_percentage_str, file, file_percentage);
+ } else {
+ Printf("[%4s] %s", overall_percentage_str, file);
+ }
+ }
+ }
+
+ void Printf(const char* fmt, ...) __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 3))) {
+ std::string s;
+
+ va_list ap;
+ va_start(ap, fmt);
+ android::base::StringAppendV(&s, fmt, ap);
+ va_end(ap);
+
+ line_printer_.Print(s, LinePrinter::INFO);
}
void Error(const char* fmt, ...) __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 3))) {
@@ -195,10 +317,36 @@
android::base::StringAppendV(&s, fmt, ap);
va_end(ap);
- line_printer_.Print(s, LinePrinter::FULL);
+ line_printer_.Print(s, LinePrinter::ERROR);
}
- uint64_t total_bytes;
+ void Warning(const char* fmt, ...) __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 3))) {
+ std::string s = "adb: warning: ";
+
+ va_list ap;
+ va_start(ap, fmt);
+ android::base::StringAppendV(&s, fmt, ap);
+ va_end(ap);
+
+ line_printer_.Print(s, LinePrinter::WARNING);
+ }
+
+ void ComputeExpectedTotalBytes(const std::vector<copyinfo>& file_list) {
+ expected_total_bytes_ = 0;
+ for (const copyinfo& ci : file_list) {
+ // Unfortunately, this doesn't work for symbolic links, because we'll copy the
+ // target of the link rather than just creating a link. (But ci.size is the link size.)
+ if (!ci.skip) expected_total_bytes_ += ci.size;
+ }
+ expect_multiple_files_ = true;
+ }
+
+ void SetExpectedTotalBytes(uint64_t expected_total_bytes) {
+ expected_total_bytes_ = expected_total_bytes;
+ expect_multiple_files_ = false;
+ }
+
+ uint64_t total_bytes_;
// TODO: add a char[max] buffer here, to replace syncsendbuf...
int fd;
@@ -207,12 +355,36 @@
private:
uint64_t start_time_ms_;
+ uint64_t expected_total_bytes_;
+ bool expect_multiple_files_;
+
LinePrinter line_printer_;
bool SendQuit() {
return SendRequest(ID_QUIT, ""); // TODO: add a SendResponse?
}
+ bool WriteOrDie(const char* from, const char* to, const void* data, size_t data_length) {
+ if (!WriteFdExactly(fd, data, data_length)) {
+ if (errno == ECONNRESET) {
+ // Assume adbd told us why it was closing the connection, and
+ // try to read failure reason from adbd.
+ syncmsg msg;
+ if (!ReadFdExactly(fd, &msg.status, sizeof(msg.status))) {
+ Error("failed to copy '%s' to '%s': no response: %s", from, to, strerror(errno));
+ } else if (msg.status.id != ID_FAIL) {
+ Error("failed to copy '%s' to '%s': not ID_FAIL: %d", from, to, msg.status.id);
+ } else {
+ ReportCopyFailure(from, to, msg);
+ }
+ } else {
+ Error("%zu-byte write failed: %s", data_length, strerror(errno));
+ }
+ _exit(1);
+ }
+ return true;
+ }
+
static uint64_t CurrentTimeMs() {
struct timeval tv;
gettimeofday(&tv, 0); // (Not clock_gettime because of Mac/Windows.)
@@ -220,9 +392,10 @@
}
};
-typedef void (*sync_ls_cb)(unsigned mode, unsigned size, unsigned time, const char* name, void* cookie);
+typedef void (sync_ls_cb)(unsigned mode, unsigned size, unsigned time, const char* name);
-static bool sync_ls(SyncConnection& sc, const char* path, sync_ls_cb func, void* cookie) {
+static bool sync_ls(SyncConnection& sc, const char* path,
+ std::function<sync_ls_cb> func) {
if (!sc.SendRequest(ID_LIST, path)) return false;
while (true) {
@@ -239,7 +412,7 @@
if (!ReadFdExactly(sc.fd, buf, len)) return false;
buf[len] = 0;
- func(msg.dent.mode, msg.dent.size, msg.dent.time, buf, cookie);
+ func(msg.dent.mode, msg.dent.size, msg.dent.time, buf);
}
}
@@ -262,68 +435,6 @@
return sc.SendRequest(ID_STAT, path) && sync_finish_stat(sc, timestamp, mode, size);
}
-static bool SendLargeFile(SyncConnection& sc, const char* path_and_mode,
- const char* lpath, const char* rpath,
- unsigned mtime) {
- if (!sc.SendRequest(ID_SEND, path_and_mode)) {
- sc.Error("failed to send ID_SEND message '%s': %s", path_and_mode, strerror(errno));
- return false;
- }
-
- struct stat st;
- if (stat(lpath, &st) == -1) {
- sc.Error("cannot stat '%s': %s", lpath, strerror(errno));
- return false;
- }
-
- uint64_t total_size = st.st_size;
- uint64_t bytes_copied = 0;
-
- int lfd = adb_open(lpath, O_RDONLY);
- if (lfd < 0) {
- sc.Error("cannot open '%s': %s", lpath, strerror(errno));
- return false;
- }
-
- syncsendbuf sbuf;
- sbuf.id = ID_DATA;
- while (true) {
- int ret = adb_read(lfd, sbuf.data, sc.max);
- if (ret <= 0) {
- if (ret < 0) {
- sc.Error("cannot read '%s': %s", lpath, strerror(errno));
- adb_close(lfd);
- return false;
- }
- break;
- }
-
- sbuf.size = ret;
- if (!WriteFdExactly(sc.fd, &sbuf, sizeof(unsigned) * 2 + ret)) {
- adb_close(lfd);
- return false;
- }
- sc.total_bytes += ret;
-
- bytes_copied += ret;
-
- int percentage = static_cast<int>(bytes_copied * 100 / total_size);
- sc.Print(android::base::StringPrintf("%s: %d%%", rpath, percentage));
- }
-
- adb_close(lfd);
-
- syncmsg msg;
- msg.data.id = ID_DONE;
- msg.data.size = mtime;
- if (!WriteFdExactly(sc.fd, &msg.data, sizeof(msg.data))) {
- sc.Error("failed to send ID_DONE message for '%s': %s", rpath, strerror(errno));
- return false;
- }
-
- return true;
-}
-
static bool sync_send(SyncConnection& sc, const char* lpath, const char* rpath,
unsigned mtime, mode_t mode)
{
@@ -339,7 +450,9 @@
}
buf[data_length++] = '\0';
- if (!sc.SendSmallFile(path_and_mode.c_str(), rpath, buf, data_length, mtime)) return false;
+ if (!sc.SendSmallFile(path_and_mode.c_str(), lpath, rpath, mtime, buf, data_length)) {
+ return false;
+ }
return sc.CopyDone(lpath, rpath);
#endif
}
@@ -360,11 +473,12 @@
sc.Error("failed to read all of '%s': %s", lpath, strerror(errno));
return false;
}
- if (!sc.SendSmallFile(path_and_mode.c_str(), rpath, data.data(), data.size(), mtime)) {
+ if (!sc.SendSmallFile(path_and_mode.c_str(), lpath, rpath, mtime,
+ data.data(), data.size())) {
return false;
}
} else {
- if (!SendLargeFile(sc, path_and_mode.c_str(), lpath, rpath, mtime)) {
+ if (!sc.SendLargeFile(path_and_mode.c_str(), lpath, rpath, mtime)) {
return false;
}
}
@@ -372,15 +486,18 @@
}
static bool sync_recv(SyncConnection& sc, const char* rpath, const char* lpath) {
- sc.Print(rpath);
-
unsigned size = 0;
if (!sync_stat(sc, rpath, nullptr, nullptr, &size)) return false;
if (!sc.SendRequest(ID_RECV, rpath)) return false;
adb_unlink(lpath);
- mkdirs(lpath);
+ const std::string dirpath = adb_dirname(lpath);
+ if (!mkdirs(dirpath.c_str())) {
+ sc.Error("failed to create parent directory '%s': %s", dirpath.c_str(), strerror(errno));
+ return false;
+ }
+
int lfd = adb_creat(lpath, 0644);
if (lfd < 0) {
sc.Error("cannot create '%s': %s", lpath, strerror(errno));
@@ -426,390 +543,477 @@
return false;
}
- sc.total_bytes += msg.data.size;
+ sc.total_bytes_ += msg.data.size;
bytes_copied += msg.data.size;
- int percentage = static_cast<int>(bytes_copied * 100 / size);
- sc.Print(android::base::StringPrintf("%s: %d%%", rpath, percentage));
+ sc.ReportProgress(rpath, bytes_copied, size);
}
adb_close(lfd);
return true;
}
-static void do_sync_ls_cb(unsigned mode, unsigned size, unsigned time,
- const char* name, void* /*cookie*/) {
- printf("%08x %08x %08x %s\n", mode, size, time, name);
-}
-
bool do_sync_ls(const char* path) {
SyncConnection sc;
if (!sc.IsValid()) return false;
- return sync_ls(sc, path, do_sync_ls_cb, 0);
-}
-
-struct copyinfo
-{
- copyinfo *next;
- const char *src;
- const char *dst;
- unsigned int time;
- unsigned int mode;
- uint64_t size;
- int flag;
-};
-
-static copyinfo* mkcopyinfo(const char* spath, const char* dpath, const char* name, int isdir) {
- int slen = strlen(spath);
- int dlen = strlen(dpath);
- int nlen = strlen(name);
- int ssize = slen + nlen + 2;
- int dsize = dlen + nlen + 2;
-
- copyinfo *ci = reinterpret_cast<copyinfo*>(malloc(sizeof(copyinfo) + ssize + dsize));
- if (ci == 0) {
- fprintf(stderr, "out of memory\n");
- abort();
- }
-
- ci->next = 0;
- ci->time = 0;
- ci->mode = 0;
- ci->size = 0;
- ci->flag = 0;
- ci->src = (const char*)(ci + 1);
- ci->dst = ci->src + ssize;
- snprintf((char*) ci->src, ssize, isdir ? "%s%s/" : "%s%s", spath, name);
- snprintf((char*) ci->dst, dsize, isdir ? "%s%s/" : "%s%s", dpath, name);
-
- return ci;
+ return sync_ls(sc, path, [](unsigned mode, unsigned size, unsigned time,
+ const char* name) {
+ printf("%08x %08x %08x %s\n", mode, size, time, name);
+ });
}
static bool IsDotOrDotDot(const char* name) {
return name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'));
}
-static int local_build_list(SyncConnection& sc,
- copyinfo** filelist, const char* lpath, const char* rpath) {
- copyinfo *dirlist = 0;
- copyinfo *ci, *next;
-
- std::unique_ptr<DIR, int(*)(DIR*)> dir(opendir(lpath), closedir);
+static bool local_build_list(SyncConnection& sc, std::vector<copyinfo>* file_list,
+ const std::string& lpath,
+ const std::string& rpath) {
+ std::vector<copyinfo> dirlist;
+ std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(lpath.c_str()), closedir);
if (!dir) {
- sc.Error("cannot open '%s': %s", lpath, strerror(errno));
- return -1;
+ sc.Error("cannot open '%s': %s", lpath.c_str(), strerror(errno));
+ return false;
}
+ bool empty_dir = true;
dirent* de;
while ((de = readdir(dir.get()))) {
- if (IsDotOrDotDot(de->d_name)) continue;
-
- char stat_path[PATH_MAX];
- if (strlen(lpath) + strlen(de->d_name) + 1 > sizeof(stat_path)) {
- sc.Error("skipping long path '%s%s'", lpath, de->d_name);
+ if (IsDotOrDotDot(de->d_name)) {
continue;
}
- strcpy(stat_path, lpath);
- strcat(stat_path, de->d_name);
+
+ empty_dir = false;
+ std::string stat_path = lpath + de->d_name;
struct stat st;
- if (!lstat(stat_path, &st)) {
- if (S_ISDIR(st.st_mode)) {
- ci = mkcopyinfo(lpath, rpath, de->d_name, 1);
- ci->next = dirlist;
- dirlist = ci;
- } else {
- ci = mkcopyinfo(lpath, rpath, de->d_name, 0);
- if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
- sc.Error("skipping special file '%s'", ci->src);
- free(ci);
- } else {
- ci->time = st.st_mtime;
- ci->mode = st.st_mode;
- ci->size = st.st_size;
- ci->next = *filelist;
- *filelist = ci;
- }
- }
+ if (lstat(stat_path.c_str(), &st) == -1) {
+ sc.Error("cannot lstat '%s': %s", stat_path.c_str(),
+ strerror(errno));
+ continue;
+ }
+
+ copyinfo ci(lpath, rpath, de->d_name, st.st_mode);
+ if (S_ISDIR(st.st_mode)) {
+ dirlist.push_back(ci);
} else {
- sc.Error("cannot lstat '%s': %s",stat_path , strerror(errno));
+ if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
+ sc.Error("skipping special file '%s'", lpath.c_str());
+ ci.skip = true;
+ } else {
+ ci.time = st.st_mtime;
+ ci.size = st.st_size;
+ }
+ file_list->push_back(ci);
}
}
// Close this directory and recurse.
dir.reset();
- for (ci = dirlist; ci != 0; ci = next) {
- next = ci->next;
- local_build_list(sc, filelist, ci->src, ci->dst);
- free(ci);
+
+ // Add the current directory to the list if it was empty, to ensure that
+ // it gets created.
+ if (empty_dir) {
+ // TODO(b/25566053): Make pushing empty directories work.
+ // TODO(b/25457350): We don't preserve permissions on directories.
+ sc.Warning("skipping empty directory '%s'", lpath.c_str());
+ copyinfo ci(adb_dirname(lpath), adb_dirname(rpath), adb_basename(lpath), S_IFDIR);
+ ci.skip = true;
+ file_list->push_back(ci);
+ return true;
}
- return 0;
+ for (const copyinfo& ci : dirlist) {
+ local_build_list(sc, file_list, ci.lpath, ci.rpath);
+ }
+
+ return true;
}
-static bool copy_local_dir_remote(SyncConnection& sc, const char* lpath, const char* rpath,
- bool check_timestamps, bool list_only) {
- copyinfo *filelist = 0;
- copyinfo *ci, *next;
+static bool copy_local_dir_remote(SyncConnection& sc, std::string lpath,
+ std::string rpath, bool check_timestamps,
+ bool list_only) {
+ // Make sure that both directory paths end in a slash.
+ // Both paths are known to be nonempty, so we don't need to check.
+ ensure_trailing_separators(lpath, rpath);
+
+ // Recursively build the list of files to copy.
+ std::vector<copyinfo> file_list;
int pushed = 0;
int skipped = 0;
-
- if ((lpath[0] == 0) || (rpath[0] == 0)) return false;
- if (lpath[strlen(lpath) - 1] != '/') {
- int tmplen = strlen(lpath)+2;
- char *tmp = reinterpret_cast<char*>(malloc(tmplen));
- if(tmp == 0) return false;
- snprintf(tmp, tmplen, "%s/",lpath);
- lpath = tmp;
- }
- if (rpath[strlen(rpath) - 1] != '/') {
- int tmplen = strlen(rpath)+2;
- char *tmp = reinterpret_cast<char*>(malloc(tmplen));
- if(tmp == 0) return false;
- snprintf(tmp, tmplen, "%s/",rpath);
- rpath = tmp;
- }
-
- if (local_build_list(sc, &filelist, lpath, rpath)) {
+ if (!local_build_list(sc, &file_list, lpath, rpath)) {
return false;
}
if (check_timestamps) {
- for (ci = filelist; ci != 0; ci = ci->next) {
- if (!sc.SendRequest(ID_STAT, ci->dst)) return false;
+ for (const copyinfo& ci : file_list) {
+ if (!sc.SendRequest(ID_STAT, ci.rpath.c_str())) {
+ return false;
+ }
}
- for(ci = filelist; ci != 0; ci = ci->next) {
+ for (copyinfo& ci : file_list) {
unsigned int timestamp, mode, size;
- if (!sync_finish_stat(sc, ×tamp, &mode, &size)) return false;
- if (size == ci->size) {
- /* for links, we cannot update the atime/mtime */
- if ((S_ISREG(ci->mode & mode) && timestamp == ci->time) ||
- (S_ISLNK(ci->mode & mode) && timestamp >= ci->time)) {
- ci->flag = 1;
+ if (!sync_finish_stat(sc, ×tamp, &mode, &size)) {
+ return false;
+ }
+ if (size == ci.size) {
+ // For links, we cannot update the atime/mtime.
+ if ((S_ISREG(ci.mode & mode) && timestamp == ci.time) ||
+ (S_ISLNK(ci.mode & mode) && timestamp >= ci.time)) {
+ ci.skip = true;
}
}
}
}
- for (ci = filelist; ci != 0; ci = next) {
- next = ci->next;
- if (ci->flag == 0) {
+
+ sc.ComputeExpectedTotalBytes(file_list);
+
+ for (const copyinfo& ci : file_list) {
+ if (!ci.skip) {
if (list_only) {
- fprintf(stderr, "would push: %s -> %s\n", ci->src, ci->dst);
+ sc.Error("would push: %s -> %s", ci.lpath.c_str(), ci.rpath.c_str());
} else {
- if (!sync_send(sc, ci->src, ci->dst, ci->time, ci->mode)) {
- return false;
+ if (!sync_send(sc, ci.lpath.c_str(), ci.rpath.c_str(), ci.time, ci.mode)) {
+ return false;
}
}
pushed++;
} else {
skipped++;
}
- free(ci);
}
- sc.Print(android::base::StringPrintf("%s: %d file%s pushed. %d file%s skipped.%s\n",
- rpath,
- pushed, (pushed == 1) ? "" : "s",
- skipped, (skipped == 1) ? "" : "s",
- sc.TransferRate().c_str()));
+ sc.Printf("%s: %d file%s pushed. %d file%s skipped.%s", rpath.c_str(),
+ pushed, (pushed == 1) ? "" : "s", skipped,
+ (skipped == 1) ? "" : "s", sc.TransferRate().c_str());
return true;
}
-bool do_sync_push(const char* lpath, const char* rpath) {
+bool do_sync_push(const std::vector<const char*>& srcs, const char* dst) {
SyncConnection sc;
if (!sc.IsValid()) return false;
- struct stat st;
- if (stat(lpath, &st)) {
- sc.Error("cannot stat '%s': %s", lpath, strerror(errno));
+ bool success = true;
+ unsigned dst_mode;
+ if (!sync_stat(sc, dst, nullptr, &dst_mode, nullptr)) return false;
+ bool dst_exists = (dst_mode != 0);
+ bool dst_isdir = S_ISDIR(dst_mode);
+
+ if (!dst_isdir) {
+ if (srcs.size() > 1) {
+ sc.Error("target '%s' is not a directory", dst);
+ return false;
+ } else {
+ size_t dst_len = strlen(dst);
+
+ // A path that ends with a slash doesn't have to be a directory if
+ // it doesn't exist yet.
+ if (dst[dst_len - 1] == '/' && dst_exists) {
+ sc.Error("failed to access '%s': Not a directory", dst);
+ return false;
+ }
+ }
+ }
+
+ for (const char* src_path : srcs) {
+ const char* dst_path = dst;
+ struct stat st;
+ if (stat(src_path, &st) == -1) {
+ sc.Error("cannot stat '%s': %s", src_path, strerror(errno));
+ success = false;
+ continue;
+ }
+
+ if (S_ISDIR(st.st_mode)) {
+ std::string dst_dir = dst;
+
+ // If the destination path existed originally, the source directory
+ // should be copied as a child of the destination.
+ if (dst_exists) {
+ if (!dst_isdir) {
+ sc.Error("target '%s' is not a directory", dst);
+ return false;
+ }
+ // dst is a POSIX path, so we don't want to use the sysdeps
+ // helpers here.
+ if (dst_dir.back() != '/') {
+ dst_dir.push_back('/');
+ }
+ dst_dir.append(adb_basename(src_path));
+ }
+
+ success &= copy_local_dir_remote(sc, src_path, dst_dir.c_str(),
+ false, false);
+ continue;
+ }
+
+ std::string path_holder;
+ if (dst_isdir) {
+ // If we're copying a local file to a remote directory,
+ // we really want to copy to remote_dir + "/" + local_filename.
+ path_holder = android::base::StringPrintf(
+ "%s/%s", dst_path, adb_basename(src_path).c_str());
+ dst_path = path_holder.c_str();
+ }
+ sc.SetExpectedTotalBytes(st.st_size);
+ success &= sync_send(sc, src_path, dst_path, st.st_mtime, st.st_mode);
+ }
+
+ return success;
+}
+
+static bool remote_symlink_isdir(SyncConnection& sc, const std::string& rpath) {
+ unsigned mode;
+ std::string dir_path = rpath;
+ dir_path.push_back('/');
+ if (!sync_stat(sc, dir_path.c_str(), nullptr, &mode, nullptr)) {
+ sc.Error("failed to stat remote symlink '%s'", dir_path.c_str());
return false;
}
-
- if (S_ISDIR(st.st_mode)) {
- return copy_local_dir_remote(sc, lpath, rpath, false, false);
- }
-
- unsigned mode;
- if (!sync_stat(sc, rpath, nullptr, &mode, nullptr)) return false;
- std::string path_holder;
- if (mode != 0 && S_ISDIR(mode)) {
- // If we're copying a local file to a remote directory,
- // we really want to copy to remote_dir + "/" + local_filename.
- path_holder = android::base::StringPrintf("%s/%s", rpath, adb_basename(lpath).c_str());
- rpath = path_holder.c_str();
- }
- bool result = sync_send(sc, lpath, rpath, st.st_mtime, st.st_mode);
- sc.Print("\n");
- return result;
+ return S_ISDIR(mode);
}
-struct sync_ls_build_list_cb_args {
- SyncConnection* sc;
- copyinfo** filelist;
- copyinfo** dirlist;
- const char* rpath;
- const char* lpath;
-};
-
-static void sync_ls_build_list_cb(unsigned mode, unsigned size, unsigned time,
- const char* name, void* cookie)
-{
- sync_ls_build_list_cb_args* args = static_cast<sync_ls_build_list_cb_args*>(cookie);
- copyinfo *ci;
-
- if (S_ISDIR(mode)) {
- copyinfo **dirlist = args->dirlist;
-
- // Don't try recursing down "." or "..".
- if (IsDotOrDotDot(name)) return;
-
- ci = mkcopyinfo(args->rpath, args->lpath, name, 1);
- ci->next = *dirlist;
- *dirlist = ci;
- } else if (S_ISREG(mode) || S_ISLNK(mode)) {
- copyinfo **filelist = args->filelist;
-
- ci = mkcopyinfo(args->rpath, args->lpath, name, 0);
- ci->time = time;
- ci->mode = mode;
- ci->size = size;
- ci->next = *filelist;
- *filelist = ci;
- } else {
- args->sc->Print(android::base::StringPrintf("skipping special file '%s'\n", name));
- }
-}
-
-static bool remote_build_list(SyncConnection& sc, copyinfo **filelist,
- const char *rpath, const char *lpath) {
- copyinfo* dirlist = nullptr;
-
- sync_ls_build_list_cb_args args;
- args.sc = ≻
- args.filelist = filelist;
- args.dirlist = &dirlist;
- args.rpath = rpath;
- args.lpath = lpath;
+static bool remote_build_list(SyncConnection& sc,
+ std::vector<copyinfo>* file_list,
+ const std::string& rpath,
+ const std::string& lpath) {
+ std::vector<copyinfo> dirlist;
+ std::vector<copyinfo> linklist;
+ bool empty_dir = true;
// Put the files/dirs in rpath on the lists.
- if (!sync_ls(sc, rpath, sync_ls_build_list_cb, &args)) {
+ auto callback = [&](unsigned mode, unsigned size, unsigned time, const char* name) {
+ if (IsDotOrDotDot(name)) {
+ return;
+ }
+
+ // We found a child that isn't '.' or '..'.
+ empty_dir = false;
+
+ copyinfo ci(lpath, rpath, name, mode);
+ if (S_ISDIR(mode)) {
+ dirlist.push_back(ci);
+ } else if (S_ISLNK(mode)) {
+ linklist.push_back(ci);
+ } else {
+ ci.time = time;
+ ci.size = size;
+ file_list->push_back(ci);
+ }
+ };
+
+ if (!sync_ls(sc, rpath.c_str(), callback)) {
return false;
}
+ // Add the current directory to the list if it was empty, to ensure that it gets created.
+ if (empty_dir) {
+ copyinfo ci(adb_dirname(lpath), adb_dirname(rpath), adb_basename(rpath), S_IFDIR);
+ file_list->push_back(ci);
+ return true;
+ }
+
+ // Check each symlink we found to see whether it's a file or directory.
+ for (copyinfo& link_ci : linklist) {
+ if (remote_symlink_isdir(sc, link_ci.rpath)) {
+ dirlist.emplace_back(std::move(link_ci));
+ } else {
+ file_list->emplace_back(std::move(link_ci));
+ }
+ }
+
// Recurse into each directory we found.
- while (dirlist != NULL) {
- copyinfo* next = dirlist->next;
- if (!remote_build_list(sc, filelist, dirlist->src, dirlist->dst)) {
+ while (!dirlist.empty()) {
+ copyinfo current = dirlist.back();
+ dirlist.pop_back();
+ if (!remote_build_list(sc, file_list, current.rpath, current.lpath)) {
return false;
}
- free(dirlist);
- dirlist = next;
}
return true;
}
-static int set_time_and_mode(const char *lpath, time_t time, unsigned int mode)
-{
+static int set_time_and_mode(const std::string& lpath, time_t time,
+ unsigned int mode) {
struct utimbuf times = { time, time };
- int r1 = utime(lpath, ×);
+ int r1 = utime(lpath.c_str(), ×);
/* use umask for permissions */
- mode_t mask=umask(0000);
+ mode_t mask = umask(0000);
umask(mask);
- int r2 = chmod(lpath, mode & ~mask);
+ int r2 = chmod(lpath.c_str(), mode & ~mask);
- return r1 ? : r2;
+ return r1 ? r1 : r2;
}
-static bool copy_remote_dir_local(SyncConnection& sc, const char* rpath, const char* lpath,
- int copy_attrs) {
+static bool copy_remote_dir_local(SyncConnection& sc, std::string rpath,
+ std::string lpath, bool copy_attrs) {
// Make sure that both directory paths end in a slash.
- std::string rpath_clean(rpath);
- std::string lpath_clean(lpath);
- if (rpath_clean.empty() || lpath_clean.empty()) return false;
- if (rpath_clean.back() != '/') rpath_clean.push_back('/');
- if (lpath_clean.back() != '/') lpath_clean.push_back('/');
+ // Both paths are known to be nonempty, so we don't need to check.
+ ensure_trailing_separators(lpath, rpath);
// Recursively build the list of files to copy.
- sc.Print("pull: building file list...");
- copyinfo* filelist = nullptr;
- if (!remote_build_list(sc, &filelist, rpath_clean.c_str(), lpath_clean.c_str())) return false;
+ sc.Printf("pull: building file list...");
+ std::vector<copyinfo> file_list;
+ if (!remote_build_list(sc, &file_list, rpath.c_str(), lpath.c_str())) {
+ return false;
+ }
+
+ sc.ComputeExpectedTotalBytes(file_list);
int pulled = 0;
int skipped = 0;
- copyinfo* ci = filelist;
- while (ci) {
- copyinfo* next = ci->next;
- if (ci->flag == 0) {
- sc.Print(android::base::StringPrintf("pull: %s -> %s", ci->src, ci->dst));
- if (!sync_recv(sc, ci->src, ci->dst)) {
+ for (const copyinfo &ci : file_list) {
+ if (!ci.skip) {
+ if (S_ISDIR(ci.mode)) {
+ // Entry is for an empty directory, create it and continue.
+ // TODO(b/25457350): We don't preserve permissions on directories.
+ if (!mkdirs(ci.lpath)) {
+ sc.Error("failed to create directory '%s': %s",
+ ci.lpath.c_str(), strerror(errno));
+ return false;
+ }
+ pulled++;
+ continue;
+ }
+
+ if (!sync_recv(sc, ci.rpath.c_str(), ci.lpath.c_str())) {
return false;
}
- if (copy_attrs && set_time_and_mode(ci->dst, ci->time, ci->mode)) {
+ if (copy_attrs && set_time_and_mode(ci.lpath, ci.time, ci.mode)) {
return false;
}
pulled++;
} else {
skipped++;
}
- free(ci);
- ci = next;
}
- sc.Print(android::base::StringPrintf("%s: %d file%s pulled. %d file%s skipped.%s\n",
- rpath,
- pulled, (pulled == 1) ? "" : "s",
- skipped, (skipped == 1) ? "" : "s",
- sc.TransferRate().c_str()));
+ sc.Printf("%s: %d file%s pulled. %d file%s skipped.%s", rpath.c_str(),
+ pulled, (pulled == 1) ? "" : "s", skipped,
+ (skipped == 1) ? "" : "s", sc.TransferRate().c_str());
return true;
}
-bool do_sync_pull(const char* rpath, const char* lpath, int copy_attrs) {
+bool do_sync_pull(const std::vector<const char*>& srcs, const char* dst,
+ bool copy_attrs) {
SyncConnection sc;
if (!sc.IsValid()) return false;
- unsigned mode, time;
- if (!sync_stat(sc, rpath, &time, &mode, nullptr)) return false;
- if (mode == 0) {
- sc.Error("remote object '%s' does not exist", rpath);
- return false;
+ bool success = true;
+ struct stat st;
+ bool dst_exists = true;
+
+ if (stat(dst, &st) == -1) {
+ dst_exists = false;
+
+ // If we're only pulling one path, the destination path might point to
+ // a path that doesn't exist yet.
+ if (srcs.size() == 1 && errno == ENOENT) {
+ // However, its parent must exist.
+ struct stat parent_st;
+ if (stat(adb_dirname(dst).c_str(), &parent_st) == -1) {
+ sc.Error("cannot create file/directory '%s': %s", dst, strerror(errno));
+ return false;
+ }
+ } else {
+ sc.Error("failed to access '%s': %s", dst, strerror(errno));
+ return false;
+ }
}
- if (S_ISREG(mode) || S_ISLNK(mode) || S_ISCHR(mode) || S_ISBLK(mode)) {
- std::string path_holder;
- struct stat st;
- if (stat(lpath, &st) == 0) {
- if (S_ISDIR(st.st_mode)) {
- // If we're copying a remote file to a local directory,
- // we really want to copy to local_dir + "/" + basename(remote).
- path_holder = android::base::StringPrintf("%s/%s", lpath, adb_basename(rpath).c_str());
- lpath = path_holder.c_str();
- }
- }
- if (!sync_recv(sc, rpath, lpath)) {
+ bool dst_isdir = dst_exists && S_ISDIR(st.st_mode);
+ if (!dst_isdir) {
+ if (srcs.size() > 1) {
+ sc.Error("target '%s' is not a directory", dst);
return false;
} else {
- if (copy_attrs && set_time_and_mode(lpath, time, mode)) {
+ size_t dst_len = strlen(dst);
+
+ // A path that ends with a slash doesn't have to be a directory if
+ // it doesn't exist yet.
+ if (adb_is_separator(dst[dst_len - 1]) && dst_exists) {
+ sc.Error("failed to access '%s': Not a directory", dst);
return false;
}
}
- sc.Print("\n");
- return true;
- } else if (S_ISDIR(mode)) {
- return copy_remote_dir_local(sc, rpath, lpath, copy_attrs);
}
- sc.Error("remote object '%s' not a file or directory", rpath);
- return false;
+ for (const char* src_path : srcs) {
+ const char* dst_path = dst;
+ unsigned src_mode, src_time, src_size;
+ if (!sync_stat(sc, src_path, &src_time, &src_mode, &src_size)) {
+ sc.Error("failed to stat remote object '%s'", src_path);
+ return false;
+ }
+ if (src_mode == 0) {
+ sc.Error("remote object '%s' does not exist", src_path);
+ success = false;
+ continue;
+ }
+
+ bool src_isdir = S_ISDIR(src_mode);
+ if (S_ISLNK(src_mode)) {
+ src_isdir = remote_symlink_isdir(sc, src_path);
+ }
+
+ if ((src_mode & (S_IFREG | S_IFDIR | S_IFBLK | S_IFCHR)) == 0) {
+ sc.Error("skipping remote object '%s' (mode = 0o%o)", src_path, src_mode);
+ continue;
+ }
+
+ if (src_isdir) {
+ std::string dst_dir = dst;
+
+ // If the destination path existed originally, the source directory
+ // should be copied as a child of the destination.
+ if (dst_exists) {
+ if (!dst_isdir) {
+ sc.Error("target '%s' is not a directory", dst);
+ return false;
+ }
+ if (!adb_is_separator(dst_dir.back())) {
+ dst_dir.push_back(OS_PATH_SEPARATOR);
+ }
+ dst_dir.append(adb_basename(src_path));
+ }
+
+ success &= copy_remote_dir_local(sc, src_path, dst_dir.c_str(), copy_attrs);
+ continue;
+ } else {
+ std::string path_holder;
+ if (dst_isdir) {
+ // If we're copying a remote file to a local directory, we
+ // really want to copy to local_dir + OS_PATH_SEPARATOR +
+ // basename(remote).
+ path_holder = android::base::StringPrintf("%s%c%s", dst_path, OS_PATH_SEPARATOR,
+ adb_basename(src_path).c_str());
+ dst_path = path_holder.c_str();
+ }
+
+ sc.SetExpectedTotalBytes(src_size);
+ if (!sync_recv(sc, src_path, dst_path)) {
+ success = false;
+ continue;
+ }
+
+ if (copy_attrs && set_time_and_mode(dst_path, src_time, src_mode) != 0) {
+ success = false;
+ continue;
+ }
+ }
+ }
+
+ return success;
}
bool do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only) {
SyncConnection sc;
if (!sc.IsValid()) return false;
- return copy_local_dir_remote(sc, lpath.c_str(), rpath.c_str(), true, list_only);
+ return copy_local_dir_remote(sc, lpath, rpath, true, list_only);
}
diff --git a/adb/file_sync_service.cpp b/adb/file_sync_service.cpp
index 298ed82..781968b 100644
--- a/adb/file_sync_service.cpp
+++ b/adb/file_sync_service.cpp
@@ -32,10 +32,11 @@
#include "adb.h"
#include "adb_io.h"
+#include "adb_utils.h"
#include "private/android_filesystem_config.h"
-#include <base/stringprintf.h>
-#include <base/strings.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
static bool should_use_fs_config(const std::string& path) {
// TODO: use fs_config to configure permissions on /data.
@@ -53,8 +54,6 @@
if (path[0] != '/') return false;
std::vector<std::string> path_components = android::base::Split(path, "/");
- path_components.pop_back(); // For "/system/bin/sh", only create "/system/bin".
-
std::string partial_path;
for (const auto& path_component : path_components) {
if (partial_path.back() != OS_PATH_SEPARATOR) partial_path += OS_PATH_SEPARATOR;
@@ -149,7 +148,7 @@
int fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, mode);
if (fd < 0 && errno == ENOENT) {
- if (!secure_mkdirs(path)) {
+ if (!secure_mkdirs(adb_dirname(path))) {
SendSyncFailErrno(s, "secure_mkdirs failed");
goto fail;
}
@@ -244,7 +243,7 @@
ret = symlink(&buffer[0], path.c_str());
if (ret && errno == ENOENT) {
- if (!secure_mkdirs(path)) {
+ if (!secure_mkdirs(adb_dirname(path))) {
SendSyncFailErrno(s, "secure_mkdirs failed");
return false;
}
@@ -395,6 +394,18 @@
void file_sync_service(int fd, void* cookie) {
std::vector<char> buffer(SYNC_DATA_MAX);
+ // If there's a problem on the device, we'll send an ID_FAIL message and
+ // close the socket. Unfortunately the kernel will sometimes throw that
+ // data away if the other end keeps writing without reading (which is
+ // the normal case with our protocol --- they won't read until the end).
+ // So set SO_LINGER to give the client 20s to get around to reading our
+ // failure response. Without this, the other side's ability to report
+ // useful errors is reduced.
+ struct linger l;
+ l.l_onoff = 1;
+ l.l_linger = 20;
+ adb_setsockopt(fd, SOL_SOCKET, SO_LINGER, &l, sizeof(l));
+
while (handle_sync_command(fd, buffer)) {
}
diff --git a/adb/file_sync_service.h b/adb/file_sync_service.h
index 7c4d554..38382c1 100644
--- a/adb/file_sync_service.h
+++ b/adb/file_sync_service.h
@@ -18,6 +18,7 @@
#define _FILE_SYNC_SERVICE_H_
#include <string>
+#include <vector>
#define MKID(a,b,c,d) ((a) | ((b) << 8) | ((c) << 16) | ((d) << 24))
@@ -64,9 +65,11 @@
void file_sync_service(int fd, void* cookie);
bool do_sync_ls(const char* path);
-bool do_sync_push(const char* lpath, const char* rpath);
+bool do_sync_push(const std::vector<const char*>& srcs, const char* dst);
+bool do_sync_pull(const std::vector<const char*>& srcs, const char* dst,
+ bool copy_attrs);
+
bool do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only);
-bool do_sync_pull(const char* rpath, const char* lpath, int copy_attrs);
#define SYNC_DATA_MAX (64*1024)
diff --git a/adb/jdwp_service.cpp b/adb/jdwp_service.cpp
index cc2d44e..3c812cc 100644
--- a/adb/jdwp_service.cpp
+++ b/adb/jdwp_service.cpp
@@ -460,11 +460,11 @@
const char* sockname,
int socknamelen )
{
- struct sockaddr_un addr;
- socklen_t addrlen;
- int s;
- int maxpath = sizeof(addr.sun_path);
- int pathlen = socknamelen;
+ sockaddr_un addr;
+ socklen_t addrlen;
+ int s;
+ int maxpath = sizeof(addr.sun_path);
+ int pathlen = socknamelen;
if (pathlen >= maxpath) {
D( "vm debug control socket name too long (%d extra chars)",
@@ -485,7 +485,7 @@
addrlen = (pathlen + sizeof(addr.sun_family));
- if (bind(s, (struct sockaddr*)&addr, addrlen) < 0) {
+ if (bind(s, reinterpret_cast<sockaddr*>(&addr), addrlen) < 0) {
D( "could not bind vm debug control socket: %d: %s",
errno, strerror(errno) );
adb_close(s);
@@ -523,13 +523,14 @@
JdwpControl* control = (JdwpControl*) _control;
if (events & FDE_READ) {
- struct sockaddr addr;
- socklen_t addrlen = sizeof(addr);
- int s = -1;
- JdwpProcess* proc;
+ sockaddr_storage ss;
+ sockaddr* addrp = reinterpret_cast<sockaddr*>(&ss);
+ socklen_t addrlen = sizeof(ss);
+ int s = -1;
+ JdwpProcess* proc;
do {
- s = adb_socket_accept( control->listen_socket, &addr, &addrlen );
+ s = adb_socket_accept(control->listen_socket, addrp, &addrlen);
if (s < 0) {
if (errno == EINTR)
continue;
diff --git a/adb/line_printer.cpp b/adb/line_printer.cpp
index 81b3f0a..e8fe6c9 100644
--- a/adb/line_printer.cpp
+++ b/adb/line_printer.cpp
@@ -43,10 +43,10 @@
return result;
}
-LinePrinter::LinePrinter() : have_blank_line_(true), console_locked_(false) {
+LinePrinter::LinePrinter() : have_blank_line_(true) {
#ifndef _WIN32
const char* term = getenv("TERM");
- smart_terminal_ = isatty(1) && term && string(term) != "dumb";
+ smart_terminal_ = unix_isatty(1) && term && string(term) != "dumb";
#else
// Disable output buffer. It'd be nice to use line buffering but
// MSDN says: "For some systems, [_IOLBF] provides line
@@ -59,25 +59,29 @@
#endif
}
+static void Out(const std::string& s) {
+ // Avoid printf and C strings, since the actual output might contain null
+ // bytes like UTF-16 does (yuck).
+ fwrite(s.data(), 1, s.size(), stdout);
+}
+
void LinePrinter::Print(string to_print, LineType type) {
- if (console_locked_) {
- line_buffer_ = to_print;
- line_type_ = type;
+ if (!smart_terminal_) {
+ Out(to_print);
return;
}
- if (smart_terminal_) {
- printf("\r"); // Print over previous line, if any.
- // On Windows, calling a C library function writing to stdout also handles
- // pausing the executable when the "Pause" key or Ctrl-S is pressed.
- }
+ // Print over previous line, if any.
+ // On Windows, calling a C library function writing to stdout also handles
+ // pausing the executable when the "Pause" key or Ctrl-S is pressed.
+ printf("\r");
- if (smart_terminal_ && type == ELIDE) {
+ if (type == INFO) {
#ifdef _WIN32
CONSOLE_SCREEN_BUFFER_INFO csbi;
GetConsoleScreenBufferInfo(console_, &csbi);
- // TODO: const std::wstring to_print_wide = widen(to_print);
+ // TODO: std::wstring to_print_wide; if (!android::base::UTF8ToWide(to_print, &to_print_wide)...
// TODO: wstring ElideMiddle.
to_print = ElideMiddle(to_print, static_cast<size_t>(csbi.dwSize.X));
// We don't want to have the cursor spamming back and forth, so instead of
@@ -105,57 +109,19 @@
if ((ioctl(0, TIOCGWINSZ, &size) == 0) && size.ws_col) {
to_print = ElideMiddle(to_print, size.ws_col);
}
- printf("%s", to_print.c_str());
+ Out(to_print);
printf("\x1B[K"); // Clear to end of line.
fflush(stdout);
#endif
have_blank_line_ = false;
} else {
- printf("%s\n", to_print.c_str());
+ Out(to_print);
+ Out("\n");
+ have_blank_line_ = true;
}
}
-void LinePrinter::PrintOrBuffer(const char* data, size_t size) {
- if (console_locked_) {
- output_buffer_.append(data, size);
- } else {
- // Avoid printf and C strings, since the actual output might contain null
- // bytes like UTF-16 does (yuck).
- fwrite(data, 1, size, stdout);
- }
-}
-
-void LinePrinter::PrintOnNewLine(const string& to_print) {
- if (console_locked_ && !line_buffer_.empty()) {
- output_buffer_.append(line_buffer_);
- output_buffer_.append(1, '\n');
- line_buffer_.clear();
- }
- if (!have_blank_line_) {
- PrintOrBuffer("\n", 1);
- }
- if (!to_print.empty()) {
- PrintOrBuffer(&to_print[0], to_print.size());
- }
- have_blank_line_ = to_print.empty() || *to_print.rbegin() == '\n';
-}
-
-void LinePrinter::SetConsoleLocked(bool locked) {
- if (locked == console_locked_)
- return;
-
- if (locked)
- PrintOnNewLine("");
-
- console_locked_ = locked;
-
- if (!locked) {
- PrintOnNewLine(output_buffer_);
- if (!line_buffer_.empty()) {
- Print(line_buffer_, line_type_);
- }
- output_buffer_.clear();
- line_buffer_.clear();
- }
+void LinePrinter::KeepInfoLine() {
+ if (!have_blank_line_) Out("\n");
}
diff --git a/adb/line_printer.h b/adb/line_printer.h
index 3d0a5bd..42345e2 100644
--- a/adb/line_printer.h
+++ b/adb/line_printer.h
@@ -26,20 +26,14 @@
bool is_smart_terminal() const { return smart_terminal_; }
void set_smart_terminal(bool smart) { smart_terminal_ = smart; }
- enum LineType {
- FULL,
- ELIDE
- };
- /// Overprints the current line. If type is ELIDE, elides to_print to fit on
- /// one line.
+ enum LineType { INFO, WARNING, ERROR };
+
+ /// Outputs the given line. INFO output will be overwritten.
+ /// WARNING and ERROR appear on a line to themselves.
void Print(std::string to_print, LineType type);
- /// Prints a string on a new line, not overprinting previous output.
- void PrintOnNewLine(const std::string& to_print);
-
- /// Lock or unlock the console. Any output sent to the LinePrinter while the
- /// console is locked will not be printed until it is unlocked.
- void SetConsoleLocked(bool locked);
+ /// If there's an INFO line, keep it. If not, do nothing.
+ void KeepInfoLine();
private:
/// Whether we can do fancy terminal control codes.
@@ -48,24 +42,9 @@
/// Whether the caret is at the beginning of a blank line.
bool have_blank_line_;
- /// Whether console is locked.
- bool console_locked_;
-
- /// Buffered current line while console is locked.
- std::string line_buffer_;
-
- /// Buffered line type while console is locked.
- LineType line_type_;
-
- /// Buffered console output while console is locked.
- std::string output_buffer_;
-
#ifdef _WIN32
void* console_;
#endif
-
- /// Print the given data to the console, or buffer it if it is locked.
- void PrintOrBuffer(const char *data, size_t size);
};
#endif // NINJA_LINE_PRINTER_H_
diff --git a/adb/mutex_list.h b/adb/mutex_list.h
index 79c48d8..b59c9f2 100644
--- a/adb/mutex_list.h
+++ b/adb/mutex_list.h
@@ -6,6 +6,7 @@
#ifndef ADB_MUTEX
#error ADB_MUTEX not defined when including this file
#endif
+ADB_MUTEX(basename_lock)
ADB_MUTEX(dirname_lock)
ADB_MUTEX(socket_list_lock)
ADB_MUTEX(transport_lock)
diff --git a/adb/remount_service.cpp b/adb/remount_service.cpp
index 35ba056..8f1c9b0 100644
--- a/adb/remount_service.cpp
+++ b/adb/remount_service.cpp
@@ -35,8 +35,6 @@
#include "cutils/properties.h"
#include "fs_mgr.h"
-const std::string kFstab_Prefix = "/fstab.";
-
// Returns the device used to mount a directory in /proc/mounts.
static std::string find_proc_mount(const char* dir) {
std::unique_ptr<FILE, int(*)(FILE*)> fp(setmntent("/proc/mounts", "r"), endmntent);
@@ -58,7 +56,7 @@
char propbuf[PROPERTY_VALUE_MAX];
property_get("ro.hardware", propbuf, "");
- std::string fstab_filename = kFstab_Prefix + propbuf;
+ std::string fstab_filename = std::string("/fstab.") + propbuf;
struct fstab* fstab = fs_mgr_read_fstab(fstab_filename.c_str());
struct fstab_rec* rec = fs_mgr_get_entry_for_mount_point(fstab, dir);
std::string dev = rec ? std::string(rec->blk_device) : "";
diff --git a/adb/services.cpp b/adb/services.cpp
index 19a6726..20166ce 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -31,9 +31,9 @@
#include <unistd.h>
#endif
-#include <base/file.h>
-#include <base/stringprintf.h>
-#include <base/strings.h>
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include <cutils/sockets.h>
#if !ADB_HOST
@@ -213,9 +213,11 @@
// Defaults:
// PTY for interactive, raw for non-interactive.
// No protocol.
+ // $TERM set to "dumb".
SubprocessType type(command.empty() ? SubprocessType::kPty
: SubprocessType::kRaw);
SubprocessProtocol protocol = SubprocessProtocol::kNone;
+ std::string terminal_type = "dumb";
for (const std::string& arg : android::base::Split(service_args, ",")) {
if (arg == kShellServiceArgRaw) {
@@ -224,14 +226,15 @@
type = SubprocessType::kPty;
} else if (arg == kShellServiceArgShellProtocol) {
protocol = SubprocessProtocol::kShell;
- }
- else if (!arg.empty()) {
- LOG(ERROR) << "Unsupported shell service arguments: " << args;
- return -1;
+ } else if (android::base::StartsWith(arg, "TERM=")) {
+ terminal_type = arg.substr(5);
+ } else if (!arg.empty()) {
+ // This is not an error to allow for future expansion.
+ LOG(WARNING) << "Ignoring unknown shell service argument: " << arg;
}
}
- return StartSubprocess(command.c_str(), type, protocol);
+ return StartSubprocess(command.c_str(), terminal_type.c_str(), type, protocol);
}
#endif // !ADB_HOST
@@ -308,8 +311,7 @@
} else if(!strncmp(name, "shell", 5)) {
ret = ShellService(name + 5, transport);
} else if(!strncmp(name, "exec:", 5)) {
- ret = StartSubprocess(name + 5, SubprocessType::kRaw,
- SubprocessProtocol::kNone);
+ ret = StartSubprocess(name + 5, nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
} else if(!strncmp(name, "sync:", 5)) {
ret = create_service_thread(file_sync_service, NULL);
} else if(!strncmp(name, "remount:", 8)) {
@@ -325,9 +327,9 @@
} else if(!strncmp(name, "backup:", 7)) {
ret = StartSubprocess(android::base::StringPrintf("/system/bin/bu backup %s",
(name + 7)).c_str(),
- SubprocessType::kRaw, SubprocessProtocol::kNone);
+ nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
} else if(!strncmp(name, "restore:", 8)) {
- ret = StartSubprocess("/system/bin/bu restore", SubprocessType::kRaw,
+ ret = StartSubprocess("/system/bin/bu restore", nullptr, SubprocessType::kRaw,
SubprocessProtocol::kNone);
} else if(!strncmp(name, "tcpip:", 6)) {
int port;
@@ -354,19 +356,19 @@
#if ADB_HOST
struct state_info {
TransportType transport_type;
- char* serial;
+ std::string serial;
ConnectionState state;
};
-static void wait_for_state(int fd, void* cookie) {
- state_info* sinfo = reinterpret_cast<state_info*>(cookie);
+static void wait_for_state(int fd, void* data) {
+ std::unique_ptr<state_info> sinfo(reinterpret_cast<state_info*>(data));
D("wait_for_state %d", sinfo->state);
while (true) {
bool is_ambiguous = false;
std::string error = "unknown error";
- atransport* t = acquire_one_transport(sinfo->transport_type, sinfo->serial,
+ atransport* t = acquire_one_transport(sinfo->transport_type, sinfo->serial.c_str(),
&is_ambiguous, &error);
if (t != nullptr && t->connection_state == sinfo->state) {
SendOkay(fd);
@@ -380,10 +382,6 @@
}
}
- if (sinfo->serial) {
- free(sinfo->serial);
- }
- free(sinfo);
adb_close(fd);
D("wait_for_state is done");
}
@@ -489,38 +487,43 @@
asocket* host_service_to_socket(const char* name, const char* serial) {
if (!strcmp(name,"track-devices")) {
return create_device_tracker();
- } else if (!strncmp(name, "wait-for-", strlen("wait-for-"))) {
- auto sinfo = reinterpret_cast<state_info*>(malloc(sizeof(state_info)));
- if (sinfo == nullptr) {
- fprintf(stderr, "couldn't allocate state_info: %s", strerror(errno));
- return NULL;
- }
-
- if (serial)
- sinfo->serial = strdup(serial);
- else
- sinfo->serial = NULL;
-
+ } else if (android::base::StartsWith(name, "wait-for-")) {
name += strlen("wait-for-");
- if (!strncmp(name, "local", strlen("local"))) {
- sinfo->transport_type = kTransportLocal;
- sinfo->state = kCsDevice;
- } else if (!strncmp(name, "usb", strlen("usb"))) {
- sinfo->transport_type = kTransportUsb;
- sinfo->state = kCsDevice;
- } else if (!strncmp(name, "any", strlen("any"))) {
- sinfo->transport_type = kTransportAny;
- sinfo->state = kCsDevice;
- } else {
- if (sinfo->serial) {
- free(sinfo->serial);
- }
- free(sinfo);
- return NULL;
+ std::unique_ptr<state_info> sinfo(new state_info);
+ if (sinfo == nullptr) {
+ fprintf(stderr, "couldn't allocate state_info: %s", strerror(errno));
+ return nullptr;
}
- int fd = create_service_thread(wait_for_state, sinfo);
+ if (serial) sinfo->serial = serial;
+
+ if (android::base::StartsWith(name, "local")) {
+ name += strlen("local");
+ sinfo->transport_type = kTransportLocal;
+ } else if (android::base::StartsWith(name, "usb")) {
+ name += strlen("usb");
+ sinfo->transport_type = kTransportUsb;
+ } else if (android::base::StartsWith(name, "any")) {
+ name += strlen("any");
+ sinfo->transport_type = kTransportAny;
+ } else {
+ return nullptr;
+ }
+
+ if (!strcmp(name, "-device")) {
+ sinfo->state = kCsDevice;
+ } else if (!strcmp(name, "-recovery")) {
+ sinfo->state = kCsRecovery;
+ } else if (!strcmp(name, "-sideload")) {
+ sinfo->state = kCsSideload;
+ } else if (!strcmp(name, "-bootloader")) {
+ sinfo->state = kCsBootloader;
+ } else {
+ return nullptr;
+ }
+
+ int fd = create_service_thread(wait_for_state, sinfo.release());
return create_local_socket(fd);
} else if (!strncmp(name, "connect:", 8)) {
char* host = strdup(name + 8);
diff --git a/adb/shell_service.cpp b/adb/shell_service.cpp
index be5921d..366ed07 100644
--- a/adb/shell_service.cpp
+++ b/adb/shell_service.cpp
@@ -88,9 +88,12 @@
#include <termios.h>
#include <memory>
+#include <string>
+#include <unordered_map>
+#include <vector>
-#include <base/logging.h>
-#include <base/stringprintf.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
#include <paths.h>
#include "adb.h"
@@ -175,8 +178,8 @@
class Subprocess {
public:
- Subprocess(const std::string& command, SubprocessType type,
- SubprocessProtocol protocol);
+ Subprocess(const std::string& command, const char* terminal_type,
+ SubprocessType type, SubprocessProtocol protocol);
~Subprocess();
const std::string& command() const { return command_; }
@@ -207,6 +210,7 @@
ScopedFd* PassOutput(ScopedFd* sfd, ShellProtocol::Id id);
const std::string command_;
+ const std::string terminal_type_;
SubprocessType type_;
SubprocessProtocol protocol_;
pid_t pid_ = -1;
@@ -220,9 +224,12 @@
DISALLOW_COPY_AND_ASSIGN(Subprocess);
};
-Subprocess::Subprocess(const std::string& command, SubprocessType type,
- SubprocessProtocol protocol)
- : command_(command), type_(type), protocol_(protocol) {
+Subprocess::Subprocess(const std::string& command, const char* terminal_type,
+ SubprocessType type, SubprocessProtocol protocol)
+ : command_(command),
+ terminal_type_(terminal_type ? terminal_type : ""),
+ type_(type),
+ protocol_(protocol) {
}
Subprocess::~Subprocess() {
@@ -233,13 +240,55 @@
ScopedFd parent_error_sfd, child_error_sfd;
char pts_name[PATH_MAX];
- // Create a socketpair for the fork() child to report any errors back to
- // the parent. Since we use threads, logging directly from the child could
- // create a race condition.
+ // Create a socketpair for the fork() child to report any errors back to the parent. Since we
+ // use threads, logging directly from the child might deadlock due to locks held in another
+ // thread during the fork.
if (!CreateSocketpair(&parent_error_sfd, &child_error_sfd)) {
LOG(ERROR) << "failed to create pipe for subprocess error reporting";
}
+ // Construct the environment for the child before we fork.
+ passwd* pw = getpwuid(getuid());
+ std::unordered_map<std::string, std::string> env;
+ if (environ) {
+ char** current = environ;
+ while (char* env_cstr = *current++) {
+ std::string env_string = env_cstr;
+ char* delimiter = strchr(env_string.c_str(), '=');
+
+ // Drop any values that don't contain '='.
+ if (delimiter) {
+ *delimiter++ = '\0';
+ env[env_string.c_str()] = delimiter;
+ }
+ }
+ }
+
+ if (pw != nullptr) {
+ // TODO: $HOSTNAME? Normally bash automatically sets that, but mksh doesn't.
+ env["HOME"] = pw->pw_dir;
+ env["LOGNAME"] = pw->pw_name;
+ env["USER"] = pw->pw_name;
+ env["SHELL"] = pw->pw_shell;
+ }
+
+ if (!terminal_type_.empty()) {
+ env["TERM"] = terminal_type_;
+ }
+
+ std::vector<std::string> joined_env;
+ for (auto it : env) {
+ const char* key = it.first.c_str();
+ const char* value = it.second.c_str();
+ joined_env.push_back(android::base::StringPrintf("%s=%s", key, value));
+ }
+
+ std::vector<const char*> cenv;
+ for (const std::string& str : joined_env) {
+ cenv.push_back(str.c_str());
+ }
+ cenv.push_back(nullptr);
+
if (type_ == SubprocessType::kPty) {
int fd;
pid_ = forkpty(&fd, pts_name, nullptr, nullptr);
@@ -282,23 +331,14 @@
parent_error_sfd.Reset();
close_on_exec(child_error_sfd.fd());
- // TODO: $HOSTNAME? Normally bash automatically sets that, but mksh doesn't.
- passwd* pw = getpwuid(getuid());
- if (pw != nullptr) {
- setenv("HOME", pw->pw_dir, 1);
- setenv("LOGNAME", pw->pw_name, 1);
- setenv("SHELL", pw->pw_shell, 1);
- setenv("USER", pw->pw_name, 1);
- }
-
if (is_interactive()) {
- execl(_PATH_BSHELL, _PATH_BSHELL, "-", nullptr);
+ execle(_PATH_BSHELL, _PATH_BSHELL, "-", nullptr, cenv.data());
} else {
- execl(_PATH_BSHELL, _PATH_BSHELL, "-c", command_.c_str(), nullptr);
+ execle(_PATH_BSHELL, _PATH_BSHELL, "-c", command_.c_str(), nullptr, cenv.data());
}
WriteFdExactly(child_error_sfd.fd(), "exec '" _PATH_BSHELL "' failed");
child_error_sfd.Reset();
- exit(-1);
+ _Exit(1);
}
// Subprocess parent.
@@ -313,6 +353,7 @@
return false;
}
+ D("subprocess parent: exec completed");
if (protocol_ == SubprocessProtocol::kNone) {
// No protocol: all streams pass through the stdinout FD and hook
// directly into the local socket for raw data transfer.
@@ -350,6 +391,7 @@
return false;
}
+ D("subprocess parent: completed");
return true;
}
@@ -513,6 +555,18 @@
if (stdinout_sfd_.valid()) {
switch (input_->id()) {
+ case ShellProtocol::kIdWindowSizeChange:
+ int rows, cols, x_pixels, y_pixels;
+ if (sscanf(input_->data(), "%dx%d,%dx%d",
+ &rows, &cols, &x_pixels, &y_pixels) == 4) {
+ winsize ws;
+ ws.ws_row = rows;
+ ws.ws_col = cols;
+ ws.ws_xpixel = x_pixels;
+ ws.ws_ypixel = y_pixels;
+ ioctl(stdinout_sfd_.fd(), TIOCSWINSZ, &ws);
+ }
+ break;
case ShellProtocol::kIdStdin:
input_bytes_left_ = input_->data_length();
break;
@@ -531,8 +585,7 @@
// non-interactively which is rare and unsupported.
// If necessary, the client can manually close the shell
// with `exit` or by killing the adb client process.
- D("can't close input for PTY FD %d",
- stdinout_sfd_.fd());
+ D("can't close input for PTY FD %d", stdinout_sfd_.fd());
}
break;
}
@@ -633,13 +686,14 @@
} // namespace
-int StartSubprocess(const char *name, SubprocessType type,
- SubprocessProtocol protocol) {
- D("starting %s subprocess (protocol=%s): '%s'",
+int StartSubprocess(const char* name, const char* terminal_type,
+ SubprocessType type, SubprocessProtocol protocol) {
+ D("starting %s subprocess (protocol=%s, TERM=%s): '%s'",
type == SubprocessType::kRaw ? "raw" : "PTY",
- protocol == SubprocessProtocol::kNone ? "none" : "shell", name);
+ protocol == SubprocessProtocol::kNone ? "none" : "shell",
+ terminal_type, name);
- Subprocess* subprocess = new Subprocess(name, type, protocol);
+ Subprocess* subprocess = new Subprocess(name, terminal_type, type, protocol);
if (!subprocess) {
LOG(ERROR) << "failed to allocate new subprocess";
return -1;
diff --git a/adb/shell_service.h b/adb/shell_service.h
index 01410a9..e3d676a 100644
--- a/adb/shell_service.h
+++ b/adb/shell_service.h
@@ -27,7 +27,7 @@
#include <stdint.h>
-#include <base/macros.h>
+#include <android-base/macros.h>
#include "adb.h"
@@ -54,8 +54,15 @@
kIdStdout = 1,
kIdStderr = 2,
kIdExit = 3,
- kIdCloseStdin = 4, // Close subprocess stdin if possible.
- kIdInvalid = 255, // Indicates an invalid or unknown packet.
+
+ // Close subprocess stdin if possible.
+ kIdCloseStdin = 4,
+
+ // Window size change (an ASCII version of struct winsize).
+ kIdWindowSizeChange = 5,
+
+ // Indicates an invalid or unknown packet.
+ kIdInvalid = 255,
};
// ShellPackets will probably be too large to allocate on the stack so they
@@ -134,8 +141,8 @@
// shell is started, otherwise |name| is executed non-interactively.
//
// Returns an open FD connected to the subprocess or -1 on failure.
-int StartSubprocess(const char* name, SubprocessType type,
- SubprocessProtocol protocol);
+int StartSubprocess(const char* name, const char* terminal_type,
+ SubprocessType type, SubprocessProtocol protocol);
#endif // !ADB_HOST
diff --git a/adb/shell_service_test.cpp b/adb/shell_service_test.cpp
index e18f905..c85232b 100644
--- a/adb/shell_service_test.cpp
+++ b/adb/shell_service_test.cpp
@@ -23,7 +23,7 @@
#include <string>
#include <vector>
-#include <base/strings.h>
+#include <android-base/strings.h>
#include "adb.h"
#include "adb_io.h"
@@ -69,7 +69,7 @@
SHELL_EXIT_NOTIFY_FD = fd[0];
shell_exit_receiver_fd_ = fd[1];
- subprocess_fd_ = StartSubprocess(command, type, protocol);
+ subprocess_fd_ = StartSubprocess(command, nullptr, type, protocol);
ASSERT_TRUE(subprocess_fd_ >= 0);
}
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index f8c2f64..eb0ce85 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -25,6 +25,8 @@
#include <string.h>
#include <unistd.h>
+#include <algorithm>
+
#if !ADB_HOST
#include "cutils/properties.h"
#endif
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index 51d09a6..2190c61 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -28,6 +28,9 @@
#include <string>
+// Include this before open/unlink are defined as macros below.
+#include <android-base/utf8.h>
+
/*
* TEMP_FAILURE_RETRY is defined by some, but not all, versions of
* <unistd.h>. (Alas, it is not as standard as we'd hoped!) So, if it's
@@ -72,7 +75,7 @@
#include <ws2tcpip.h>
#include <memory> // unique_ptr
-#include <string> // Prototypes for narrow() and widen() use std::(w)string.
+#include <string>
#include "fdevent.h"
@@ -81,6 +84,10 @@
#define OS_PATH_SEPARATOR_STR "\\"
#define ENV_PATH_SEPARATOR_STR ";"
+static __inline__ bool adb_is_separator(char c) {
+ return c == '\\' || c == '/';
+}
+
typedef CRITICAL_SECTION adb_mutex_t;
#define ADB_MUTEX_DEFINE(x) adb_mutex_t x
@@ -181,6 +188,17 @@
extern int unix_open(const char* path, int options, ...);
#define open ___xxx_unix_open
+// Checks if |fd| corresponds to a console.
+// Standard Windows isatty() returns 1 for both console FDs and character
+// devices like NUL. unix_isatty() performs some extra checking to only match
+// console FDs.
+// |fd| must be a real file descriptor, meaning STDxx_FILENO or unix_open() FDs
+// will work but adb_open() FDs will not. Additionally the OS handle associated
+// with |fd| must have GENERIC_READ access (which console FDs have by default).
+// Returns 1 if |fd| is a console FD, 0 otherwise. The value of errno after
+// calling this function is unreliable and should not be used.
+int unix_isatty(int fd);
+#define isatty ___xxx_isatty
/* normally provided by <cutils/misc.h> */
extern void* load_file(const char* pathname, unsigned* psize);
@@ -224,18 +242,6 @@
#undef setsockopt
#define setsockopt ___xxx_setsockopt
-static __inline__ int adb_socket_setbufsize( int fd, int bufsize )
-{
- int opt = bufsize;
- return adb_setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (const void*)&opt, sizeof(opt));
-}
-
-static __inline__ void disable_tcp_nagle( int fd )
-{
- int on = 1;
- adb_setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (const void*)&on, sizeof(on));
-}
-
extern int adb_socketpair( int sv[2] );
static __inline__ int adb_is_absolute_host_path(const char* path) {
@@ -298,7 +304,12 @@
#define closedir adb_closedir
#define rewinddir rewinddir_utf8_not_yet_implemented
#define telldir telldir_utf8_not_yet_implemented
-#define seekdir seekdir_utf8_not_yet_implemented
+// Some compiler's C++ headers have members named seekdir, so we can't do the
+// macro technique and instead cause a link error if seekdir is called.
+inline void seekdir(DIR*, long) {
+ extern int seekdir_utf8_not_yet_implemented;
+ seekdir_utf8_not_yet_implemented = 1;
+}
#define utime adb_utime
#define chmod adb_chmod
@@ -322,18 +333,6 @@
char* adb_strerror(int err);
#define strerror adb_strerror
-// Convert from UTF-8 to UTF-16, typically used to convert char strings into
-// wchar_t strings that can be passed to wchar_t-based OS and C Runtime APIs
-// on Windows.
-extern std::wstring widen(const std::string& utf8);
-extern std::wstring widen(const char* utf8);
-
-// Convert from UTF-16 to UTF-8, typically used to convert strings from OS and
-// C Runtime APIs that return wchar_t, to a format for our char-based data
-// structures.
-extern std::string narrow(const std::wstring& utf16);
-extern std::string narrow(const wchar_t* utf16);
-
// Helper class to convert UTF-16 argv from wmain() to UTF-8 args that can be
// passed to main().
class NarrowArgs {
@@ -409,6 +408,10 @@
#define OS_PATH_SEPARATOR_STR "/"
#define ENV_PATH_SEPARATOR_STR ":"
+static __inline__ bool adb_is_separator(char c) {
+ return c == '/';
+}
+
typedef pthread_mutex_t adb_mutex_t;
#define ADB_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER
@@ -551,6 +554,11 @@
#undef creat
#define creat ___xxx_creat
+static __inline__ int unix_isatty(int fd) {
+ return isatty(fd);
+}
+#define isatty ___xxx_isatty
+
// Helper for network_* functions.
inline int _fd_set_error_str(int fd, std::string* error) {
if (fd == -1) {
@@ -650,18 +658,6 @@
#endif
}
-static __inline__ int adb_socket_setbufsize(int fd, int bufsize )
-{
- int opt = bufsize;
- return setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt));
-}
-
-static __inline__ void disable_tcp_nagle(int fd)
-{
- int on = 1;
- setsockopt( fd, IPPROTO_TCP, TCP_NODELAY, (void*)&on, sizeof(on) );
-}
-
static __inline__ int adb_setsockopt( int fd, int level, int optname, const void* optval, socklen_t optlen )
{
return setsockopt( fd, level, optname, optval, optlen );
@@ -719,4 +715,9 @@
#endif /* !_WIN32 */
+static inline void disable_tcp_nagle(int fd) {
+ int off = 1;
+ adb_setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &off, sizeof(off));
+}
+
#endif /* _ADB_SYSDEPS_H */
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
index 14d1375..0a2a8f6 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -32,9 +32,10 @@
#include <cutils/sockets.h>
-#include <base/logging.h>
-#include <base/stringprintf.h>
-#include <base/strings.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/utf8.h>
#include "adb.h"
@@ -102,7 +103,13 @@
}
// Convert UTF-16 to UTF-8.
- std::string msg(narrow(msgbuf));
+ std::string msg;
+ if (!android::base::WideToUTF8(msgbuf, &msg)) {
+ return android::base::StringPrintf(
+ "Error (%d) converting from UTF-16 to UTF-8 while retrieving error. (%lu)", errno,
+ error_code);
+ }
+
// Messages returned by the system end with line breaks.
msg = android::base::Trim(msg);
// There are many Windows error messages compared to POSIX, so include the
@@ -143,7 +150,11 @@
char *data;
DWORD file_size;
- file = CreateFileW( widen(fn).c_str(),
+ std::wstring fn_wide;
+ if (!android::base::UTF8ToWide(fn, &fn_wide))
+ return NULL;
+
+ file = CreateFileW( fn_wide.c_str(),
GENERIC_READ,
FILE_SHARE_READ,
NULL,
@@ -433,7 +444,11 @@
return -1;
}
- f->fh_handle = CreateFileW( widen(path).c_str(), desiredAccess, shareMode,
+ std::wstring path_wide;
+ if (!android::base::UTF8ToWide(path, &path_wide)) {
+ return -1;
+ }
+ f->fh_handle = CreateFileW( path_wide.c_str(), desiredAccess, shareMode,
NULL, OPEN_EXISTING, 0, NULL );
if ( f->fh_handle == INVALID_HANDLE_VALUE ) {
@@ -474,7 +489,11 @@
return -1;
}
- f->fh_handle = CreateFileW( widen(path).c_str(), GENERIC_WRITE,
+ std::wstring path_wide;
+ if (!android::base::UTF8ToWide(path, &path_wide)) {
+ return -1;
+ }
+ f->fh_handle = CreateFileW( path_wide.c_str(), GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
NULL );
@@ -980,7 +999,7 @@
#if (NTDDI_VERSION >= NTDDI_WINXPSP2) || (_WIN32_WINNT >= _WIN32_WINNT_WS03)
// TODO: When the Android SDK tools increases the Windows system
- // requirements >= WinXP SP2, switch to GetAddrInfoW(widen(host).c_str()).
+ // requirements >= WinXP SP2, switch to android::base::UTF8ToWide() + GetAddrInfoW().
#else
// Otherwise, keep using getaddrinfo(), or do runtime API detection
// with GetProcAddress("GetAddrInfoW").
@@ -2499,20 +2518,61 @@
//
// Code organization:
//
+// * _get_console_handle() and unix_isatty() provide console information.
// * stdin_raw_init() and stdin_raw_restore() reconfigure the console.
// * unix_read() detects console windows (as opposed to pipes, files, etc.).
// * _console_read() is the main code of the emulation.
+// Returns a console HANDLE if |fd| is a console, otherwise returns nullptr.
+// If a valid HANDLE is returned and |mode| is not null, |mode| is also filled
+// with the console mode. Requires GENERIC_READ access to the underlying HANDLE.
+static HANDLE _get_console_handle(int fd, DWORD* mode=nullptr) {
+ // First check isatty(); this is very fast and eliminates most non-console
+ // FDs, but returns 1 for both consoles and character devices like NUL.
+#pragma push_macro("isatty")
+#undef isatty
+ if (!isatty(fd)) {
+ return nullptr;
+ }
+#pragma pop_macro("isatty")
-// Read an input record from the console; one that should be processed.
-static bool _get_interesting_input_record_uncached(const HANDLE console,
- INPUT_RECORD* const input_record) {
+ // To differentiate between character devices and consoles we need to get
+ // the underlying HANDLE and use GetConsoleMode(), which is what requires
+ // GENERIC_READ permissions.
+ const intptr_t intptr_handle = _get_osfhandle(fd);
+ if (intptr_handle == -1) {
+ return nullptr;
+ }
+ const HANDLE handle = reinterpret_cast<const HANDLE>(intptr_handle);
+ DWORD temp_mode = 0;
+ if (!GetConsoleMode(handle, mode ? mode : &temp_mode)) {
+ return nullptr;
+ }
+
+ return handle;
+}
+
+// Returns a console handle if |stream| is a console, otherwise returns nullptr.
+static HANDLE _get_console_handle(FILE* const stream) {
+ const int fd = fileno(stream);
+ if (fd < 0) {
+ return nullptr;
+ }
+ return _get_console_handle(fd);
+}
+
+int unix_isatty(int fd) {
+ return _get_console_handle(fd) ? 1 : 0;
+}
+
+// Get the next KEY_EVENT_RECORD that should be processed.
+static bool _get_key_event_record(const HANDLE console, INPUT_RECORD* const input_record) {
for (;;) {
DWORD read_count = 0;
memset(input_record, 0, sizeof(*input_record));
if (!ReadConsoleInputA(console, input_record, 1, &read_count)) {
- D("_get_interesting_input_record_uncached: ReadConsoleInputA() "
- "failed: %s\n", SystemErrorCodeToString(GetLastError()).c_str());
+ D("_get_key_event_record: ReadConsoleInputA() failed: %s\n",
+ SystemErrorCodeToString(GetLastError()).c_str());
errno = EIO;
return false;
}
@@ -2538,28 +2598,6 @@
}
}
-// Cached input record (in case _console_read() is passed a buffer that doesn't
-// have enough space to fit wRepeatCount number of key sequences). A non-zero
-// wRepeatCount indicates that a record is cached.
-static INPUT_RECORD _win32_input_record;
-
-// Get the next KEY_EVENT_RECORD that should be processed.
-static KEY_EVENT_RECORD* _get_key_event_record(const HANDLE console) {
- // If nothing cached, read directly from the console until we get an
- // interesting record.
- if (_win32_input_record.Event.KeyEvent.wRepeatCount == 0) {
- if (!_get_interesting_input_record_uncached(console,
- &_win32_input_record)) {
- // There was an error, so make sure wRepeatCount is zero because
- // that signifies no cached input record.
- _win32_input_record.Event.KeyEvent.wRepeatCount = 0;
- return NULL;
- }
- }
-
- return &_win32_input_record.Event.KeyEvent;
-}
-
static __inline__ bool _is_shift_pressed(const DWORD control_key_state) {
return (control_key_state & SHIFT_PRESSED) != 0;
}
@@ -2904,16 +2942,34 @@
return len + 1;
}
-// Writes to buffer buf (of length len), returning number of bytes written or
-// -1 on error. Never returns zero because Win32 consoles are never 'closed'
-// (as far as I can tell).
+// Internal buffer to satisfy future _console_read() calls.
+static auto& g_console_input_buffer = *new std::vector<char>();
+
+// Writes to buffer buf (of length len), returning number of bytes written or -1 on error. Never
+// returns zero on console closure because Win32 consoles are never 'closed' (as far as I can tell).
static int _console_read(const HANDLE console, void* buf, size_t len) {
for (;;) {
- KEY_EVENT_RECORD* const key_event = _get_key_event_record(console);
- if (key_event == NULL) {
+ // Read of zero bytes should not block waiting for something from the console.
+ if (len == 0) {
+ return 0;
+ }
+
+ // Flush as much as possible from input buffer.
+ if (!g_console_input_buffer.empty()) {
+ const int bytes_read = std::min(len, g_console_input_buffer.size());
+ memcpy(buf, g_console_input_buffer.data(), bytes_read);
+ const auto begin = g_console_input_buffer.begin();
+ g_console_input_buffer.erase(begin, begin + bytes_read);
+ return bytes_read;
+ }
+
+ // Read from the actual console. This may block until input.
+ INPUT_RECORD input_record;
+ if (!_get_key_event_record(console, &input_record)) {
return -1;
}
+ KEY_EVENT_RECORD* const key_event = &input_record.Event.KeyEvent;
const WORD vk = key_event->wVirtualKeyCode;
const CHAR ch = key_event->uChar.AsciiChar;
const DWORD control_key_state = _normalize_altgr_control_key_state(
@@ -3091,7 +3147,12 @@
break;
case 0x32: // 2
+ case 0x33: // 3
+ case 0x34: // 4
+ case 0x35: // 5
case 0x36: // 6
+ case 0x37: // 7
+ case 0x38: // 8
case VK_OEM_MINUS: // -_
{
seqbuflen = _get_control_character(seqbuf, key_event,
@@ -3107,25 +3168,6 @@
}
break;
- case 0x33: // 3
- case 0x34: // 4
- case 0x35: // 5
- case 0x37: // 7
- case 0x38: // 8
- {
- seqbuflen = _get_control_character(seqbuf, key_event,
- control_key_state);
-
- // If Alt is pressed and it isn't Ctrl-Alt-ShiftUp, then
- // prefix with escape.
- if (_is_alt_pressed(control_key_state) &&
- !(_is_ctrl_pressed(control_key_state) &&
- !_is_shift_pressed(control_key_state))) {
- seqbuflen = _escape_prefix(seqbuf, seqbuflen);
- }
- }
- break;
-
case 0x41: // a
case 0x42: // b
case 0x43: // c
@@ -3254,103 +3296,56 @@
// event.
D("_console_read: unknown virtual key code: %d, enhanced: %s",
vk, _is_enhanced_key(control_key_state) ? "true" : "false");
- key_event->wRepeatCount = 0;
continue;
}
- int bytesRead = 0;
-
- // put output wRepeatCount times into buf/len
- while (key_event->wRepeatCount > 0) {
- if (len >= outlen) {
- // Write to buf/len
- memcpy(buf, out, outlen);
- buf = (void*)((char*)buf + outlen);
- len -= outlen;
- bytesRead += outlen;
-
- // consume the input
- --key_event->wRepeatCount;
- } else {
- // Not enough space, so just leave it in _win32_input_record
- // for a subsequent retrieval.
- if (bytesRead == 0) {
- // We didn't write anything because there wasn't enough
- // space to even write one sequence. This should never
- // happen if the caller uses sensible buffer sizes
- // (i.e. >= maximum sequence length which is probably a
- // few bytes long).
- D("_console_read: no buffer space to write one sequence; "
- "buffer: %ld, sequence: %ld\n", (long)len,
- (long)outlen);
- errno = ENOMEM;
- return -1;
- } else {
- // Stop trying to write to buf/len, just return whatever
- // we wrote so far.
- break;
- }
- }
+ // put output wRepeatCount times into g_console_input_buffer
+ while (key_event->wRepeatCount-- > 0) {
+ g_console_input_buffer.insert(g_console_input_buffer.end(), out, out + outlen);
}
- return bytesRead;
+ // Loop around and try to flush g_console_input_buffer
}
}
static DWORD _old_console_mode; // previous GetConsoleMode() result
static HANDLE _console_handle; // when set, console mode should be restored
-void stdin_raw_init(const int fd) {
- if (STDIN_FILENO == fd) {
- const HANDLE in = GetStdHandle(STD_INPUT_HANDLE);
- if ((in == INVALID_HANDLE_VALUE) || (in == NULL)) {
- return;
- }
+void stdin_raw_init() {
+ const HANDLE in = _get_console_handle(STDIN_FILENO, &_old_console_mode);
- if (GetFileType(in) != FILE_TYPE_CHAR) {
- // stdin might be a file or pipe.
- return;
- }
-
- if (!GetConsoleMode(in, &_old_console_mode)) {
- // If GetConsoleMode() fails, stdin is probably is not a console.
- return;
- }
-
- // Disable ENABLE_PROCESSED_INPUT so that Ctrl-C is read instead of
- // calling the process Ctrl-C routine (configured by
- // SetConsoleCtrlHandler()).
- // Disable ENABLE_LINE_INPUT so that input is immediately sent.
- // Disable ENABLE_ECHO_INPUT to disable local echo. Disabling this
- // flag also seems necessary to have proper line-ending processing.
- if (!SetConsoleMode(in, _old_console_mode & ~(ENABLE_PROCESSED_INPUT |
- ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT))) {
- // This really should not fail.
- D("stdin_raw_init: SetConsoleMode() failed: %s",
- SystemErrorCodeToString(GetLastError()).c_str());
- }
-
- // Once this is set, it means that stdin has been configured for
- // reading from and that the old console mode should be restored later.
- _console_handle = in;
-
- // Note that we don't need to configure C Runtime line-ending
- // translation because _console_read() does not call the C Runtime to
- // read from the console.
+ // Disable ENABLE_PROCESSED_INPUT so that Ctrl-C is read instead of
+ // calling the process Ctrl-C routine (configured by
+ // SetConsoleCtrlHandler()).
+ // Disable ENABLE_LINE_INPUT so that input is immediately sent.
+ // Disable ENABLE_ECHO_INPUT to disable local echo. Disabling this
+ // flag also seems necessary to have proper line-ending processing.
+ if (!SetConsoleMode(in, _old_console_mode & ~(ENABLE_PROCESSED_INPUT |
+ ENABLE_LINE_INPUT |
+ ENABLE_ECHO_INPUT))) {
+ // This really should not fail.
+ D("stdin_raw_init: SetConsoleMode() failed: %s",
+ SystemErrorCodeToString(GetLastError()).c_str());
}
+
+ // Once this is set, it means that stdin has been configured for
+ // reading from and that the old console mode should be restored later.
+ _console_handle = in;
+
+ // Note that we don't need to configure C Runtime line-ending
+ // translation because _console_read() does not call the C Runtime to
+ // read from the console.
}
-void stdin_raw_restore(const int fd) {
- if (STDIN_FILENO == fd) {
- if (_console_handle != NULL) {
- const HANDLE in = _console_handle;
- _console_handle = NULL; // clear state
+void stdin_raw_restore() {
+ if (_console_handle != NULL) {
+ const HANDLE in = _console_handle;
+ _console_handle = NULL; // clear state
- if (!SetConsoleMode(in, _old_console_mode)) {
- // This really should not fail.
- D("stdin_raw_restore: SetConsoleMode() failed: %s",
- SystemErrorCodeToString(GetLastError()).c_str());
- }
+ if (!SetConsoleMode(in, _old_console_mode)) {
+ // This really should not fail.
+ D("stdin_raw_restore: SetConsoleMode() failed: %s",
+ SystemErrorCodeToString(GetLastError()).c_str());
}
}
}
@@ -3366,11 +3361,8 @@
} else {
// On older versions of Windows (definitely 7, definitely not 10),
// ReadConsole() with a size >= 31367 fails, so if |fd| is a console
- // we need to limit the read size. This may also catch devices like NUL,
- // but that is OK as we just want to avoid capping pipes and files which
- // don't need size limiting. This isatty() test is very simple and quick
- // and doesn't call the OS.
- if (isatty(fd) && len > 4096) {
+ // we need to limit the read size.
+ if (len > 4096 && unix_isatty(fd)) {
len = 4096;
}
// Just call into C Runtime which can read from pipes/files and which
@@ -3431,11 +3423,11 @@
// The Choice
// ----------
//
-// The code below chooses option 3, the UTF-8 everywhere strategy. It
-// introduces narrow() which converts UTF-16 to UTF-8. This is used by the
+// The code below chooses option 3, the UTF-8 everywhere strategy. It uses
+// android::base::WideToUTF8() which converts UTF-16 to UTF-8. This is used by the
// NarrowArgs helper class that is used to convert wmain() args into UTF-8
-// args that are passed to main() at the beginning of program startup. We also
-// introduce widen() which converts from UTF-8 to UTF-16. This is used to
+// args that are passed to main() at the beginning of program startup. We also use
+// android::base::UTF8ToWide() which converts from UTF-8 to UTF-16. This is used to
// implement wrappers below that call UTF-16 OS and C Runtime APIs.
//
// Unicode console output
@@ -3465,140 +3457,17 @@
// to UTF-16 and then calls WriteConsoleW().
-// Function prototype because attributes cannot be placed on func definitions.
-static void _widen_fatal(const char *fmt, ...)
- __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 1, 2)));
-
-// A version of fatal() that does not call adb_(v)fprintf(), so it can be
-// called from those functions.
-static void _widen_fatal(const char *fmt, ...) {
- va_list ap;
- va_start(ap, fmt);
- // If (v)fprintf are macros that point to adb_(v)fprintf, when random adb
- // code calls (v)fprintf, it may end up calling adb_(v)fprintf, which then
- // calls _widen_fatal(). So then how does _widen_fatal() output a error?
- // By directly calling real C Runtime APIs that don't properly output
- // Unicode, but will be able to get a comprehendible message out. To do
- // this, make sure we don't call (v)fprintf macros by undefining them.
-#pragma push_macro("fprintf")
-#pragma push_macro("vfprintf")
-#undef fprintf
-#undef vfprintf
- fprintf(stderr, "error: ");
- vfprintf(stderr, fmt, ap);
- fprintf(stderr, "\n");
-#pragma pop_macro("vfprintf")
-#pragma pop_macro("fprintf")
- va_end(ap);
- exit(-1);
-}
-
-// TODO: Consider implementing widen() and narrow() out of std::wstring_convert
-// once libcxx is supported on Windows. Or, consider libutils/Unicode.cpp.
-
-// Convert from UTF-8 to UTF-16. A size of -1 specifies a NULL terminated
-// string. Any other size specifies the number of chars to convert, excluding
-// any NULL terminator (if you're passing an explicit size, you probably don't
-// have a NULL terminated string in the first place).
-std::wstring widen(const char* utf8, const int size) {
- // Note: Do not call SystemErrorCodeToString() from widen() because
- // SystemErrorCodeToString() calls narrow() which may call fatal() which
- // calls adb_vfprintf() which calls widen(), potentially causing infinite
- // recursion.
- const int chars_to_convert = MultiByteToWideChar(CP_UTF8, 0, utf8, size,
- NULL, 0);
- if (chars_to_convert <= 0) {
- // UTF-8 to UTF-16 should be lossless, so we don't expect this to fail.
- _widen_fatal("MultiByteToWideChar failed counting: %d, "
- "GetLastError: %lu", chars_to_convert, GetLastError());
- }
-
- std::wstring utf16;
- size_t chars_to_allocate = chars_to_convert;
- if (size == -1) {
- // chars_to_convert includes a NULL terminator, so subtract space
- // for that because resize() includes that itself.
- --chars_to_allocate;
- }
- utf16.resize(chars_to_allocate);
-
- // This uses &string[0] to get write-access to the entire string buffer
- // which may be assuming that the chars are all contiguous, but it seems
- // to work and saves us the hassle of using a temporary
- // std::vector<wchar_t>.
- const int result = MultiByteToWideChar(CP_UTF8, 0, utf8, size, &utf16[0],
- chars_to_convert);
- if (result != chars_to_convert) {
- // UTF-8 to UTF-16 should be lossless, so we don't expect this to fail.
- _widen_fatal("MultiByteToWideChar failed conversion: %d, "
- "GetLastError: %lu", result, GetLastError());
- }
-
- // If a size was passed in (size != -1), then the string is NULL terminated
- // by a NULL char that was written by std::string::resize(). If size == -1,
- // then MultiByteToWideChar() read a NULL terminator from the original
- // string and converted it to a NULL UTF-16 char in the output.
-
- return utf16;
-}
-
-// Convert a NULL terminated string from UTF-8 to UTF-16.
-std::wstring widen(const char* utf8) {
- // Pass -1 to let widen() determine the string length.
- return widen(utf8, -1);
-}
-
-// Convert from UTF-8 to UTF-16.
-std::wstring widen(const std::string& utf8) {
- return widen(utf8.c_str(), utf8.length());
-}
-
-// Convert from UTF-16 to UTF-8.
-std::string narrow(const std::wstring& utf16) {
- return narrow(utf16.c_str());
-}
-
-// Convert from UTF-16 to UTF-8.
-std::string narrow(const wchar_t* utf16) {
- // Note: Do not call SystemErrorCodeToString() from narrow() because
- // SystemErrorCodeToString() calls narrow() and we don't want potential
- // infinite recursion.
- const int chars_required = WideCharToMultiByte(CP_UTF8, 0, utf16, -1, NULL,
- 0, NULL, NULL);
- if (chars_required <= 0) {
- // UTF-16 to UTF-8 should be lossless, so we don't expect this to fail.
- fatal("WideCharToMultiByte failed counting: %d, GetLastError: %lu",
- chars_required, GetLastError());
- }
-
- std::string utf8;
- // Subtract space for the NULL terminator because resize() includes
- // that itself. Note that this could potentially throw a std::bad_alloc
- // exception.
- utf8.resize(chars_required - 1);
-
- // This uses &string[0] to get write-access to the entire string buffer
- // which may be assuming that the chars are all contiguous, but it seems
- // to work and saves us the hassle of using a temporary
- // std::vector<char>.
- const int result = WideCharToMultiByte(CP_UTF8, 0, utf16, -1, &utf8[0],
- chars_required, NULL, NULL);
- if (result != chars_required) {
- // UTF-16 to UTF-8 should be lossless, so we don't expect this to fail.
- fatal("WideCharToMultiByte failed conversion: %d, GetLastError: %lu",
- result, GetLastError());
- }
-
- return utf8;
-}
-
// Constructor for helper class to convert wmain() UTF-16 args to UTF-8 to
// be passed to main().
NarrowArgs::NarrowArgs(const int argc, wchar_t** const argv) {
narrow_args = new char*[argc + 1];
for (int i = 0; i < argc; ++i) {
- narrow_args[i] = strdup(narrow(argv[i]).c_str());
+ std::string arg_narrow;
+ if (!android::base::WideToUTF8(argv[i], &arg_narrow)) {
+ fatal_errno("cannot convert argument from UTF-16 to UTF-8");
+ }
+ narrow_args[i] = strdup(arg_narrow.c_str());
}
narrow_args[argc] = nullptr; // terminate
}
@@ -3614,20 +3483,24 @@
}
int unix_open(const char* path, int options, ...) {
+ std::wstring path_wide;
+ if (!android::base::UTF8ToWide(path, &path_wide)) {
+ return -1;
+ }
if ((options & O_CREAT) == 0) {
- return _wopen(widen(path).c_str(), options);
+ return _wopen(path_wide.c_str(), options);
} else {
int mode;
va_list args;
va_start(args, options);
mode = va_arg(args, int);
va_end(args);
- return _wopen(widen(path).c_str(), options, mode);
+ return _wopen(path_wide.c_str(), options, mode);
}
}
// Version of stat() that takes a UTF-8 path.
-int adb_stat(const char* f, struct adb_stat* s) {
+int adb_stat(const char* path, struct adb_stat* s) {
#pragma push_macro("wstat")
// This definition of wstat seems to be missing from <sys/stat.h>.
#if defined(_FILE_OFFSET_BITS) && (_FILE_OFFSET_BITS == 64)
@@ -3640,17 +3513,27 @@
// <sys/stat.h> has a function prototype for wstat() that should be available.
#endif
- return wstat(widen(f).c_str(), s);
+ std::wstring path_wide;
+ if (!android::base::UTF8ToWide(path, &path_wide)) {
+ return -1;
+ }
+
+ return wstat(path_wide.c_str(), s);
#pragma pop_macro("wstat")
}
// Version of opendir() that takes a UTF-8 path.
-DIR* adb_opendir(const char* name) {
+DIR* adb_opendir(const char* path) {
+ std::wstring path_wide;
+ if (!android::base::UTF8ToWide(path, &path_wide)) {
+ return nullptr;
+ }
+
// Just cast _WDIR* to DIR*. This doesn't work if the caller reads any of
// the fields, but right now all the callers treat the structure as
// opaque.
- return reinterpret_cast<DIR*>(_wopendir(widen(name).c_str()));
+ return reinterpret_cast<DIR*>(_wopendir(path_wide.c_str()));
}
// Version of readdir() that returns UTF-8 paths.
@@ -3660,8 +3543,12 @@
if (went == nullptr) {
return nullptr;
}
+
// Convert from UTF-16 to UTF-8.
- const std::string name_utf8(narrow(went->d_name));
+ std::string name_utf8;
+ if (!android::base::WideToUTF8(went->d_name, &name_utf8)) {
+ return nullptr;
+ }
// Cast the _wdirent* to dirent* and overwrite the d_name field (which has
// space for UTF-16 wchar_t's) with UTF-8 char's.
@@ -3693,7 +3580,10 @@
// Version of unlink() that takes a UTF-8 path.
int adb_unlink(const char* path) {
- const std::wstring wpath(widen(path));
+ std::wstring wpath;
+ if (!android::base::UTF8ToWide(path, &wpath)) {
+ return -1;
+ }
int rc = _wunlink(wpath.c_str());
@@ -3709,63 +3599,47 @@
// Version of mkdir() that takes a UTF-8 path.
int adb_mkdir(const std::string& path, int mode) {
- return _wmkdir(widen(path.c_str()).c_str());
+ std::wstring path_wide;
+ if (!android::base::UTF8ToWide(path, &path_wide)) {
+ return -1;
+ }
+
+ return _wmkdir(path_wide.c_str());
}
// Version of utime() that takes a UTF-8 path.
int adb_utime(const char* path, struct utimbuf* u) {
+ std::wstring path_wide;
+ if (!android::base::UTF8ToWide(path, &path_wide)) {
+ return -1;
+ }
+
static_assert(sizeof(struct utimbuf) == sizeof(struct _utimbuf),
"utimbuf and _utimbuf should be the same size because they both "
"contain the same types, namely time_t");
- return _wutime(widen(path).c_str(), reinterpret_cast<struct _utimbuf*>(u));
+ return _wutime(path_wide.c_str(), reinterpret_cast<struct _utimbuf*>(u));
}
// Version of chmod() that takes a UTF-8 path.
int adb_chmod(const char* path, int mode) {
- return _wchmod(widen(path).c_str(), mode);
-}
-
-// Internal function to get a Win32 console HANDLE from a C Runtime FILE*.
-static HANDLE _get_console_handle(FILE* const stream) {
- // Get a C Runtime file descriptor number from the FILE* structure.
- const int fd = fileno(stream);
- if (fd < 0) {
- return NULL;
+ std::wstring path_wide;
+ if (!android::base::UTF8ToWide(path, &path_wide)) {
+ return -1;
}
- // If it is not a "character device", it is probably a file and not a
- // console. Do this check early because it is probably cheap. Still do more
- // checks after this since there are devices that pass this test, but are
- // not a console, such as NUL, the Windows /dev/null equivalent (I think).
- if (!isatty(fd)) {
- return NULL;
- }
-
- // Given a C Runtime file descriptor number, get the underlying OS
- // file handle.
- const intptr_t osfh = _get_osfhandle(fd);
- if (osfh == -1) {
- return NULL;
- }
-
- const HANDLE h = reinterpret_cast<const HANDLE>(osfh);
-
- DWORD old_mode = 0;
- if (!GetConsoleMode(h, &old_mode)) {
- return NULL;
- }
-
- // If GetConsoleMode() was successful, assume this is a console.
- return h;
+ return _wchmod(path_wide.c_str(), mode);
}
// Internal helper function to write UTF-8 bytes to a console. Returns -1
// on error.
static int _console_write_utf8(const char* buf, size_t size, FILE* stream,
HANDLE console) {
- // Convert from UTF-8 to UTF-16.
+ std::wstring output;
+
+ // Try to convert from data that might be UTF-8 to UTF-16, ignoring errors.
+ // Data might not be UTF-8 if the user cat's random data, runs dmesg, etc.
// This could throw std::bad_alloc.
- const std::wstring output(widen(buf, size));
+ (void)android::base::UTF8ToWide(buf, size, &output);
// Note that this does not do \n => \r\n translation because that
// doesn't seem necessary for the Windows console. For the Windows
@@ -3915,8 +3789,18 @@
// Version of fopen() that takes a UTF-8 filename and can access a file with
// a Unicode filename.
-FILE* adb_fopen(const char* f, const char* m) {
- return _wfopen(widen(f).c_str(), widen(m).c_str());
+FILE* adb_fopen(const char* path, const char* mode) {
+ std::wstring path_wide;
+ if (!android::base::UTF8ToWide(path, &path_wide)) {
+ return nullptr;
+ }
+
+ std::wstring mode_wide;
+ if (!android::base::UTF8ToWide(mode, &mode_wide)) {
+ return nullptr;
+ }
+
+ return _wfopen(path_wide.c_str(), mode_wide.c_str());
}
// Return a lowercase version of the argument. Uses C Runtime tolower() on
@@ -3947,7 +3831,7 @@
// currently updated if putenv, setenv, unsetenv are called. Note that no
// thread synchronization is done, but we're called early enough in
// single-threaded startup that things work ok.
-static std::unordered_map<std::string, char*> g_environ_utf8;
+static auto& g_environ_utf8 = *new std::unordered_map<std::string, char*>();
// Make sure that shadow UTF-8 environment variables are setup.
static void _ensure_env_setup() {
@@ -3976,15 +3860,27 @@
continue;
}
+ // If we encounter an error converting UTF-16, don't error-out on account of a single env
+ // var because the program might never even read this particular variable.
+ std::string name_utf8;
+ if (!android::base::WideToUTF8(*env, equal - *env, &name_utf8)) {
+ continue;
+ }
+
// Store lowercase name so that we can do case-insensitive searches.
- const std::string name_utf8(ToLower(narrow(
- std::wstring(*env, equal - *env))));
- char* const value_utf8 = strdup(narrow(equal + 1).c_str());
+ name_utf8 = ToLower(name_utf8);
+
+ std::string value_utf8;
+ if (!android::base::WideToUTF8(equal + 1, &value_utf8)) {
+ continue;
+ }
+
+ char* const value_dup = strdup(value_utf8.c_str());
// Don't overwrite a previus env var with the same name. In reality,
// the system probably won't let two env vars with the same name exist
// in _wenviron.
- g_environ_utf8.insert({name_utf8, value_utf8});
+ g_environ_utf8.insert({name_utf8, value_dup});
}
}
@@ -4010,10 +3906,15 @@
return nullptr;
}
- const std::string buf_utf8(narrow(wbuf));
+ std::string buf_utf8;
+ const bool narrow_result = android::base::WideToUTF8(wbuf, &buf_utf8);
free(wbuf);
wbuf = nullptr;
+ if (!narrow_result) {
+ return nullptr;
+ }
+
// If size was specified, make sure all the chars will fit.
if (size != 0) {
if (size < static_cast<int>(buf_utf8.length() + 1)) {
diff --git a/adb/sysdeps_win32_test.cpp b/adb/sysdeps_win32_test.cpp
index 66d1ba8..81923cb 100755
--- a/adb/sysdeps_win32_test.cpp
+++ b/adb/sysdeps_win32_test.cpp
@@ -18,6 +18,8 @@
#include "sysdeps.h"
+#include <android-base/test_utils.h>
+
TEST(sysdeps_win32, adb_getenv) {
// Insert all test env vars before first call to adb_getenv() which will
// read the env var block only once.
@@ -93,3 +95,45 @@
// adb_strerror() returns.
TestAdbStrError(ECONNRESET, "Connection reset by peer");
}
+
+TEST(sysdeps_win32, unix_isatty) {
+ // stdin and stdout should be consoles. Use CONIN$ and CONOUT$ special files
+ // so that we can test this even if stdin/stdout have been redirected. Read
+ // permissions are required for unix_isatty().
+ int conin_fd = unix_open("CONIN$", O_RDONLY);
+ int conout_fd = unix_open("CONOUT$", O_RDWR);
+ for (const int fd : {conin_fd, conout_fd}) {
+ EXPECT_TRUE(fd >= 0);
+ EXPECT_EQ(1, unix_isatty(fd));
+ EXPECT_EQ(0, unix_close(fd));
+ }
+
+ // nul returns 1 from isatty(), make sure unix_isatty() corrects that.
+ for (auto flags : {O_RDONLY, O_RDWR}) {
+ int nul_fd = unix_open("nul", flags);
+ EXPECT_TRUE(nul_fd >= 0);
+ EXPECT_EQ(0, unix_isatty(nul_fd));
+ EXPECT_EQ(0, unix_close(nul_fd));
+ }
+
+ // Check a real file, both read-write and read-only.
+ TemporaryFile temp_file;
+ EXPECT_TRUE(temp_file.fd >= 0);
+ EXPECT_EQ(0, unix_isatty(temp_file.fd));
+
+ int temp_file_ro_fd = unix_open(temp_file.path, O_RDONLY);
+ EXPECT_TRUE(temp_file_ro_fd >= 0);
+ EXPECT_EQ(0, unix_isatty(temp_file_ro_fd));
+ EXPECT_EQ(0, unix_close(temp_file_ro_fd));
+
+ // Check a real OS pipe.
+ int pipe_fds[2];
+ EXPECT_EQ(0, _pipe(pipe_fds, 64, _O_BINARY));
+ EXPECT_EQ(0, unix_isatty(pipe_fds[0]));
+ EXPECT_EQ(0, unix_isatty(pipe_fds[1]));
+ EXPECT_EQ(0, _close(pipe_fds[0]));
+ EXPECT_EQ(0, _close(pipe_fds[1]));
+
+ // Make sure an invalid FD is handled correctly.
+ EXPECT_EQ(0, unix_isatty(-1));
+}
diff --git a/adb/test_device.py b/adb/test_device.py
new file mode 100644
index 0000000..afc061a
--- /dev/null
+++ b/adb/test_device.py
@@ -0,0 +1,985 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+from __future__ import print_function
+
+import contextlib
+import hashlib
+import os
+import posixpath
+import random
+import re
+import shlex
+import shutil
+import signal
+import socket
+import string
+import subprocess
+import sys
+import tempfile
+import unittest
+
+import mock
+
+import adb
+
+
+def requires_root(func):
+ def wrapper(self, *args):
+ if self.device.get_prop('ro.debuggable') != '1':
+ raise unittest.SkipTest('requires rootable build')
+
+ was_root = self.device.shell(['id', '-un'])[0].strip() == 'root'
+ if not was_root:
+ self.device.root()
+ self.device.wait()
+
+ try:
+ func(self, *args)
+ finally:
+ if not was_root:
+ self.device.unroot()
+ self.device.wait()
+
+ return wrapper
+
+
+def requires_non_root(func):
+ def wrapper(self, *args):
+ was_root = self.device.shell(['id', '-un'])[0].strip() == 'root'
+ if was_root:
+ self.device.unroot()
+ self.device.wait()
+
+ try:
+ func(self, *args)
+ finally:
+ if was_root:
+ self.device.root()
+ self.device.wait()
+
+ return wrapper
+
+
+class GetDeviceTest(unittest.TestCase):
+ def setUp(self):
+ self.android_serial = os.getenv('ANDROID_SERIAL')
+ if 'ANDROID_SERIAL' in os.environ:
+ del os.environ['ANDROID_SERIAL']
+
+ def tearDown(self):
+ if self.android_serial is not None:
+ os.environ['ANDROID_SERIAL'] = self.android_serial
+ else:
+ if 'ANDROID_SERIAL' in os.environ:
+ del os.environ['ANDROID_SERIAL']
+
+ @mock.patch('adb.device.get_devices')
+ def test_explicit(self, mock_get_devices):
+ mock_get_devices.return_value = ['foo', 'bar']
+ device = adb.get_device('foo')
+ self.assertEqual(device.serial, 'foo')
+
+ @mock.patch('adb.device.get_devices')
+ def test_from_env(self, mock_get_devices):
+ mock_get_devices.return_value = ['foo', 'bar']
+ os.environ['ANDROID_SERIAL'] = 'foo'
+ device = adb.get_device()
+ self.assertEqual(device.serial, 'foo')
+
+ @mock.patch('adb.device.get_devices')
+ def test_arg_beats_env(self, mock_get_devices):
+ mock_get_devices.return_value = ['foo', 'bar']
+ os.environ['ANDROID_SERIAL'] = 'bar'
+ device = adb.get_device('foo')
+ self.assertEqual(device.serial, 'foo')
+
+ @mock.patch('adb.device.get_devices')
+ def test_no_such_device(self, mock_get_devices):
+ mock_get_devices.return_value = ['foo', 'bar']
+ self.assertRaises(adb.DeviceNotFoundError, adb.get_device, ['baz'])
+
+ os.environ['ANDROID_SERIAL'] = 'baz'
+ self.assertRaises(adb.DeviceNotFoundError, adb.get_device)
+
+ @mock.patch('adb.device.get_devices')
+ def test_unique_device(self, mock_get_devices):
+ mock_get_devices.return_value = ['foo']
+ device = adb.get_device()
+ self.assertEqual(device.serial, 'foo')
+
+ @mock.patch('adb.device.get_devices')
+ def test_no_unique_device(self, mock_get_devices):
+ mock_get_devices.return_value = ['foo', 'bar']
+ self.assertRaises(adb.NoUniqueDeviceError, adb.get_device)
+
+
+class DeviceTest(unittest.TestCase):
+ def setUp(self):
+ self.device = adb.get_device()
+
+
+class ForwardReverseTest(DeviceTest):
+ def _test_no_rebind(self, description, direction_list, direction,
+ direction_no_rebind, direction_remove_all):
+ msg = direction_list()
+ self.assertEqual('', msg.strip(),
+ description + ' list must be empty to run this test.')
+
+ # Use --no-rebind with no existing binding
+ direction_no_rebind('tcp:5566', 'tcp:6655')
+ msg = direction_list()
+ self.assertTrue(re.search(r'tcp:5566.+tcp:6655', msg))
+
+ # Use --no-rebind with existing binding
+ with self.assertRaises(subprocess.CalledProcessError):
+ direction_no_rebind('tcp:5566', 'tcp:6677')
+ msg = direction_list()
+ self.assertFalse(re.search(r'tcp:5566.+tcp:6677', msg))
+ self.assertTrue(re.search(r'tcp:5566.+tcp:6655', msg))
+
+ # Use the absence of --no-rebind with existing binding
+ direction('tcp:5566', 'tcp:6677')
+ msg = direction_list()
+ self.assertFalse(re.search(r'tcp:5566.+tcp:6655', msg))
+ self.assertTrue(re.search(r'tcp:5566.+tcp:6677', msg))
+
+ direction_remove_all()
+ msg = direction_list()
+ self.assertEqual('', msg.strip())
+
+ def test_forward_no_rebind(self):
+ self._test_no_rebind('forward', self.device.forward_list,
+ self.device.forward, self.device.forward_no_rebind,
+ self.device.forward_remove_all)
+
+ def test_reverse_no_rebind(self):
+ self._test_no_rebind('reverse', self.device.reverse_list,
+ self.device.reverse, self.device.reverse_no_rebind,
+ self.device.reverse_remove_all)
+
+ def test_forward(self):
+ msg = self.device.forward_list()
+ self.assertEqual('', msg.strip(),
+ 'Forwarding list must be empty to run this test.')
+ self.device.forward('tcp:5566', 'tcp:6655')
+ msg = self.device.forward_list()
+ self.assertTrue(re.search(r'tcp:5566.+tcp:6655', msg))
+ self.device.forward('tcp:7788', 'tcp:8877')
+ msg = self.device.forward_list()
+ self.assertTrue(re.search(r'tcp:5566.+tcp:6655', msg))
+ self.assertTrue(re.search(r'tcp:7788.+tcp:8877', msg))
+ self.device.forward_remove('tcp:5566')
+ msg = self.device.forward_list()
+ self.assertFalse(re.search(r'tcp:5566.+tcp:6655', msg))
+ self.assertTrue(re.search(r'tcp:7788.+tcp:8877', msg))
+ self.device.forward_remove_all()
+ msg = self.device.forward_list()
+ self.assertEqual('', msg.strip())
+
+ def test_reverse(self):
+ msg = self.device.reverse_list()
+ self.assertEqual('', msg.strip(),
+ 'Reverse forwarding list must be empty to run this test.')
+ self.device.reverse('tcp:5566', 'tcp:6655')
+ msg = self.device.reverse_list()
+ self.assertTrue(re.search(r'tcp:5566.+tcp:6655', msg))
+ self.device.reverse('tcp:7788', 'tcp:8877')
+ msg = self.device.reverse_list()
+ self.assertTrue(re.search(r'tcp:5566.+tcp:6655', msg))
+ self.assertTrue(re.search(r'tcp:7788.+tcp:8877', msg))
+ self.device.reverse_remove('tcp:5566')
+ msg = self.device.reverse_list()
+ self.assertFalse(re.search(r'tcp:5566.+tcp:6655', msg))
+ self.assertTrue(re.search(r'tcp:7788.+tcp:8877', msg))
+ self.device.reverse_remove_all()
+ msg = self.device.reverse_list()
+ self.assertEqual('', msg.strip())
+
+ # Note: If you run this test when adb connect'd to a physical device over
+ # TCP, it will fail in adb reverse due to https://code.google.com/p/android/issues/detail?id=189821
+ def test_forward_reverse_echo(self):
+ """Send data through adb forward and read it back via adb reverse"""
+ forward_port = 12345
+ reverse_port = forward_port + 1
+ forward_spec = "tcp:" + str(forward_port)
+ reverse_spec = "tcp:" + str(reverse_port)
+ forward_setup = False
+ reverse_setup = False
+
+ try:
+ # listen on localhost:forward_port, connect to remote:forward_port
+ self.device.forward(forward_spec, forward_spec)
+ forward_setup = True
+ # listen on remote:forward_port, connect to localhost:reverse_port
+ self.device.reverse(forward_spec, reverse_spec)
+ reverse_setup = True
+
+ listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ with contextlib.closing(listener):
+ # Use SO_REUSEADDR so that 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)
+
+ # Listen on localhost:reverse_port before connecting to
+ # localhost:forward_port because that will cause adb to connect
+ # back to localhost:reverse_port.
+ listener.bind(('127.0.0.1', reverse_port))
+ listener.listen(4)
+
+ client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ with contextlib.closing(client):
+ # Connect to the listener.
+ client.connect(('127.0.0.1', forward_port))
+
+ # Accept the client connection.
+ accepted_connection, addr = listener.accept()
+ with contextlib.closing(accepted_connection) as server:
+ data = 'hello'
+
+ # Send data into the port setup by adb forward.
+ client.sendall(data)
+ # Explicitly close() so that server gets EOF.
+ client.close()
+
+ # Verify that the data came back via adb reverse.
+ self.assertEqual(data, server.makefile().read())
+ finally:
+ if reverse_setup:
+ self.device.reverse_remove(forward_spec)
+ if forward_setup:
+ self.device.forward_remove(forward_spec)
+
+
+class ShellTest(DeviceTest):
+ def _interactive_shell(self, shell_args, input):
+ """Runs an interactive adb shell.
+
+ Args:
+ shell_args: List of string arguments to `adb shell`.
+ input: String input to send to the interactive shell.
+
+ Returns:
+ The remote exit code.
+
+ Raises:
+ unittest.SkipTest: The device doesn't support exit codes.
+ """
+ if self.device.SHELL_PROTOCOL_FEATURE not in self.device.features:
+ raise unittest.SkipTest('exit codes are unavailable on this device')
+
+ proc = subprocess.Popen(
+ self.device.adb_cmd + ['shell'] + shell_args,
+ stdin=subprocess.PIPE, stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ # Closing host-side stdin doesn't trigger a PTY shell to exit so we need
+ # to explicitly add an exit command to close the session from the device
+ # side, plus the necessary newline to complete the interactive command.
+ proc.communicate(input + '; exit\n')
+ return proc.returncode
+
+ def test_cat(self):
+ """Check that we can at least cat a file."""
+ out = self.device.shell(['cat', '/proc/uptime'])[0].strip()
+ elements = out.split()
+ self.assertEqual(len(elements), 2)
+
+ uptime, idle = elements
+ self.assertGreater(float(uptime), 0.0)
+ self.assertGreater(float(idle), 0.0)
+
+ def test_throws_on_failure(self):
+ self.assertRaises(adb.ShellError, self.device.shell, ['false'])
+
+ def test_output_not_stripped(self):
+ out = self.device.shell(['echo', 'foo'])[0]
+ self.assertEqual(out, 'foo' + self.device.linesep)
+
+ def test_shell_nocheck_failure(self):
+ rc, out, _ = self.device.shell_nocheck(['false'])
+ self.assertNotEqual(rc, 0)
+ self.assertEqual(out, '')
+
+ def test_shell_nocheck_output_not_stripped(self):
+ rc, out, _ = self.device.shell_nocheck(['echo', 'foo'])
+ self.assertEqual(rc, 0)
+ self.assertEqual(out, 'foo' + self.device.linesep)
+
+ def test_can_distinguish_tricky_results(self):
+ # If result checking on ADB shell is naively implemented as
+ # `adb shell <cmd>; echo $?`, we would be unable to distinguish the
+ # output from the result for a cmd of `echo -n 1`.
+ rc, out, _ = self.device.shell_nocheck(['echo', '-n', '1'])
+ self.assertEqual(rc, 0)
+ self.assertEqual(out, '1')
+
+ def test_line_endings(self):
+ """Ensure that line ending translation is not happening in the pty.
+
+ Bug: http://b/19735063
+ """
+ output = self.device.shell(['uname'])[0]
+ self.assertEqual(output, 'Linux' + self.device.linesep)
+
+ def test_pty_logic(self):
+ """Tests that a PTY is allocated when it should be.
+
+ PTY allocation behavior should match ssh; some behavior requires
+ a terminal stdin to test so this test will be skipped if stdin
+ is not a terminal.
+ """
+ if self.device.SHELL_PROTOCOL_FEATURE not in self.device.features:
+ raise unittest.SkipTest('PTY arguments unsupported on this device')
+ if not os.isatty(sys.stdin.fileno()):
+ raise unittest.SkipTest('PTY tests require stdin terminal')
+
+ def check_pty(args):
+ """Checks adb shell PTY allocation.
+
+ Tests |args| for terminal and non-terminal stdin.
+
+ Args:
+ args: -Tt args in a list (e.g. ['-t', '-t']).
+
+ Returns:
+ A tuple (<terminal>, <non-terminal>). True indicates
+ the corresponding shell allocated a remote PTY.
+ """
+ test_cmd = self.device.adb_cmd + ['shell'] + args + ['[ -t 0 ]']
+
+ terminal = subprocess.Popen(
+ test_cmd, stdin=None,
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ terminal.communicate()
+
+ non_terminal = subprocess.Popen(
+ test_cmd, stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ non_terminal.communicate()
+
+ return (terminal.returncode == 0, non_terminal.returncode == 0)
+
+ # -T: never allocate PTY.
+ self.assertEqual((False, False), check_pty(['-T']))
+
+ # No args: PTY only if stdin is a terminal and shell is interactive,
+ # which is difficult to reliably test from a script.
+ self.assertEqual((False, False), check_pty([]))
+
+ # -t: PTY if stdin is a terminal.
+ self.assertEqual((True, False), check_pty(['-t']))
+
+ # -t -t: always allocate PTY.
+ self.assertEqual((True, True), check_pty(['-t', '-t']))
+
+ def test_shell_protocol(self):
+ """Tests the shell protocol on the device.
+
+ If the device supports shell protocol, this gives us the ability
+ to separate stdout/stderr and return the exit code directly.
+
+ Bug: http://b/19734861
+ """
+ if self.device.SHELL_PROTOCOL_FEATURE not in self.device.features:
+ raise unittest.SkipTest('shell protocol unsupported on this device')
+
+ # Shell protocol should be used by default.
+ result = self.device.shell_nocheck(
+ shlex.split('echo foo; echo bar >&2; exit 17'))
+ self.assertEqual(17, result[0])
+ self.assertEqual('foo' + self.device.linesep, result[1])
+ self.assertEqual('bar' + self.device.linesep, result[2])
+
+ self.assertEqual(17, self._interactive_shell([], 'exit 17'))
+
+ # -x flag should disable shell protocol.
+ result = self.device.shell_nocheck(
+ shlex.split('-x echo foo; echo bar >&2; exit 17'))
+ self.assertEqual(0, result[0])
+ self.assertEqual('foo{0}bar{0}'.format(self.device.linesep), result[1])
+ self.assertEqual('', result[2])
+
+ self.assertEqual(0, self._interactive_shell(['-x'], 'exit 17'))
+
+ def test_non_interactive_sigint(self):
+ """Tests that SIGINT in a non-interactive shell kills the process.
+
+ This requires the shell protocol in order to detect the broken
+ pipe; raw data transfer mode will only see the break once the
+ subprocess tries to read or write.
+
+ Bug: http://b/23825725
+ """
+ if self.device.SHELL_PROTOCOL_FEATURE not in self.device.features:
+ raise unittest.SkipTest('shell protocol unsupported on this device')
+
+ # Start a long-running process.
+ sleep_proc = subprocess.Popen(
+ self.device.adb_cmd + shlex.split('shell echo $$; sleep 60'),
+ stdin=subprocess.PIPE, stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT)
+ remote_pid = sleep_proc.stdout.readline().strip()
+ self.assertIsNone(sleep_proc.returncode, 'subprocess terminated early')
+ proc_query = shlex.split('ps {0} | grep {0}'.format(remote_pid))
+
+ # Verify that the process is running, send signal, verify it stopped.
+ self.device.shell(proc_query)
+ os.kill(sleep_proc.pid, signal.SIGINT)
+ sleep_proc.communicate()
+ self.assertEqual(1, self.device.shell_nocheck(proc_query)[0],
+ 'subprocess failed to terminate')
+
+ def test_non_interactive_stdin(self):
+ """Tests that non-interactive shells send stdin."""
+ if self.device.SHELL_PROTOCOL_FEATURE not in self.device.features:
+ raise unittest.SkipTest('non-interactive stdin unsupported '
+ 'on this device')
+
+ # Test both small and large inputs.
+ small_input = 'foo'
+ large_input = '\n'.join(c * 100 for c in (string.ascii_letters +
+ string.digits))
+
+ for input in (small_input, large_input):
+ proc = subprocess.Popen(self.device.adb_cmd + ['shell', 'cat'],
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ stdout, stderr = proc.communicate(input)
+ self.assertEqual(input.splitlines(), stdout.splitlines())
+ self.assertEqual('', stderr)
+
+
+class ArgumentEscapingTest(DeviceTest):
+ def test_shell_escaping(self):
+ """Make sure that argument escaping is somewhat sane."""
+
+ # http://b/19734868
+ # Note that this actually matches ssh(1)'s behavior --- it's
+ # converted to `sh -c echo hello; echo world` which sh interprets
+ # as `sh -c echo` (with an argument to that shell of "hello"),
+ # and then `echo world` back in the first shell.
+ result = self.device.shell(
+ shlex.split("sh -c 'echo hello; echo world'"))[0]
+ result = result.splitlines()
+ self.assertEqual(['', 'world'], result)
+ # If you really wanted "hello" and "world", here's what you'd do:
+ result = self.device.shell(
+ shlex.split(r'echo hello\;echo world'))[0].splitlines()
+ self.assertEqual(['hello', 'world'], result)
+
+ # http://b/15479704
+ result = self.device.shell(shlex.split("'true && echo t'"))[0].strip()
+ self.assertEqual('t', result)
+ result = self.device.shell(
+ shlex.split("sh -c 'true && echo t'"))[0].strip()
+ self.assertEqual('t', result)
+
+ # http://b/20564385
+ result = self.device.shell(shlex.split('FOO=a BAR=b echo t'))[0].strip()
+ self.assertEqual('t', result)
+ result = self.device.shell(
+ shlex.split(r'echo -n 123\;uname'))[0].strip()
+ self.assertEqual('123Linux', result)
+
+ def test_install_argument_escaping(self):
+ """Make sure that install argument escaping works."""
+ # http://b/20323053, http://b/3090932.
+ for file_suffix in ('-text;ls;1.apk', "-Live Hold'em.apk"):
+ tf = tempfile.NamedTemporaryFile('wb', suffix=file_suffix,
+ delete=False)
+ tf.close()
+
+ # Installing bogus .apks fails if the device supports exit codes.
+ try:
+ output = self.device.install(tf.name)
+ except subprocess.CalledProcessError as e:
+ output = e.output
+
+ self.assertIn(file_suffix, output)
+ os.remove(tf.name)
+
+
+class RootUnrootTest(DeviceTest):
+ def _test_root(self):
+ message = self.device.root()
+ if 'adbd cannot run as root in production builds' in message:
+ return
+ self.device.wait()
+ self.assertEqual('root', self.device.shell(['id', '-un'])[0].strip())
+
+ def _test_unroot(self):
+ self.device.unroot()
+ self.device.wait()
+ self.assertEqual('shell', self.device.shell(['id', '-un'])[0].strip())
+
+ def test_root_unroot(self):
+ """Make sure that adb root and adb unroot work, using id(1)."""
+ if self.device.get_prop('ro.debuggable') != '1':
+ raise unittest.SkipTest('requires rootable build')
+
+ original_user = self.device.shell(['id', '-un'])[0].strip()
+ try:
+ if original_user == 'root':
+ self._test_unroot()
+ self._test_root()
+ elif original_user == 'shell':
+ self._test_root()
+ self._test_unroot()
+ finally:
+ if original_user == 'root':
+ self.device.root()
+ else:
+ self.device.unroot()
+ self.device.wait()
+
+
+class TcpIpTest(DeviceTest):
+ def test_tcpip_failure_raises(self):
+ """adb tcpip requires a port.
+
+ Bug: http://b/22636927
+ """
+ self.assertRaises(
+ subprocess.CalledProcessError, self.device.tcpip, '')
+ self.assertRaises(
+ subprocess.CalledProcessError, self.device.tcpip, 'foo')
+
+
+class SystemPropertiesTest(DeviceTest):
+ def test_get_prop(self):
+ self.assertEqual(self.device.get_prop('init.svc.adbd'), 'running')
+
+ @requires_root
+ def test_set_prop(self):
+ prop_name = 'foo.bar'
+ self.device.shell(['setprop', prop_name, '""'])
+
+ self.device.set_prop(prop_name, 'qux')
+ self.assertEqual(
+ self.device.shell(['getprop', prop_name])[0].strip(), 'qux')
+
+
+def compute_md5(string):
+ hsh = hashlib.md5()
+ hsh.update(string)
+ return hsh.hexdigest()
+
+
+def get_md5_prog(device):
+ """Older platforms (pre-L) had the name md5 rather than md5sum."""
+ try:
+ device.shell(['md5sum', '/proc/uptime'])
+ return 'md5sum'
+ except adb.ShellError:
+ return 'md5'
+
+
+class HostFile(object):
+ def __init__(self, handle, checksum):
+ self.handle = handle
+ self.checksum = checksum
+ self.full_path = handle.name
+ self.base_name = os.path.basename(self.full_path)
+
+
+class DeviceFile(object):
+ def __init__(self, checksum, full_path):
+ self.checksum = checksum
+ self.full_path = full_path
+ self.base_name = posixpath.basename(self.full_path)
+
+
+def make_random_host_files(in_dir, num_files):
+ min_size = 1 * (1 << 10)
+ max_size = 16 * (1 << 10)
+
+ files = []
+ for _ in xrange(num_files):
+ file_handle = tempfile.NamedTemporaryFile(dir=in_dir, delete=False)
+
+ size = random.randrange(min_size, max_size, 1024)
+ rand_str = os.urandom(size)
+ file_handle.write(rand_str)
+ file_handle.flush()
+ file_handle.close()
+
+ md5 = compute_md5(rand_str)
+ files.append(HostFile(file_handle, md5))
+ return files
+
+
+def make_random_device_files(device, in_dir, num_files, prefix='device_tmpfile'):
+ min_size = 1 * (1 << 10)
+ max_size = 16 * (1 << 10)
+
+ files = []
+ for file_num in xrange(num_files):
+ size = random.randrange(min_size, max_size, 1024)
+
+ base_name = prefix + str(file_num)
+ full_path = posixpath.join(in_dir, base_name)
+
+ device.shell(['dd', 'if=/dev/urandom', 'of={}'.format(full_path),
+ 'bs={}'.format(size), 'count=1'])
+ dev_md5, _ = device.shell([get_md5_prog(device), full_path])[0].split()
+
+ files.append(DeviceFile(dev_md5, full_path))
+ return files
+
+
+class FileOperationsTest(DeviceTest):
+ SCRATCH_DIR = '/data/local/tmp'
+ DEVICE_TEMP_FILE = SCRATCH_DIR + '/adb_test_file'
+ DEVICE_TEMP_DIR = SCRATCH_DIR + '/adb_test_dir'
+
+ def _verify_remote(self, checksum, remote_path):
+ dev_md5, _ = self.device.shell([get_md5_prog(self.device),
+ remote_path])[0].split()
+ self.assertEqual(checksum, dev_md5)
+
+ def _verify_local(self, checksum, local_path):
+ with open(local_path, 'rb') as host_file:
+ host_md5 = compute_md5(host_file.read())
+ self.assertEqual(host_md5, checksum)
+
+ def test_push(self):
+ """Push a randomly generated file to specified device."""
+ kbytes = 512
+ tmp = tempfile.NamedTemporaryFile(mode='wb', delete=False)
+ rand_str = os.urandom(1024 * kbytes)
+ tmp.write(rand_str)
+ tmp.close()
+
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_FILE])
+ self.device.push(local=tmp.name, remote=self.DEVICE_TEMP_FILE)
+
+ self._verify_remote(compute_md5(rand_str), self.DEVICE_TEMP_FILE)
+ self.device.shell(['rm', '-f', self.DEVICE_TEMP_FILE])
+
+ os.remove(tmp.name)
+
+ def test_push_dir(self):
+ """Push a randomly generated directory of files to the device."""
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ self.device.shell(['mkdir', self.DEVICE_TEMP_DIR])
+
+ try:
+ host_dir = tempfile.mkdtemp()
+
+ # Make sure the temp directory isn't setuid, or else adb will complain.
+ os.chmod(host_dir, 0o700)
+
+ # Create 32 random files.
+ temp_files = make_random_host_files(in_dir=host_dir, num_files=32)
+ self.device.push(host_dir, self.DEVICE_TEMP_DIR)
+
+ for temp_file in temp_files:
+ remote_path = posixpath.join(self.DEVICE_TEMP_DIR,
+ os.path.basename(host_dir),
+ temp_file.base_name)
+ self._verify_remote(temp_file.checksum, remote_path)
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ finally:
+ if host_dir is not None:
+ shutil.rmtree(host_dir)
+
+ @unittest.expectedFailure # b/25566053
+ def test_push_empty(self):
+ """Push a directory containing an empty directory to the device."""
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ self.device.shell(['mkdir', self.DEVICE_TEMP_DIR])
+
+ try:
+ host_dir = tempfile.mkdtemp()
+
+ # Make sure the temp directory isn't setuid, or else adb will complain.
+ os.chmod(host_dir, 0o700)
+
+ # Create an empty directory.
+ os.mkdir(os.path.join(host_dir, 'empty'))
+
+ self.device.push(host_dir, self.DEVICE_TEMP_DIR)
+
+ test_empty_cmd = ['[', '-d',
+ os.path.join(self.DEVICE_TEMP_DIR, 'empty')]
+ rc, _, _ = self.device.shell_nocheck(test_empty_cmd)
+ self.assertEqual(rc, 0)
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ finally:
+ if host_dir is not None:
+ shutil.rmtree(host_dir)
+
+ def test_multiple_push(self):
+ """Push multiple files to the device in one adb push command.
+
+ Bug: http://b/25324823
+ """
+
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ self.device.shell(['mkdir', self.DEVICE_TEMP_DIR])
+
+ try:
+ host_dir = tempfile.mkdtemp()
+
+ # Create some random files and a subdirectory containing more files.
+ temp_files = make_random_host_files(in_dir=host_dir, num_files=4)
+
+ subdir = os.path.join(host_dir, "subdir")
+ os.mkdir(subdir)
+ subdir_temp_files = make_random_host_files(in_dir=subdir,
+ num_files=4)
+
+ paths = map(lambda temp_file: temp_file.full_path, temp_files)
+ paths.append(subdir)
+ self.device._simple_call(['push'] + paths + [self.DEVICE_TEMP_DIR])
+
+ for temp_file in temp_files:
+ remote_path = posixpath.join(self.DEVICE_TEMP_DIR,
+ temp_file.base_name)
+ self._verify_remote(temp_file.checksum, remote_path)
+
+ for subdir_temp_file in subdir_temp_files:
+ remote_path = posixpath.join(self.DEVICE_TEMP_DIR,
+ # BROKEN: http://b/25394682
+ # "subdir",
+ temp_file.base_name)
+ self._verify_remote(temp_file.checksum, remote_path)
+
+
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ finally:
+ if host_dir is not None:
+ shutil.rmtree(host_dir)
+
+
+ def _test_pull(self, remote_file, checksum):
+ tmp_write = tempfile.NamedTemporaryFile(mode='wb', delete=False)
+ tmp_write.close()
+ self.device.pull(remote=remote_file, local=tmp_write.name)
+ with open(tmp_write.name, 'rb') as tmp_read:
+ host_contents = tmp_read.read()
+ host_md5 = compute_md5(host_contents)
+ self.assertEqual(checksum, host_md5)
+ os.remove(tmp_write.name)
+
+ @requires_non_root
+ def test_pull_error_reporting(self):
+ self.device.shell(['touch', self.DEVICE_TEMP_FILE])
+ self.device.shell(['chmod', 'a-rwx', self.DEVICE_TEMP_FILE])
+
+ try:
+ output = self.device.pull(remote=self.DEVICE_TEMP_FILE, local='x')
+ except subprocess.CalledProcessError as e:
+ output = e.output
+
+ self.assertIn('Permission denied', output)
+
+ self.device.shell(['rm', '-f', self.DEVICE_TEMP_FILE])
+
+ def test_pull(self):
+ """Pull a randomly generated file from specified device."""
+ kbytes = 512
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_FILE])
+ cmd = ['dd', 'if=/dev/urandom',
+ 'of={}'.format(self.DEVICE_TEMP_FILE), 'bs=1024',
+ 'count={}'.format(kbytes)]
+ self.device.shell(cmd)
+ dev_md5, _ = self.device.shell(
+ [get_md5_prog(self.device), self.DEVICE_TEMP_FILE])[0].split()
+ self._test_pull(self.DEVICE_TEMP_FILE, dev_md5)
+ self.device.shell_nocheck(['rm', self.DEVICE_TEMP_FILE])
+
+ def test_pull_dir(self):
+ """Pull a randomly generated directory of files from the device."""
+ try:
+ host_dir = tempfile.mkdtemp()
+
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ self.device.shell(['mkdir', '-p', self.DEVICE_TEMP_DIR])
+
+ # Populate device directory with random files.
+ temp_files = make_random_device_files(
+ self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=32)
+
+ self.device.pull(remote=self.DEVICE_TEMP_DIR, local=host_dir)
+
+ for temp_file in temp_files:
+ host_path = os.path.join(
+ host_dir, posixpath.basename(self.DEVICE_TEMP_DIR),
+ temp_file.base_name)
+ self._verify_local(temp_file.checksum, host_path)
+
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ finally:
+ if host_dir is not None:
+ shutil.rmtree(host_dir)
+
+ def test_pull_symlink_dir(self):
+ """Pull a symlink to a directory of symlinks to files."""
+ try:
+ host_dir = tempfile.mkdtemp()
+
+ remote_dir = posixpath.join(self.DEVICE_TEMP_DIR, 'contents')
+ remote_links = posixpath.join(self.DEVICE_TEMP_DIR, 'links')
+ remote_symlink = posixpath.join(self.DEVICE_TEMP_DIR, 'symlink')
+
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ self.device.shell(['mkdir', '-p', remote_dir, remote_links])
+ self.device.shell(['ln', '-s', remote_links, remote_symlink])
+
+ # Populate device directory with random files.
+ temp_files = make_random_device_files(
+ self.device, in_dir=remote_dir, num_files=32)
+
+ for temp_file in temp_files:
+ self.device.shell(
+ ['ln', '-s', '../contents/{}'.format(temp_file.base_name),
+ posixpath.join(remote_links, temp_file.base_name)])
+
+ self.device.pull(remote=remote_symlink, local=host_dir)
+
+ for temp_file in temp_files:
+ host_path = os.path.join(
+ host_dir, 'symlink', temp_file.base_name)
+ self._verify_local(temp_file.checksum, host_path)
+
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ finally:
+ if host_dir is not None:
+ shutil.rmtree(host_dir)
+
+ def test_pull_empty(self):
+ """Pull a directory containing an empty directory from the device."""
+ try:
+ host_dir = tempfile.mkdtemp()
+
+ remote_empty_path = posixpath.join(self.DEVICE_TEMP_DIR, 'empty')
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ self.device.shell(['mkdir', '-p', remote_empty_path])
+
+ self.device.pull(remote=remote_empty_path, local=host_dir)
+ self.assertTrue(os.path.isdir(os.path.join(host_dir, 'empty')))
+ finally:
+ if host_dir is not None:
+ shutil.rmtree(host_dir)
+
+ def test_multiple_pull(self):
+ """Pull a randomly generated directory of files from the device."""
+
+ try:
+ host_dir = tempfile.mkdtemp()
+
+ subdir = posixpath.join(self.DEVICE_TEMP_DIR, "subdir")
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ self.device.shell(['mkdir', '-p', subdir])
+
+ # Create some random files and a subdirectory containing more files.
+ temp_files = make_random_device_files(
+ self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=4)
+
+ subdir_temp_files = make_random_device_files(
+ self.device, in_dir=subdir, num_files=4, prefix='subdir_')
+
+ paths = map(lambda temp_file: temp_file.full_path, temp_files)
+ paths.append(subdir)
+ self.device._simple_call(['pull'] + paths + [host_dir])
+
+ for temp_file in temp_files:
+ local_path = os.path.join(host_dir, temp_file.base_name)
+ self._verify_local(temp_file.checksum, local_path)
+
+ for subdir_temp_file in subdir_temp_files:
+ local_path = os.path.join(host_dir,
+ "subdir",
+ subdir_temp_file.base_name)
+ self._verify_local(subdir_temp_file.checksum, local_path)
+
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ finally:
+ if host_dir is not None:
+ shutil.rmtree(host_dir)
+
+ def test_sync(self):
+ """Sync a randomly generated directory of files to specified device."""
+
+ try:
+ base_dir = tempfile.mkdtemp()
+
+ # Create mirror device directory hierarchy within base_dir.
+ full_dir_path = base_dir + self.DEVICE_TEMP_DIR
+ os.makedirs(full_dir_path)
+
+ # Create 32 random files within the host mirror.
+ temp_files = make_random_host_files(in_dir=full_dir_path, num_files=32)
+
+ # Clean up any trash on the device.
+ device = adb.get_device(product=base_dir)
+ device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+
+ device.sync('data')
+
+ # Confirm that every file on the device mirrors that on the host.
+ for temp_file in temp_files:
+ device_full_path = posixpath.join(self.DEVICE_TEMP_DIR,
+ temp_file.base_name)
+ dev_md5, _ = device.shell(
+ [get_md5_prog(self.device), device_full_path])[0].split()
+ self.assertEqual(temp_file.checksum, dev_md5)
+
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ finally:
+ if base_dir is not None:
+ shutil.rmtree(base_dir)
+
+ def test_unicode_paths(self):
+ """Ensure that we can support non-ASCII paths, even on Windows."""
+ name = u'로보카 폴리'
+
+ self.device.shell(['rm', '-f', '/data/local/tmp/adb-test-*'])
+ remote_path = u'/data/local/tmp/adb-test-{}'.format(name)
+
+ ## push.
+ tf = tempfile.NamedTemporaryFile('wb', suffix=name, delete=False)
+ tf.close()
+ self.device.push(tf.name, remote_path)
+ os.remove(tf.name)
+ self.assertFalse(os.path.exists(tf.name))
+
+ # Verify that the device ended up with the expected UTF-8 path
+ output = self.device.shell(
+ ['ls', '/data/local/tmp/adb-test-*'])[0].strip()
+ self.assertEqual(remote_path.encode('utf-8'), output)
+
+ # pull.
+ self.device.pull(remote_path, tf.name)
+ self.assertTrue(os.path.exists(tf.name))
+ os.remove(tf.name)
+ self.device.shell(['rm', '-f', '/data/local/tmp/adb-test-*'])
+
+
+def main():
+ 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')
+
+
+if __name__ == '__main__':
+ main()
diff --git a/adb/test_track_devices.cpp b/adb/test_track_devices.cpp
index 6f658f6..b10f8ee 100644
--- a/adb/test_track_devices.cpp
+++ b/adb/test_track_devices.cpp
@@ -9,7 +9,7 @@
#include <sys/socket.h>
#include <unistd.h>
-#include <base/file.h>
+#include <android-base/file.h>
static void
panic( const char* msg )
diff --git a/adb/transport.cpp b/adb/transport.cpp
index e9e774f..6020ad5 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -26,22 +26,27 @@
#include <string.h>
#include <unistd.h>
+#include <algorithm>
#include <list>
-#include <base/logging.h>
-#include <base/stringprintf.h>
-#include <base/strings.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include "adb.h"
#include "adb_utils.h"
+#include "diagnose_usb.h"
static void transport_unref(atransport *t);
-static std::list<atransport*> transport_list;
-static std::list<atransport*> pending_list;
+static auto& transport_list = *new std::list<atransport*>();
+static auto& pending_list = *new std::list<atransport*>();
ADB_MUTEX_DEFINE( transport_lock );
+const char* const kFeatureShell2 = "shell_v2";
+const char* const kFeatureCmd = "cmd";
+
static std::string dump_packet(const char* name, const char* func, apacket* p) {
unsigned command = p->msg.command;
int len = p->msg.data_length;
@@ -670,7 +675,9 @@
adb_mutex_lock(&transport_lock);
for (const auto& t : transport_list) {
if (t->connection_state == kCsNoPerm) {
- *error_out = "insufficient permissions for device";
+#if ADB_HOST
+ *error_out = UsbNoPermissionsLongHelpText();
+#endif
continue;
}
@@ -744,17 +751,17 @@
return result;
}
-const char* atransport::connection_state_name() const {
+const std::string atransport::connection_state_name() const {
switch (connection_state) {
- case kCsOffline: return "offline";
- case kCsBootloader: return "bootloader";
- case kCsDevice: return "device";
- case kCsHost: return "host";
- case kCsRecovery: return "recovery";
- case kCsNoPerm: return "no permissions";
- case kCsSideload: return "sideload";
- case kCsUnauthorized: return "unauthorized";
- default: return "unknown";
+ case kCsOffline: return "offline";
+ case kCsBootloader: return "bootloader";
+ case kCsDevice: return "device";
+ case kCsHost: return "host";
+ case kCsRecovery: return "recovery";
+ case kCsNoPerm: return UsbNoPermissionsShortHelpText();
+ case kCsSideload: return "sideload";
+ case kCsUnauthorized: return "unauthorized";
+ default: return "unknown";
}
}
@@ -780,7 +787,10 @@
const FeatureSet& supported_features() {
// Local static allocation to avoid global non-POD variables.
static const FeatureSet* features = new FeatureSet{
- kFeatureShell2
+ kFeatureShell2,
+ // Internal master has 'cmd'. AOSP master doesn't.
+ // kFeatureCmd
+
// Increment ADB_SERVER_VERSION whenever the feature list changes to
// make sure that the adb client and server features stay in sync
// (http://b/24370690).
@@ -859,7 +869,8 @@
*result += '\t';
*result += t->connection_state_name();
} else {
- android::base::StringAppendF(result, "%-22s %s", serial, t->connection_state_name());
+ android::base::StringAppendF(result, "%-22s %s", serial,
+ t->connection_state_name().c_str());
append_transport_info(result, "", t->devpath, false);
append_transport_info(result, "product:", t->product, false);
diff --git a/adb/transport.h b/adb/transport.h
index f41a8d4..76d6afa 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -38,7 +38,9 @@
// Do not use any of [:;=,] in feature strings, they have special meaning
// in the connection banner.
-constexpr char kFeatureShell2[] = "shell_v2";
+extern const char* const kFeatureShell2;
+// The 'cmd' command is available
+extern const char* const kFeatureCmd;
class atransport {
public:
@@ -88,7 +90,7 @@
fdevent auth_fde;
size_t failed_auth_attempts = 0;
- const char* connection_state_name() const;
+ const std::string connection_state_name() const;
void update_version(int version, size_t payload);
int get_protocol_version() const;
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index bf0cc3c..d2a375a 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -25,7 +25,7 @@
#include <string.h>
#include <sys/types.h>
-#include <base/stringprintf.h>
+#include <android-base/stringprintf.h>
#include <cutils/sockets.h>
#if !ADB_HOST
@@ -143,7 +143,8 @@
static void *server_socket_thread(void * arg)
{
int serverfd, fd;
- struct sockaddr addr;
+ sockaddr_storage ss;
+ sockaddr *addrp = reinterpret_cast<sockaddr*>(&ss);
socklen_t alen;
int port = (int) (uintptr_t) arg;
@@ -162,9 +163,9 @@
close_on_exec(serverfd);
}
- alen = sizeof(addr);
+ alen = sizeof(ss);
D("server: trying to get new connection from %d", port);
- fd = adb_socket_accept(serverfd, &addr, &alen);
+ fd = adb_socket_accept(serverfd, addrp, &alen);
if(fd >= 0) {
D("server: new connection on fd %d", fd);
close_on_exec(fd);
diff --git a/adb/usb_linux.cpp b/adb/usb_linux.cpp
index c633f7f..ed5d2d67 100644
--- a/adb/usb_linux.cpp
+++ b/adb/usb_linux.cpp
@@ -39,9 +39,9 @@
#include <mutex>
#include <string>
-#include <base/file.h>
-#include <base/stringprintf.h>
-#include <base/strings.h>
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include "adb.h"
#include "transport.h"
@@ -81,8 +81,8 @@
pthread_t reaper_thread = 0;
};
-static std::mutex g_usb_handles_mutex;
-static std::list<usb_handle*> g_usb_handles;
+static auto& g_usb_handles_mutex = *new std::mutex();
+static auto& g_usb_handles = *new std::list<usb_handle*>();
static int is_known_device(const char* dev_name) {
std::lock_guard<std::mutex> lock(g_usb_handles_mutex);
diff --git a/adb/usb_linux_client.cpp b/adb/usb_linux_client.cpp
index 3495a71..a4f1a70 100644
--- a/adb/usb_linux_client.cpp
+++ b/adb/usb_linux_client.cpp
@@ -30,6 +30,8 @@
#include <sys/types.h>
#include <unistd.h>
+#include <algorithm>
+
#include "adb.h"
#include "transport.h"
@@ -37,6 +39,13 @@
#define MAX_PACKET_SIZE_HS 512
#define MAX_PACKET_SIZE_SS 1024
+// Writes larger than 16k fail on some devices (seed with 3.10.49-g209ea2f in particular).
+#define USB_FFS_MAX_WRITE 16384
+
+// The kernel allocates a contiguous buffer for reads, which can fail for large ones due to
+// fragmentation. 16k chosen arbitrarily to match the write limit.
+#define USB_FFS_MAX_READ 16384
+
#define cpu_to_le16(x) htole16(x)
#define cpu_to_le32(x) htole32(x)
@@ -456,56 +465,40 @@
return 0;
}
-static int bulk_write(int bulk_in, const uint8_t* buf, size_t length)
-{
- size_t count = 0;
-
- while (count < length) {
- int ret = adb_write(bulk_in, buf + count, length - count);
- if (ret < 0) return -1;
- count += ret;
- }
-
- D("[ bulk_write done fd=%d ]", bulk_in);
- return count;
-}
-
-static int usb_ffs_write(usb_handle* h, const void* data, int len)
-{
+static int usb_ffs_write(usb_handle* h, const void* data, int len) {
D("about to write (fd=%d, len=%d)", h->bulk_in, len);
- int n = bulk_write(h->bulk_in, reinterpret_cast<const uint8_t*>(data), len);
- if (n != len) {
- D("ERROR: fd = %d, n = %d: %s", h->bulk_in, n, strerror(errno));
- return -1;
+
+ const char* buf = static_cast<const char*>(data);
+ while (len > 0) {
+ int write_len = std::min(USB_FFS_MAX_WRITE, len);
+ int n = adb_write(h->bulk_in, buf, write_len);
+ if (n < 0) {
+ D("ERROR: fd = %d, n = %d: %s", h->bulk_in, n, strerror(errno));
+ return -1;
+ }
+ buf += n;
+ len -= n;
}
+
D("[ done fd=%d ]", h->bulk_in);
return 0;
}
-static int bulk_read(int bulk_out, uint8_t* buf, size_t length)
-{
- size_t count = 0;
+static int usb_ffs_read(usb_handle* h, void* data, int len) {
+ D("about to read (fd=%d, len=%d)", h->bulk_out, len);
- while (count < length) {
- int ret = adb_read(bulk_out, buf + count, length - count);
- if (ret < 0) {
- D("[ bulk_read failed fd=%d length=%zu count=%zu ]", bulk_out, length, count);
+ char* buf = static_cast<char*>(data);
+ while (len > 0) {
+ int read_len = std::min(USB_FFS_MAX_READ, len);
+ int n = adb_read(h->bulk_out, buf, read_len);
+ if (n < 0) {
+ D("ERROR: fd = %d, n = %d: %s", h->bulk_out, n, strerror(errno));
return -1;
}
- count += ret;
+ buf += n;
+ len -= n;
}
- return count;
-}
-
-static int usb_ffs_read(usb_handle* h, void* data, int len)
-{
- D("about to read (fd=%d, len=%d)", h->bulk_out, len);
- int n = bulk_read(h->bulk_out, reinterpret_cast<uint8_t*>(data), len);
- if (n != len) {
- D("ERROR: fd = %d, n = %d: %s", h->bulk_out, n, strerror(errno));
- return -1;
- }
D("[ done fd=%d ]", h->bulk_out);
return 0;
}
diff --git a/adb/usb_osx.cpp b/adb/usb_osx.cpp
index e0dcc756..148be1d 100644
--- a/adb/usb_osx.cpp
+++ b/adb/usb_osx.cpp
@@ -29,8 +29,8 @@
#include <inttypes.h>
#include <stdio.h>
-#include <base/logging.h>
-#include <base/stringprintf.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
#include "adb.h"
#include "transport.h"
diff --git a/adb/usb_windows.cpp b/adb/usb_windows.cpp
index d811b24..8d3501e 100644
--- a/adb/usb_windows.cpp
+++ b/adb/usb_windows.cpp
@@ -55,7 +55,7 @@
ADBAPIHANDLE adb_write_pipe;
/// Interface name
- char* interface_name;
+ wchar_t* interface_name;
/// Mask for determining when to use zero length packets
unsigned zero_mask;
@@ -74,11 +74,11 @@
ADB_MUTEX_DEFINE( usb_lock );
/// Checks if there is opened usb handle in handle_list for this device.
-int known_device(const char* dev_name);
+int known_device(const wchar_t* dev_name);
/// Checks if there is opened usb handle in handle_list for this device.
/// usb_lock mutex must be held before calling this routine.
-int known_device_locked(const char* dev_name);
+int known_device_locked(const wchar_t* dev_name);
/// Registers opened usb handle (adds it to handle_list).
int register_new_device(usb_handle* handle);
@@ -118,7 +118,7 @@
/// Closes opened usb handle
int usb_close(usb_handle* handle);
-int known_device_locked(const char* dev_name) {
+int known_device_locked(const wchar_t* dev_name) {
usb_handle* usb;
if (NULL != dev_name) {
@@ -126,7 +126,7 @@
for(usb = handle_list.next; usb != &handle_list; usb = usb->next) {
// In Windows names are not case sensetive!
if((NULL != usb->interface_name) &&
- (0 == stricmp(usb->interface_name, dev_name))) {
+ (0 == wcsicmp(usb->interface_name, dev_name))) {
return 1;
}
}
@@ -135,7 +135,7 @@
return 0;
}
-int known_device(const char* dev_name) {
+int known_device(const wchar_t* dev_name) {
int ret = 0;
if (NULL != dev_name) {
@@ -316,17 +316,16 @@
AdbGetInterfaceName(ret->adb_interface,
NULL,
&name_len,
- true);
+ false);
if (0 == name_len) {
D("AdbGetInterfaceName returned name length of zero: %s",
SystemErrorCodeToString(GetLastError()).c_str());
goto fail;
}
- ret->interface_name = (char*)malloc(name_len);
+ ret->interface_name = (wchar_t*)malloc(name_len * sizeof(ret->interface_name[0]));
if (NULL == ret->interface_name) {
- D("Could not allocate %lu bytes for interface_name: %s", name_len,
- strerror(errno));
+ D("Could not allocate %lu characters for interface_name: %s", name_len, strerror(errno));
goto fail;
}
@@ -334,7 +333,7 @@
if (!AdbGetInterfaceName(ret->adb_interface,
ret->interface_name,
&name_len,
- true)) {
+ false)) {
D("AdbGetInterfaceName failed: %s",
SystemErrorCodeToString(GetLastError()).c_str());
goto fail;
@@ -581,12 +580,10 @@
}
void find_devices() {
- usb_handle* handle = NULL;
+ usb_handle* handle = NULL;
char entry_buffer[2048];
- char interf_name[2048];
AdbInterfaceInfo* next_interface = (AdbInterfaceInfo*)(&entry_buffer[0]);
unsigned long entry_buffer_size = sizeof(entry_buffer);
- char* copy_name;
// Enumerate all present and active interfaces.
ADBAPIHANDLE enum_handle =
@@ -599,25 +596,21 @@
}
while (AdbNextInterface(enum_handle, next_interface, &entry_buffer_size)) {
- // TODO: FIXME - temp hack converting wchar_t into char.
- // It would be better to change AdbNextInterface so it will return
- // interface name as single char string.
- const wchar_t* wchar_name = next_interface->device_name;
- for(copy_name = interf_name;
- L'\0' != *wchar_name;
- wchar_name++, copy_name++) {
- *copy_name = (char)(*wchar_name);
- }
- *copy_name = '\0';
-
// Lets see if we already have this device in the list
- if (!known_device(interf_name)) {
+ if (!known_device(next_interface->device_name)) {
// This seems to be a new device. Open it!
- handle = do_usb_open(next_interface->device_name);
- if (NULL != handle) {
+ handle = do_usb_open(next_interface->device_name);
+ if (NULL != handle) {
// Lets see if this interface (device) belongs to us
if (recognized_device(handle)) {
- D("adding a new device %s", interf_name);
+ D("adding a new device %ls", next_interface->device_name);
+
+ // We don't request a wchar_t string from AdbGetSerialNumber() because of a bug in
+ // adb_winusb_interface.cpp:CopyMemory(buffer, ser_num->bString, bytes_written) where the
+ // last parameter should be (str_len * sizeof(wchar_t)). The bug reads 2 bytes past the
+ // end of a stack buffer in the best case, and in the unlikely case of a long serial
+ // number, it will read 2 bytes past the end of a heap allocation. This doesn't affect the
+ // resulting string, but we should avoid the bad reads in the first place.
char serial_number[512];
unsigned long serial_number_len = sizeof(serial_number);
if (AdbGetSerialNumber(handle->adb_interface,
@@ -628,7 +621,7 @@
if (register_new_device(handle)) {
register_usb_transport(handle, serial_number, NULL, 1);
} else {
- D("register_new_device failed for %s", interf_name);
+ D("register_new_device failed for %ls", next_interface->device_name);
usb_cleanup_handle(handle);
free(handle);
}
diff --git a/base/Android.mk b/base/Android.mk
index 613636b..cba70d4 100644
--- a/base/Android.mk
+++ b/base/Android.mk
@@ -23,6 +23,9 @@
strings.cpp \
test_utils.cpp \
+libbase_windows_src_files := \
+ utf8.cpp \
+
libbase_test_src_files := \
file_test.cpp \
logging_test.cpp \
@@ -31,19 +34,31 @@
strings_test.cpp \
test_main.cpp \
+libbase_test_windows_src_files := \
+ utf8_test.cpp \
+
libbase_cppflags := \
-Wall \
-Wextra \
-Werror \
+libbase_linux_cppflags := \
+ -Wexit-time-destructors \
+
+libbase_darwin_cppflags := \
+ -Wexit-time-destructors \
+
# Device
# ------------------------------------------------------------------------------
include $(CLEAR_VARS)
LOCAL_MODULE := libbase
LOCAL_CLANG := true
LOCAL_SRC_FILES := $(libbase_src_files)
+LOCAL_SRC_FILES_darwin := $(libbase_darwin_src_files)
+LOCAL_SRC_FILES_linux := $(libbase_linux_src_files)
+LOCAL_SRC_FILES_windows := $(libbase_windows_src_files)
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_CPPFLAGS := $(libbase_cppflags)
+LOCAL_CPPFLAGS := $(libbase_cppflags) $(libbase_linux_cppflags)
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
LOCAL_STATIC_LIBRARIES := libcutils
LOCAL_MULTILIB := both
@@ -64,8 +79,13 @@
include $(CLEAR_VARS)
LOCAL_MODULE := libbase
LOCAL_SRC_FILES := $(libbase_src_files)
+LOCAL_SRC_FILES_darwin := $(libbase_darwin_src_files)
+LOCAL_SRC_FILES_linux := $(libbase_linux_src_files)
+LOCAL_SRC_FILES_windows := $(libbase_windows_src_files)
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
LOCAL_CPPFLAGS := $(libbase_cppflags)
+LOCAL_CPPFLAGS_darwin := $(libbase_darwin_cppflags)
+LOCAL_CPPFLAGS_linux := $(libbase_linux_cppflags)
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
LOCAL_STATIC_LIBRARIES := libcutils
LOCAL_MULTILIB := both
@@ -88,6 +108,9 @@
LOCAL_MODULE := libbase_test
LOCAL_CLANG := true
LOCAL_SRC_FILES := $(libbase_test_src_files)
+LOCAL_SRC_FILES_darwin := $(libbase_test_darwin_src_files)
+LOCAL_SRC_FILES_linux := $(libbase_test_linux_src_files)
+LOCAL_SRC_FILES_windows := $(libbase_test_windows_src_files)
LOCAL_C_INCLUDES := $(LOCAL_PATH)
LOCAL_CPPFLAGS := $(libbase_cppflags)
LOCAL_SHARED_LIBRARIES := libbase
@@ -100,6 +123,9 @@
LOCAL_MODULE := libbase_test
LOCAL_MODULE_HOST_OS := darwin linux windows
LOCAL_SRC_FILES := $(libbase_test_src_files)
+LOCAL_SRC_FILES_darwin := $(libbase_test_darwin_src_files)
+LOCAL_SRC_FILES_linux := $(libbase_test_linux_src_files)
+LOCAL_SRC_FILES_windows := $(libbase_test_windows_src_files)
LOCAL_C_INCLUDES := $(LOCAL_PATH)
LOCAL_CPPFLAGS := $(libbase_cppflags)
LOCAL_SHARED_LIBRARIES := libbase
diff --git a/base/file.cpp b/base/file.cpp
index 3468dcf..f444c0c 100644
--- a/base/file.cpp
+++ b/base/file.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "base/file.h"
+#include "android-base/file.h"
#include <errno.h>
#include <fcntl.h>
@@ -23,7 +23,8 @@
#include <string>
-#include "base/macros.h" // For TEMP_FAILURE_RETRY on Darwin.
+#include "android-base/macros.h" // For TEMP_FAILURE_RETRY on Darwin.
+#include "android-base/utf8.h"
#define LOG_TAG "base.file"
#include "cutils/log.h"
#include "utils/Compat.h"
@@ -35,6 +36,9 @@
namespace android {
namespace base {
+// Versions of standard library APIs that support UTF-8 strings.
+using namespace android::base::utf8;
+
bool ReadFdToString(int fd, std::string* content) {
content->clear();
diff --git a/base/file_test.cpp b/base/file_test.cpp
index 77b9268..1bf83a4 100644
--- a/base/file_test.cpp
+++ b/base/file_test.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "base/file.h"
+#include "android-base/file.h"
#include <gtest/gtest.h>
@@ -24,7 +24,7 @@
#include <string>
-#include "base/test_utils.h"
+#include "android-base/test_utils.h"
TEST(file, ReadFileToString_ENOENT) {
std::string s("hello");
diff --git a/base/include/base/file.h b/base/include/android-base/file.h
similarity index 100%
rename from base/include/base/file.h
rename to base/include/android-base/file.h
diff --git a/base/include/base/logging.h b/base/include/android-base/logging.h
similarity index 99%
rename from base/include/base/logging.h
rename to base/include/android-base/logging.h
index 30f6906..cd526d0 100644
--- a/base/include/base/logging.h
+++ b/base/include/android-base/logging.h
@@ -29,7 +29,7 @@
#include <memory>
#include <ostream>
-#include "base/macros.h"
+#include "android-base/macros.h"
namespace android {
namespace base {
diff --git a/base/include/base/macros.h b/base/include/android-base/macros.h
similarity index 100%
rename from base/include/base/macros.h
rename to base/include/android-base/macros.h
diff --git a/base/include/base/memory.h b/base/include/android-base/memory.h
similarity index 100%
rename from base/include/base/memory.h
rename to base/include/android-base/memory.h
diff --git a/base/include/base/parseint.h b/base/include/android-base/parseint.h
similarity index 100%
rename from base/include/base/parseint.h
rename to base/include/android-base/parseint.h
diff --git a/base/include/base/stringprintf.h b/base/include/android-base/stringprintf.h
similarity index 100%
rename from base/include/base/stringprintf.h
rename to base/include/android-base/stringprintf.h
diff --git a/base/include/base/strings.h b/base/include/android-base/strings.h
similarity index 100%
rename from base/include/base/strings.h
rename to base/include/android-base/strings.h
diff --git a/base/include/base/test_utils.h b/base/include/android-base/test_utils.h
similarity index 96%
rename from base/include/base/test_utils.h
rename to base/include/android-base/test_utils.h
index 402e0a5..3f6872c 100644
--- a/base/include/base/test_utils.h
+++ b/base/include/android-base/test_utils.h
@@ -19,7 +19,7 @@
#include <string>
-#include <base/macros.h>
+#include <android-base/macros.h>
class TemporaryFile {
public:
diff --git a/base/include/android-base/unique_fd.h b/base/include/android-base/unique_fd.h
new file mode 100644
index 0000000..d3b27ca
--- /dev/null
+++ b/base/include/android-base/unique_fd.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_BASE_UNIQUE_FD_H
+#define ANDROID_BASE_UNIQUE_FD_H
+
+#include <unistd.h>
+
+#include <android-base/macros.h>
+
+/* Container for a file descriptor that automatically closes the descriptor as
+ * it goes out of scope.
+ *
+ * unique_fd ufd(open("/some/path", "r"));
+ *
+ * if (ufd.get() < 0) // invalid descriptor
+ * return error;
+ *
+ * // Do something useful
+ *
+ * return 0; // descriptor is closed here
+ */
+namespace android {
+namespace base {
+
+class unique_fd final {
+ public:
+ unique_fd() : value_(-1) {}
+
+ explicit unique_fd(int value) : value_(value) {}
+ ~unique_fd() { clear(); }
+
+ unique_fd(unique_fd&& other) : value_(other.release()) {}
+ unique_fd& operator = (unique_fd&& s) {
+ reset(s.release());
+ return *this;
+ }
+
+ void reset(int new_value) {
+ if (value_ >= 0)
+ close(value_);
+ value_ = new_value;
+ }
+
+ void clear() {
+ reset(-1);
+ }
+
+ int get() const { return value_; }
+
+ int release() {
+ int ret = value_;
+ value_ = -1;
+ return ret;
+ }
+
+ private:
+ int value_;
+
+ DISALLOW_COPY_AND_ASSIGN(unique_fd);
+};
+
+} // namespace base
+} // namespace android
+
+#endif // ANDROID_BASE_UNIQUE_FD_H
diff --git a/base/include/android-base/utf8.h b/base/include/android-base/utf8.h
new file mode 100755
index 0000000..3b0ed0a
--- /dev/null
+++ b/base/include/android-base/utf8.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef BASE_UTF8_H
+#define BASE_UTF8_H
+
+#ifdef _WIN32
+#include <string>
+#else
+// Bring in prototypes for standard APIs so that we can import them into the utf8 namespace.
+#include <fcntl.h> // open
+#include <unistd.h> // unlink
+#endif
+
+namespace android {
+namespace base {
+
+// Only available on Windows because this is only needed on Windows.
+#ifdef _WIN32
+// Convert size number of UTF-16 wchar_t's to UTF-8. Returns whether the
+// conversion was done successfully.
+bool WideToUTF8(const wchar_t* utf16, const size_t size, std::string* utf8);
+
+// Convert a NULL-terminated string of UTF-16 characters to UTF-8. Returns
+// whether the conversion was done successfully.
+bool WideToUTF8(const wchar_t* utf16, std::string* utf8);
+
+// Convert a UTF-16 std::wstring (including any embedded NULL characters) to
+// UTF-8. Returns whether the conversion was done successfully.
+bool WideToUTF8(const std::wstring& utf16, std::string* utf8);
+
+// Convert size number of UTF-8 char's to UTF-16. Returns whether the conversion
+// was done successfully.
+bool UTF8ToWide(const char* utf8, const size_t size, std::wstring* utf16);
+
+// Convert a NULL-terminated string of UTF-8 characters to UTF-16. Returns
+// whether the conversion was done successfully.
+bool UTF8ToWide(const char* utf8, std::wstring* utf16);
+
+// Convert a UTF-8 std::string (including any embedded NULL characters) to
+// UTF-16. Returns whether the conversion was done successfully.
+bool UTF8ToWide(const std::string& utf8, std::wstring* utf16);
+#endif
+
+// The functions in the utf8 namespace take UTF-8 strings. For Windows, these
+// are wrappers, for non-Windows these just expose existing APIs. To call these
+// functions, use:
+//
+// // anonymous namespace to avoid conflict with existing open(), unlink(), etc.
+// namespace {
+// // Import functions into anonymous namespace.
+// using namespace android::base::utf8;
+//
+// void SomeFunction(const char* name) {
+// int fd = open(name, ...); // Calls android::base::utf8::open().
+// ...
+// unlink(name); // Calls android::base::utf8::unlink().
+// }
+// }
+namespace utf8 {
+
+#ifdef _WIN32
+int open(const char* name, int flags, ...);
+int unlink(const char* name);
+#else
+using ::open;
+using ::unlink;
+#endif
+
+} // namespace utf8
+} // namespace base
+} // namespace android
+
+#endif // BASE_UTF8_H
diff --git a/base/logging.cpp b/base/logging.cpp
index 6bfaaec..a385902 100644
--- a/base/logging.cpp
+++ b/base/logging.cpp
@@ -18,7 +18,7 @@
#include <windows.h>
#endif
-#include "base/logging.h"
+#include "android-base/logging.h"
#include <libgen.h>
@@ -40,8 +40,8 @@
#include <mutex>
#endif
-#include "base/macros.h"
-#include "base/strings.h"
+#include "android-base/macros.h"
+#include "android-base/strings.h"
#include "cutils/threads.h"
// Headers for LogMessage::LogLine.
@@ -53,6 +53,33 @@
#include <unistd.h>
#endif
+// For gettid.
+#if defined(__APPLE__)
+#include "AvailabilityMacros.h" // For MAC_OS_X_VERSION_MAX_ALLOWED
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/syscall.h>
+#include <sys/time.h>
+#include <unistd.h>
+#elif defined(__linux__) && !defined(__ANDROID__)
+#include <syscall.h>
+#include <unistd.h>
+#elif defined(_WIN32)
+#include <windows.h>
+#endif
+
+static pid_t GetThreadId() {
+#if defined(__BIONIC__)
+ return gettid();
+#elif defined(__APPLE__)
+ return syscall(SYS_thread_selfid);
+#elif defined(__linux__)
+ return syscall(__NR_gettid);
+#elif defined(_WIN32)
+ return GetCurrentThreadId();
+#endif
+}
+
namespace {
#ifndef _WIN32
using std::mutex;
@@ -127,17 +154,17 @@
namespace android {
namespace base {
-static mutex logging_lock;
+static auto& logging_lock = *new mutex();
#ifdef __ANDROID__
-static LogFunction gLogger = LogdLogger();
+static auto& gLogger = *new LogFunction(LogdLogger());
#else
-static LogFunction gLogger = StderrLogger;
+static auto& gLogger = *new LogFunction(StderrLogger);
#endif
static bool gInitialized = false;
static LogSeverity gMinimumLogSeverity = INFO;
-static std::unique_ptr<std::string> gProgramInvocationName;
+static auto& gProgramInvocationName = *new std::unique_ptr<std::string>();
LogSeverity GetMinimumLogSeverity() {
return gMinimumLogSeverity;
@@ -158,7 +185,7 @@
"Mismatch in size of log_characters and values in LogSeverity");
char severity_char = log_characters[severity];
fprintf(stderr, "%s %c %5d %5d %s:%u] %s\n", ProgramInvocationName(),
- severity_char, getpid(), gettid(), file, line, message);
+ severity_char, getpid(), GetThreadId(), file, line, message);
}
@@ -212,8 +239,8 @@
gInitialized = true;
// Stash the command line for later use. We can use /proc/self/cmdline on
- // Linux to recover this, but we don't have that luxury on the Mac, and there
- // are a couple of argv[0] variants that are commonly used.
+ // Linux to recover this, but we don't have that luxury on the Mac/Windows,
+ // and there are a couple of argv[0] variants that are commonly used.
if (argv != nullptr) {
gProgramInvocationName.reset(new std::string(basename(argv[0])));
}
@@ -264,11 +291,20 @@
gLogger = std::move(logger);
}
-// We can't use basename(3) because this code runs on the Mac, which doesn't
-// have a non-modifying basename.
static const char* GetFileBasename(const char* file) {
+ // We can't use basename(3) even on Unix because the Mac doesn't
+ // have a non-modifying basename.
const char* last_slash = strrchr(file, '/');
- return (last_slash == nullptr) ? file : last_slash + 1;
+ if (last_slash != nullptr) {
+ return last_slash + 1;
+ }
+#if defined(_WIN32)
+ const char* last_backslash = strrchr(file, '\\');
+ if (last_backslash != nullptr) {
+ return last_backslash + 1;
+ }
+#endif
+ return file;
}
// This indirection greatly reduces the stack impact of having lots of
diff --git a/base/logging_test.cpp b/base/logging_test.cpp
index 70f9952..3de42b7 100644
--- a/base/logging_test.cpp
+++ b/base/logging_test.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "base/logging.h"
+#include "android-base/logging.h"
#include <libgen.h>
@@ -25,9 +25,9 @@
#include <regex>
#include <string>
-#include "base/file.h"
-#include "base/stringprintf.h"
-#include "base/test_utils.h"
+#include "android-base/file.h"
+#include "android-base/stringprintf.h"
+#include "android-base/test_utils.h"
#include <gtest/gtest.h>
diff --git a/base/parseint_test.cpp b/base/parseint_test.cpp
index 8a11d29..6a3ba31 100644
--- a/base/parseint_test.cpp
+++ b/base/parseint_test.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "base/parseint.h"
+#include "android-base/parseint.h"
#include <gtest/gtest.h>
diff --git a/base/stringprintf.cpp b/base/stringprintf.cpp
index d55ff52..78e1e8d 100644
--- a/base/stringprintf.cpp
+++ b/base/stringprintf.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "base/stringprintf.h"
+#include "android-base/stringprintf.h"
#include <stdio.h>
diff --git a/base/stringprintf_test.cpp b/base/stringprintf_test.cpp
index 5cc2086..fc009b1 100644
--- a/base/stringprintf_test.cpp
+++ b/base/stringprintf_test.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "base/stringprintf.h"
+#include "android-base/stringprintf.h"
#include <gtest/gtest.h>
diff --git a/base/strings.cpp b/base/strings.cpp
index d687e3c..b8775df 100644
--- a/base/strings.cpp
+++ b/base/strings.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "base/strings.h"
+#include "android-base/strings.h"
#include <stdlib.h>
#include <string.h>
diff --git a/base/strings_test.cpp b/base/strings_test.cpp
index 5f67575..30ae29e 100644
--- a/base/strings_test.cpp
+++ b/base/strings_test.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "base/strings.h"
+#include "android-base/strings.h"
#include <gtest/gtest.h>
diff --git a/base/test_main.cpp b/base/test_main.cpp
index 546923d..7fa6a84 100644
--- a/base/test_main.cpp
+++ b/base/test_main.cpp
@@ -16,7 +16,7 @@
#include <gtest/gtest.h>
-#include "base/logging.h"
+#include "android-base/logging.h"
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
diff --git a/base/test_utils.cpp b/base/test_utils.cpp
index 22641e7..337ba7c 100644
--- a/base/test_utils.cpp
+++ b/base/test_utils.cpp
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#include "base/logging.h"
-#include "base/test_utils.h"
+#include "android-base/logging.h"
+#include "android-base/test_utils.h"
#include "utils/Compat.h" // For OS_PATH_SEPARATOR.
#include <fcntl.h>
diff --git a/base/utf8.cpp b/base/utf8.cpp
new file mode 100755
index 0000000..3cca700
--- /dev/null
+++ b/base/utf8.cpp
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <windows.h>
+
+#include "android-base/utf8.h"
+
+#include <fcntl.h>
+
+#include <string>
+
+#include "android-base/logging.h"
+
+namespace android {
+namespace base {
+
+// Helper to set errno based on GetLastError() after WideCharToMultiByte()/MultiByteToWideChar().
+static void SetErrnoFromLastError() {
+ switch (GetLastError()) {
+ case ERROR_NO_UNICODE_TRANSLATION:
+ errno = EILSEQ;
+ break;
+ default:
+ errno = EINVAL;
+ break;
+ }
+}
+
+bool WideToUTF8(const wchar_t* utf16, const size_t size, std::string* utf8) {
+ utf8->clear();
+
+ if (size == 0) {
+ return true;
+ }
+
+ // TODO: Consider using std::wstring_convert once libcxx is supported on
+ // Windows.
+
+ // Only Vista or later has this flag that causes WideCharToMultiByte() to
+ // return an error on invalid characters.
+ const DWORD flags =
+#if (WINVER >= 0x0600)
+ WC_ERR_INVALID_CHARS;
+#else
+ 0;
+#endif
+
+ const int chars_required = WideCharToMultiByte(CP_UTF8, flags, utf16, size,
+ NULL, 0, NULL, NULL);
+ if (chars_required <= 0) {
+ SetErrnoFromLastError();
+ return false;
+ }
+
+ // This could potentially throw a std::bad_alloc exception.
+ utf8->resize(chars_required);
+
+ const int result = WideCharToMultiByte(CP_UTF8, flags, utf16, size,
+ &(*utf8)[0], chars_required, NULL,
+ NULL);
+ if (result != chars_required) {
+ SetErrnoFromLastError();
+ CHECK_LE(result, chars_required) << "WideCharToMultiByte wrote " << result
+ << " chars to buffer of " << chars_required << " chars";
+ utf8->clear();
+ return false;
+ }
+
+ return true;
+}
+
+bool WideToUTF8(const wchar_t* utf16, std::string* utf8) {
+ // Compute string length of NULL-terminated string with wcslen().
+ return WideToUTF8(utf16, wcslen(utf16), utf8);
+}
+
+bool WideToUTF8(const std::wstring& utf16, std::string* utf8) {
+ // Use the stored length of the string which allows embedded NULL characters
+ // to be converted.
+ return WideToUTF8(utf16.c_str(), utf16.length(), utf8);
+}
+
+// Internal helper function that takes MultiByteToWideChar() flags.
+static bool UTF8ToWideWithFlags(const char* utf8, const size_t size, std::wstring* utf16,
+ const DWORD flags) {
+ utf16->clear();
+
+ if (size == 0) {
+ return true;
+ }
+
+ // TODO: Consider using std::wstring_convert once libcxx is supported on
+ // Windows.
+ const int chars_required = MultiByteToWideChar(CP_UTF8, flags, utf8, size,
+ NULL, 0);
+ if (chars_required <= 0) {
+ SetErrnoFromLastError();
+ return false;
+ }
+
+ // This could potentially throw a std::bad_alloc exception.
+ utf16->resize(chars_required);
+
+ const int result = MultiByteToWideChar(CP_UTF8, flags, utf8, size,
+ &(*utf16)[0], chars_required);
+ if (result != chars_required) {
+ SetErrnoFromLastError();
+ CHECK_LE(result, chars_required) << "MultiByteToWideChar wrote " << result
+ << " chars to buffer of " << chars_required << " chars";
+ utf16->clear();
+ return false;
+ }
+
+ return true;
+}
+
+bool UTF8ToWide(const char* utf8, const size_t size, std::wstring* utf16) {
+ // If strictly interpreting as UTF-8 succeeds, return success.
+ if (UTF8ToWideWithFlags(utf8, size, utf16, MB_ERR_INVALID_CHARS)) {
+ return true;
+ }
+
+ const int saved_errno = errno;
+
+ // Fallback to non-strict interpretation, allowing invalid characters and
+ // converting as best as possible, and return false to signify a problem.
+ (void)UTF8ToWideWithFlags(utf8, size, utf16, 0);
+ errno = saved_errno;
+ return false;
+}
+
+bool UTF8ToWide(const char* utf8, std::wstring* utf16) {
+ // Compute string length of NULL-terminated string with strlen().
+ return UTF8ToWide(utf8, strlen(utf8), utf16);
+}
+
+bool UTF8ToWide(const std::string& utf8, std::wstring* utf16) {
+ // Use the stored length of the string which allows embedded NULL characters
+ // to be converted.
+ return UTF8ToWide(utf8.c_str(), utf8.length(), utf16);
+}
+
+// Versions of standard library APIs that support UTF-8 strings.
+namespace utf8 {
+
+int open(const char* name, int flags, ...) {
+ std::wstring name_utf16;
+ if (!UTF8ToWide(name, &name_utf16)) {
+ return -1;
+ }
+
+ int mode = 0;
+ if ((flags & O_CREAT) != 0) {
+ va_list args;
+ va_start(args, flags);
+ mode = va_arg(args, int);
+ va_end(args);
+ }
+
+ return _wopen(name_utf16.c_str(), flags, mode);
+}
+
+int unlink(const char* name) {
+ std::wstring name_utf16;
+ if (!UTF8ToWide(name, &name_utf16)) {
+ return -1;
+ }
+
+ return _wunlink(name_utf16.c_str());
+}
+
+} // namespace utf8
+} // namespace base
+} // namespace android
diff --git a/base/utf8_test.cpp b/base/utf8_test.cpp
new file mode 100755
index 0000000..dde7490
--- /dev/null
+++ b/base/utf8_test.cpp
@@ -0,0 +1,412 @@
+/*
+* Copyright (C) 2015 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#include "android-base/utf8.h"
+
+#include <gtest/gtest.h>
+
+#include "android-base/macros.h"
+
+namespace android {
+namespace base {
+
+TEST(UTFStringConversionsTest, ConvertInvalidUTF8) {
+ std::wstring wide;
+
+ errno = 0;
+
+ // Standalone \xa2 is an invalid UTF-8 sequence, so this should return an
+ // error. Concatenate two C/C++ literal string constants to prevent the
+ // compiler from giving an error about "\xa2af" containing a "hex escape
+ // sequence out of range".
+ EXPECT_FALSE(android::base::UTF8ToWide("before\xa2" "after", &wide));
+
+ EXPECT_EQ(EILSEQ, errno);
+
+ // Even if an invalid character is encountered, UTF8ToWide() should still do
+ // its best to convert the rest of the string. sysdeps_win32.cpp:
+ // _console_write_utf8() depends on this behavior.
+ //
+ // Thus, we verify that the valid characters are converted, but we ignore the
+ // specific replacement character that UTF8ToWide() may replace the invalid
+ // UTF-8 characters with because we want to allow that to change if the
+ // implementation changes.
+ EXPECT_EQ(0, wide.find(L"before"));
+ const wchar_t after_wide[] = L"after";
+ EXPECT_EQ(wide.length() - (arraysize(after_wide) - 1), wide.find(after_wide));
+}
+
+// Below is adapted from https://chromium.googlesource.com/chromium/src/+/master/base/strings/utf_string_conversions_unittest.cc
+
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// The tests below from utf_string_conversions_unittest.cc check for this
+// preprocessor symbol, so define it, as it is appropriate for Windows.
+#define WCHAR_T_IS_UTF16
+static_assert(sizeof(wchar_t) == 2, "wchar_t is not 2 bytes");
+
+// The tests below from utf_string_conversions_unittest.cc call versions of
+// UTF8ToWide() and WideToUTF8() that don't return success/failure, so these are
+// stub implementations with that signature. These are just for testing and
+// should not be moved to base because they assert/expect no errors which is
+// probably not a good idea (or at least it is something that should be left
+// up to the caller, not a base library).
+
+static std::wstring UTF8ToWide(const std::string& utf8) {
+ std::wstring utf16;
+ EXPECT_TRUE(UTF8ToWide(utf8, &utf16));
+ return utf16;
+}
+
+static std::string WideToUTF8(const std::wstring& utf16) {
+ std::string utf8;
+ EXPECT_TRUE(WideToUTF8(utf16, &utf8));
+ return utf8;
+}
+
+namespace {
+
+const wchar_t* const kConvertRoundtripCases[] = {
+ L"Google Video",
+ // "网页 图片 资讯更多 »"
+ L"\x7f51\x9875\x0020\x56fe\x7247\x0020\x8d44\x8baf\x66f4\x591a\x0020\x00bb",
+ // "Παγκόσμιος Ιστός"
+ L"\x03a0\x03b1\x03b3\x03ba\x03cc\x03c3\x03bc\x03b9"
+ L"\x03bf\x03c2\x0020\x0399\x03c3\x03c4\x03cc\x03c2",
+ // "Поиск страниц на русском"
+ L"\x041f\x043e\x0438\x0441\x043a\x0020\x0441\x0442"
+ L"\x0440\x0430\x043d\x0438\x0446\x0020\x043d\x0430"
+ L"\x0020\x0440\x0443\x0441\x0441\x043a\x043e\x043c",
+ // "전체서비스"
+ L"\xc804\xccb4\xc11c\xbe44\xc2a4",
+
+ // Test characters that take more than 16 bits. This will depend on whether
+ // wchar_t is 16 or 32 bits.
+#if defined(WCHAR_T_IS_UTF16)
+ L"\xd800\xdf00",
+ // ????? (Mathematical Alphanumeric Symbols (U+011d40 - U+011d44 : A,B,C,D,E)
+ L"\xd807\xdd40\xd807\xdd41\xd807\xdd42\xd807\xdd43\xd807\xdd44",
+#elif defined(WCHAR_T_IS_UTF32)
+ L"\x10300",
+ // ????? (Mathematical Alphanumeric Symbols (U+011d40 - U+011d44 : A,B,C,D,E)
+ L"\x11d40\x11d41\x11d42\x11d43\x11d44",
+#endif
+};
+
+} // namespace
+
+TEST(UTFStringConversionsTest, ConvertUTF8AndWide) {
+ // we round-trip all the wide strings through UTF-8 to make sure everything
+ // agrees on the conversion. This uses the stream operators to test them
+ // simultaneously.
+ for (size_t i = 0; i < arraysize(kConvertRoundtripCases); ++i) {
+ std::ostringstream utf8;
+ utf8 << WideToUTF8(kConvertRoundtripCases[i]);
+ std::wostringstream wide;
+ wide << UTF8ToWide(utf8.str());
+
+ EXPECT_EQ(kConvertRoundtripCases[i], wide.str());
+ }
+}
+
+TEST(UTFStringConversionsTest, ConvertUTF8AndWideEmptyString) {
+ // An empty std::wstring should be converted to an empty std::string,
+ // and vice versa.
+ std::wstring wempty;
+ std::string empty;
+ EXPECT_EQ(empty, WideToUTF8(wempty));
+ EXPECT_EQ(wempty, UTF8ToWide(empty));
+}
+
+TEST(UTFStringConversionsTest, ConvertUTF8ToWide) {
+ struct UTF8ToWideCase {
+ const char* utf8;
+ const wchar_t* wide;
+ bool success;
+ } convert_cases[] = {
+ // Regular UTF-8 input.
+ {"\xe4\xbd\xa0\xe5\xa5\xbd", L"\x4f60\x597d", true},
+ // Non-character is passed through.
+ {"\xef\xbf\xbfHello", L"\xffffHello", true},
+ // Truncated UTF-8 sequence.
+ {"\xe4\xa0\xe5\xa5\xbd", L"\xfffd\x597d", false},
+ // Truncated off the end.
+ {"\xe5\xa5\xbd\xe4\xa0", L"\x597d\xfffd", false},
+ // Non-shortest-form UTF-8.
+ {"\xf0\x84\xbd\xa0\xe5\xa5\xbd", L"\xfffd\x597d", false},
+ // This UTF-8 character decodes to a UTF-16 surrogate, which is illegal.
+ // Note that for whatever reason, this test fails on Windows XP.
+ {"\xed\xb0\x80", L"\xfffd", false},
+ // Non-BMP characters. The second is a non-character regarded as valid.
+ // The result will either be in UTF-16 or UTF-32.
+#if defined(WCHAR_T_IS_UTF16)
+ {"A\xF0\x90\x8C\x80z", L"A\xd800\xdf00z", true},
+ {"A\xF4\x8F\xBF\xBEz", L"A\xdbff\xdffez", true},
+#elif defined(WCHAR_T_IS_UTF32)
+ {"A\xF0\x90\x8C\x80z", L"A\x10300z", true},
+ {"A\xF4\x8F\xBF\xBEz", L"A\x10fffez", true},
+#endif
+ };
+
+ for (size_t i = 0; i < arraysize(convert_cases); i++) {
+ std::wstring converted;
+ errno = 0;
+ const bool success = UTF8ToWide(convert_cases[i].utf8,
+ strlen(convert_cases[i].utf8),
+ &converted);
+ EXPECT_EQ(convert_cases[i].success, success);
+ // The original test always compared expected and converted, but don't do
+ // that because our implementation of UTF8ToWide() does not guarantee to
+ // produce the same output in error situations.
+ if (success) {
+ std::wstring expected(convert_cases[i].wide);
+ EXPECT_EQ(expected, converted);
+ } else {
+ EXPECT_EQ(EILSEQ, errno);
+ }
+ }
+
+ // Manually test an embedded NULL.
+ std::wstring converted;
+ EXPECT_TRUE(UTF8ToWide("\00Z\t", 3, &converted));
+ ASSERT_EQ(3U, converted.length());
+ EXPECT_EQ(static_cast<wchar_t>(0), converted[0]);
+ EXPECT_EQ('Z', converted[1]);
+ EXPECT_EQ('\t', converted[2]);
+
+ // Make sure that conversion replaces, not appends.
+ EXPECT_TRUE(UTF8ToWide("B", 1, &converted));
+ ASSERT_EQ(1U, converted.length());
+ EXPECT_EQ('B', converted[0]);
+}
+
+#if defined(WCHAR_T_IS_UTF16)
+// This test is only valid when wchar_t == UTF-16.
+TEST(UTFStringConversionsTest, ConvertUTF16ToUTF8) {
+ struct WideToUTF8Case {
+ const wchar_t* utf16;
+ const char* utf8;
+ bool success;
+ } convert_cases[] = {
+ // Regular UTF-16 input.
+ {L"\x4f60\x597d", "\xe4\xbd\xa0\xe5\xa5\xbd", true},
+ // Test a non-BMP character.
+ {L"\xd800\xdf00", "\xF0\x90\x8C\x80", true},
+ // Non-characters are passed through.
+ {L"\xffffHello", "\xEF\xBF\xBFHello", true},
+ {L"\xdbff\xdffeHello", "\xF4\x8F\xBF\xBEHello", true},
+ // The first character is a truncated UTF-16 character.
+ // Note that for whatever reason, this test fails on Windows XP.
+ {L"\xd800\x597d", "\xef\xbf\xbd\xe5\xa5\xbd",
+#if (WINVER >= 0x0600)
+ // Only Vista and later has a new API/flag that correctly returns false.
+ false
+#else
+ true
+#endif
+ },
+ // Truncated at the end.
+ // Note that for whatever reason, this test fails on Windows XP.
+ {L"\x597d\xd800", "\xe5\xa5\xbd\xef\xbf\xbd",
+#if (WINVER >= 0x0600)
+ // Only Vista and later has a new API/flag that correctly returns false.
+ false
+#else
+ true
+#endif
+ },
+ };
+
+ for (size_t i = 0; i < arraysize(convert_cases); i++) {
+ std::string converted;
+ errno = 0;
+ const bool success = WideToUTF8(convert_cases[i].utf16,
+ wcslen(convert_cases[i].utf16),
+ &converted);
+ EXPECT_EQ(convert_cases[i].success, success);
+ // The original test always compared expected and converted, but don't do
+ // that because our implementation of WideToUTF8() does not guarantee to
+ // produce the same output in error situations.
+ if (success) {
+ std::string expected(convert_cases[i].utf8);
+ EXPECT_EQ(expected, converted);
+ } else {
+ EXPECT_EQ(EILSEQ, errno);
+ }
+ }
+}
+
+#elif defined(WCHAR_T_IS_UTF32)
+// This test is only valid when wchar_t == UTF-32.
+TEST(UTFStringConversionsTest, ConvertUTF32ToUTF8) {
+ struct WideToUTF8Case {
+ const wchar_t* utf32;
+ const char* utf8;
+ bool success;
+ } convert_cases[] = {
+ // Regular 16-bit input.
+ {L"\x4f60\x597d", "\xe4\xbd\xa0\xe5\xa5\xbd", true},
+ // Test a non-BMP character.
+ {L"A\x10300z", "A\xF0\x90\x8C\x80z", true},
+ // Non-characters are passed through.
+ {L"\xffffHello", "\xEF\xBF\xBFHello", true},
+ {L"\x10fffeHello", "\xF4\x8F\xBF\xBEHello", true},
+ // Invalid Unicode code points.
+ {L"\xfffffffHello", "\xEF\xBF\xBDHello", false},
+ // The first character is a truncated UTF-16 character.
+ {L"\xd800\x597d", "\xef\xbf\xbd\xe5\xa5\xbd", false},
+ {L"\xdc01Hello", "\xef\xbf\xbdHello", false},
+ };
+
+ for (size_t i = 0; i < arraysize(convert_cases); i++) {
+ std::string converted;
+ EXPECT_EQ(convert_cases[i].success,
+ WideToUTF8(convert_cases[i].utf32,
+ wcslen(convert_cases[i].utf32),
+ &converted));
+ std::string expected(convert_cases[i].utf8);
+ EXPECT_EQ(expected, converted);
+ }
+}
+#endif // defined(WCHAR_T_IS_UTF32)
+
+// The test below uses these types and functions, so just do enough to get the
+// test running.
+typedef wchar_t char16;
+typedef std::wstring string16;
+
+template<typename T>
+static void* WriteInto(T* t, size_t size) {
+ // std::(w)string::resize() already includes space for a NULL terminator.
+ t->resize(size - 1);
+ return &(*t)[0];
+}
+
+// A stub implementation that calls a helper from above, just to get the test
+// below working. This is just for testing and should not be moved to base
+// because this ignores errors which is probably not a good idea, plus it takes
+// a string16 type which we don't really have.
+static std::string UTF16ToUTF8(const string16& utf16) {
+ return WideToUTF8(utf16);
+}
+
+TEST(UTFStringConversionsTest, ConvertMultiString) {
+ static char16 multi16[] = {
+ 'f', 'o', 'o', '\0',
+ 'b', 'a', 'r', '\0',
+ 'b', 'a', 'z', '\0',
+ '\0'
+ };
+ static char multi[] = {
+ 'f', 'o', 'o', '\0',
+ 'b', 'a', 'r', '\0',
+ 'b', 'a', 'z', '\0',
+ '\0'
+ };
+ string16 multistring16;
+ memcpy(WriteInto(&multistring16, arraysize(multi16)), multi16,
+ sizeof(multi16));
+ EXPECT_EQ(arraysize(multi16) - 1, multistring16.length());
+ std::string expected;
+ memcpy(WriteInto(&expected, arraysize(multi)), multi, sizeof(multi));
+ EXPECT_EQ(arraysize(multi) - 1, expected.length());
+ const std::string& converted = UTF16ToUTF8(multistring16);
+ EXPECT_EQ(arraysize(multi) - 1, converted.length());
+ EXPECT_EQ(expected, converted);
+}
+
+// The tests below from sys_string_conversions_unittest.cc call SysWideToUTF8()
+// and SysUTF8ToWide(), so these are stub implementations that call the helpers
+// above. These are just for testing and should not be moved to base because
+// they ignore errors which is probably not a good idea.
+
+static std::string SysWideToUTF8(const std::wstring& utf16) {
+ return WideToUTF8(utf16);
+}
+
+static std::wstring SysUTF8ToWide(const std::string& utf8) {
+ return UTF8ToWide(utf8);
+}
+
+// Below is adapted from https://chromium.googlesource.com/chromium/src/+/master/base/strings/sys_string_conversions_unittest.cc
+
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifdef WCHAR_T_IS_UTF32
+static const std::wstring kSysWideOldItalicLetterA = L"\x10300";
+#else
+static const std::wstring kSysWideOldItalicLetterA = L"\xd800\xdf00";
+#endif
+
+TEST(SysStrings, SysWideToUTF8) {
+ EXPECT_EQ("Hello, world", SysWideToUTF8(L"Hello, world"));
+ EXPECT_EQ("\xe4\xbd\xa0\xe5\xa5\xbd", SysWideToUTF8(L"\x4f60\x597d"));
+
+ // >16 bits
+ EXPECT_EQ("\xF0\x90\x8C\x80", SysWideToUTF8(kSysWideOldItalicLetterA));
+
+ // Error case. When Windows finds a UTF-16 character going off the end of
+ // a string, it just converts that literal value to UTF-8, even though this
+ // is invalid.
+ //
+ // This is what XP does, but Vista has different behavior, so we don't bother
+ // verifying it:
+ // EXPECT_EQ("\xE4\xBD\xA0\xED\xA0\x80zyxw",
+ // SysWideToUTF8(L"\x4f60\xd800zyxw"));
+
+ // Test embedded NULLs.
+ std::wstring wide_null(L"a");
+ wide_null.push_back(0);
+ wide_null.push_back('b');
+
+ std::string expected_null("a");
+ expected_null.push_back(0);
+ expected_null.push_back('b');
+
+ EXPECT_EQ(expected_null, SysWideToUTF8(wide_null));
+}
+
+TEST(SysStrings, SysUTF8ToWide) {
+ EXPECT_EQ(L"Hello, world", SysUTF8ToWide("Hello, world"));
+ EXPECT_EQ(L"\x4f60\x597d", SysUTF8ToWide("\xe4\xbd\xa0\xe5\xa5\xbd"));
+ // >16 bits
+ EXPECT_EQ(kSysWideOldItalicLetterA, SysUTF8ToWide("\xF0\x90\x8C\x80"));
+
+ // Error case. When Windows finds an invalid UTF-8 character, it just skips
+ // it. This seems weird because it's inconsistent with the reverse conversion.
+ //
+ // This is what XP does, but Vista has different behavior, so we don't bother
+ // verifying it:
+ // EXPECT_EQ(L"\x4f60zyxw", SysUTF8ToWide("\xe4\xbd\xa0\xe5\xa5zyxw"));
+
+ // Test embedded NULLs.
+ std::string utf8_null("a");
+ utf8_null.push_back(0);
+ utf8_null.push_back('b');
+
+ std::wstring expected_null(L"a");
+ expected_null.push_back(0);
+ expected_null.push_back('b');
+
+ EXPECT_EQ(expected_null, SysUTF8ToWide(utf8_null));
+}
+
+} // namespace base
+} // namespace android
diff --git a/crash_reporter/Android.mk b/crash_reporter/Android.mk
index c5ca4e4..bc023b0 100644
--- a/crash_reporter/Android.mk
+++ b/crash_reporter/Android.mk
@@ -41,13 +41,13 @@
LOCAL_MODULE := libcrash
LOCAL_CPP_EXTENSION := $(crash_reporter_cpp_extension)
LOCAL_C_INCLUDES := $(crash_reporter_includes)
-LOCAL_RTTI_FLAG := -frtti
LOCAL_SHARED_LIBRARIES := libchrome \
+ libbinder \
libbrillo \
libcutils \
- libdbus \
libmetrics \
libpcrecpp
+LOCAL_STATIC_LIBRARIES := libmetricscollectorservice
LOCAL_SRC_FILES := $(crash_reporter_src)
include $(BUILD_STATIC_LIBRARY)
@@ -60,18 +60,18 @@
LOCAL_REQUIRED_MODULES := core2md \
crash_reporter_logs.conf \
crash_sender \
- crash_server \
- dbus-send
+ crash_server
LOCAL_INIT_RC := crash_reporter.rc
-LOCAL_RTTI_FLAG := -frtti
LOCAL_SHARED_LIBRARIES := libchrome \
+ libbinder \
libbrillo \
libcutils \
- libdbus \
libmetrics \
- libpcrecpp
+ libpcrecpp \
+ libutils
LOCAL_SRC_FILES := crash_reporter.cc
-LOCAL_STATIC_LIBRARIES := libcrash
+LOCAL_STATIC_LIBRARIES := libcrash \
+ libmetricscollectorservice
include $(BUILD_EXECUTABLE)
# Crash sender script.
@@ -102,9 +102,13 @@
LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/$(OSRELEASED_DIRECTORY)
include $(BUILD_SYSTEM)/base_rules.mk
+# Optionally populate the BRILLO_CRASH_SERVER variable from a product
+# configuration file: brillo/crash_server.
+LOADED_BRILLO_CRASH_SERVER := $(call cfgtree-get-if-exists,brillo/crash_server)
+
# If the crash server isn't set, use a blank value. crash_sender
# will log it as a configuration error.
-$(LOCAL_BUILT_MODULE): BRILLO_CRASH_SERVER ?= ""
+$(LOCAL_BUILT_MODULE): BRILLO_CRASH_SERVER ?= "$(LOADED_BRILLO_CRASH_SERVER)"
$(LOCAL_BUILT_MODULE):
$(hide)mkdir -p $(dir $@)
echo $(BRILLO_CRASH_SERVER) > $@
@@ -136,7 +140,6 @@
LOCAL_SHARED_LIBRARIES := libchrome \
libbrillo \
libcutils \
- libdbus \
libpcrecpp
LOCAL_SRC_FILES := $(crash_reporter_test_src)
LOCAL_STATIC_LIBRARIES := libcrash libgmock
diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc
index 2a9d1d3..b3fdcb4 100644
--- a/crash_reporter/crash_collector.cc
+++ b/crash_reporter/crash_collector.cc
@@ -47,7 +47,6 @@
const char kDefaultLogConfig[] = "/etc/crash_reporter_logs.conf";
const char kDefaultUserName[] = "chronos";
const char kLeaveCoreFile[] = "/data/misc/crash_reporter/.leave_core";
-const char kLsbRelease[] = "/etc/lsb-release";
const char kShellPath[] = "/system/bin/sh";
const char kSystemCrashPath[] = "/data/misc/crash_reporter/crash";
const char kUploadVarPrefix[] = "upload_var_";
@@ -90,8 +89,7 @@
using base::StringPrintf;
CrashCollector::CrashCollector()
- : lsb_release_(kLsbRelease),
- log_config_path_(kDefaultLogConfig) {
+ : log_config_path_(kDefaultLogConfig) {
}
CrashCollector::~CrashCollector() {
diff --git a/crash_reporter/crash_collector.h b/crash_reporter/crash_collector.h
index cfd76fd..24cbfb3 100644
--- a/crash_reporter/crash_collector.h
+++ b/crash_reporter/crash_collector.h
@@ -158,7 +158,6 @@
IsFeedbackAllowedFunction is_feedback_allowed_function_;
std::string extra_metadata_;
base::FilePath forced_crash_directory_;
- std::string lsb_release_;
base::FilePath log_config_path_;
private:
diff --git a/crash_reporter/crash_collector_test.cc b/crash_reporter/crash_collector_test.cc
index d00a5b5..b55c324 100644
--- a/crash_reporter/crash_collector_test.cc
+++ b/crash_reporter/crash_collector_test.cc
@@ -177,15 +177,8 @@
TEST_F(CrashCollectorTest, MetaData) {
const char kMetaFileBasename[] = "generated.meta";
FilePath meta_file = test_dir_.Append(kMetaFileBasename);
- FilePath lsb_release = test_dir_.Append("lsb-release");
FilePath payload_file = test_dir_.Append("payload-file");
std::string contents;
- collector_.lsb_release_ = lsb_release.value();
- const char kLsbContents[] =
- "CHROMEOS_RELEASE_BOARD=lumpy\n"
- "CHROMEOS_RELEASE_VERSION=6727.0.2015_01_26_0853\n"
- "CHROMEOS_RELEASE_NAME=Chromium OS\n";
- ASSERT_TRUE(base::WriteFile(lsb_release, kLsbContents, strlen(kLsbContents)));
const char kPayload[] = "foo";
ASSERT_TRUE(base::WriteFile(payload_file, kPayload, strlen(kPayload)));
collector_.AddCrashMetaData("foo", "bar");
@@ -194,7 +187,6 @@
const char kExpectedMeta[] =
"foo=bar\n"
"exec_name=kernel\n"
- "ver=6727.0.2015_01_26_0853\n"
"payload=test/payload-file\n"
"payload_size=3\n"
"done=1\n";
diff --git a/crash_reporter/crash_reporter.cc b/crash_reporter/crash_reporter.cc
index 3955fe5..26ffa38 100644
--- a/crash_reporter/crash_reporter.cc
+++ b/crash_reporter/crash_reporter.cc
@@ -25,10 +25,13 @@
#include <base/strings/string_split.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
+#include <binder/IServiceManager.h>
#include <brillo/flag_helper.h>
-#include <brillo/process.h>
#include <brillo/syslog_logging.h>
+#include <metrics/metrics_collector_service_client.h>
#include <metrics/metrics_library.h>
+#include <utils/String16.h>
+
#include "kernel_collector.h"
#include "kernel_warning_collector.h"
@@ -37,8 +40,6 @@
#include "user_collector.h"
static const char kCrashCounterHistogram[] = "Logging.CrashCounter";
-static const char kUserCrashSignal[] =
- "org.chromium.CrashReporter.UserCrash";
static const char kKernelCrashDetected[] = "/var/run/kernel-crash-detected";
static const char kUncleanShutdownDetected[] =
"/var/run/unclean-shutdown-detected";
@@ -56,6 +57,7 @@
static MetricsLibrary s_metrics_lib;
+using android::brillo::metrics::IMetricsCollectorService;
using base::FilePath;
using base::StringPrintf;
@@ -88,32 +90,14 @@
static void CountUserCrash() {
SendCrashMetrics(kCrashKindUser, "user");
- // Announce through D-Bus whenever a user crash happens. This is
- // used by the metrics daemon to log active use time between
- // crashes.
- //
- // We run in the background in case dbus-daemon itself is crashed
- // and not responding. This allows us to not block and potentially
- // deadlock on a dbus-daemon crash. If dbus-daemon crashes without
- // restarting, each crash will fork off a lot of dbus-send
- // processes. Such a system is in a unusable state and will need
- // to be restarted anyway.
- //
- // Note: This will mean that the dbus-send process will become a zombie and
- // reparent to init for reaping, but that's OK -- see above.
+ // Tell the metrics collector about the user crash, in order to log active
+ // use time between crashes.
+ MetricsCollectorServiceClient metrics_collector_service;
- brillo::ProcessImpl dbus_send;
- dbus_send.AddArg("/system/bin/dbus-send");
- dbus_send.AddArg("--type=signal");
- dbus_send.AddArg("--system");
- dbus_send.AddArg("/");
- dbus_send.AddArg(kUserCrashSignal);
- bool status = dbus_send.Start();
- if (status) {
- dbus_send.Release();
- } else {
- PLOG(WARNING) << "Sending UserCrash DBus signal failed";
- }
+ if (metrics_collector_service.Init())
+ metrics_collector_service.notifyUserCrash();
+ else
+ LOG(ERROR) << "Failed to send user crash notification to metrics_collector";
}
diff --git a/crash_reporter/crash_reporter_logs_test.cc b/crash_reporter/crash_reporter_logs_test.cc
index e778002..77f5a7f 100644
--- a/crash_reporter/crash_reporter_logs_test.cc
+++ b/crash_reporter/crash_reporter_logs_test.cc
@@ -23,10 +23,11 @@
namespace {
// Name of the checked-in configuration file containing log-collection commands.
-const char kConfigFile[] = "crash_reporter_logs.conf";
+const char kConfigFile[] = "/system/etc/crash_reporter_logs.conf";
-// Executable name for Chrome. kConfigFile is expected to contain this entry.
-const char kChromeExecName[] = "chrome";
+// Signature name for crash_reporter user collection.
+// kConfigFile is expected to contain this entry.
+const char kUserCollectorSignature[] = "crash_reporter-user-collection";
} // namespace
@@ -35,6 +36,6 @@
brillo::KeyValueStore store;
ASSERT_TRUE(store.Load(base::FilePath(kConfigFile)));
std::string command;
- EXPECT_TRUE(store.GetString(kChromeExecName, &command));
+ EXPECT_TRUE(store.GetString(kUserCollectorSignature, &command));
EXPECT_FALSE(command.empty());
}
diff --git a/crash_reporter/crash_sender b/crash_reporter/crash_sender
index 95204a4..a430ab5 100755
--- a/crash_reporter/crash_sender
+++ b/crash_reporter/crash_sender
@@ -65,6 +65,7 @@
# Path to a directory of restricted certificates which includes
# a certificate for the crash server.
RESTRICTED_CERTIFICATES_PATH="/system/etc/security/cacerts"
+RESTRICTED_CERTIFICATES_PATH_GOOGLE="/system/etc/security/cacerts_google"
# File whose existence implies we're running and not to start again.
RUN_FILE="${CRASH_STATE_DIR}/run/crash_sender.pid"
@@ -183,6 +184,18 @@
fi
}
+# Returns the path of the certificates directory to be used when sending
+# reports to the crash server.
+# If crash_reporter.full_certs=1, return the full certificates path.
+# Otherwise return the Google-specific certificates path.
+get_certificates_path() {
+ if [ "$(getprop crash_reporter.full_certs)" = "1" ]; then
+ echo "${RESTRICTED_CERTIFICATES_PATH}"
+ else
+ echo "${RESTRICTED_CERTIFICATES_PATH_GOOGLE}"
+ fi
+}
+
# Return 0 if the uploading of device coredumps is allowed.
is_device_coredump_upload_allowed() {
[ -f "${DEVCOREDUMP_UPLOAD_FLAG_FILE}" ] && return 0
@@ -455,7 +468,7 @@
set +e
curl "${url}" -f -v ${proxy:+--proxy "$proxy"} \
- --capath "${RESTRICTED_CERTIFICATES_PATH}" --ciphers HIGH \
+ --capath "$(get_certificates_path)" --ciphers HIGH \
-F "prod=${product}" \
-F "ver=${version}" \
-F "bdk_version=${bdk_version}" \
@@ -682,7 +695,7 @@
# (like with autotests) that we're still running.
echo $$ > "${RUN_FILE}"
- for dependency in "${RESTRICTED_CERTIFICATES_PATH}"; do
+ for dependency in "$(get_certificates_path)"; do
if [ ! -x "${dependency}" ]; then
lecho "Fatal: Crash sending disabled: ${dependency} not found."
exit 1
diff --git a/crash_reporter/kernel_collector_test.cc b/crash_reporter/kernel_collector_test.cc
index cdb0ae7..015f624 100644
--- a/crash_reporter/kernel_collector_test.cc
+++ b/crash_reporter/kernel_collector_test.cc
@@ -253,15 +253,6 @@
ASSERT_EQ(0, s_crashes);
}
-TEST_F(KernelCollectorTest, CollectBadDirectory) {
- WriteStringToFile(kcrash_file(), "====1.1\nsomething");
- ASSERT_TRUE(collector_.Collect());
- ASSERT_TRUE(FindLog("Unable to create appropriate crash directory"))
- << "Did not find expected error string in log: {\n"
- << GetLog() << "}";
- ASSERT_EQ(1, s_crashes);
-}
-
void KernelCollectorTest::SetUpSuccessfulCollect() {
collector_.ForceCrashDirectory(test_crash_directory());
WriteStringToFile(kcrash_file(), "====1.1\nsomething");
diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc
index 56e7bb9..6714f52 100644
--- a/crash_reporter/user_collector.cc
+++ b/crash_reporter/user_collector.cc
@@ -90,9 +90,9 @@
directory_failure_ = directory_failure;
filter_in_ = filter_in;
- gid_t groups[] = { AID_ROOT, AID_SYSTEM, AID_DBUS };
+ gid_t groups[] = { AID_ROOT, AID_SYSTEM, AID_DBUS, AID_READPROC };
if (setgroups(arraysize(groups), groups) != 0) {
- PLOG(FATAL) << "Unable to set groups to root, system, and dbus";
+ PLOG(FATAL) << "Unable to set groups to root, system, dbus, and readproc";
}
}
diff --git a/crash_reporter/user_collector_test.cc b/crash_reporter/user_collector_test.cc
index 72e61e6..638ea34 100644
--- a/crash_reporter/user_collector_test.cc
+++ b/crash_reporter/user_collector_test.cc
@@ -37,11 +37,6 @@
const char kFilePath[] = "/my/path";
-// Keep in sync with UserCollector::ShouldDump.
-const char kChromeIgnoreMsg[] =
- "ignoring call by kernel - chrome crash; "
- "waiting for chrome to call us directly";
-
void CountCrash() {
++s_crashes;
}
@@ -167,24 +162,6 @@
ASSERT_EQ(s_crashes, 1);
}
-TEST_F(UserCollectorTest, HandleChromeCrashWithConsent) {
- s_metrics = true;
- collector_.HandleCrash("5:2:ignored", "chrome");
- EXPECT_TRUE(FindLog(
- "Received crash notification for chrome[5] sig 2"));
- EXPECT_TRUE(FindLog(kChromeIgnoreMsg));
- ASSERT_EQ(s_crashes, 0);
-}
-
-TEST_F(UserCollectorTest, HandleSuppliedChromeCrashWithConsent) {
- s_metrics = true;
- collector_.HandleCrash("0:2:chrome", nullptr);
- EXPECT_TRUE(FindLog(
- "Received crash notification for supplied_chrome[0] sig 2"));
- EXPECT_TRUE(FindLog(kChromeIgnoreMsg));
- ASSERT_EQ(s_crashes, 0);
-}
-
TEST_F(UserCollectorTest, GetProcessPath) {
FilePath path = collector_.GetProcessPath(100);
ASSERT_EQ("/proc/100", path.value());
@@ -226,7 +203,7 @@
pid_t my_pid = getpid();
EXPECT_TRUE(collector_.GetExecutableBaseNameFromPid(my_pid, &base_name));
EXPECT_FALSE(FindLog("Readlink failed"));
- EXPECT_EQ("crash_reporter_test", base_name);
+ EXPECT_EQ("crash_reporter_tests", base_name);
}
TEST_F(UserCollectorTest, GetFirstLineWithPrefix) {
diff --git a/debuggerd/.clang-format b/debuggerd/.clang-format
new file mode 100644
index 0000000..9b7478c
--- /dev/null
+++ b/debuggerd/.clang-format
@@ -0,0 +1,15 @@
+BasedOnStyle: Google
+AllowShortBlocksOnASingleLine: false
+AllowShortFunctionsOnASingleLine: false
+
+ColumnLimit: 100
+CommentPragmas: NOLINT:.*
+DerivePointerAlignment: false
+IndentWidth: 2
+ContinuationIndentWidth: 2
+PointerAlignment: Left
+TabWidth: 2
+UseTab: Never
+PenaltyExcessCharacter: 32
+
+Cpp11BracedListStyle: false
diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp
index 1287fb9..58b629b 100644
--- a/debuggerd/debuggerd.cpp
+++ b/debuggerd/debuggerd.cpp
@@ -24,19 +24,21 @@
#include <dirent.h>
#include <time.h>
-#include <sys/ptrace.h>
-#include <sys/wait.h>
#include <elf.h>
-#include <sys/stat.h>
#include <sys/poll.h>
+#include <sys/prctl.h>
+#include <sys/ptrace.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
#include <selinux/android.h>
#include <log/logger.h>
-#include <cutils/sockets.h>
-#include <cutils/properties.h>
#include <cutils/debugger.h>
+#include <cutils/properties.h>
+#include <cutils/sockets.h>
+#include <nativehelper/ScopedFd.h>
#include <linux/input.h>
@@ -336,161 +338,184 @@
static void handle_request(int fd) {
ALOGV("handle_request(%d)\n", fd);
+ ScopedFd closer(fd);
debugger_request_t request;
memset(&request, 0, sizeof(request));
int status = read_request(fd, &request);
- if (!status) {
- ALOGV("BOOM: pid=%d uid=%d gid=%d tid=%d\n",
- request.pid, request.uid, request.gid, request.tid);
+ if (status != 0) {
+ return;
+ }
+
+ ALOGV("BOOM: pid=%d uid=%d gid=%d tid=%d\n", request.pid, request.uid, request.gid, request.tid);
#if defined(__LP64__)
- // On 64 bit systems, requests to dump 32 bit and 64 bit tids come
- // to the 64 bit debuggerd. If the process is a 32 bit executable,
- // redirect the request to the 32 bit debuggerd.
- if (is32bit(request.tid)) {
- // Only dump backtrace and dump tombstone requests can be redirected.
- if (request.action == DEBUGGER_ACTION_DUMP_BACKTRACE
- || request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) {
- redirect_to_32(fd, &request);
- } else {
- ALOGE("debuggerd: Not allowed to redirect action %d to 32 bit debuggerd\n",
- request.action);
- }
- close(fd);
- return;
- }
-#endif
-
- // At this point, the thread that made the request is blocked in
- // a read() call. If the thread has crashed, then this gives us
- // time to PTRACE_ATTACH to it before it has a chance to really fault.
- //
- // The PTRACE_ATTACH sends a SIGSTOP to the target process, but it
- // won't necessarily have stopped by the time ptrace() returns. (We
- // currently assume it does.) We write to the file descriptor to
- // ensure that it can run as soon as we call PTRACE_CONT below.
- // See details in bionic/libc/linker/debugger.c, in function
- // debugger_signal_handler().
- if (ptrace(PTRACE_ATTACH, request.tid, 0, 0)) {
- ALOGE("ptrace attach failed: %s\n", strerror(errno));
+ // On 64 bit systems, requests to dump 32 bit and 64 bit tids come
+ // to the 64 bit debuggerd. If the process is a 32 bit executable,
+ // redirect the request to the 32 bit debuggerd.
+ if (is32bit(request.tid)) {
+ // Only dump backtrace and dump tombstone requests can be redirected.
+ if (request.action == DEBUGGER_ACTION_DUMP_BACKTRACE ||
+ request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) {
+ redirect_to_32(fd, &request);
} else {
- bool detach_failed = false;
- bool tid_unresponsive = false;
- bool attach_gdb = should_attach_gdb(&request);
- if (TEMP_FAILURE_RETRY(write(fd, "\0", 1)) != 1) {
- ALOGE("failed responding to client: %s\n", strerror(errno));
- } else {
- char* tombstone_path = NULL;
-
- if (request.action == DEBUGGER_ACTION_CRASH) {
- close(fd);
- fd = -1;
- }
-
- int total_sleep_time_usec = 0;
- for (;;) {
- int signal = wait_for_sigstop(request.tid, &total_sleep_time_usec, &detach_failed);
- if (signal == -1) {
- tid_unresponsive = true;
- break;
- }
-
- switch (signal) {
- case SIGSTOP:
- if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) {
- ALOGV("stopped -- dumping to tombstone\n");
- tombstone_path = engrave_tombstone(request.pid, request.tid,
- signal, request.original_si_code,
- request.abort_msg_address, true,
- &detach_failed, &total_sleep_time_usec);
- } else if (request.action == DEBUGGER_ACTION_DUMP_BACKTRACE) {
- ALOGV("stopped -- dumping to fd\n");
- dump_backtrace(fd, -1, request.pid, request.tid, &detach_failed,
- &total_sleep_time_usec);
- } else {
- ALOGV("stopped -- continuing\n");
- status = ptrace(PTRACE_CONT, request.tid, 0, 0);
- if (status) {
- ALOGE("ptrace continue failed: %s\n", strerror(errno));
- }
- continue; // loop again
- }
- break;
-
- case SIGABRT:
- case SIGBUS:
- case SIGFPE:
- case SIGILL:
- case SIGSEGV:
-#ifdef SIGSTKFLT
- case SIGSTKFLT:
+ ALOGE("debuggerd: Not allowed to redirect action %d to 32 bit debuggerd\n", request.action);
+ }
+ return;
+ }
#endif
- case SIGTRAP:
- ALOGV("stopped -- fatal signal\n");
- // Send a SIGSTOP to the process to make all of
- // the non-signaled threads stop moving. Without
- // this we get a lot of "ptrace detach failed:
- // No such process".
- kill(request.pid, SIGSTOP);
- // don't dump sibling threads when attaching to GDB because it
- // makes the process less reliable, apparently...
- tombstone_path = engrave_tombstone(request.pid, request.tid,
- signal, request.original_si_code,
- request.abort_msg_address, !attach_gdb,
- &detach_failed, &total_sleep_time_usec);
- break;
- default:
- ALOGE("process stopped due to unexpected signal %d\n", signal);
- break;
- }
- break;
- }
+ // Fork a child to handle the rest of the request.
+ pid_t fork_pid = fork();
+ if (fork_pid == -1) {
+ ALOGE("debuggerd: failed to fork: %s\n", strerror(errno));
+ return;
+ } else if (fork_pid != 0) {
+ waitpid(fork_pid, nullptr, 0);
+ return;
+ }
- if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) {
- if (tombstone_path) {
- write(fd, tombstone_path, strlen(tombstone_path));
- }
- close(fd);
- fd = -1;
- }
- free(tombstone_path);
+ // Open the tombstone file if we need it.
+ std::string tombstone_path;
+ int tombstone_fd = -1;
+ switch (request.action) {
+ case DEBUGGER_ACTION_DUMP_TOMBSTONE:
+ case DEBUGGER_ACTION_CRASH:
+ tombstone_fd = open_tombstone(&tombstone_path);
+ if (tombstone_fd == -1) {
+ ALOGE("debuggerd: failed to open tombstone file: %s\n", strerror(errno));
+ exit(1);
}
+ break;
- if (!tid_unresponsive) {
- ALOGV("detaching");
- if (attach_gdb) {
- // stop the process so we can debug
- kill(request.pid, SIGSTOP);
- }
- if (ptrace(PTRACE_DETACH, request.tid, 0, 0)) {
- ALOGE("ptrace detach from %d failed: %s", request.tid, strerror(errno));
- detach_failed = true;
- } else if (attach_gdb) {
- // if debug.db.uid is set, its value indicates if we should wait
- // for user action for the crashing process.
- // in this case, we log a message and turn the debug LED on
- // waiting for a gdb connection (for instance)
- wait_for_user_action(request);
- }
- }
+ case DEBUGGER_ACTION_DUMP_BACKTRACE:
+ break;
- // resume stopped process (so it can crash in peace).
- kill(request.pid, SIGCONT);
+ default:
+ ALOGE("debuggerd: unexpected request action: %d", request.action);
+ exit(1);
+ }
- // If we didn't successfully detach, we're still the parent, and the
- // actual parent won't receive a death notification via wait(2). At this point
- // there's not much we can do about that.
- if (detach_failed) {
- ALOGE("debuggerd committing suicide to free the zombie!\n");
- kill(getpid(), SIGKILL);
- }
+ // At this point, the thread that made the request is blocked in
+ // a read() call. If the thread has crashed, then this gives us
+ // time to PTRACE_ATTACH to it before it has a chance to really fault.
+ //
+ // The PTRACE_ATTACH sends a SIGSTOP to the target process, but it
+ // won't necessarily have stopped by the time ptrace() returns. (We
+ // currently assume it does.) We write to the file descriptor to
+ // ensure that it can run as soon as we call PTRACE_CONT below.
+ // See details in bionic/libc/linker/debugger.c, in function
+ // debugger_signal_handler().
+ if (ptrace(PTRACE_ATTACH, request.tid, 0, 0)) {
+ ALOGE("debuggerd: ptrace attach failed: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ // Generate the backtrace map before dropping privileges.
+ std::unique_ptr<BacktraceMap> backtrace_map(BacktraceMap::Create(request.pid));
+
+ // Now that we've done everything that requires privileges, we can drop them.
+ if (setresgid(AID_DEBUGGERD, AID_DEBUGGERD, AID_DEBUGGERD) != 0) {
+ ALOGE("debuggerd: failed to setresgid");
+ exit(1);
+ }
+
+ if (setresuid(AID_DEBUGGERD, AID_DEBUGGERD, AID_DEBUGGERD) != 0) {
+ ALOGE("debuggerd: failed to setresuid");
+ exit(1);
+ }
+
+ bool detach_failed = false;
+ bool tid_unresponsive = false;
+ bool attach_gdb = should_attach_gdb(&request);
+ if (TEMP_FAILURE_RETRY(write(fd, "\0", 1)) != 1) {
+ ALOGE("debuggerd: failed to respond to client: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ int total_sleep_time_usec = 0;
+ while (true) {
+ int signal = wait_for_sigstop(request.tid, &total_sleep_time_usec, &detach_failed);
+ if (signal == -1) {
+ tid_unresponsive = true;
+ break;
}
+ switch (signal) {
+ case SIGSTOP:
+ if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) {
+ ALOGV("stopped -- dumping to tombstone\n");
+ engrave_tombstone(tombstone_fd, backtrace_map.get(), request.pid, request.tid, signal,
+ request.original_si_code, request.abort_msg_address, true,
+ &detach_failed, &total_sleep_time_usec);
+ } else if (request.action == DEBUGGER_ACTION_DUMP_BACKTRACE) {
+ ALOGV("stopped -- dumping to fd\n");
+ dump_backtrace(fd, -1, request.pid, request.tid, &detach_failed, &total_sleep_time_usec);
+ } else {
+ ALOGV("stopped -- continuing\n");
+ status = ptrace(PTRACE_CONT, request.tid, 0, 0);
+ if (status) {
+ ALOGE("debuggerd: ptrace continue failed: %s\n", strerror(errno));
+ }
+ continue; // loop again
+ }
+ break;
+
+ case SIGABRT:
+ case SIGBUS:
+ case SIGFPE:
+ case SIGILL:
+ case SIGSEGV:
+#ifdef SIGSTKFLT
+ case SIGSTKFLT:
+#endif
+ case SIGTRAP:
+ ALOGV("stopped -- fatal signal\n");
+ // Send a SIGSTOP to the process to make all of
+ // the non-signaled threads stop moving. Without
+ // this we get a lot of "ptrace detach failed:
+ // No such process".
+ kill(request.pid, SIGSTOP);
+ // don't dump sibling threads when attaching to GDB because it
+ // makes the process less reliable, apparently...
+ engrave_tombstone(tombstone_fd, backtrace_map.get(), request.pid, request.tid, signal,
+ request.original_si_code, request.abort_msg_address, !attach_gdb,
+ &detach_failed, &total_sleep_time_usec);
+ break;
+
+ default:
+ ALOGE("debuggerd: process stopped due to unexpected signal %d\n", signal);
+ break;
+ }
+ break;
}
- if (fd >= 0) {
- close(fd);
+
+ if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) {
+ if (!tombstone_path.empty()) {
+ write(fd, tombstone_path.c_str(), tombstone_path.length());
+ }
}
+
+ if (!tid_unresponsive) {
+ ALOGV("detaching");
+ if (attach_gdb) {
+ // stop the process so we can debug
+ kill(request.pid, SIGSTOP);
+ }
+ if (ptrace(PTRACE_DETACH, request.tid, 0, 0)) {
+ ALOGE("debuggerd: ptrace detach from %d failed: %s", request.tid, strerror(errno));
+ detach_failed = true;
+ } else if (attach_gdb) {
+ // if debug.db.uid is set, its value indicates if we should wait
+ // for user action for the crashing process.
+ // in this case, we log a message and turn the debug LED on
+ // waiting for a gdb connection (for instance)
+ wait_for_user_action(request);
+ }
+ }
+
+ // Resume the stopped process so it can crash in peace, and exit.
+ kill(request.pid, SIGCONT);
+ exit(0);
}
static int do_server() {
@@ -531,11 +556,12 @@
ALOGI("debuggerd: starting\n");
for (;;) {
- sockaddr addr;
- socklen_t alen = sizeof(addr);
+ sockaddr_storage ss;
+ sockaddr* addrp = reinterpret_cast<sockaddr*>(&ss);
+ socklen_t alen = sizeof(ss);
ALOGV("waiting for connection\n");
- int fd = accept(s, &addr, &alen);
+ int fd = accept(s, addrp, &alen);
if (fd < 0) {
ALOGV("accept failed: %s\n", strerror(errno));
continue;
diff --git a/debuggerd/debuggerd.rc b/debuggerd/debuggerd.rc
index 4338ae9..e43fe96 100644
--- a/debuggerd/debuggerd.rc
+++ b/debuggerd/debuggerd.rc
@@ -1,2 +1,4 @@
service debuggerd /system/bin/debuggerd
class main
+ group root readproc
+ writepid /dev/cpuset/system-background/tasks
diff --git a/debuggerd/debuggerd64.rc b/debuggerd/debuggerd64.rc
index 341a329..35b5af3 100644
--- a/debuggerd/debuggerd64.rc
+++ b/debuggerd/debuggerd64.rc
@@ -1,2 +1,4 @@
service debuggerd64 /system/bin/debuggerd64
class main
+ group root readproc
+ writepid /dev/cpuset/system-background/tasks
diff --git a/debuggerd/elf_utils.cpp b/debuggerd/elf_utils.cpp
index 5ea03e7..3f0dbde 100644
--- a/debuggerd/elf_utils.cpp
+++ b/debuggerd/elf_utils.cpp
@@ -24,7 +24,7 @@
#include <string>
#include <backtrace/Backtrace.h>
-#include <base/stringprintf.h>
+#include <android-base/stringprintf.h>
#include <log/log.h>
#include "elf_utils.h"
diff --git a/debuggerd/test/dump_memory_test.cpp b/debuggerd/test/dump_memory_test.cpp
index 75e7028..2addd5d 100644
--- a/debuggerd/test/dump_memory_test.cpp
+++ b/debuggerd/test/dump_memory_test.cpp
@@ -20,7 +20,7 @@
#include <string>
#include <gtest/gtest.h>
-#include <base/file.h>
+#include <android-base/file.h>
#include "BacktraceMock.h"
#include "log_fake.h"
diff --git a/debuggerd/test/log_fake.cpp b/debuggerd/test/log_fake.cpp
index d584a5e..e27e9f6 100644
--- a/debuggerd/test/log_fake.cpp
+++ b/debuggerd/test/log_fake.cpp
@@ -19,7 +19,7 @@
#include <string>
-#include <base/stringprintf.h>
+#include <android-base/stringprintf.h>
#include <log/log.h>
#include <log/logger.h>
diff --git a/debuggerd/test/tombstone_test.cpp b/debuggerd/test/tombstone_test.cpp
index d945d27..96b3a7a 100644
--- a/debuggerd/test/tombstone_test.cpp
+++ b/debuggerd/test/tombstone_test.cpp
@@ -20,7 +20,7 @@
#include <string>
#include <gtest/gtest.h>
-#include <base/file.h>
+#include <android-base/file.h>
#include "utility.h"
diff --git a/debuggerd/tombstone.cpp b/debuggerd/tombstone.cpp
index e283923..b2f203d 100644
--- a/debuggerd/tombstone.cpp
+++ b/debuggerd/tombstone.cpp
@@ -37,7 +37,7 @@
#include <private/android_filesystem_config.h>
-#include <base/stringprintf.h>
+#include <android-base/stringprintf.h>
#include <cutils/properties.h>
#include <log/log.h>
#include <log/logger.h>
@@ -632,7 +632,7 @@
}
// Dumps all information about the specified pid to the tombstone.
-static bool dump_crash(log_t* log, pid_t pid, pid_t tid, int signal, int si_code,
+static bool dump_crash(log_t* log, BacktraceMap* map, pid_t pid, pid_t tid, int signal, int si_code,
uintptr_t abort_msg_address, bool dump_sibling_threads,
int* total_sleep_time_usec) {
// don't copy log messages to tombstone unless this is a dev device
@@ -659,8 +659,7 @@
dump_signal_info(log, tid, signal, si_code);
}
- std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(pid));
- std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, tid, map.get()));
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, tid, map));
dump_abort_message(backtrace.get(), log, abort_msg_address);
dump_registers(log, tid);
if (backtrace->Unwind(0)) {
@@ -669,8 +668,8 @@
ALOGE("Unwind failed: pid = %d, tid = %d", pid, tid);
}
dump_memory_and_code(log, backtrace.get());
- if (map.get() != nullptr) {
- dump_all_maps(backtrace.get(), map.get(), log, tid);
+ if (map) {
+ dump_all_maps(backtrace.get(), map, log, tid);
}
if (want_logs) {
@@ -679,7 +678,7 @@
bool detach_failed = false;
if (dump_sibling_threads) {
- detach_failed = dump_sibling_thread_report(log, pid, tid, total_sleep_time_usec, map.get());
+ detach_failed = dump_sibling_thread_report(log, pid, tid, total_sleep_time_usec, map);
}
if (want_logs) {
@@ -698,53 +697,57 @@
return detach_failed;
}
-// find_and_open_tombstone - find an available tombstone slot, if any, of the
+// open_tombstone - find an available tombstone slot, if any, of the
// form tombstone_XX where XX is 00 to MAX_TOMBSTONES-1, inclusive. If no
// file is available, we reuse the least-recently-modified file.
-//
-// Returns the path of the tombstone file, allocated using malloc(). Caller must free() it.
-static char* find_and_open_tombstone(int* fd) {
+int open_tombstone(std::string* out_path) {
// In a single pass, find an available slot and, in case none
// exist, find and record the least-recently-modified file.
char path[128];
+ int fd = -1;
int oldest = -1;
struct stat oldest_sb;
for (int i = 0; i < MAX_TOMBSTONES; i++) {
snprintf(path, sizeof(path), TOMBSTONE_TEMPLATE, i);
struct stat sb;
- if (!stat(path, &sb)) {
+ if (stat(path, &sb) == 0) {
if (oldest < 0 || sb.st_mtime < oldest_sb.st_mtime) {
oldest = i;
oldest_sb.st_mtime = sb.st_mtime;
}
continue;
}
- if (errno != ENOENT)
- continue;
+ if (errno != ENOENT) continue;
- *fd = open(path, O_CREAT | O_EXCL | O_WRONLY | O_NOFOLLOW | O_CLOEXEC, 0600);
- if (*fd < 0)
- continue; // raced ?
+ fd = open(path, O_CREAT | O_EXCL | O_WRONLY | O_NOFOLLOW | O_CLOEXEC, 0600);
+ if (fd < 0) continue; // raced ?
- fchown(*fd, AID_SYSTEM, AID_SYSTEM);
- return strdup(path);
+ if (out_path) {
+ *out_path = path;
+ }
+ fchown(fd, AID_SYSTEM, AID_SYSTEM);
+ return fd;
}
if (oldest < 0) {
- ALOGE("Failed to find a valid tombstone, default to using tombstone 0.\n");
+ ALOGE("debuggerd: failed to find a valid tombstone, default to using tombstone 0.\n");
oldest = 0;
}
// we didn't find an available file, so we clobber the oldest one
snprintf(path, sizeof(path), TOMBSTONE_TEMPLATE, oldest);
- *fd = open(path, O_CREAT | O_TRUNC | O_WRONLY | O_NOFOLLOW | O_CLOEXEC, 0600);
- if (*fd < 0) {
- ALOGE("failed to open tombstone file '%s': %s\n", path, strerror(errno));
- return NULL;
+ fd = open(path, O_CREAT | O_TRUNC | O_WRONLY | O_NOFOLLOW | O_CLOEXEC, 0600);
+ if (fd < 0) {
+ ALOGE("debuggerd: failed to open tombstone file '%s': %s\n", path, strerror(errno));
+ return -1;
}
- fchown(*fd, AID_SYSTEM, AID_SYSTEM);
- return strdup(path);
+
+ if (out_path) {
+ *out_path = path;
+ }
+ fchown(fd, AID_SYSTEM, AID_SYSTEM);
+ return fd;
}
static int activity_manager_connect() {
@@ -777,49 +780,27 @@
return amfd;
}
-char* engrave_tombstone(pid_t pid, pid_t tid, int signal, int original_si_code,
- uintptr_t abort_msg_address, bool dump_sibling_threads,
- bool* detach_failed, int* total_sleep_time_usec) {
-
+void engrave_tombstone(int tombstone_fd, BacktraceMap* map, pid_t pid, pid_t tid, int signal,
+ int original_si_code, uintptr_t abort_msg_address, bool dump_sibling_threads,
+ bool* detach_failed, int* total_sleep_time_usec) {
log_t log;
log.current_tid = tid;
log.crashed_tid = tid;
- if ((mkdir(TOMBSTONE_DIR, 0755) == -1) && (errno != EEXIST)) {
- ALOGE("failed to create %s: %s\n", TOMBSTONE_DIR, strerror(errno));
- }
-
- if (chown(TOMBSTONE_DIR, AID_SYSTEM, AID_SYSTEM) == -1) {
- ALOGE("failed to change ownership of %s: %s\n", TOMBSTONE_DIR, strerror(errno));
- }
-
- int fd = -1;
- char* path = NULL;
- if (selinux_android_restorecon(TOMBSTONE_DIR, 0) == 0) {
- path = find_and_open_tombstone(&fd);
- } else {
- ALOGE("Failed to restore security context, not writing tombstone.\n");
- }
-
- if (fd < 0) {
- ALOGE("Skipping tombstone write, nothing to do.\n");
+ if (tombstone_fd < 0) {
+ ALOGE("debuggerd: skipping tombstone write, nothing to do.\n");
*detach_failed = false;
- return NULL;
+ return;
}
- log.tfd = fd;
+ log.tfd = tombstone_fd;
// Preserve amfd since it can be modified through the calls below without
// being closed.
int amfd = activity_manager_connect();
log.amfd = amfd;
- *detach_failed = dump_crash(&log, pid, tid, signal, original_si_code, abort_msg_address,
+ *detach_failed = dump_crash(&log, map, pid, tid, signal, original_si_code, abort_msg_address,
dump_sibling_threads, total_sleep_time_usec);
- _LOG(&log, logtype::BACKTRACE, "\nTombstone written to: %s\n", path);
-
- // Either of these file descriptors can be -1, any error is ignored.
+ // This file descriptor can be -1, any error is ignored.
close(amfd);
- close(fd);
-
- return path;
}
diff --git a/debuggerd/tombstone.h b/debuggerd/tombstone.h
index 7e2b2fe..5f2d239 100644
--- a/debuggerd/tombstone.h
+++ b/debuggerd/tombstone.h
@@ -17,15 +17,23 @@
#ifndef _DEBUGGERD_TOMBSTONE_H
#define _DEBUGGERD_TOMBSTONE_H
-#include <stddef.h>
#include <stdbool.h>
+#include <stddef.h>
#include <sys/types.h>
+#include <string>
+
+class BacktraceMap;
+
+/* Create and open a tombstone file for writing.
+ * Returns a writable file descriptor, or -1 with errno set appropriately.
+ * If out_path is non-null, *out_path is set to the path of the tombstone file.
+ */
+int open_tombstone(std::string* path);
/* Creates a tombstone file and writes the crash dump to it.
* Returns the path of the tombstone, which must be freed using free(). */
-char* engrave_tombstone(pid_t pid, pid_t tid, int signal, int original_si_code,
- uintptr_t abort_msg_address,
- bool dump_sibling_threads, bool* detach_failed,
- int* total_sleep_time_usec);
+void engrave_tombstone(int tombstone_fd, BacktraceMap* map, pid_t pid, pid_t tid, int signal,
+ int original_si_code, uintptr_t abort_msg_address, bool dump_sibling_threads,
+ bool* detach_failed, int* total_sleep_time_usec);
#endif // _DEBUGGERD_TOMBSTONE_H
diff --git a/debuggerd/utility.cpp b/debuggerd/utility.cpp
index f5d6ec1..ce214f9 100644
--- a/debuggerd/utility.cpp
+++ b/debuggerd/utility.cpp
@@ -25,9 +25,9 @@
#include <sys/ptrace.h>
#include <sys/wait.h>
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
#include <backtrace/Backtrace.h>
-#include <base/file.h>
-#include <base/stringprintf.h>
#include <log/log.h>
const int SLEEP_TIME_USEC = 50000; // 0.05 seconds
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index 3f201ec..8cbc79b 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -18,9 +18,12 @@
include $(CLEAR_VARS)
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/../mkbootimg \
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/../adb \
+ $(LOCAL_PATH)/../mkbootimg \
$(LOCAL_PATH)/../../extras/ext4_utils \
- $(LOCAL_PATH)/../../extras/f2fs_utils
+ $(LOCAL_PATH)/../../extras/f2fs_utils \
+
LOCAL_SRC_FILES := protocol.cpp engine.cpp bootimg_utils.cpp fastboot.cpp util.cpp fs.cpp
LOCAL_MODULE := fastboot
LOCAL_MODULE_TAGS := debug
@@ -30,13 +33,15 @@
LOCAL_CFLAGS += -DFASTBOOT_REVISION='"$(fastboot_version)"'
-LOCAL_SRC_FILES_linux := usb_linux.cpp util_linux.cpp
+LOCAL_SRC_FILES_linux := socket_unix.cpp usb_linux.cpp util_linux.cpp
+LOCAL_STATIC_LIBRARIES_linux := libcutils libselinux
-LOCAL_SRC_FILES_darwin := usb_osx.cpp util_osx.cpp
+LOCAL_SRC_FILES_darwin := socket_unix.cpp usb_osx.cpp util_osx.cpp
+LOCAL_STATIC_LIBRARIES_darwin := libcutils libselinux
LOCAL_LDLIBS_darwin := -lpthread -framework CoreFoundation -framework IOKit -framework Carbon
LOCAL_CFLAGS_darwin := -Wno-unused-parameter
-LOCAL_SRC_FILES_windows := usb_windows.cpp util_windows.cpp
+LOCAL_SRC_FILES_windows := socket_windows.cpp usb_windows.cpp util_windows.cpp
LOCAL_STATIC_LIBRARIES_windows := AdbWinApi
LOCAL_REQUIRED_MODULES_windows := AdbWinApi
LOCAL_LDLIBS_windows := -lws2_32
@@ -49,10 +54,8 @@
libutils \
liblog \
libz \
- libbase
-
-LOCAL_STATIC_LIBRARIES_darwin := libselinux
-LOCAL_STATIC_LIBRARIES_linux := libselinux
+ libdiagnose_usb \
+ libbase \
# libf2fs_dlutils_host will dlopen("libf2fs_fmt_host_dyn")
LOCAL_CFLAGS_linux := -DUSE_F2FS
@@ -83,5 +86,31 @@
LOCAL_SRC_FILES := usbtest.cpp usb_linux.cpp util.cpp
LOCAL_MODULE := usbtest
LOCAL_CFLAGS := -Werror
+LOCAL_STATIC_LIBRARIES := libbase
include $(BUILD_HOST_EXECUTABLE)
endif
+
+# fastboot_test
+# =========================================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := fastboot_test
+LOCAL_MODULE_HOST_OS := darwin linux windows
+
+LOCAL_SRC_FILES := socket_test.cpp
+LOCAL_STATIC_LIBRARIES := libbase
+
+LOCAL_CFLAGS += -Wall -Wextra -Werror -Wunreachable-code
+
+LOCAL_SRC_FILES_linux := socket_unix.cpp
+LOCAL_STATIC_LIBRARIES_linux := libcutils
+
+LOCAL_SRC_FILES_darwin := socket_unix.cpp
+LOCAL_LDLIBS_darwin := -lpthread -framework CoreFoundation -framework IOKit -framework Carbon
+LOCAL_CFLAGS_darwin := -Wno-unused-parameter
+LOCAL_STATIC_LIBRARIES_darwin := libcutils
+
+LOCAL_SRC_FILES_windows := socket_windows.cpp
+LOCAL_LDLIBS_windows := -lws2_32
+
+include $(BUILD_HOST_NATIVE_TEST)
diff --git a/fastboot/engine.cpp b/fastboot/engine.cpp
index 63e8971..ac5a17a 100644
--- a/fastboot/engine.cpp
+++ b/fastboot/engine.cpp
@@ -75,37 +75,19 @@
-bool fb_getvar(usb_handle* usb, const std::string& key, std::string* value) {
+bool fb_getvar(Transport* transport, const std::string& key, std::string* value) {
std::string cmd = "getvar:";
cmd += key;
char buf[FB_RESPONSE_SZ + 1];
memset(buf, 0, sizeof(buf));
- if (fb_command_response(usb, cmd.c_str(), buf)) {
+ if (fb_command_response(transport, cmd.c_str(), buf)) {
return false;
}
*value = buf;
return true;
}
-
-// Return true if this partition is supported by the fastboot format command.
-// It is also used to determine if we should first erase a partition before
-// flashing it with an ext4 filesystem. See needs_erase()
-//
-// Not all devices report the filesystem type, so don't report any errors,
-// just return false.
-bool fb_format_supported(usb_handle *usb, const char *partition, const char *type_override) {
- if (type_override) {
- return fs_get_generator(type_override) != nullptr;
- }
- std::string partition_type;
- if (!fb_getvar(usb, std::string("partition-type:") + partition, &partition_type)) {
- return false;
- }
- return fs_get_generator(partition_type.c_str()) != nullptr;
-}
-
static int cb_default(Action* a, int status, const char* resp) {
if (status) {
fprintf(stderr,"FAILED (%s)\n", resp);
@@ -148,6 +130,13 @@
return a;
}
+void fb_set_active(const char *slot)
+{
+ Action *a;
+ a = queue_action(OP_COMMAND, "set_active:%s", slot);
+ a->msg = mkmsg("Setting current slot to '%s'", slot);
+}
+
void fb_queue_erase(const char *ptn)
{
Action *a;
@@ -341,7 +330,7 @@
queue_action(OP_WAIT_FOR_DISCONNECT, "");
}
-int fb_execute_queue(usb_handle *usb)
+int fb_execute_queue(Transport* transport)
{
Action *a;
char resp[FB_RESPONSE_SZ+1];
@@ -361,25 +350,25 @@
fprintf(stderr,"%s...\n",a->msg);
}
if (a->op == OP_DOWNLOAD) {
- status = fb_download_data(usb, a->data, a->size);
+ status = fb_download_data(transport, a->data, a->size);
status = a->func(a, status, status ? fb_get_error() : "");
if (status) break;
} else if (a->op == OP_COMMAND) {
- status = fb_command(usb, a->cmd);
+ status = fb_command(transport, a->cmd);
status = a->func(a, status, status ? fb_get_error() : "");
if (status) break;
} else if (a->op == OP_QUERY) {
- status = fb_command_response(usb, a->cmd, resp);
+ status = fb_command_response(transport, a->cmd, resp);
status = a->func(a, status, status ? fb_get_error() : resp);
if (status) break;
} else if (a->op == OP_NOTICE) {
fprintf(stderr,"%s\n",(char*)a->data);
} else if (a->op == OP_DOWNLOAD_SPARSE) {
- status = fb_download_data_sparse(usb, reinterpret_cast<sparse_file*>(a->data));
+ status = fb_download_data_sparse(transport, reinterpret_cast<sparse_file*>(a->data));
status = a->func(a, status, status ? fb_get_error() : "");
if (status) break;
} else if (a->op == OP_WAIT_FOR_DISCONNECT) {
- usb_wait_for_disconnect(usb);
+ transport->WaitForDisconnect();
} else {
die("bogus action");
}
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 6b845a0..5b66366 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -42,14 +42,22 @@
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
+#include <functional>
-#include <base/parseint.h>
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
#include <sparse/sparse.h>
#include <ziparchive/zip_archive.h>
+#include <android-base/strings.h>
+#include <android-base/parseint.h>
+
#include "bootimg_utils.h"
+#include "diagnose_usb.h"
#include "fastboot.h"
#include "fs.h"
+#include "transport.h"
+#include "usb.h"
#ifndef O_BINARY
#define O_BINARY 0
@@ -197,36 +205,36 @@
static int list_devices_callback(usb_ifc_info* info) {
if (match_fastboot_with_serial(info, nullptr) == 0) {
- const char* serial = info->serial_number;
+ std::string serial = info->serial_number;
if (!info->writable) {
- serial = "no permissions"; // like "adb devices"
+ serial = UsbNoPermissionsShortHelpText();
}
if (!serial[0]) {
serial = "????????????";
}
// output compatible with "adb devices"
if (!long_listing) {
- printf("%s\tfastboot\n", serial);
- } else if (strcmp("", info->device_path) == 0) {
- printf("%-22s fastboot\n", serial);
+ printf("%s\tfastboot", serial.c_str());
} else {
- printf("%-22s fastboot %s\n", serial, info->device_path);
+ printf("%-22s fastboot", serial.c_str());
+ if (strlen(info->device_path) > 0) printf(" %s", info->device_path);
}
+ putchar('\n');
}
return -1;
}
-static usb_handle* open_device() {
- static usb_handle *usb = 0;
+static Transport* open_device() {
+ static Transport* transport = nullptr;
int announce = 1;
- if(usb) return usb;
+ if (transport) return transport;
- for(;;) {
- usb = usb_open(match_fastboot);
- if(usb) return usb;
- if(announce) {
+ for (;;) {
+ transport = usb_open(match_fastboot);
+ if (transport) return transport;
+ if (announce) {
announce = 0;
fprintf(stderr, "< waiting for %s >\n", serial ? serial : "any device");
}
@@ -261,12 +269,21 @@
" partitions.\n"
" flashing get_unlock_ability Queries bootloader to see if the\n"
" device is unlocked.\n"
+ " flashing get_unlock_bootloader_nonce Queries the bootloader to get the\n"
+ " unlock nonce.\n"
+ " flashing unlock_bootloader <request> Issue unlock bootloader using request.\n"
+ " flashing lock_bootloader Locks the bootloader to prevent\n"
+ " bootloader version rollback.\n"
" erase <partition> Erase a flash partition.\n"
" format[:[<fs type>][:[<size>]] <partition>\n"
" Format a flash partition. Can\n"
" override the fs type and/or size\n"
" the bootloader reports.\n"
" getvar <variable> Display a bootloader variable.\n"
+ " set_active <suffix> Sets the active slot. If slots are\n"
+ " not supported, this does nothing.\n"
+ " note: suffixes starting with a '-'\n"
+ " must use set_active -- <suffix>\n"
" boot <kernel> [ <ramdisk> [ <second> ] ] Download and boot kernel.\n"
" flash:raw boot <kernel> [ <ramdisk> [ <second> ] ]\n"
" Create bootimage and flash it.\n"
@@ -287,15 +304,35 @@
" -p <product> Specify product name.\n"
" -c <cmdline> Override kernel commandline.\n"
" -i <vendor id> Specify a custom USB vendor id.\n"
- " -b <base_addr> Specify a custom kernel base\n"
+ " -b, --base <base_addr> Specify a custom kernel base\n"
" address (default: 0x10000000).\n"
- " -n <page size> Specify the nand page size\n"
+ " --kernel-offset Specify a custom kernel offset.\n"
+ " (default: 0x00008000)\n"
+ " --ramdisk-offset Specify a custom ramdisk offset.\n"
+ " (default: 0x01000000)\n"
+ " --tags-offset Specify a custom tags offset.\n"
+ " (default: 0x00000100)\n"
+ " -n, --page-size <page size> Specify the nand page size\n"
" (default: 2048).\n"
" -S <size>[K|M|G] Automatically sparse files greater\n"
" than 'size'. 0 to disable.\n"
+ " --slot <suffix> Specify slot suffix to be used if the\n"
+ " device supports slots. This will be\n"
+ " added to all partition names that use\n"
+ " slots. 'all' can be given to refer\n"
+ " to all slots. If this is not given,\n"
+ " slotted partitions will default to\n"
+ " the current active slot.\n"
+ " -a, --set-active[=<suffix>] Sets the active slot. If no suffix is\n"
+ " provided, this will default to the value\n"
+ " given by --slot. If slots are not\n"
+ " supported, this does nothing. This will\n"
+ " run after all non-reboot commands.\n"
+ " --unbuffered Do not buffer input or output.\n"
+ " --version Display version.\n"
+ " -h, --help show this message.\n"
);
}
-
static void* load_bootable_image(const char* kernel, const char* ramdisk,
const char* secondstage, int64_t* sz,
const char* cmdline) {
@@ -568,13 +605,17 @@
return out_s;
}
-static int64_t get_target_sparse_limit(usb_handle* usb) {
+static int64_t get_target_sparse_limit(Transport* transport) {
std::string max_download_size;
- if (!fb_getvar(usb, "max-download-size", &max_download_size)) {
+ if (!fb_getvar(transport, "max-download-size", &max_download_size) ||
+ max_download_size.empty()) {
fprintf(stderr, "target didn't report max-download-size\n");
return 0;
}
+ // Some bootloaders (angler, for example) send spurious whitespace too.
+ max_download_size = android::base::Trim(max_download_size);
+
uint64_t limit;
if (!android::base::ParseUint(max_download_size.c_str(), &limit)) {
fprintf(stderr, "couldn't parse max-download-size '%s'\n", max_download_size.c_str());
@@ -586,7 +627,7 @@
return limit;
}
-static int64_t get_sparse_limit(usb_handle* usb, int64_t size) {
+static int64_t get_sparse_limit(Transport* transport, int64_t size) {
int64_t limit;
if (sparse_limit == 0) {
@@ -595,7 +636,7 @@
limit = sparse_limit;
} else {
if (target_sparse_limit == -1) {
- target_sparse_limit = get_target_sparse_limit(usb);
+ target_sparse_limit = get_target_sparse_limit(transport);
}
if (target_sparse_limit > 0) {
limit = target_sparse_limit;
@@ -614,18 +655,22 @@
// Until we get lazy inode table init working in make_ext4fs, we need to
// erase partitions of type ext4 before flashing a filesystem so no stale
// inodes are left lying around. Otherwise, e2fsck gets very upset.
-static bool needs_erase(usb_handle* usb, const char* part) {
- return !fb_format_supported(usb, part, nullptr);
+static bool needs_erase(Transport* transport, const char* partition) {
+ std::string partition_type;
+ if (!fb_getvar(transport, std::string("partition-type:") + partition, &partition_type)) {
+ return false;
+ }
+ return partition_type == "ext4";
}
-static int load_buf_fd(usb_handle* usb, int fd, struct fastboot_buffer* buf) {
+static int load_buf_fd(Transport* transport, int fd, struct fastboot_buffer* buf) {
int64_t sz = get_file_size(fd);
if (sz == -1) {
return -1;
}
lseek64(fd, 0, SEEK_SET);
- int64_t limit = get_sparse_limit(usb, sz);
+ int64_t limit = get_sparse_limit(transport, sz);
if (limit) {
sparse_file** s = load_sparse_files(fd, limit);
if (s == nullptr) {
@@ -644,8 +689,7 @@
return 0;
}
-static int load_buf(usb_handle *usb, const char *fname,
- struct fastboot_buffer *buf)
+static int load_buf(Transport* transport, const char *fname, struct fastboot_buffer *buf)
{
int fd;
@@ -654,7 +698,7 @@
return -1;
}
- return load_buf_fd(usb, fd, buf);
+ return load_buf_fd(transport, fd, buf);
}
static void flash_buf(const char *pname, struct fastboot_buffer *buf)
@@ -677,10 +721,102 @@
}
}
-static void do_flash(usb_handle* usb, const char* pname, const char* fname) {
+static std::vector<std::string> get_suffixes(Transport* transport) {
+ std::vector<std::string> suffixes;
+ std::string suffix_list;
+ if (!fb_getvar(transport, "slot-suffixes", &suffix_list)) {
+ die("Could not get suffixes.\n");
+ }
+ return android::base::Split(suffix_list, ",");
+}
+
+static std::string verify_slot(Transport* transport, const char *slot, bool allow_all) {
+ if (strcmp(slot, "all") == 0) {
+ if (allow_all) {
+ return "all";
+ } else {
+ std::vector<std::string> suffixes = get_suffixes(transport);
+ if (!suffixes.empty()) {
+ return suffixes[0];
+ } else {
+ fprintf(stderr, "No known slots.\n");
+ exit(1);
+ }
+ }
+ }
+ std::vector<std::string> suffixes = get_suffixes(transport);
+ for (const std::string &suffix : suffixes) {
+ if (suffix == slot)
+ return slot;
+ }
+ fprintf(stderr, "Slot %s does not exist. supported slots are:\n", slot);
+ for (const std::string &suffix : suffixes) {
+ fprintf(stderr, "%s\n", suffix.c_str());
+ }
+ exit(1);
+}
+
+static std::string verify_slot(Transport* transport, const char *slot) {
+ return verify_slot(transport, slot, true);
+}
+
+static void do_for_partition(Transport* transport, const char *part, const char *slot,
+ std::function<void(const std::string&)> func, bool force_slot) {
+ std::string has_slot;
+ std::string current_slot;
+
+ if (!fb_getvar(transport, std::string("has-slot:")+part, &has_slot)) {
+ /* If has-slot is not supported, the answer is no. */
+ has_slot = "no";
+ }
+ if (has_slot == "yes") {
+ if (!slot || slot[0] == 0) {
+ if (!fb_getvar(transport, "current-slot", ¤t_slot)) {
+ die("Failed to identify current slot.\n");
+ }
+ func(std::string(part) + current_slot);
+ } else {
+ func(std::string(part) + slot);
+ }
+ } else {
+ if (force_slot && slot && slot[0]) {
+ fprintf(stderr, "Warning: %s does not support slots, and slot %s was requested.\n",
+ part, slot);
+ }
+ func(part);
+ }
+}
+
+/* This function will find the real partition name given a base name, and a slot. If slot is NULL or
+ * empty, it will use the current slot. If slot is "all", it will return a list of all possible
+ * partition names. If force_slot is true, it will fail if a slot is specified, and the given
+ * partition does not support slots.
+ */
+static void do_for_partitions(Transport* transport, const char *part, const char *slot,
+ std::function<void(const std::string&)> func, bool force_slot) {
+ std::string has_slot;
+
+ if (slot && strcmp(slot, "all") == 0) {
+ if (!fb_getvar(transport, std::string("has-slot:") + part, &has_slot)) {
+ die("Could not check if partition %s has slot.", part);
+ }
+ if (has_slot == "yes") {
+ std::vector<std::string> suffixes = get_suffixes(transport);
+ for (std::string &suffix : suffixes) {
+ do_for_partition(transport, part, suffix.c_str(), func, force_slot);
+ }
+ } else {
+ do_for_partition(transport, part, "", func, force_slot);
+ }
+ } else {
+ do_for_partition(transport, part, slot, func, force_slot);
+ }
+}
+
+static void do_flash(Transport* transport, const char* pname, const char* fname) {
struct fastboot_buffer buf;
- if (load_buf(usb, fname, &buf)) {
+ if (load_buf(transport, fname, &buf)) {
die("cannot load '%s'", fname);
}
flash_buf(pname, &buf);
@@ -694,7 +830,7 @@
fb_queue_command("signature", "installing signature");
}
-static void do_update(usb_handle* usb, const char* filename, bool erase_first) {
+static void do_update(Transport* transport, const char* filename, const char* slot_override, bool erase_first) {
queue_info_dump();
fb_queue_query_save("product", cur_product, sizeof(cur_product));
@@ -725,17 +861,21 @@
exit(1); // unzip_to_file already explained why.
}
fastboot_buffer buf;
- int rc = load_buf_fd(usb, fd, &buf);
+ int rc = load_buf_fd(transport, fd, &buf);
if (rc) die("cannot load %s from flash", images[i].img_name);
- do_update_signature(zip, images[i].sig_name);
- if (erase_first && needs_erase(usb, images[i].part_name)) {
- fb_queue_erase(images[i].part_name);
- }
- flash_buf(images[i].part_name, &buf);
- /* not closing the fd here since the sparse code keeps the fd around
- * but hasn't mmaped data yet. The tmpfile will get cleaned up when the
- * program exits.
- */
+
+ auto update = [&](const std::string &partition) {
+ do_update_signature(zip, images[i].sig_name);
+ if (erase_first && needs_erase(transport, partition.c_str())) {
+ fb_queue_erase(partition.c_str());
+ }
+ flash_buf(partition.c_str(), &buf);
+ /* not closing the fd here since the sparse code keeps the fd around
+ * but hasn't mmaped data yet. The tmpfile will get cleaned up when the
+ * program exits.
+ */
+ };
+ do_for_partitions(transport, images[i].part_name, slot_override, update, false);
}
CloseArchive(zip);
@@ -757,7 +897,7 @@
fb_queue_command("signature", "installing signature");
}
-static void do_flashall(usb_handle* usb, int erase_first) {
+static void do_flashall(Transport* transport, const char* slot_override, int erase_first) {
queue_info_dump();
fb_queue_query_save("product", cur_product, sizeof(cur_product));
@@ -774,23 +914,48 @@
for (size_t i = 0; i < ARRAY_SIZE(images); i++) {
fname = find_item(images[i].part_name, product);
fastboot_buffer buf;
- if (load_buf(usb, fname, &buf)) {
+ if (load_buf(transport, fname, &buf)) {
if (images[i].is_optional)
continue;
die("could not load %s\n", images[i].img_name);
}
- do_send_signature(fname);
- if (erase_first && needs_erase(usb, images[i].part_name)) {
- fb_queue_erase(images[i].part_name);
- }
- flash_buf(images[i].part_name, &buf);
+
+ auto flashall = [&](const std::string &partition) {
+ do_send_signature(fname);
+ if (erase_first && needs_erase(transport, partition.c_str())) {
+ fb_queue_erase(partition.c_str());
+ }
+ flash_buf(partition.c_str(), &buf);
+ };
+ do_for_partitions(transport, images[i].part_name, slot_override, flashall, false);
}
}
#define skip(n) do { argc -= (n); argv += (n); } while (0)
#define require(n) do { if (argc < (n)) {usage(); exit(1);}} while (0)
-static int do_oem_command(int argc, char** argv) {
+static int do_bypass_unlock_command(int argc, char **argv)
+{
+ if (argc <= 2) return 0;
+ skip(2);
+
+ /*
+ * Process unlock_bootloader, we have to load the message file
+ * and send that to the remote device.
+ */
+ require(1);
+
+ int64_t sz;
+ void* data = load_file(*argv, &sz);
+ if (data == nullptr) die("could not load '%s': %s", *argv, strerror(errno));
+ fb_queue_download("unlock_message", data, sz);
+ fb_queue_command("flashing unlock_bootloader", "unlocking bootloader");
+ skip(1);
+ return 0;
+}
+
+static int do_oem_command(int argc, char **argv)
+{
char command[256];
if (argc <= 1) return 0;
@@ -847,7 +1012,7 @@
return num;
}
-static void fb_perform_format(usb_handle* usb,
+static void fb_perform_format(Transport* transport,
const char* partition, int skip_if_not_supported,
const char* type_override, const char* size_override) {
std::string partition_type, partition_size;
@@ -865,7 +1030,7 @@
limit = sparse_limit;
}
- if (!fb_getvar(usb, std::string("partition-type:") + partition, &partition_type)) {
+ if (!fb_getvar(transport, std::string("partition-type:") + partition, &partition_type)) {
errMsg = "Can't determine partition type.\n";
goto failed;
}
@@ -877,7 +1042,7 @@
partition_type = type_override;
}
- if (!fb_getvar(usb, std::string("partition-size:") + partition, &partition_size)) {
+ if (!fb_getvar(transport, std::string("partition-size:") + partition, &partition_size)) {
errMsg = "Unable to get partition size\n";
goto failed;
}
@@ -888,8 +1053,13 @@
}
partition_size = size_override;
}
+ // Some bootloaders (angler, for example), send spurious leading whitespace.
+ partition_size = android::base::Trim(partition_size);
+ // Some bootloaders (hammerhead, for example) use implicit hex.
+ // This code used to use strtol with base 16.
+ if (!android::base::StartsWith(partition_size, "0x")) partition_size = "0x" + partition_size;
- gen = fs_get_generator(partition_type.c_str());
+ gen = fs_get_generator(partition_type);
if (!gen) {
if (skip_if_not_supported) {
fprintf(stderr, "Erase successful, but not automatically formatting.\n");
@@ -914,7 +1084,7 @@
return;
}
- if (load_buf_fd(usb, fd, &buf)) {
+ if (load_buf_fd(transport, fd, &buf)) {
fprintf(stderr, "Cannot read image: %s\n", strerror(errno));
close(fd);
return;
@@ -932,35 +1102,50 @@
int main(int argc, char **argv)
{
- int wants_wipe = 0;
- int wants_reboot = 0;
- int wants_reboot_bootloader = 0;
+ bool wants_wipe = false;
+ bool wants_reboot = false;
+ bool wants_reboot_bootloader = false;
+ bool wants_set_active = false;
bool erase_first = true;
void *data;
int64_t sz;
int longindex;
+ std::string slot_override;
+ std::string next_active;
const struct option longopts[] = {
{"base", required_argument, 0, 'b'},
{"kernel_offset", required_argument, 0, 'k'},
+ {"kernel-offset", required_argument, 0, 'k'},
{"page_size", required_argument, 0, 'n'},
+ {"page-size", required_argument, 0, 'n'},
{"ramdisk_offset", required_argument, 0, 'r'},
+ {"ramdisk-offset", required_argument, 0, 'r'},
{"tags_offset", required_argument, 0, 't'},
+ {"tags-offset", required_argument, 0, 't'},
{"help", no_argument, 0, 'h'},
{"unbuffered", no_argument, 0, 0},
{"version", no_argument, 0, 0},
+ {"slot", required_argument, 0, 0},
+ {"set_active", optional_argument, 0, 'a'},
+ {"set-active", optional_argument, 0, 'a'},
{0, 0, 0, 0}
};
serial = getenv("ANDROID_SERIAL");
while (1) {
- int c = getopt_long(argc, argv, "wub:k:n:r:t:s:S:lp:c:i:m:h", longopts, &longindex);
+ int c = getopt_long(argc, argv, "wub:k:n:r:t:s:S:lp:c:i:m:ha::", longopts, &longindex);
if (c < 0) {
break;
}
/* Alphabetical cases */
switch (c) {
+ case 'a':
+ wants_set_active = true;
+ if (optarg)
+ next_active = optarg;
+ break;
case 'b':
base_addr = strtoul(optarg, 0, 16);
break;
@@ -1012,7 +1197,7 @@
erase_first = false;
break;
case 'w':
- wants_wipe = 1;
+ wants_wipe = true;
break;
case '?':
return 1;
@@ -1023,6 +1208,8 @@
} else if (strcmp("version", longopts[longindex].name) == 0) {
fprintf(stdout, "fastboot version %s\n", FASTBOOT_REVISION);
return 0;
+ } else if (strcmp("slot", longopts[longindex].name) == 0) {
+ slot_override = std::string(optarg);
}
break;
default:
@@ -1033,7 +1220,7 @@
argc -= optind;
argv += optind;
- if (argc == 0 && !wants_wipe) {
+ if (argc == 0 && !wants_wipe && !wants_set_active) {
usage();
return 1;
}
@@ -1049,7 +1236,21 @@
return 0;
}
- usb_handle* usb = open_device();
+ Transport* transport = open_device();
+ if (slot_override != "")
+ slot_override = verify_slot(transport, slot_override.c_str());
+ if (next_active != "")
+ next_active = verify_slot(transport, next_active.c_str(), false);
+
+ if (wants_set_active) {
+ if (next_active == "") {
+ if (slot_override == "") {
+ wants_set_active = false;
+ } else {
+ next_active = verify_slot(transport, slot_override.c_str(), false);
+ }
+ }
+ }
while (argc > 0) {
if (!strcmp(*argv, "getvar")) {
@@ -1059,11 +1260,17 @@
} else if(!strcmp(*argv, "erase")) {
require(2);
- if (!fb_format_supported(usb, argv[1], nullptr)) {
- fprintf(stderr, "******** Did you mean to fastboot format this partition?\n");
- }
+ auto erase = [&](const std::string &partition) {
+ std::string partition_type;
+ if (fb_getvar(transport, std::string("partition-type:") + argv[1], &partition_type) &&
+ fs_get_generator(partition_type) != nullptr) {
+ fprintf(stderr, "******** Did you mean to fastboot format this %s partition?\n",
+ partition_type.c_str());
+ }
- fb_queue_erase(argv[1]);
+ fb_queue_erase(partition.c_str());
+ };
+ do_for_partitions(transport, argv[1], slot_override.c_str(), erase, true);
skip(2);
} else if(!strncmp(*argv, "format", strlen("format"))) {
char *overrides;
@@ -1091,10 +1298,14 @@
}
if (type_override && !type_override[0]) type_override = nullptr;
if (size_override && !size_override[0]) size_override = nullptr;
- if (erase_first && needs_erase(usb, argv[1])) {
- fb_queue_erase(argv[1]);
- }
- fb_perform_format(usb, argv[1], 0, type_override, size_override);
+
+ auto format = [&](const std::string &partition) {
+ if (erase_first && needs_erase(transport, partition.c_str())) {
+ fb_queue_erase(partition.c_str());
+ }
+ fb_perform_format(transport, partition.c_str(), 0, type_override, size_override);
+ };
+ do_for_partitions(transport, argv[1], slot_override.c_str(), format, true);
skip(2);
} else if(!strcmp(*argv, "signature")) {
require(2);
@@ -1105,18 +1316,18 @@
fb_queue_command("signature", "installing signature");
skip(2);
} else if(!strcmp(*argv, "reboot")) {
- wants_reboot = 1;
+ wants_reboot = true;
skip(1);
if (argc > 0) {
if (!strcmp(*argv, "bootloader")) {
- wants_reboot = 0;
- wants_reboot_bootloader = 1;
+ wants_reboot = false;
+ wants_reboot_bootloader = true;
skip(1);
}
}
require(0);
} else if(!strcmp(*argv, "reboot-bootloader")) {
- wants_reboot_bootloader = 1;
+ wants_reboot_bootloader = true;
skip(1);
} else if (!strcmp(*argv, "continue")) {
fb_queue_command("continue", "resuming boot");
@@ -1154,12 +1365,15 @@
skip(2);
}
if (fname == 0) die("cannot determine image filename for '%s'", pname);
- if (erase_first && needs_erase(usb, pname)) {
- fb_queue_erase(pname);
- }
- do_flash(usb, pname, fname);
+
+ auto flash = [&](const std::string &partition) {
+ if (erase_first && needs_erase(transport, partition.c_str())) {
+ fb_queue_erase(partition.c_str());
+ }
+ do_flash(transport, partition.c_str(), fname);
+ };
+ do_for_partitions(transport, pname, slot_override.c_str(), flash, true);
} else if(!strcmp(*argv, "flash:raw")) {
- char *pname = argv[1];
char *kname = argv[2];
char *rname = 0;
char *sname = 0;
@@ -1175,28 +1389,43 @@
}
data = load_bootable_image(kname, rname, sname, &sz, cmdline);
if (data == 0) die("cannot load bootable image");
- fb_queue_flash(pname, data, sz);
+ auto flashraw = [&](const std::string &partition) {
+ fb_queue_flash(partition.c_str(), data, sz);
+ };
+ do_for_partitions(transport, argv[1], slot_override.c_str(), flashraw, true);
} else if(!strcmp(*argv, "flashall")) {
skip(1);
- do_flashall(usb, erase_first);
- wants_reboot = 1;
+ do_flashall(transport, slot_override.c_str(), erase_first);
+ wants_reboot = true;
} else if(!strcmp(*argv, "update")) {
if (argc > 1) {
- do_update(usb, argv[1], erase_first);
+ do_update(transport, argv[1], slot_override.c_str(), erase_first);
skip(2);
} else {
- do_update(usb, "update.zip", erase_first);
+ do_update(transport, "update.zip", slot_override.c_str(), erase_first);
skip(1);
}
wants_reboot = 1;
+ } else if(!strcmp(*argv, "set_active")) {
+ require(2);
+ std::string slot = verify_slot(transport, argv[1], false);
+ fb_set_active(slot.c_str());
+ skip(2);
+ wants_reboot = true;
} else if(!strcmp(*argv, "oem")) {
argc = do_oem_command(argc, argv);
- } else if(!strcmp(*argv, "flashing") && argc == 2) {
- if(!strcmp(*(argv+1), "unlock") || !strcmp(*(argv+1), "lock")
- || !strcmp(*(argv+1), "unlock_critical")
- || !strcmp(*(argv+1), "lock_critical")
- || !strcmp(*(argv+1), "get_unlock_ability")) {
- argc = do_oem_command(argc, argv);
+ } else if(!strcmp(*argv, "flashing")) {
+ if (argc == 2 && (!strcmp(*(argv+1), "unlock") ||
+ !strcmp(*(argv+1), "lock") ||
+ !strcmp(*(argv+1), "unlock_critical") ||
+ !strcmp(*(argv+1), "lock_critical") ||
+ !strcmp(*(argv+1), "get_unlock_ability") ||
+ !strcmp(*(argv+1), "get_unlock_bootloader_nonce") ||
+ !strcmp(*(argv+1), "lock_bootloader"))) {
+ argc = do_oem_command(argc, argv);
+ } else
+ if (argc == 3 && !strcmp(*(argv+1), "unlock_bootloader")) {
+ argc = do_bypass_unlock_command(argc, argv);
} else {
usage();
return 1;
@@ -1210,15 +1439,18 @@
if (wants_wipe) {
fprintf(stderr, "wiping userdata...\n");
fb_queue_erase("userdata");
- fb_perform_format(usb, "userdata", 1, nullptr, nullptr);
+ fb_perform_format(transport, "userdata", 1, nullptr, nullptr);
std::string cache_type;
- if (fb_getvar(usb, "partition-type:cache", &cache_type) && !cache_type.empty()) {
+ if (fb_getvar(transport, "partition-type:cache", &cache_type) && !cache_type.empty()) {
fprintf(stderr, "wiping cache...\n");
fb_queue_erase("cache");
- fb_perform_format(usb, "cache", 1, nullptr, nullptr);
+ fb_perform_format(transport, "cache", 1, nullptr, nullptr);
}
}
+ if (wants_set_active) {
+ fb_set_active(next_active.c_str());
+ }
if (wants_reboot) {
fb_queue_reboot();
fb_queue_wait_for_disconnect();
@@ -1227,5 +1459,5 @@
fb_queue_wait_for_disconnect();
}
- return fb_execute_queue(usb) ? EXIT_FAILURE : EXIT_SUCCESS;
+ return fb_execute_queue(transport) ? EXIT_FAILURE : EXIT_SUCCESS;
}
diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h
index a66c211..acfbc13 100644
--- a/fastboot/fastboot.h
+++ b/fastboot/fastboot.h
@@ -34,23 +34,22 @@
#include <string>
-#include "usb.h"
+#include "transport.h"
struct sparse_file;
/* protocol.c - fastboot protocol */
-int fb_command(usb_handle *usb, const char *cmd);
-int fb_command_response(usb_handle *usb, const char *cmd, char *response);
-int fb_download_data(usb_handle *usb, const void *data, uint32_t size);
-int fb_download_data_sparse(usb_handle *usb, struct sparse_file *s);
+int fb_command(Transport* transport, const char* cmd);
+int fb_command_response(Transport* transport, const char* cmd, char* response);
+int fb_download_data(Transport* transport, const void* data, uint32_t size);
+int fb_download_data_sparse(Transport* transport, struct sparse_file* s);
char *fb_get_error(void);
#define FB_COMMAND_SZ 64
#define FB_RESPONSE_SZ 64
/* engine.c - high level command queue engine */
-bool fb_getvar(usb_handle* usb, const std::string& key, std::string* value);
-bool fb_format_supported(usb_handle* usb, const char* partition, const char* type_override);
+bool fb_getvar(Transport* transport, const std::string& key, std::string* value);
void fb_queue_flash(const char *ptn, void *data, uint32_t sz);
void fb_queue_flash_sparse(const char *ptn, struct sparse_file *s, uint32_t sz);
void fb_queue_erase(const char *ptn);
@@ -64,7 +63,8 @@
void fb_queue_download(const char *name, void *data, uint32_t size);
void fb_queue_notice(const char *notice);
void fb_queue_wait_for_disconnect(void);
-int fb_execute_queue(usb_handle *usb);
+int fb_execute_queue(Transport* transport);
+void fb_set_active(const char *slot);
/* util stuff */
double now();
diff --git a/fastboot/fs.cpp b/fastboot/fs.cpp
index c58a505..90d8474 100644
--- a/fastboot/fs.cpp
+++ b/fastboot/fs.cpp
@@ -39,9 +39,9 @@
#endif
};
-const struct fs_generator* fs_get_generator(const char* fs_type) {
+const struct fs_generator* fs_get_generator(const std::string& fs_type) {
for (size_t i = 0; i < sizeof(generators) / sizeof(*generators); i++) {
- if (strcmp(generators[i].fs_type, fs_type) == 0) {
+ if (fs_type == generators[i].fs_type) {
return generators + i;
}
}
diff --git a/fastboot/fs.h b/fastboot/fs.h
index 8444081..289488b 100644
--- a/fastboot/fs.h
+++ b/fastboot/fs.h
@@ -5,8 +5,7 @@
struct fs_generator;
-const struct fs_generator* fs_get_generator(const char *fs_type);
+const struct fs_generator* fs_get_generator(const std::string& fs_type);
int fs_generator_generate(const struct fs_generator* gen, int tmpFileNo, long long partSize);
#endif
-
diff --git a/fastboot/genkey.sh b/fastboot/genkey.sh
deleted file mode 100755
index 011e902..0000000
--- a/fastboot/genkey.sh
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/bin/bash
-
-if [ $# -ne 2 ]
-then
- echo "Usage: $0 alias \"pass phrase\""
- exit -1
-fi
-
-# Generate a 2048 bit RSA key with public exponent 3.
-# Encrypt private key with provided password.
-openssl genrsa -3 -out $1.pem -passout pass:"$2" 2048
-
-# Create a self-signed cert for this key.
-openssl req -new -x509 -key $1.pem -passin pass:"$2" \
- -out $1-cert.pem \
- -batch -days 10000
-
-# Create a PKCS12 store containing the generated private key.
-# Protect the keystore and the private key with the provided password.
-openssl pkcs12 -export -in $1-cert.pem -inkey $1.pem -passin pass:"$2" \
- -out $1.p12 -name $1 -passout pass:"$2"
-
-rm $1.pem
-rm $1-cert.pem
-
diff --git a/fastboot/protocol.cpp b/fastboot/protocol.cpp
index cbd48e8..4850b4a 100644
--- a/fastboot/protocol.cpp
+++ b/fastboot/protocol.cpp
@@ -39,6 +39,7 @@
#include <sparse/sparse.h>
#include "fastboot.h"
+#include "transport.h"
static char ERROR[128];
@@ -47,21 +48,21 @@
return ERROR;
}
-static int check_response(usb_handle* usb, uint32_t size, char* response) {
+static int check_response(Transport* transport, uint32_t size, char* response) {
char status[65];
while (true) {
- int r = usb_read(usb, status, 64);
+ int r = transport->Read(status, 64);
if (r < 0) {
sprintf(ERROR, "status read failed (%s)", strerror(errno));
- usb_close(usb);
+ transport->Close();
return -1;
}
status[r] = 0;
if (r < 4) {
sprintf(ERROR, "status malformed (%d bytes)", r);
- usb_close(usb);
+ transport->Close();
return -1;
}
@@ -90,21 +91,21 @@
uint32_t dsize = strtol(status + 4, 0, 16);
if (dsize > size) {
strcpy(ERROR, "data size too large");
- usb_close(usb);
+ transport->Close();
return -1;
}
return dsize;
}
strcpy(ERROR,"unknown status code");
- usb_close(usb);
+ transport->Close();
break;
}
return -1;
}
-static int _command_start(usb_handle* usb, const char* cmd, uint32_t size, char* response) {
+static int _command_start(Transport* transport, const char* cmd, uint32_t size, char* response) {
size_t cmdsize = strlen(cmd);
if (cmdsize > 64) {
sprintf(ERROR, "command too large");
@@ -115,51 +116,51 @@
response[0] = 0;
}
- if (usb_write(usb, cmd, cmdsize) != static_cast<int>(cmdsize)) {
+ if (transport->Write(cmd, cmdsize) != static_cast<int>(cmdsize)) {
sprintf(ERROR, "command write failed (%s)", strerror(errno));
- usb_close(usb);
+ transport->Close();
return -1;
}
- return check_response(usb, size, response);
+ return check_response(transport, size, response);
}
-static int _command_data(usb_handle* usb, const void* data, uint32_t size) {
- int r = usb_write(usb, data, size);
+static int _command_data(Transport* transport, const void* data, uint32_t size) {
+ int r = transport->Write(data, size);
if (r < 0) {
sprintf(ERROR, "data transfer failure (%s)", strerror(errno));
- usb_close(usb);
+ transport->Close();
return -1;
}
if (r != ((int) size)) {
sprintf(ERROR, "data transfer failure (short transfer)");
- usb_close(usb);
+ transport->Close();
return -1;
}
return r;
}
-static int _command_end(usb_handle* usb) {
- return check_response(usb, 0, 0) < 0 ? -1 : 0;
+static int _command_end(Transport* transport) {
+ return check_response(transport, 0, 0) < 0 ? -1 : 0;
}
-static int _command_send(usb_handle* usb, const char* cmd, const void* data, uint32_t size,
+static int _command_send(Transport* transport, const char* cmd, const void* data, uint32_t size,
char* response) {
if (size == 0) {
return -1;
}
- int r = _command_start(usb, cmd, size, response);
+ int r = _command_start(transport, cmd, size, response);
if (r < 0) {
return -1;
}
- r = _command_data(usb, data, size);
+ r = _command_data(transport, data, size);
if (r < 0) {
return -1;
}
- r = _command_end(usb);
+ r = _command_end(transport);
if (r < 0) {
return -1;
}
@@ -167,59 +168,59 @@
return size;
}
-static int _command_send_no_data(usb_handle* usb, const char* cmd, char* response) {
- return _command_start(usb, cmd, 0, response);
+static int _command_send_no_data(Transport* transport, const char* cmd, char* response) {
+ return _command_start(transport, cmd, 0, response);
}
-int fb_command(usb_handle* usb, const char* cmd) {
- return _command_send_no_data(usb, cmd, 0);
+int fb_command(Transport* transport, const char* cmd) {
+ return _command_send_no_data(transport, cmd, 0);
}
-int fb_command_response(usb_handle* usb, const char* cmd, char* response) {
- return _command_send_no_data(usb, cmd, response);
+int fb_command_response(Transport* transport, const char* cmd, char* response) {
+ return _command_send_no_data(transport, cmd, response);
}
-int fb_download_data(usb_handle* usb, const void* data, uint32_t size) {
+int fb_download_data(Transport* transport, const void* data, uint32_t size) {
char cmd[64];
sprintf(cmd, "download:%08x", size);
- return _command_send(usb, cmd, data, size, 0) < 0 ? -1 : 0;
+ return _command_send(transport, cmd, data, size, 0) < 0 ? -1 : 0;
}
-#define USB_BUF_SIZE 1024
-static char usb_buf[USB_BUF_SIZE];
-static int usb_buf_len;
+#define TRANSPORT_BUF_SIZE 1024
+static char transport_buf[TRANSPORT_BUF_SIZE];
+static int transport_buf_len;
static int fb_download_data_sparse_write(void *priv, const void *data, int len)
{
int r;
- usb_handle* usb = reinterpret_cast<usb_handle*>(priv);
+ Transport* transport = reinterpret_cast<Transport*>(priv);
int to_write;
const char* ptr = reinterpret_cast<const char*>(data);
- if (usb_buf_len) {
- to_write = std::min(USB_BUF_SIZE - usb_buf_len, len);
+ if (transport_buf_len) {
+ to_write = std::min(TRANSPORT_BUF_SIZE - transport_buf_len, len);
- memcpy(usb_buf + usb_buf_len, ptr, to_write);
- usb_buf_len += to_write;
+ memcpy(transport_buf + transport_buf_len, ptr, to_write);
+ transport_buf_len += to_write;
ptr += to_write;
len -= to_write;
}
- if (usb_buf_len == USB_BUF_SIZE) {
- r = _command_data(usb, usb_buf, USB_BUF_SIZE);
- if (r != USB_BUF_SIZE) {
+ if (transport_buf_len == TRANSPORT_BUF_SIZE) {
+ r = _command_data(transport, transport_buf, TRANSPORT_BUF_SIZE);
+ if (r != TRANSPORT_BUF_SIZE) {
return -1;
}
- usb_buf_len = 0;
+ transport_buf_len = 0;
}
- if (len > USB_BUF_SIZE) {
- if (usb_buf_len > 0) {
- sprintf(ERROR, "internal error: usb_buf not empty\n");
+ if (len > TRANSPORT_BUF_SIZE) {
+ if (transport_buf_len > 0) {
+ sprintf(ERROR, "internal error: transport_buf not empty\n");
return -1;
}
- to_write = round_down(len, USB_BUF_SIZE);
- r = _command_data(usb, ptr, to_write);
+ to_write = round_down(len, TRANSPORT_BUF_SIZE);
+ r = _command_data(transport, ptr, to_write);
if (r != to_write) {
return -1;
}
@@ -228,28 +229,28 @@
}
if (len > 0) {
- if (len > USB_BUF_SIZE) {
- sprintf(ERROR, "internal error: too much left for usb_buf\n");
+ if (len > TRANSPORT_BUF_SIZE) {
+ sprintf(ERROR, "internal error: too much left for transport_buf\n");
return -1;
}
- memcpy(usb_buf, ptr, len);
- usb_buf_len = len;
+ memcpy(transport_buf, ptr, len);
+ transport_buf_len = len;
}
return 0;
}
-static int fb_download_data_sparse_flush(usb_handle* usb) {
- if (usb_buf_len > 0) {
- if (_command_data(usb, usb_buf, usb_buf_len) != usb_buf_len) {
+static int fb_download_data_sparse_flush(Transport* transport) {
+ if (transport_buf_len > 0) {
+ if (_command_data(transport, transport_buf, transport_buf_len) != transport_buf_len) {
return -1;
}
- usb_buf_len = 0;
+ transport_buf_len = 0;
}
return 0;
}
-int fb_download_data_sparse(usb_handle* usb, struct sparse_file* s) {
+int fb_download_data_sparse(Transport* transport, struct sparse_file* s) {
int size = sparse_file_len(s, true, false);
if (size <= 0) {
return -1;
@@ -257,20 +258,20 @@
char cmd[64];
sprintf(cmd, "download:%08x", size);
- int r = _command_start(usb, cmd, size, 0);
+ int r = _command_start(transport, cmd, size, 0);
if (r < 0) {
return -1;
}
- r = sparse_file_callback(s, true, false, fb_download_data_sparse_write, usb);
+ r = sparse_file_callback(s, true, false, fb_download_data_sparse_write, transport);
if (r < 0) {
return -1;
}
- r = fb_download_data_sparse_flush(usb);
+ r = fb_download_data_sparse_flush(transport);
if (r < 0) {
return -1;
}
- return _command_end(usb);
+ return _command_end(transport);
}
diff --git a/fastboot/socket.h b/fastboot/socket.h
new file mode 100644
index 0000000..888b530
--- /dev/null
+++ b/fastboot/socket.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+// This file provides a class interface for cross-platform UDP functionality. The main fastboot
+// engine should not be using this interface directly, but instead should use a higher-level
+// interface that enforces the fastboot UDP protocol.
+
+#ifndef SOCKET_H_
+#define SOCKET_H_
+
+#include "android-base/macros.h"
+
+#include <memory>
+#include <string>
+
+// UdpSocket interface to be implemented for each platform.
+class UdpSocket {
+ public:
+ // Creates a new client connection. Clients are connected to a specific hostname/port and can
+ // only send to that destination.
+ // On failure, |error| is filled (if non-null) and nullptr is returned.
+ static std::unique_ptr<UdpSocket> NewUdpClient(const std::string& hostname, int port,
+ std::string* error);
+
+ // Creates a new server bound to local |port|. This is only meant for testing, during normal
+ // fastboot operation the device acts as the server.
+ // The server saves sender addresses in Receive(), and uses the most recent address during
+ // calls to Send().
+ static std::unique_ptr<UdpSocket> NewUdpServer(int port);
+
+ virtual ~UdpSocket() = default;
+
+ // Sends |length| bytes of |data|. Returns the number of bytes actually sent or -1 on error.
+ virtual ssize_t Send(const void* data, size_t length) = 0;
+
+ // Waits up to |timeout_ms| to receive up to |length| bytes of data. |timout_ms| of 0 will
+ // block forever. Returns the number of bytes received or -1 on error/timeout. On timeout
+ // errno will be set to EAGAIN or EWOULDBLOCK.
+ virtual ssize_t Receive(void* data, size_t length, int timeout_ms) = 0;
+
+ // Closes the socket. Returns 0 on success, -1 on error.
+ virtual int Close() = 0;
+
+ protected:
+ // Protected constructor to force factory function use.
+ UdpSocket() = default;
+
+ DISALLOW_COPY_AND_ASSIGN(UdpSocket);
+};
+
+#endif // SOCKET_H_
diff --git a/fastboot/socket_test.cpp b/fastboot/socket_test.cpp
new file mode 100644
index 0000000..6ada964
--- /dev/null
+++ b/fastboot/socket_test.cpp
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Tests UDP functionality using loopback connections. Requires that kDefaultPort is available
+// for loopback communication on the host. These tests also assume that no UDP packets are lost,
+// which should be the case for loopback communication, but is not guaranteed.
+
+#include "socket.h"
+
+#include <errno.h>
+#include <time.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+enum {
+ // This port must be available for loopback communication.
+ kDefaultPort = 54321,
+
+ // Don't wait forever in a unit test.
+ kDefaultTimeoutMs = 3000,
+};
+
+static const char kReceiveStringError[] = "Error receiving string";
+
+// Test fixture to provide some helper functions. Makes each test a little simpler since we can
+// just check a bool for socket creation and don't have to pass hostname or port information.
+class SocketTest : public ::testing::Test {
+ protected:
+ bool StartServer(int port = kDefaultPort) {
+ server_ = UdpSocket::NewUdpServer(port);
+ return server_ != nullptr;
+ }
+
+ bool StartClient(const std::string hostname = "localhost", int port = kDefaultPort) {
+ client_ = UdpSocket::NewUdpClient(hostname, port, nullptr);
+ return client_ != nullptr;
+ }
+
+ bool StartClient2(const std::string hostname = "localhost", int port = kDefaultPort) {
+ client2_ = UdpSocket::NewUdpClient(hostname, port, nullptr);
+ return client2_ != nullptr;
+ }
+
+ std::unique_ptr<UdpSocket> server_, client_, client2_;
+};
+
+// Sends a string over a UdpSocket. Returns true if the full string (without terminating char)
+// was sent.
+static bool SendString(UdpSocket* udp, const std::string& message) {
+ return udp->Send(message.c_str(), message.length()) == static_cast<ssize_t>(message.length());
+}
+
+// Receives a string from a UdpSocket. Returns the string, or kReceiveStringError on failure.
+static std::string ReceiveString(UdpSocket* udp, size_t receive_size = 128) {
+ std::vector<char> buffer(receive_size);
+
+ ssize_t result = udp->Receive(buffer.data(), buffer.size(), kDefaultTimeoutMs);
+ if (result >= 0) {
+ return std::string(buffer.data(), result);
+ }
+ return kReceiveStringError;
+}
+
+// Calls Receive() on the UdpSocket with the given timeout. Returns true if the call timed out.
+static bool ReceiveTimeout(UdpSocket* udp, int timeout_ms) {
+ char buffer[1];
+
+ errno = 0;
+ return udp->Receive(buffer, 1, timeout_ms) == -1 && (errno == EAGAIN || errno == EWOULDBLOCK);
+}
+
+// Tests sending packets client -> server, then server -> client.
+TEST_F(SocketTest, SendAndReceive) {
+ ASSERT_TRUE(StartServer());
+ ASSERT_TRUE(StartClient());
+
+ EXPECT_TRUE(SendString(client_.get(), "foo"));
+ EXPECT_EQ("foo", ReceiveString(server_.get()));
+
+ EXPECT_TRUE(SendString(server_.get(), "bar baz"));
+ EXPECT_EQ("bar baz", ReceiveString(client_.get()));
+}
+
+// Tests sending and receiving large packets.
+TEST_F(SocketTest, LargePackets) {
+ std::string message(512, '\0');
+
+ ASSERT_TRUE(StartServer());
+ ASSERT_TRUE(StartClient());
+
+ // Run through the test a few times.
+ for (int i = 0; i < 10; ++i) {
+ // Use a different message each iteration to prevent false positives.
+ for (size_t j = 0; j < message.length(); ++j) {
+ message[j] = static_cast<char>(i + j);
+ }
+
+ EXPECT_TRUE(SendString(client_.get(), message));
+ EXPECT_EQ(message, ReceiveString(server_.get(), message.length()));
+ }
+}
+
+// Tests IPv4 client/server.
+TEST_F(SocketTest, IPv4) {
+ ASSERT_TRUE(StartServer());
+ ASSERT_TRUE(StartClient("127.0.0.1"));
+
+ EXPECT_TRUE(SendString(client_.get(), "foo"));
+ EXPECT_EQ("foo", ReceiveString(server_.get()));
+
+ EXPECT_TRUE(SendString(server_.get(), "bar"));
+ EXPECT_EQ("bar", ReceiveString(client_.get()));
+}
+
+// Tests IPv6 client/server.
+TEST_F(SocketTest, IPv6) {
+ ASSERT_TRUE(StartServer());
+ ASSERT_TRUE(StartClient("::1"));
+
+ EXPECT_TRUE(SendString(client_.get(), "foo"));
+ EXPECT_EQ("foo", ReceiveString(server_.get()));
+
+ EXPECT_TRUE(SendString(server_.get(), "bar"));
+ EXPECT_EQ("bar", ReceiveString(client_.get()));
+}
+
+// Tests receive timeout. The timing verification logic must be very coarse to make sure different
+// systems running different loads can all pass these tests.
+TEST_F(SocketTest, ReceiveTimeout) {
+ time_t start_time;
+
+ ASSERT_TRUE(StartServer());
+
+ // Make sure a 20ms timeout completes in 1 second or less.
+ start_time = time(nullptr);
+ EXPECT_TRUE(ReceiveTimeout(server_.get(), 20));
+ EXPECT_LE(difftime(time(nullptr), start_time), 1.0);
+
+ // Make sure a 1250ms timeout takes 1 second or more.
+ start_time = time(nullptr);
+ EXPECT_TRUE(ReceiveTimeout(server_.get(), 1250));
+ EXPECT_LE(1.0, difftime(time(nullptr), start_time));
+}
+
+// Tests receive overflow (the UDP packet is larger than the receive buffer).
+TEST_F(SocketTest, ReceiveOverflow) {
+ ASSERT_TRUE(StartServer());
+ ASSERT_TRUE(StartClient());
+
+ EXPECT_TRUE(SendString(client_.get(), "1234567890"));
+
+ // This behaves differently on different systems; some give us a truncated UDP packet, others
+ // will error out and not return anything at all.
+ std::string rx_string = ReceiveString(server_.get(), 5);
+
+ // If we didn't get an error then the packet should have been truncated.
+ if (rx_string != kReceiveStringError) {
+ EXPECT_EQ("12345", rx_string);
+ }
+}
+
+// Tests multiple clients sending to the same server.
+TEST_F(SocketTest, MultipleClients) {
+ ASSERT_TRUE(StartServer());
+ ASSERT_TRUE(StartClient());
+ ASSERT_TRUE(StartClient2());
+
+ EXPECT_TRUE(SendString(client_.get(), "client"));
+ EXPECT_TRUE(SendString(client2_.get(), "client2"));
+
+ // Receive the packets and send a response for each (note that packets may be received
+ // out-of-order).
+ for (int i = 0; i < 2; ++i) {
+ std::string received = ReceiveString(server_.get());
+ EXPECT_TRUE(SendString(server_.get(), received + " response"));
+ }
+
+ EXPECT_EQ("client response", ReceiveString(client_.get()));
+ EXPECT_EQ("client2 response", ReceiveString(client2_.get()));
+}
diff --git a/fastboot/socket_unix.cpp b/fastboot/socket_unix.cpp
new file mode 100644
index 0000000..462256a
--- /dev/null
+++ b/fastboot/socket_unix.cpp
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2015 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 "socket.h"
+
+#include <errno.h>
+#include <netdb.h>
+
+#include <android-base/stringprintf.h>
+#include <cutils/sockets.h>
+
+class UnixUdpSocket : public UdpSocket {
+ public:
+ enum class Type { kClient, kServer };
+
+ UnixUdpSocket(int fd, Type type);
+ ~UnixUdpSocket() override;
+
+ ssize_t Send(const void* data, size_t length) override;
+ ssize_t Receive(void* data, size_t length, int timeout_ms) override;
+ int Close() override;
+
+ private:
+ int fd_;
+ int receive_timeout_ms_ = 0;
+ std::unique_ptr<sockaddr_storage> addr_;
+ socklen_t addr_size_ = 0;
+
+ DISALLOW_COPY_AND_ASSIGN(UnixUdpSocket);
+};
+
+UnixUdpSocket::UnixUdpSocket(int fd, Type type) : fd_(fd) {
+ // Only servers need to remember addresses; clients are connected to a server in NewUdpClient()
+ // so will send to that server without needing to specify the address again.
+ if (type == Type::kServer) {
+ addr_.reset(new sockaddr_storage);
+ addr_size_ = sizeof(*addr_);
+ memset(addr_.get(), 0, addr_size_);
+ }
+}
+
+UnixUdpSocket::~UnixUdpSocket() {
+ Close();
+}
+
+ssize_t UnixUdpSocket::Send(const void* data, size_t length) {
+ return TEMP_FAILURE_RETRY(
+ sendto(fd_, data, length, 0, reinterpret_cast<sockaddr*>(addr_.get()), addr_size_));
+}
+
+ssize_t UnixUdpSocket::Receive(void* data, size_t length, int timeout_ms) {
+ // Only set socket timeout if it's changed.
+ if (receive_timeout_ms_ != timeout_ms) {
+ timeval tv;
+ tv.tv_sec = timeout_ms / 1000;
+ tv.tv_usec = (timeout_ms % 1000) * 1000;
+ if (setsockopt(fd_, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) {
+ return -1;
+ }
+ receive_timeout_ms_ = timeout_ms;
+ }
+
+ socklen_t* addr_size_ptr = nullptr;
+ if (addr_ != nullptr) {
+ // Reset addr_size as it may have been modified by previous recvfrom() calls.
+ addr_size_ = sizeof(*addr_);
+ addr_size_ptr = &addr_size_;
+ }
+ return TEMP_FAILURE_RETRY(recvfrom(fd_, data, length, 0,
+ reinterpret_cast<sockaddr*>(addr_.get()), addr_size_ptr));
+}
+
+int UnixUdpSocket::Close() {
+ int result = 0;
+ if (fd_ != -1) {
+ result = close(fd_);
+ fd_ = -1;
+ }
+ return result;
+}
+
+std::unique_ptr<UdpSocket> UdpSocket::NewUdpClient(const std::string& host, int port,
+ std::string* error) {
+ int getaddrinfo_error = 0;
+ int fd = socket_network_client_timeout(host.c_str(), port, SOCK_DGRAM, 0, &getaddrinfo_error);
+ if (fd == -1) {
+ if (error) {
+ *error = android::base::StringPrintf(
+ "Failed to connect to %s:%d: %s", host.c_str(), port,
+ getaddrinfo_error ? gai_strerror(getaddrinfo_error) : strerror(errno));
+ }
+ return nullptr;
+ }
+
+ return std::unique_ptr<UdpSocket>(new UnixUdpSocket(fd, UnixUdpSocket::Type::kClient));
+}
+
+std::unique_ptr<UdpSocket> UdpSocket::NewUdpServer(int port) {
+ int fd = socket_inaddr_any_server(port, SOCK_DGRAM);
+ if (fd == -1) {
+ // This is just used in testing, no need for an error message.
+ return nullptr;
+ }
+
+ return std::unique_ptr<UdpSocket>(new UnixUdpSocket(fd, UnixUdpSocket::Type::kServer));
+}
diff --git a/fastboot/socket_windows.cpp b/fastboot/socket_windows.cpp
new file mode 100644
index 0000000..4ad379f
--- /dev/null
+++ b/fastboot/socket_windows.cpp
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2015 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 "socket.h"
+
+#include <winsock2.h>
+#include <ws2tcpip.h>
+
+#include <memory>
+
+#include <android-base/stringprintf.h>
+
+// Windows UDP socket functionality.
+class WindowsUdpSocket : public UdpSocket {
+ public:
+ enum class Type { kClient, kServer };
+
+ WindowsUdpSocket(SOCKET sock, Type type);
+ ~WindowsUdpSocket() override;
+
+ ssize_t Send(const void* data, size_t len) override;
+ ssize_t Receive(void* data, size_t len, int timeout_ms) override;
+ int Close() override;
+
+ private:
+ SOCKET sock_;
+ int receive_timeout_ms_ = 0;
+ std::unique_ptr<sockaddr_storage> addr_;
+ int addr_size_ = 0;
+
+ DISALLOW_COPY_AND_ASSIGN(WindowsUdpSocket);
+};
+
+WindowsUdpSocket::WindowsUdpSocket(SOCKET sock, Type type) : sock_(sock) {
+ // Only servers need to remember addresses; clients are connected to a server in NewUdpClient()
+ // so will send to that server without needing to specify the address again.
+ if (type == Type::kServer) {
+ addr_.reset(new sockaddr_storage);
+ addr_size_ = sizeof(*addr_);
+ memset(addr_.get(), 0, addr_size_);
+ }
+}
+
+WindowsUdpSocket::~WindowsUdpSocket() {
+ Close();
+}
+
+ssize_t WindowsUdpSocket::Send(const void* data, size_t len) {
+ return sendto(sock_, reinterpret_cast<const char*>(data), len, 0,
+ reinterpret_cast<sockaddr*>(addr_.get()), addr_size_);
+}
+
+ssize_t WindowsUdpSocket::Receive(void* data, size_t len, int timeout_ms) {
+ // Only set socket timeout if it's changed.
+ if (receive_timeout_ms_ != timeout_ms) {
+ if (setsockopt(sock_, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast<const char*>(&timeout_ms),
+ sizeof(timeout_ms)) < 0) {
+ return -1;
+ }
+ receive_timeout_ms_ = timeout_ms;
+ }
+
+ int* addr_size_ptr = nullptr;
+ if (addr_ != nullptr) {
+ // Reset addr_size as it may have been modified by previous recvfrom() calls.
+ addr_size_ = sizeof(*addr_);
+ addr_size_ptr = &addr_size_;
+ }
+ int result = recvfrom(sock_, reinterpret_cast<char*>(data), len, 0,
+ reinterpret_cast<sockaddr*>(addr_.get()), addr_size_ptr);
+ if (result < 0 && WSAGetLastError() == WSAETIMEDOUT) {
+ errno = EAGAIN;
+ }
+ return result;
+}
+
+int WindowsUdpSocket::Close() {
+ int result = 0;
+ if (sock_ != INVALID_SOCKET) {
+ result = closesocket(sock_);
+ sock_ = INVALID_SOCKET;
+ }
+ return result;
+}
+
+static int GetProtocol(int sock_type) {
+ switch (sock_type) {
+ case SOCK_DGRAM:
+ return IPPROTO_UDP;
+ case SOCK_STREAM:
+ return IPPROTO_TCP;
+ default:
+ // 0 lets the system decide which protocol to use.
+ return 0;
+ }
+}
+
+// Windows implementation of this libcutils function. This function does not make any calls to
+// WSAStartup() or WSACleanup() so that must be handled by the caller.
+// TODO(dpursell): share this code with adb.
+static SOCKET socket_network_client(const std::string& host, int port, int type) {
+ // First resolve the host and port parameters into a usable network address.
+ addrinfo hints;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_socktype = type;
+ hints.ai_protocol = GetProtocol(type);
+
+ addrinfo* address = nullptr;
+ getaddrinfo(host.c_str(), android::base::StringPrintf("%d", port).c_str(), &hints, &address);
+ if (address == nullptr) {
+ return INVALID_SOCKET;
+ }
+
+ // Now create and connect the socket.
+ SOCKET sock = socket(address->ai_family, address->ai_socktype, address->ai_protocol);
+ if (sock == INVALID_SOCKET) {
+ freeaddrinfo(address);
+ return INVALID_SOCKET;
+ }
+
+ if (connect(sock, address->ai_addr, address->ai_addrlen) == SOCKET_ERROR) {
+ closesocket(sock);
+ freeaddrinfo(address);
+ return INVALID_SOCKET;
+ }
+
+ freeaddrinfo(address);
+ return sock;
+}
+
+// Windows implementation of this libcutils function. This implementation creates a dual-stack
+// server socket that can accept incoming IPv4 or IPv6 packets. This function does not make any
+// calls to WSAStartup() or WSACleanup() so that must be handled by the caller.
+// TODO(dpursell): share this code with adb.
+static SOCKET socket_inaddr_any_server(int port, int type) {
+ SOCKET sock = socket(AF_INET6, type, GetProtocol(type));
+ if (sock == INVALID_SOCKET) {
+ return INVALID_SOCKET;
+ }
+
+ // Enforce exclusive addresses (1), and enable dual-stack so both IPv4 and IPv6 work (2).
+ // (1) https://msdn.microsoft.com/en-us/library/windows/desktop/ms740621(v=vs.85).aspx.
+ // (2) https://msdn.microsoft.com/en-us/library/windows/desktop/bb513665(v=vs.85).aspx.
+ int exclusive = 1;
+ DWORD v6_only = 0;
+ if (setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, reinterpret_cast<const char*>(&exclusive),
+ sizeof(exclusive)) == SOCKET_ERROR ||
+ setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<const char*>(&v6_only),
+ sizeof(v6_only)) == SOCKET_ERROR) {
+ closesocket(sock);
+ return INVALID_SOCKET;
+ }
+
+ // Bind the socket to our local port.
+ sockaddr_in6 addr;
+ memset(&addr, 0, sizeof(addr));
+ addr.sin6_family = AF_INET6;
+ addr.sin6_port = htons(port);
+ addr.sin6_addr = in6addr_any;
+ if (bind(sock, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) == SOCKET_ERROR) {
+ closesocket(sock);
+ return INVALID_SOCKET;
+ }
+
+ return sock;
+}
+
+// Documentation at https://msdn.microsoft.com/en-us/library/windows/desktop/ms741549(v=vs.85).aspx
+// claims WSACleanup() should be called before program exit, but general consensus seems to be that
+// it hasn't actually been necessary for a long time, possibly since Windows 3.1.
+//
+// Both adb (1) and Chrome (2) purposefully avoid WSACleanup(), and since no adverse affects have
+// been found we may as well do the same here to keep this code simpler.
+// (1) https://android.googlesource.com/platform/system/core.git/+/master/adb/sysdeps_win32.cpp#816
+// (2) https://code.google.com/p/chromium/codesearch#chromium/src/net/base/winsock_init.cc&l=35
+static bool InitWinsock() {
+ static bool init_success = false;
+
+ if (!init_success) {
+ WSADATA wsaData;
+ init_success = (WSAStartup(MAKEWORD(2, 2), &wsaData) == 0);
+ }
+
+ return init_success;
+}
+
+std::unique_ptr<UdpSocket> UdpSocket::NewUdpClient(const std::string& host, int port,
+ std::string* error) {
+ if (!InitWinsock()) {
+ if (error) {
+ *error = android::base::StringPrintf("Failed to initialize Winsock (error %d)",
+ WSAGetLastError());
+ }
+ return nullptr;
+ }
+
+ SOCKET sock = socket_network_client(host, port, SOCK_DGRAM);
+ if (sock == INVALID_SOCKET) {
+ if (error) {
+ *error = android::base::StringPrintf("Failed to connect to %s:%d (error %d)",
+ host.c_str(), port, WSAGetLastError());
+ }
+ return nullptr;
+ }
+
+ return std::unique_ptr<UdpSocket>(new WindowsUdpSocket(sock, WindowsUdpSocket::Type::kClient));
+}
+
+// This functionality is currently only used by tests so we don't need any error messages.
+std::unique_ptr<UdpSocket> UdpSocket::NewUdpServer(int port) {
+ if (!InitWinsock()) {
+ return nullptr;
+ }
+
+ SOCKET sock = socket_inaddr_any_server(port, SOCK_DGRAM);
+ if (sock == INVALID_SOCKET) {
+ return nullptr;
+ }
+
+ return std::unique_ptr<UdpSocket>(new WindowsUdpSocket(sock, WindowsUdpSocket::Type::kServer));
+}
diff --git a/fastboot/transport.h b/fastboot/transport.h
new file mode 100644
index 0000000..67d01f9
--- /dev/null
+++ b/fastboot/transport.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TRANSPORT_H_
+#define TRANSPORT_H_
+
+#include <android-base/macros.h>
+
+// General interface to allow the fastboot protocol to be used over different
+// types of transports.
+class Transport {
+ public:
+ Transport() = default;
+ virtual ~Transport() = default;
+
+ // Reads |len| bytes into |data|. Returns the number of bytes actually
+ // read or -1 on error.
+ virtual ssize_t Read(void* data, size_t len) = 0;
+
+ // Writes |len| bytes from |data|. Returns the number of bytes actually
+ // written or -1 on error.
+ virtual ssize_t Write(const void* data, size_t len) = 0;
+
+ // Closes the underlying transport. Returns 0 on success.
+ virtual int Close() = 0;
+
+ // Blocks until the transport disconnects. Transports that don't support
+ // this will return immediately. Returns 0 on success.
+ virtual int WaitForDisconnect() { return 0; }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Transport);
+};
+
+#endif // TRANSPORT_H_
diff --git a/fastboot/usb.h b/fastboot/usb.h
index 0fda41a..4acf12d 100644
--- a/fastboot/usb.h
+++ b/fastboot/usb.h
@@ -29,7 +29,7 @@
#ifndef _USB_H_
#define _USB_H_
-struct usb_handle;
+#include "transport.h"
struct usb_ifc_info {
/* from device descriptor */
@@ -55,10 +55,6 @@
typedef int (*ifc_match_func)(usb_ifc_info *ifc);
-usb_handle *usb_open(ifc_match_func callback);
-int usb_close(usb_handle *h);
-int usb_read(usb_handle *h, void *_data, int len);
-int usb_write(usb_handle *h, const void *_data, int len);
-int usb_wait_for_disconnect(usb_handle *h);
+Transport* usb_open(ifc_match_func callback);
#endif
diff --git a/fastboot/usb_linux.cpp b/fastboot/usb_linux.cpp
index 7b87907..02ffcd9 100644
--- a/fastboot/usb_linux.cpp
+++ b/fastboot/usb_linux.cpp
@@ -43,6 +43,8 @@
#include <linux/version.h>
#include <linux/usb/ch9.h>
+#include <memory>
+
#include "fastboot.h"
#include "usb.h"
@@ -85,6 +87,22 @@
unsigned char ep_out;
};
+class LinuxUsbTransport : public Transport {
+ public:
+ LinuxUsbTransport(std::unique_ptr<usb_handle> handle) : handle_(std::move(handle)) {}
+ ~LinuxUsbTransport() override = default;
+
+ ssize_t Read(void* data, size_t len) override;
+ ssize_t Write(const void* data, size_t len) override;
+ int Close() override;
+ int WaitForDisconnect() override;
+
+ private:
+ std::unique_ptr<usb_handle> handle_;
+
+ DISALLOW_COPY_AND_ASSIGN(LinuxUsbTransport);
+};
+
/* True if name isn't a valid name for a USB device in /sys/bus/usb/devices.
* Device names are made up of numbers, dots, and dashes, e.g., '7-1.5'.
* We reject interfaces (e.g., '7-1.5:1.0') and host controllers (e.g. 'usb1').
@@ -308,9 +326,9 @@
return 0;
}
-static usb_handle *find_usb_device(const char *base, ifc_match_func callback)
+static std::unique_ptr<usb_handle> find_usb_device(const char* base, ifc_match_func callback)
{
- usb_handle *usb = 0;
+ std::unique_ptr<usb_handle> usb;
char devname[64];
char desc[1024];
int n, in, out, ifc;
@@ -321,39 +339,37 @@
int writable;
busdir = opendir(base);
- if(busdir == 0) return 0;
+ if (busdir == 0) return 0;
- while((de = readdir(busdir)) && (usb == 0)) {
- if(badname(de->d_name)) continue;
+ while ((de = readdir(busdir)) && (usb == nullptr)) {
+ if (badname(de->d_name)) continue;
- if(!convert_to_devfs_name(de->d_name, devname, sizeof(devname))) {
+ if (!convert_to_devfs_name(de->d_name, devname, sizeof(devname))) {
// DBG("[ scanning %s ]\n", devname);
writable = 1;
- if((fd = open(devname, O_RDWR)) < 0) {
+ if ((fd = open(devname, O_RDWR)) < 0) {
// Check if we have read-only access, so we can give a helpful
// diagnostic like "adb devices" does.
writable = 0;
- if((fd = open(devname, O_RDONLY)) < 0) {
+ if ((fd = open(devname, O_RDONLY)) < 0) {
continue;
}
}
n = read(fd, desc, sizeof(desc));
- if(filter_usb_device(de->d_name, desc, n, writable, callback,
- &in, &out, &ifc) == 0) {
- usb = reinterpret_cast<usb_handle*>(calloc(1, sizeof(usb_handle)));
+ if (filter_usb_device(de->d_name, desc, n, writable, callback, &in, &out, &ifc) == 0) {
+ usb.reset(new usb_handle());
strcpy(usb->fname, devname);
usb->ep_in = in;
usb->ep_out = out;
usb->desc = fd;
n = ioctl(fd, USBDEVFS_CLAIMINTERFACE, &ifc);
- if(n != 0) {
+ if (n != 0) {
close(fd);
- free(usb);
- usb = 0;
+ usb.reset();
continue;
}
} else {
@@ -366,14 +382,14 @@
return usb;
}
-int usb_write(usb_handle *h, const void *_data, int len)
+ssize_t LinuxUsbTransport::Write(const void* _data, size_t len)
{
unsigned char *data = (unsigned char*) _data;
unsigned count = 0;
struct usbdevfs_bulktransfer bulk;
int n;
- if(h->ep_out == 0 || h->desc == -1) {
+ if (handle_->ep_out == 0 || handle_->desc == -1) {
return -1;
}
@@ -381,12 +397,12 @@
int xfer;
xfer = (len > MAX_USBFS_BULK_SIZE) ? MAX_USBFS_BULK_SIZE : len;
- bulk.ep = h->ep_out;
+ bulk.ep = handle_->ep_out;
bulk.len = xfer;
bulk.data = data;
bulk.timeout = 0;
- n = ioctl(h->desc, USBDEVFS_BULK, &bulk);
+ n = ioctl(handle_->desc, USBDEVFS_BULK, &bulk);
if(n != xfer) {
DBG("ERROR: n = %d, errno = %d (%s)\n",
n, errno, strerror(errno));
@@ -401,30 +417,30 @@
return count;
}
-int usb_read(usb_handle *h, void *_data, int len)
+ssize_t LinuxUsbTransport::Read(void* _data, size_t len)
{
unsigned char *data = (unsigned char*) _data;
unsigned count = 0;
struct usbdevfs_bulktransfer bulk;
int n, retry;
- if(h->ep_in == 0 || h->desc == -1) {
+ if (handle_->ep_in == 0 || handle_->desc == -1) {
return -1;
}
while(len > 0) {
int xfer = (len > MAX_USBFS_BULK_SIZE) ? MAX_USBFS_BULK_SIZE : len;
- bulk.ep = h->ep_in;
+ bulk.ep = handle_->ep_in;
bulk.len = xfer;
bulk.data = data;
bulk.timeout = 0;
retry = 0;
do{
- DBG("[ usb read %d fd = %d], fname=%s\n", xfer, h->desc, h->fname);
- n = ioctl(h->desc, USBDEVFS_BULK, &bulk);
- DBG("[ usb read %d ] = %d, fname=%s, Retry %d \n", xfer, n, h->fname, retry);
+ DBG("[ usb read %d fd = %d], fname=%s\n", xfer, handle_->desc, handle_->fname);
+ n = ioctl(handle_->desc, USBDEVFS_BULK, &bulk);
+ DBG("[ usb read %d ] = %d, fname=%s, Retry %d \n", xfer, n, handle_->fname, retry);
if( n < 0 ) {
DBG1("ERROR: n = %d, errno = %d (%s)\n",n, errno, strerror(errno));
@@ -446,24 +462,12 @@
return count;
}
-void usb_kick(usb_handle *h)
+int LinuxUsbTransport::Close()
{
int fd;
- fd = h->desc;
- h->desc = -1;
- if(fd >= 0) {
- close(fd);
- DBG("[ usb closed %d ]\n", fd);
- }
-}
-
-int usb_close(usb_handle *h)
-{
- int fd;
-
- fd = h->desc;
- h->desc = -1;
+ fd = handle_->desc;
+ handle_->desc = -1;
if(fd >= 0) {
close(fd);
DBG("[ usb closed %d ]\n", fd);
@@ -472,20 +476,21 @@
return 0;
}
-usb_handle *usb_open(ifc_match_func callback)
+Transport* usb_open(ifc_match_func callback)
{
- return find_usb_device("/sys/bus/usb/devices", callback);
+ std::unique_ptr<usb_handle> handle = find_usb_device("/sys/bus/usb/devices", callback);
+ return handle ? new LinuxUsbTransport(std::move(handle)) : nullptr;
}
/* Wait for the system to notice the device is gone, so that a subsequent
* fastboot command won't try to access the device before it's rebooted.
* Returns 0 for success, -1 for timeout.
*/
-int usb_wait_for_disconnect(usb_handle *usb)
+int LinuxUsbTransport::WaitForDisconnect()
{
double deadline = now() + WAIT_FOR_DISCONNECT_TIMEOUT;
while (now() < deadline) {
- if (access(usb->fname, F_OK))
+ if (access(handle_->fname, F_OK))
return 0;
usleep(50000);
}
diff --git a/fastboot/usb_osx.cpp b/fastboot/usb_osx.cpp
index 45ae833..ee5d575 100644
--- a/fastboot/usb_osx.cpp
+++ b/fastboot/usb_osx.cpp
@@ -35,6 +35,8 @@
#include <IOKit/IOMessage.h>
#include <mach/mach_port.h>
+#include <memory>
+
#include "usb.h"
@@ -63,6 +65,21 @@
unsigned int zero_mask;
};
+class OsxUsbTransport : public Transport {
+ public:
+ OsxUsbTransport(std::unique_ptr<usb_handle> handle) : handle_(std::move(handle)) {}
+ ~OsxUsbTransport() override = default;
+
+ ssize_t Read(void* data, size_t len) override;
+ ssize_t Write(const void* data, size_t len) override;
+ int Close() override;
+
+ private:
+ std::unique_ptr<usb_handle> handle_;
+
+ DISALLOW_COPY_AND_ASSIGN(OsxUsbTransport);
+};
+
/** Try out all the interfaces and see if there's a match. Returns 0 on
* success, -1 on failure. */
static int try_interfaces(IOUSBDeviceInterface182 **dev, usb_handle *handle) {
@@ -390,7 +407,7 @@
/** Initializes the USB system. Returns 0 on success, -1 on error. */
-static int init_usb(ifc_match_func callback, usb_handle **handle) {
+static int init_usb(ifc_match_func callback, std::unique_ptr<usb_handle>* handle) {
int ret = -1;
CFMutableDictionaryRef matchingDict;
kern_return_t result;
@@ -443,8 +460,8 @@
}
if (h.success) {
- *handle = reinterpret_cast<usb_handle*>(calloc(1, sizeof(usb_handle)));
- memcpy(*handle, &h, sizeof(usb_handle));
+ handle->reset(new usb_handle);
+ memcpy(handle->get(), &h, sizeof(usb_handle));
ret = 0;
break;
}
@@ -463,28 +480,23 @@
* Definitions of this file's public functions.
*/
-usb_handle *usb_open(ifc_match_func callback) {
- usb_handle *handle = NULL;
+Transport* usb_open(ifc_match_func callback) {
+ std::unique_ptr<usb_handle> handle;
if (init_usb(callback, &handle) < 0) {
/* Something went wrong initializing USB. */
- return NULL;
+ return nullptr;
}
- return handle;
+ return new OsxUsbTransport(std::move(handle));
}
-int usb_close(usb_handle *h) {
+int OsxUsbTransport::Close() {
/* TODO: Something better here? */
return 0;
}
-int usb_wait_for_disconnect(usb_handle *usb) {
- /* TODO: Punt for now */
- return 0;
-}
-
-int usb_read(usb_handle *h, void *data, int len) {
+ssize_t OsxUsbTransport::Read(void* data, size_t len) {
IOReturn result;
UInt32 numBytes = len;
@@ -492,22 +504,21 @@
return 0;
}
- if (h == NULL) {
+ if (handle_ == nullptr) {
return -1;
}
- if (h->interface == NULL) {
+ if (handle_->interface == nullptr) {
ERR("usb_read interface was null\n");
return -1;
}
- if (h->bulkIn == 0) {
+ if (handle_->bulkIn == 0) {
ERR("bulkIn endpoint not assigned\n");
return -1;
}
- result = (*h->interface)->ReadPipe(
- h->interface, h->bulkIn, data, &numBytes);
+ result = (*handle_->interface)->ReadPipe(handle_->interface, handle_->bulkIn, data, &numBytes);
if (result == 0) {
return (int) numBytes;
@@ -518,30 +529,30 @@
return -1;
}
-int usb_write(usb_handle *h, const void *data, int len) {
+ssize_t OsxUsbTransport::Write(const void* data, size_t len) {
IOReturn result;
if (len == 0) {
return 0;
}
- if (h == NULL) {
+ if (handle_ == NULL) {
return -1;
}
- if (h->interface == NULL) {
+ if (handle_->interface == NULL) {
ERR("usb_write interface was null\n");
return -1;
}
- if (h->bulkOut == 0) {
+ if (handle_->bulkOut == 0) {
ERR("bulkOut endpoint not assigned\n");
return -1;
}
#if 0
- result = (*h->interface)->WritePipe(
- h->interface, h->bulkOut, (void *)data, len);
+ result = (*handle_->interface)->WritePipe(
+ handle_->interface, handle_->bulkOut, (void *)data, len);
#else
/* Attempt to work around crashes in the USB driver that may be caused
* by trying to write too much data at once. The kernel IOCopyMapper
@@ -554,8 +565,8 @@
int lenToSend = lenRemaining > maxLenToSend
? maxLenToSend : lenRemaining;
- result = (*h->interface)->WritePipe(
- h->interface, h->bulkOut, (void *)data, lenToSend);
+ result = (*handle_->interface)->WritePipe(
+ handle_->interface, handle_->bulkOut, (void *)data, lenToSend);
if (result != 0) break;
lenRemaining -= lenToSend;
@@ -564,11 +575,11 @@
#endif
#if 0
- if ((result == 0) && (h->zero_mask)) {
+ if ((result == 0) && (handle_->zero_mask)) {
/* we need 0-markers and our transfer */
- if(!(len & h->zero_mask)) {
- result = (*h->interface)->WritePipe(
- h->interface, h->bulkOut, (void *)data, 0);
+ if(!(len & handle_->zero_mask)) {
+ result = (*handle_->interface)->WritePipe(
+ handle_->interface, handle_->bulkOut, (void *)data, 0);
}
}
#endif
diff --git a/fastboot/usb_windows.cpp b/fastboot/usb_windows.cpp
index a09610f..1cdeb32 100644
--- a/fastboot/usb_windows.cpp
+++ b/fastboot/usb_windows.cpp
@@ -34,6 +34,9 @@
#include <stdio.h>
#include <stdlib.h>
+#include <memory>
+#include <string>
+
#include "usb.h"
//#define TRACE_USB 1
@@ -60,24 +63,32 @@
ADBAPIHANDLE adb_write_pipe;
/// Interface name
- char* interface_name;
+ std::string interface_name;
+};
+
+class WindowsUsbTransport : public Transport {
+ public:
+ WindowsUsbTransport(std::unique_ptr<usb_handle> handle) : handle_(std::move(handle)) {}
+ ~WindowsUsbTransport() override = default;
+
+ ssize_t Read(void* data, size_t len) override;
+ ssize_t Write(const void* data, size_t len) override;
+ int Close() override;
+
+ private:
+ std::unique_ptr<usb_handle> handle_;
+
+ DISALLOW_COPY_AND_ASSIGN(WindowsUsbTransport);
};
/// Class ID assigned to the device by androidusb.sys
static const GUID usb_class_id = ANDROID_USB_CLASS_ID;
-
/// Checks if interface (device) matches certain criteria
int recognized_device(usb_handle* handle, ifc_match_func callback);
/// Opens usb interface (device) by interface (device) name.
-usb_handle* do_usb_open(const wchar_t* interface_name);
-
-/// Writes data to the opened usb handle
-int usb_write(usb_handle* handle, const void* data, int len);
-
-/// Reads data using the opened usb handle
-int usb_read(usb_handle *handle, void* data, int len);
+std::unique_ptr<usb_handle> do_usb_open(const wchar_t* interface_name);
/// Cleans up opened usb handle
void usb_cleanup_handle(usb_handle* handle);
@@ -85,23 +96,17 @@
/// Cleans up (but don't close) opened usb handle
void usb_kick(usb_handle* handle);
-/// Closes opened usb handle
-int usb_close(usb_handle* handle);
-
-usb_handle* do_usb_open(const wchar_t* interface_name) {
+std::unique_ptr<usb_handle> do_usb_open(const wchar_t* interface_name) {
// Allocate our handle
- usb_handle* ret = (usb_handle*)malloc(sizeof(usb_handle));
- if (NULL == ret)
- return NULL;
+ std::unique_ptr<usb_handle> ret(new usb_handle);
// Create interface.
ret->adb_interface = AdbCreateInterfaceByName(interface_name);
- if (NULL == ret->adb_interface) {
- free(ret);
+ if (nullptr == ret->adb_interface) {
errno = GetLastError();
- return NULL;
+ return nullptr;
}
// Open read pipe (endpoint)
@@ -109,35 +114,30 @@
AdbOpenDefaultBulkReadEndpoint(ret->adb_interface,
AdbOpenAccessTypeReadWrite,
AdbOpenSharingModeReadWrite);
- if (NULL != ret->adb_read_pipe) {
+ if (nullptr != ret->adb_read_pipe) {
// Open write pipe (endpoint)
ret->adb_write_pipe =
AdbOpenDefaultBulkWriteEndpoint(ret->adb_interface,
AdbOpenAccessTypeReadWrite,
AdbOpenSharingModeReadWrite);
- if (NULL != ret->adb_write_pipe) {
+ if (nullptr != ret->adb_write_pipe) {
// Save interface name
unsigned long name_len = 0;
// First get expected name length
AdbGetInterfaceName(ret->adb_interface,
- NULL,
+ nullptr,
&name_len,
true);
if (0 != name_len) {
- ret->interface_name = (char*)malloc(name_len);
-
- if (NULL != ret->interface_name) {
- // Now save the name
- if (AdbGetInterfaceName(ret->adb_interface,
- ret->interface_name,
- &name_len,
- true)) {
- // We're done at this point
- return ret;
- }
- } else {
- SetLastError(ERROR_OUTOFMEMORY);
+ // Now save the name
+ ret->interface_name.resize(name_len);
+ if (AdbGetInterfaceName(ret->adb_interface,
+ &ret->interface_name[0],
+ &name_len,
+ true)) {
+ // We're done at this point
+ return ret;
}
}
}
@@ -145,35 +145,31 @@
// Something went wrong.
errno = GetLastError();
- usb_cleanup_handle(ret);
- free(ret);
+ usb_cleanup_handle(ret.get());
SetLastError(errno);
- return NULL;
+ return nullptr;
}
-int usb_write(usb_handle* handle, const void* data, int len) {
+ssize_t WindowsUsbTransport::Write(const void* data, size_t len) {
unsigned long time_out = 5000;
unsigned long written = 0;
unsigned count = 0;
int ret;
DBG("usb_write %d\n", len);
- if (NULL != handle) {
+ if (nullptr != handle_) {
// Perform write
while(len > 0) {
int xfer = (len > MAX_USBFS_BULK_SIZE) ? MAX_USBFS_BULK_SIZE : len;
- ret = AdbWriteEndpointSync(handle->adb_write_pipe,
- (void*)data,
- (unsigned long)xfer,
- &written,
- time_out);
+ ret = AdbWriteEndpointSync(handle_->adb_write_pipe, const_cast<void*>(data), xfer,
+ &written, time_out);
errno = GetLastError();
DBG("AdbWriteEndpointSync returned %d, errno: %d\n", ret, errno);
if (ret == 0) {
// assume ERROR_INVALID_HANDLE indicates we are disconnected
if (errno == ERROR_INVALID_HANDLE)
- usb_kick(handle);
+ usb_kick(handle_.get());
return -1;
}
@@ -194,21 +190,17 @@
return -1;
}
-int usb_read(usb_handle *handle, void* data, int len) {
+ssize_t WindowsUsbTransport::Read(void* data, size_t len) {
unsigned long time_out = 0;
unsigned long read = 0;
int ret;
DBG("usb_read %d\n", len);
- if (NULL != handle) {
+ if (nullptr != handle_) {
while (1) {
int xfer = (len > MAX_USBFS_BULK_SIZE) ? MAX_USBFS_BULK_SIZE : len;
- ret = AdbReadEndpointSync(handle->adb_read_pipe,
- (void*)data,
- (unsigned long)xfer,
- &read,
- time_out);
+ ret = AdbReadEndpointSync(handle_->adb_read_pipe, data, xfer, &read, time_out);
errno = GetLastError();
DBG("usb_read got: %ld, expected: %d, errno: %d\n", read, xfer, errno);
if (ret) {
@@ -216,7 +208,7 @@
} else {
// assume ERROR_INVALID_HANDLE indicates we are disconnected
if (errno == ERROR_INVALID_HANDLE)
- usb_kick(handle);
+ usb_kick(handle_.get());
break;
}
// else we timed out - try again
@@ -233,8 +225,6 @@
void usb_cleanup_handle(usb_handle* handle) {
if (NULL != handle) {
- if (NULL != handle->interface_name)
- free(handle->interface_name);
if (NULL != handle->adb_write_pipe)
AdbCloseHandle(handle->adb_write_pipe);
if (NULL != handle->adb_read_pipe)
@@ -242,7 +232,7 @@
if (NULL != handle->adb_interface)
AdbCloseHandle(handle->adb_interface);
- handle->interface_name = NULL;
+ handle->interface_name.clear();
handle->adb_write_pipe = NULL;
handle->adb_read_pipe = NULL;
handle->adb_interface = NULL;
@@ -258,23 +248,18 @@
}
}
-int usb_close(usb_handle* handle) {
+int WindowsUsbTransport::Close() {
DBG("usb_close\n");
- if (NULL != handle) {
+ if (nullptr != handle_) {
// Cleanup handle
- usb_cleanup_handle(handle);
- free(handle);
+ usb_cleanup_handle(handle_.get());
+ handle_.reset();
}
return 0;
}
-int usb_wait_for_disconnect(usb_handle *usb) {
- /* TODO: Punt for now */
- return 0;
-}
-
int recognized_device(usb_handle* handle, ifc_match_func callback) {
struct usb_ifc_info info;
USB_DEVICE_DESCRIPTOR device_desc;
@@ -326,8 +311,8 @@
return 0;
}
-static usb_handle *find_usb_device(ifc_match_func callback) {
- usb_handle* handle = NULL;
+static std::unique_ptr<usb_handle> find_usb_device(ifc_match_func callback) {
+ std::unique_ptr<usb_handle> handle;
char entry_buffer[2048];
char interf_name[2048];
AdbInterfaceInfo* next_interface = (AdbInterfaceInfo*)(&entry_buffer[0]);
@@ -356,13 +341,12 @@
handle = do_usb_open(next_interface->device_name);
if (NULL != handle) {
// Lets see if this interface (device) belongs to us
- if (recognized_device(handle, callback)) {
+ if (recognized_device(handle.get(), callback)) {
// found it!
break;
} else {
- usb_cleanup_handle(handle);
- free(handle);
- handle = NULL;
+ usb_cleanup_handle(handle.get());
+ handle.reset();
}
}
@@ -373,9 +357,10 @@
return handle;
}
-usb_handle *usb_open(ifc_match_func callback)
+Transport* usb_open(ifc_match_func callback)
{
- return find_usb_device(callback);
+ std::unique_ptr<usb_handle> handle = find_usb_device(callback);
+ return handle ? new WindowsUsbTransport(std::move(handle)) : nullptr;
}
// called from fastboot.c
diff --git a/fastboot/usbtest.cpp b/fastboot/usbtest.cpp
index e6e2b37..9423c6d 100644
--- a/fastboot/usbtest.cpp
+++ b/fastboot/usbtest.cpp
@@ -86,7 +86,7 @@
return 0;
}
-int test_null(usb_handle *usb)
+int test_null(Transport* usb)
{
unsigned i;
unsigned char buf[4096];
@@ -94,8 +94,8 @@
long long t0, t1;
t0 = NOW();
- for(i = 0; i < arg_count; i++) {
- if(usb_write(usb, buf, arg_size) != (int)arg_size) {
+ for (i = 0; i < arg_count; i++) {
+ if (usb->Write(buf, arg_size) != static_cast<int>(arg_size)) {
fprintf(stderr,"write failed (%s)\n", strerror(errno));
return -1;
}
@@ -105,15 +105,15 @@
return 0;
}
-int test_zero(usb_handle *usb)
+int test_zero(Transport* usb)
{
unsigned i;
unsigned char buf[4096];
long long t0, t1;
t0 = NOW();
- for(i = 0; i < arg_count; i++) {
- if(usb_read(usb, buf, arg_size) != (int)arg_size) {
+ for (i = 0; i < arg_count; i++) {
+ if (usb->Read(buf, arg_size) != static_cast<int>(arg_size)) {
fprintf(stderr,"read failed (%s)\n", strerror(errno));
return -1;
}
@@ -127,7 +127,7 @@
{
const char *cmd;
ifc_match_func match;
- int (*test)(usb_handle *usb);
+ int (*test)(Transport* usb);
const char *help;
} tests[] = {
{ "list", printifc, NULL, "list interfaces" },
@@ -177,7 +177,7 @@
int main(int argc, char **argv)
{
- usb_handle *usb;
+ Transport* usb;
int i;
if(argc < 2)
diff --git a/fs_mgr/fs_mgr_verity.cpp b/fs_mgr/fs_mgr_verity.cpp
index 6983b72..b5141c9 100644
--- a/fs_mgr/fs_mgr_verity.cpp
+++ b/fs_mgr/fs_mgr_verity.cpp
@@ -29,7 +29,7 @@
#include <libgen.h>
#include <time.h>
-#include <base/file.h>
+#include <android-base/file.h>
#include <private/android_filesystem_config.h>
#include <cutils/properties.h>
#include <logwrap/logwrap.h>
@@ -254,7 +254,7 @@
res = snprintf(buf, bufsize, "%s 2 " VERITY_TABLE_OPT_IGNZERO " %s", params->table,
mode_flag);
} else {
- res = strlcpy(buf, params->table, bufsize);
+ res = snprintf(buf, bufsize, "%s 1 " VERITY_TABLE_OPT_IGNZERO, params->table);
}
if (res < 0 || (size_t)res >= bufsize) {
@@ -695,31 +695,27 @@
int match = 0;
off64_t offset = 0;
+ /* unless otherwise specified, use EIO mode */
+ *mode = VERITY_MODE_EIO;
+
/* use the kernel parameter if set */
property_get("ro.boot.veritymode", propbuf, "");
if (*propbuf != '\0') {
if (!strcmp(propbuf, "enforcing")) {
*mode = VERITY_MODE_DEFAULT;
- return 0;
- } else if (!strcmp(propbuf, "logging")) {
- *mode = VERITY_MODE_LOGGING;
- return 0;
- } else {
- INFO("Unknown value %s for veritymode; ignoring", propbuf);
}
+ return 0;
}
if (get_verity_state_offset(fstab, &offset) < 0) {
/* fall back to stateless behavior */
- *mode = VERITY_MODE_EIO;
return 0;
}
if (was_verity_restart()) {
/* device was restarted after dm-verity detected a corrupted
- * block, so switch to logging mode */
- *mode = VERITY_MODE_LOGGING;
+ * block, so use EIO mode */
return write_verity_state(fstab->verity_loc, offset, *mode);
}
@@ -784,7 +780,6 @@
int fs_mgr_update_verity_state(fs_mgr_verity_state_callback callback)
{
alignas(dm_ioctl) char buffer[DM_BUF_SIZE];
- bool use_state = true;
char fstab_filename[PROPERTY_VALUE_MAX + sizeof(FSTAB_PREFIX)];
char *mount_point;
char propbuf[PROPERTY_VALUE_MAX];
@@ -793,15 +788,11 @@
int i;
int mode;
int rc = -1;
- off64_t offset = 0;
struct dm_ioctl *io = (struct dm_ioctl *) buffer;
struct fstab *fstab = NULL;
- /* check if we need to store the state */
- property_get("ro.boot.veritymode", propbuf, "");
-
- if (*propbuf != '\0') {
- use_state = false; /* state is kept by the bootloader */
+ if (!callback) {
+ return -1;
}
if (fs_mgr_load_verity_state(&mode) == -1) {
@@ -841,16 +832,7 @@
status = &buffer[io->data_start + sizeof(struct dm_target_spec)];
- if (use_state && *status == 'C') {
- if (write_verity_state(fstab->recs[i].verity_loc, offset,
- VERITY_MODE_LOGGING) < 0) {
- continue;
- }
- }
-
- if (callback) {
- callback(&fstab->recs[i], mount_point, mode, *status);
- }
+ callback(&fstab->recs[i], mount_point, mode, *status);
}
rc = 0;
@@ -962,13 +944,43 @@
// load the verity mapping table
if (load_verity_table(io, mount_point, verity.data_size, fd, ¶ms,
- format_verity_table) < 0 &&
- // try the legacy format for backwards compatibility
- load_verity_table(io, mount_point, verity.data_size, fd, ¶ms,
- format_legacy_verity_table) < 0) {
- goto out;
+ format_verity_table) == 0) {
+ goto loaded;
}
+ if (params.ecc.valid) {
+ // kernel may not support error correction, try without
+ INFO("Disabling error correction for %s\n", mount_point);
+ params.ecc.valid = false;
+
+ if (load_verity_table(io, mount_point, verity.data_size, fd, ¶ms,
+ format_verity_table) == 0) {
+ goto loaded;
+ }
+ }
+
+ // try the legacy format for backwards compatibility
+ if (load_verity_table(io, mount_point, verity.data_size, fd, ¶ms,
+ format_legacy_verity_table) == 0) {
+ goto loaded;
+ }
+
+ if (params.mode != VERITY_MODE_EIO) {
+ // as a last resort, EIO mode should always be supported
+ INFO("Falling back to EIO mode for %s\n", mount_point);
+ params.mode = VERITY_MODE_EIO;
+
+ if (load_verity_table(io, mount_point, verity.data_size, fd, ¶ms,
+ format_legacy_verity_table) == 0) {
+ goto loaded;
+ }
+ }
+
+ ERROR("Failed to load verity table for %s\n", mount_point);
+ goto out;
+
+loaded:
+
// activate the device
if (resume_verity_table(io, mount_point, fd) < 0) {
goto out;
diff --git a/gatekeeperd/SoftGateKeeper.h b/gatekeeperd/SoftGateKeeper.h
index 75fe11d..8b15d72 100644
--- a/gatekeeperd/SoftGateKeeper.h
+++ b/gatekeeperd/SoftGateKeeper.h
@@ -25,8 +25,10 @@
#include <crypto_scrypt.h>
}
+#include <android-base/memory.h>
#include <UniquePtr.h>
#include <gatekeeper/gatekeeper.h>
+
#include <iostream>
#include <unordered_map>
@@ -150,14 +152,15 @@
}
bool DoVerify(const password_handle_t *expected_handle, const SizedBuffer &password) {
- FastHashMap::const_iterator it = fast_hash_map_.find(expected_handle->user_id);
+ uint64_t user_id = android::base::get_unaligned(&expected_handle->user_id);
+ FastHashMap::const_iterator it = fast_hash_map_.find(user_id);
if (it != fast_hash_map_.end() && VerifyFast(it->second, password)) {
return true;
} else {
if (GateKeeper::DoVerify(expected_handle, password)) {
uint64_t salt;
GetRandom(&salt, sizeof(salt));
- fast_hash_map_[expected_handle->user_id] = ComputeFastHash(password, salt);
+ fast_hash_map_[user_id] = ComputeFastHash(password, salt);
return true;
}
}
@@ -177,4 +180,3 @@
}
#endif // SOFT_GATEKEEPER_H_
-
diff --git a/gatekeeperd/tests/Android.mk b/gatekeeperd/tests/Android.mk
index 6fc4ac0..a62b1d4 100644
--- a/gatekeeperd/tests/Android.mk
+++ b/gatekeeperd/tests/Android.mk
@@ -20,7 +20,7 @@
LOCAL_MODULE := gatekeeperd-unit-tests
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
LOCAL_CFLAGS += -g -Wall -Werror -std=gnu++11 -Wno-missing-field-initializers
-LOCAL_SHARED_LIBRARIES := libgatekeeper libcrypto
+LOCAL_SHARED_LIBRARIES := libgatekeeper libcrypto libbase
LOCAL_STATIC_LIBRARIES := libscrypt_static
LOCAL_C_INCLUDES := external/scrypt/lib/crypto
LOCAL_SRC_FILES := \
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index e280fdc..cdfe9c5 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -30,7 +30,6 @@
#include <batteryservice/BatteryService.h>
#include <cutils/klog.h>
#include <cutils/properties.h>
-#include <log/log_read.h>
#include <utils/Errors.h>
#include <utils/String8.h>
#include <utils/Vector.h>
@@ -39,6 +38,7 @@
#define POWER_SUPPLY_SYSFS_PATH "/sys/class/" POWER_SUPPLY_SUBSYSTEM
#define FAKE_BATTERY_CAPACITY 42
#define FAKE_BATTERY_TEMPERATURE 424
+#define ALWAYS_PLUGGED_CAPACITY 100
namespace android {
@@ -136,6 +136,9 @@
{ "USB_DCP", ANDROID_POWER_SUPPLY_TYPE_AC },
{ "USB_CDP", ANDROID_POWER_SUPPLY_TYPE_AC },
{ "USB_ACA", ANDROID_POWER_SUPPLY_TYPE_AC },
+ { "USB_C", ANDROID_POWER_SUPPLY_TYPE_AC },
+ { "USB_PD", ANDROID_POWER_SUPPLY_TYPE_AC },
+ { "USB_PD_DRP", ANDROID_POWER_SUPPLY_TYPE_USB },
{ "Wireless", ANDROID_POWER_SUPPLY_TYPE_WIRELESS },
{ NULL, 0 },
};
@@ -183,6 +186,7 @@
props.chargerWirelessOnline = false;
props.batteryStatus = BATTERY_STATUS_UNKNOWN;
props.batteryHealth = BATTERY_HEALTH_UNKNOWN;
+ props.maxChargingCurrent = 0;
if (!mHealthdConfig->batteryPresentPath.isEmpty())
props.batteryPresent = getBooleanField(mHealthdConfig->batteryPresentPath);
@@ -207,6 +211,15 @@
mBatteryFixedTemperature :
getIntField(mHealthdConfig->batteryTemperaturePath);
+ // For devices which do not have battery and are always plugged
+ // into power souce.
+ if (mAlwaysPluggedDevice) {
+ props.chargerAcOnline = true;
+ props.batteryPresent = true;
+ props.batteryStatus = BATTERY_STATUS_CHARGING;
+ props.batteryHealth = BATTERY_HEALTH_GOOD;
+ }
+
const int SIZE = 128;
char buf[SIZE];
String8 btech;
@@ -246,6 +259,15 @@
KLOG_WARNING(LOG_TAG, "%s: Unknown power supply type\n",
mChargerNames[i].string());
}
+ path.clear();
+ path.appendFormat("%s/%s/current_max", POWER_SUPPLY_SYSFS_PATH,
+ mChargerNames[i].string());
+ if (access(path.string(), R_OK) == 0) {
+ int maxChargingCurrent = getIntField(path);
+ if (props.maxChargingCurrent < maxChargingCurrent) {
+ props.maxChargingCurrent = maxChargingCurrent;
+ }
+ }
}
}
}
@@ -290,25 +312,6 @@
props.chargerUsbOnline ? "u" : "",
props.chargerWirelessOnline ? "w" : "");
- log_time realtime(CLOCK_REALTIME);
- time_t t = realtime.tv_sec;
- struct tm *tmp = gmtime(&t);
- if (tmp) {
- static const char fmt[] = " %Y-%m-%d %H:%M:%S.XXXXXXXXX UTC";
- len = strlen(dmesgline);
- if ((len < (sizeof(dmesgline) - sizeof(fmt) - 8)) // margin
- && strftime(dmesgline + len, sizeof(dmesgline) - len,
- fmt, tmp)) {
- char *usec = strchr(dmesgline + len, 'X');
- if (usec) {
- len = usec - dmesgline;
- snprintf(dmesgline + len, sizeof(dmesgline) - len,
- "%09u", realtime.tv_nsec);
- usec[9] = ' ';
- }
- }
- }
-
KLOG_WARNING(LOG_TAG, "%s\n", dmesgline);
}
@@ -382,9 +385,9 @@
int v;
char vs[128];
- snprintf(vs, sizeof(vs), "ac: %d usb: %d wireless: %d\n",
+ snprintf(vs, sizeof(vs), "ac: %d usb: %d wireless: %d current_max: %d\n",
props.chargerAcOnline, props.chargerUsbOnline,
- props.chargerWirelessOnline);
+ props.chargerWirelessOnline, props.maxChargingCurrent);
write(fd, vs, strlen(vs));
snprintf(vs, sizeof(vs), "status: %d health: %d present: %d\n",
props.batteryStatus, props.batteryHealth, props.batteryPresent);
@@ -580,8 +583,15 @@
closedir(dir);
}
- if (!mChargerNames.size())
+ // This indicates that there is no charger driver registered.
+ // Typically the case for devices which do not have a battery and
+ // and are always plugged into AC mains.
+ if (!mChargerNames.size()) {
KLOG_ERROR(LOG_TAG, "No charger supplies found\n");
+ mBatteryFixedCapacity = ALWAYS_PLUGGED_CAPACITY;
+ mBatteryFixedTemperature = FAKE_BATTERY_TEMPERATURE;
+ mAlwaysPluggedDevice = true;
+ }
if (!mBatteryDevicePresent) {
KLOG_WARNING(LOG_TAG, "No battery devices found\n");
hc->periodic_chores_interval_fast = -1;
diff --git a/healthd/BatteryMonitor.h b/healthd/BatteryMonitor.h
index 3425f27..a61171f 100644
--- a/healthd/BatteryMonitor.h
+++ b/healthd/BatteryMonitor.h
@@ -46,6 +46,7 @@
struct healthd_config *mHealthdConfig;
Vector<String8> mChargerNames;
bool mBatteryDevicePresent;
+ bool mAlwaysPluggedDevice;
int mBatteryFixedCapacity;
int mBatteryFixedTemperature;
struct BatteryProperties props;
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index 6800ad2..46bad4e 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -699,7 +699,10 @@
GRSurface** scale_frames;
int scale_count;
- ret = res_create_multi_display_surface("charger/battery_scale", &scale_count, &scale_frames);
+ int scale_fps; // Not in use (charger/battery_scale doesn't have FPS text
+ // chunk). We are using hard-coded frame.disp_time instead.
+ ret = res_create_multi_display_surface("charger/battery_scale", &scale_count, &scale_fps,
+ &scale_frames);
if (ret < 0) {
LOGE("Cannot load battery_scale image\n");
charger->batt_anim->num_frames = 0;
diff --git a/include/binderwrapper/binder_wrapper.h b/include/binderwrapper/binder_wrapper.h
index 921c4ed..ccda825 100644
--- a/include/binderwrapper/binder_wrapper.h
+++ b/include/binderwrapper/binder_wrapper.h
@@ -30,6 +30,7 @@
class IBinder;
// Wraps libbinder to make it testable.
+// NOTE: Static methods of this class are not thread-safe.
class BinderWrapper {
public:
virtual ~BinderWrapper() {}
@@ -50,6 +51,10 @@
// InitForTesting().
static BinderWrapper* Get();
+ // Returns the singleton instance if it was previously created by Create() or
+ // set by InitForTesting(), or creates a new one by calling Create().
+ static BinderWrapper* GetOrCreateInstance();
+
// Gets the binder for communicating with the service identified by
// |service_name|, returning null immediately if it doesn't exist.
virtual sp<IBinder> GetService(const std::string& service_name) = 0;
diff --git a/include/cutils/trace.h b/include/cutils/trace.h
index 087a0c4..c9790ad 100644
--- a/include/cutils/trace.h
+++ b/include/cutils/trace.h
@@ -69,7 +69,8 @@
#define ATRACE_TAG_POWER (1<<17)
#define ATRACE_TAG_PACKAGE_MANAGER (1<<18)
#define ATRACE_TAG_SYSTEM_SERVER (1<<19)
-#define ATRACE_TAG_LAST ATRACE_TAG_SYSTEM_SERVER
+#define ATRACE_TAG_DATABASE (1<<20)
+#define ATRACE_TAG_LAST ATRACE_TAG_DATABASE
// Reserved for initialization.
#define ATRACE_TAG_NOT_READY (1ULL<<63)
diff --git a/include/log/log.h b/include/log/log.h
index 1cdf7bc..3d9240d 100644
--- a/include/log/log.h
+++ b/include/log/log.h
@@ -604,7 +604,8 @@
LOG_ID_EVENTS = 2,
LOG_ID_SYSTEM = 3,
LOG_ID_CRASH = 4,
- LOG_ID_KERNEL = 5,
+ LOG_ID_SECURITY = 5,
+ LOG_ID_KERNEL = 6, /* place last, third-parties can not use it */
#endif
LOG_ID_MAX
@@ -614,9 +615,13 @@
/*
* Use the per-tag properties "log.tag.<tagname>" to generate a runtime
- * result of non-zero to expose a log.
+ * result of non-zero to expose a log. prio is ANDROID_LOG_VERBOSE to
+ * ANDROID_LOG_FATAL. default_prio if no property. Undefined behavior if
+ * any other value.
*/
-int __android_log_is_loggable(int prio, const char *tag, int def);
+int __android_log_is_loggable(int prio, const char *tag, int default_prio);
+
+int __android_log_security(); /* Device Owner is present */
int __android_log_error_write(int tag, const char *subTag, int32_t uid, const char *data,
uint32_t dataLen);
diff --git a/include/log/logd.h b/include/log/logd.h
index 0fe515f..b7aedaf 100644
--- a/include/log/logd.h
+++ b/include/log/logd.h
@@ -44,6 +44,8 @@
size_t len);
int __android_log_bswrite(int32_t tag, const char *payload);
+int __android_log_security_bwrite(int32_t tag, const void *payload, size_t len);
+
#ifdef __cplusplus
}
#endif
diff --git a/include/log/logger.h b/include/log/logger.h
index c795253..60d47a2 100644
--- a/include/log/logger.h
+++ b/include/log/logger.h
@@ -11,6 +11,10 @@
#define _LIBS_LOG_LOGGER_H
#include <stdint.h>
+#ifdef __linux__
+#include <time.h> /* clockid_t definition */
+#endif
+
#include <log/log.h>
#include <log/log_read.h>
@@ -60,12 +64,24 @@
char msg[0]; /* the entry's payload */
} __attribute__((__packed__));
+struct logger_entry_v4 {
+ uint16_t len; /* length of the payload */
+ uint16_t hdr_size; /* sizeof(struct logger_entry_v4) */
+ int32_t pid; /* generating process's pid */
+ uint32_t tid; /* generating process's tid */
+ uint32_t sec; /* seconds since Epoch */
+ uint32_t nsec; /* nanoseconds */
+ uint32_t lid; /* log id of the payload, bottom 4 bits currently */
+ uint32_t uid; /* generating process's uid */
+ char msg[0]; /* the entry's payload */
+} __attribute__((__packed__));
+
/*
* The maximum size of the log entry payload that can be
* written to the logger. An attempt to write more than
* this amount will result in a truncated log entry.
*/
-#define LOGGER_ENTRY_MAX_PAYLOAD 4076
+#define LOGGER_ENTRY_MAX_PAYLOAD 4068
/*
* The maximum size of a log entry which can be read from the
@@ -79,7 +95,8 @@
struct log_msg {
union {
unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1];
- struct logger_entry_v3 entry;
+ struct logger_entry_v4 entry;
+ struct logger_entry_v4 entry_v4;
struct logger_entry_v3 entry_v3;
struct logger_entry_v2 entry_v2;
struct logger_entry entry_v1;
@@ -159,6 +176,8 @@
#define ANDROID_LOG_RDWR O_RDWR
#define ANDROID_LOG_ACCMODE O_ACCMODE
#define ANDROID_LOG_NONBLOCK O_NONBLOCK
+#define ANDROID_LOG_WRAP 0x40000000 /* Block until buffer about to wrap */
+#define ANDROID_LOG_WRAP_DEFAULT_TIMEOUT 7200 /* 2 hour default */
#define ANDROID_LOG_PSTORE 0x80000000
struct logger_list *android_logger_list_alloc(int mode,
@@ -183,7 +202,9 @@
pid_t pid);
#define android_logger_list_close android_logger_list_free
-char android_log_timestamp();
+#ifdef __linux__
+clockid_t android_log_clockid();
+#endif
/*
* log_id_t helpers
diff --git a/include/log/logprint.h b/include/log/logprint.h
index 204b3f2..539d1dc 100644
--- a/include/log/logprint.h
+++ b/include/log/logprint.h
@@ -44,6 +44,7 @@
FORMAT_MODIFIER_ZONE, /* Adds zone to date */
FORMAT_MODIFIER_EPOCH, /* Print time as seconds since Jan 1 1970 */
FORMAT_MODIFIER_MONOTONIC, /* Print cpu time as seconds since start */
+ FORMAT_MODIFIER_UID, /* Adds uid */
} AndroidLogPrintFormat;
typedef struct AndroidLogFormat_t AndroidLogFormat;
@@ -52,6 +53,7 @@
time_t tv_sec;
long tv_nsec;
android_LogPriority priority;
+ int32_t uid;
int32_t pid;
int32_t tid;
const char * tag;
diff --git a/include/nativeloader/native_loader.h b/include/nativeloader/native_loader.h
new file mode 100644
index 0000000..da07253
--- /dev/null
+++ b/include/nativeloader/native_loader.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef NATIVE_LOADER_H_
+#define NATIVE_LOADER_H_
+
+#include "jni.h"
+#include <stdint.h>
+
+namespace android {
+
+__attribute__((visibility("default")))
+void* OpenNativeLibrary(JNIEnv* env, int32_t target_sdk_version, const char* path,
+ jobject class_loader, bool is_shared, jstring library_path,
+ jstring permitted_path);
+
+}; // namespace android
+
+#endif // NATIVE_BRIDGE_H_
diff --git a/include/private/android_filesystem_config.h b/include/private/android_filesystem_config.h
index c7eb34b..9876e34 100644
--- a/include/private/android_filesystem_config.h
+++ b/include/private/android_filesystem_config.h
@@ -81,6 +81,12 @@
#define AID_SHARED_RELRO 1037 /* creator of shared GNU RELRO files */
#define AID_DBUS 1038 /* dbus-daemon IPC broker process */
#define AID_TLSDATE 1039 /* tlsdate unprivileged user */
+#define AID_MEDIA_EX 1040 /* mediaextractor process */
+#define AID_AUDIOSERVER 1041 /* audioserver process */
+#define AID_METRICS_COLL 1042 /* metrics_collector process */
+#define AID_METRICSD 1043 /* metricsd process */
+#define AID_WEBSERV 1044 /* webservd process */
+#define AID_DEBUGGERD 1045 /* debuggerd unprivileged user */
#define AID_SHELL 2000 /* adb and debug shell user */
#define AID_CACHE 2001 /* cache access */
@@ -101,6 +107,8 @@
#define AID_NET_BW_STATS 3006 /* read bandwidth statistics */
#define AID_NET_BW_ACCT 3007 /* change bandwidth statistics accounting */
#define AID_NET_BT_STACK 3008 /* bluetooth: access config files */
+#define AID_READPROC 3009 /* Allow /proc read access */
+#define AID_WAKELOCK 3010 /* Allow system wakelock read/write access */
/* The range 5000-5999 is also reserved for OEM, and must never be used here. */
#define AID_OEM_RESERVED_2_START 5000
@@ -178,6 +186,12 @@
{ "shared_relro", AID_SHARED_RELRO, },
{ "dbus", AID_DBUS, },
{ "tlsdate", AID_TLSDATE, },
+ { "mediaex", AID_MEDIA_EX, },
+ { "audioserver", AID_AUDIOSERVER, },
+ { "metrics_coll", AID_METRICS_COLL },
+ { "metricsd", AID_METRICSD },
+ { "webserv", AID_WEBSERV },
+ { "debuggerd", AID_DEBUGGERD, },
{ "shell", AID_SHELL, },
{ "cache", AID_CACHE, },
@@ -191,6 +205,8 @@
{ "net_bw_stats", AID_NET_BW_STATS, },
{ "net_bw_acct", AID_NET_BW_ACCT, },
{ "net_bt_stack", AID_NET_BT_STACK, },
+ { "readproc", AID_READPROC, },
+ { "wakelock", AID_WAKELOCK, },
{ "everybody", AID_EVERYBODY, },
{ "misc", AID_MISC, },
diff --git a/include/utils/Errors.h b/include/utils/Errors.h
index 08ddcd2..16e1fa2 100644
--- a/include/utils/Errors.h
+++ b/include/utils/Errors.h
@@ -72,6 +72,7 @@
UNKNOWN_TRANSACTION = (UNKNOWN_ERROR + 6),
#endif
FDS_NOT_ALLOWED = (UNKNOWN_ERROR + 7),
+ UNEXPECTED_NULL = (UNKNOWN_ERROR + 8),
};
// Restore define; enumeration is in "android" namespace, so the value defined
diff --git a/include/utils/LruCache.h b/include/utils/LruCache.h
index 20a379e..ed96fe4 100644
--- a/include/utils/LruCache.h
+++ b/include/utils/LruCache.h
@@ -17,10 +17,9 @@
#ifndef ANDROID_UTILS_LRU_CACHE_H
#define ANDROID_UTILS_LRU_CACHE_H
+#include <memory>
#include <unordered_set>
-#include <UniquePtr.h>
-
#include "utils/TypeHelpers.h" // hash_t
namespace android {
@@ -91,7 +90,7 @@
return result;
}
- UniquePtr<LruCacheSet> mSet;
+ std::unique_ptr<LruCacheSet> mSet;
OnEntryRemoved<TKey, TValue>* mListener;
Entry* mOldest;
Entry* mYoungest;
@@ -201,11 +200,11 @@
return false;
}
Entry* entry = *find_result;
+ mSet->erase(entry);
if (mListener) {
(*mListener)(entry->key, entry->value);
}
detachFromCache(*entry);
- mSet->erase(entry);
delete entry;
return true;
}
diff --git a/include/ziparchive/zip_writer.h b/include/ziparchive/zip_writer.h
index d996c4a..0efade8 100644
--- a/include/ziparchive/zip_writer.h
+++ b/include/ziparchive/zip_writer.h
@@ -17,7 +17,7 @@
#ifndef LIBZIPARCHIVE_ZIPWRITER_H_
#define LIBZIPARCHIVE_ZIPWRITER_H_
-#include "base/macros.h"
+#include "android-base/macros.h"
#include <utils/Compat.h>
#include <cstdio>
diff --git a/init/action.cpp b/init/action.cpp
index c6cbc2e..510ea89 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -18,8 +18,8 @@
#include <errno.h>
-#include <base/strings.h>
-#include <base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/stringprintf.h>
#include "builtins.h"
#include "error.h"
diff --git a/init/bootchart.cpp b/init/bootchart.cpp
index e1e0c48..5704d28 100644
--- a/init/bootchart.cpp
+++ b/init/bootchart.cpp
@@ -33,7 +33,7 @@
#include <string>
#include <vector>
-#include <base/file.h>
+#include <android-base/file.h>
#define LOG_ROOT "/data/bootchart"
#define LOG_STAT LOG_ROOT"/proc_stat.log"
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 3ffa2e8..10f9d81 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -39,7 +39,7 @@
#include <selinux/label.h>
#include <fs_mgr.h>
-#include <base/stringprintf.h>
+#include <android-base/stringprintf.h>
#include <cutils/partition_utils.h>
#include <cutils/android_reboot.h>
#include <logwrap/logwrap.h>
@@ -642,7 +642,7 @@
static int do_verity_load_state(const std::vector<std::string>& args) {
int mode = -1;
int rc = fs_mgr_load_verity_state(&mode);
- if (rc == 0 && mode == VERITY_MODE_LOGGING) {
+ if (rc == 0 && mode != VERITY_MODE_DEFAULT) {
ActionManager::GetInstance().QueueEventTrigger("verity-logging");
}
return rc;
@@ -788,13 +788,13 @@
return 0;
}
-int do_load_persist_props(const std::vector<std::string>& args) {
+static int do_load_persist_props(const std::vector<std::string>& args) {
load_persist_props();
return 0;
}
-static int do_load_all_props(const std::vector<std::string>& args) {
- load_all_props();
+static int do_load_system_props(const std::vector<std::string>& args) {
+ load_system_props();
return 0;
}
@@ -818,16 +818,26 @@
return 0;
}
+static bool is_file_crypto() {
+ std::string value = property_get("ro.crypto.type");
+ return value == "file";
+}
+
static int do_installkey(const std::vector<std::string>& args) {
- std::string prop_value = property_get("ro.crypto.type");
- if (prop_value != "file") {
+ if (!is_file_crypto()) {
return 0;
}
-
return e4crypt_create_device_key(args[1].c_str(),
do_installkeys_ensure_dir_exists);
}
+static int do_setusercryptopolicies(const std::vector<std::string>& args) {
+ if (!is_file_crypto()) {
+ return 0;
+ }
+ return e4crypt_set_user_crypto_policies(args[1].c_str());
+}
+
BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
static const Map builtin_functions = {
@@ -846,8 +856,8 @@
{"ifup", {1, 1, do_ifup}},
{"insmod", {1, kMax, do_insmod}},
{"installkey", {1, 1, do_installkey}},
- {"load_all_props", {0, 0, do_load_all_props}},
{"load_persist_props", {0, 0, do_load_persist_props}},
+ {"load_system_props", {0, 0, do_load_system_props}},
{"loglevel", {1, 1, do_loglevel}},
{"mkdir", {1, 4, do_mkdir}},
{"mount_all", {1, 1, do_mount_all}},
@@ -860,6 +870,7 @@
{"rmdir", {1, 1, do_rmdir}},
{"setprop", {2, 2, do_setprop}},
{"setrlimit", {3, 3, do_setrlimit}},
+ {"setusercryptopolicies", {1, 1, do_setusercryptopolicies}},
{"start", {1, 1, do_start}},
{"stop", {1, 1, do_stop}},
{"swapon_all", {1, 1, do_swapon_all}},
diff --git a/init/init.cpp b/init/init.cpp
index a898b03..4aef823 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -40,9 +40,9 @@
#include <selinux/label.h>
#include <selinux/android.h>
-#include <base/file.h>
-#include <base/stringprintf.h>
-#include <base/strings.h>
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include <cutils/android_reboot.h>
#include <cutils/fs.h>
#include <cutils/iosched_policy.h>
@@ -350,6 +350,18 @@
}
}
+static void export_oem_lock_status() {
+ if (property_get("ro.oem_unlock_supported") != "1") {
+ return;
+ }
+
+ std::string value = property_get("ro.boot.verifiedbootstate");
+
+ if (!value.empty()) {
+ property_set("ro.boot.flash.locked", value == "orange" ? "0" : "1");
+ }
+}
+
static void export_kernel_boot_props() {
struct {
const char *src_prop;
@@ -386,7 +398,7 @@
struct dirent *dp;
while ((dp = readdir(dir.get())) != NULL) {
- if (dp->d_type != DT_REG || !strcmp(dp->d_name, "compatible")) {
+ if (dp->d_type != DT_REG || !strcmp(dp->d_name, "compatible") || !strcmp(dp->d_name, "name")) {
continue;
}
@@ -546,7 +558,8 @@
mkdir("/dev/pts", 0755);
mkdir("/dev/socket", 0755);
mount("devpts", "/dev/pts", "devpts", 0, NULL);
- mount("proc", "/proc", "proc", 0, NULL);
+ #define MAKE_STR(x) __STRING(x)
+ mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC));
mount("sysfs", "/sys", "sysfs", 0, NULL);
}
@@ -601,6 +614,7 @@
restorecon("/dev");
restorecon("/dev/socket");
restorecon("/dev/__properties__");
+ restorecon("/property_contexts");
restorecon_recursive("/sys");
epoll_fd = epoll_create1(EPOLL_CLOEXEC);
@@ -612,6 +626,7 @@
signal_handler_init();
property_load_boot_defaults();
+ export_oem_lock_status();
start_property_service();
const BuiltinFunctionMap function_map;
diff --git a/init/init_parser.cpp b/init/init_parser.cpp
index 5b8e27b..b44ca59 100644
--- a/init/init_parser.cpp
+++ b/init/init_parser.cpp
@@ -25,7 +25,7 @@
#include "service.h"
#include "util.h"
-#include <base/stringprintf.h>
+#include <android-base/stringprintf.h>
Parser::Parser() {
}
diff --git a/init/keyword_map.h b/init/keyword_map.h
index dc2357b..693d82a 100644
--- a/init/keyword_map.h
+++ b/init/keyword_map.h
@@ -20,7 +20,7 @@
#include <map>
#include <string>
-#include <base/stringprintf.h>
+#include <android-base/stringprintf.h>
template <typename Function>
class KeywordMap {
diff --git a/init/log.cpp b/init/log.cpp
index eb5ec42..a72906b 100644
--- a/init/log.cpp
+++ b/init/log.cpp
@@ -22,7 +22,7 @@
#include <selinux/selinux.h>
-#include <base/stringprintf.h>
+#include <android-base/stringprintf.h>
static void init_klog_vwrite(int level, const char* fmt, va_list ap) {
static const char* tag = basename(getprogname());
diff --git a/init/property_service.cpp b/init/property_service.cpp
index ebf768e..806608e 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -47,7 +47,7 @@
#include <selinux/label.h>
#include <fs_mgr.h>
-#include <base/file.h>
+#include <android-base/file.h>
#include "bootimg.h"
#include "property_service.h"
@@ -60,33 +60,13 @@
#define RECOVERY_MOUNT_POINT "/recovery"
static int persistent_properties_loaded = 0;
-static bool property_area_initialized = false;
static int property_set_fd = -1;
-struct workspace {
- size_t size;
- int fd;
-};
-
-static workspace pa_workspace;
-
void property_init() {
- if (property_area_initialized) {
- return;
- }
-
- property_area_initialized = true;
-
if (__system_property_area_init()) {
- return;
- }
-
- pa_workspace.size = 0;
- pa_workspace.fd = open(PROP_FILENAME, O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
- if (pa_workspace.fd == -1) {
- ERROR("Failed to open %s: %s\n", PROP_FILENAME, strerror(errno));
- return;
+ ERROR("Failed to initialize property area\n");
+ exit(1);
}
}
@@ -353,12 +333,6 @@
}
}
-void get_property_workspace(int *fd, int *sz)
-{
- *fd = pa_workspace.fd;
- *sz = pa_workspace.size;
-}
-
static void load_properties_from_file(const char *, const char *);
/*
@@ -497,10 +471,6 @@
load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT, NULL);
}
-bool properties_initialized() {
- return property_area_initialized;
-}
-
static void load_override_properties() {
if (ALLOW_LOCAL_PROP_OVERRIDE) {
std::string debuggable = property_get("ro.debuggable");
@@ -559,16 +529,10 @@
close(fd);
}
-void load_all_props() {
+void load_system_props() {
load_properties_from_file(PROP_PATH_SYSTEM_BUILD, NULL);
load_properties_from_file(PROP_PATH_VENDOR_BUILD, NULL);
load_properties_from_file(PROP_PATH_FACTORY, "ro.*");
-
- load_override_properties();
-
- /* Read persistent properties after all default values have been loaded. */
- load_persistent_properties();
-
load_recovery_id_prop();
}
diff --git a/init/property_service.h b/init/property_service.h
index 1a48fb1..dbaed34 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -30,12 +30,10 @@
extern void property_init(void);
extern void property_load_boot_defaults(void);
extern void load_persist_props(void);
-extern void load_all_props(void);
+extern void load_system_props(void);
extern void start_property_service(void);
-void get_property_workspace(int *fd, int *sz);
std::string property_get(const char* name);
extern int property_set(const char *name, const char *value);
-extern bool properties_initialized();
-#endif /* _INIT_PROPERTY_H */
+#endif /* _INIT_PROPERTY_H */
diff --git a/init/readme.txt b/init/readme.txt
index bf440c2..bacd6bd 100644
--- a/init/readme.txt
+++ b/init/readme.txt
@@ -109,9 +109,16 @@
user <username>
Change to username before exec'ing this service.
Currently defaults to root. (??? probably should default to nobody)
- Currently, if your process requires linux capabilities then you cannot use
- this command. You must instead request the capabilities in-process while
- still root, and then drop to your desired uid.
+ As of Android M, processes should use this option even if they
+ require linux capabilities. Previously, to acquire linux
+ capabilities, a process would need to run as root, request the
+ capabilities, then drop to its desired uid. There is a new
+ mechanism through fs_config that allows device manufacturers to add
+ linux capabilities to specific binaries on a file system that should
+ be used instead. This mechanism is described on
+ http://source.android.com/devices/tech/config/filesystem.html. When
+ using this new mechanism, processes can use the user option to
+ select their desired uid without ever running as root.
group <groupname> [ <groupname> ]*
Change to groupname before exec'ing this service. Additional
diff --git a/init/service.cpp b/init/service.cpp
index a3c5ca4..40a4bc7 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -24,8 +24,8 @@
#include <selinux/selinux.h>
-#include <base/file.h>
-#include <base/stringprintf.h>
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
#include <cutils/android_reboot.h>
#include <cutils/sockets.h>
@@ -76,11 +76,6 @@
}
void Service::NotifyStateChange(const std::string& new_state) const {
- if (!properties_initialized()) {
- // If properties aren't available yet, we can't set them.
- return;
- }
-
if ((flags_ & SVC_EXEC) != 0) {
// 'exec' commands don't have properties tracking their state.
return;
@@ -400,14 +395,7 @@
pid_t pid = fork();
if (pid == 0) {
- int fd, sz;
-
umask(077);
- if (properties_initialized()) {
- get_property_workspace(&fd, &sz);
- std::string tmp = StringPrintf("%d,%d", dup(fd), sz);
- add_environment("ANDROID_PROPERTY_WORKSPACE", tmp.c_str());
- }
for (const auto& ei : envvars_) {
add_environment(ei.name.c_str(), ei.value.c_str());
diff --git a/init/signal_handler.cpp b/init/signal_handler.cpp
index 867abbc..e7d42cb 100644
--- a/init/signal_handler.cpp
+++ b/init/signal_handler.cpp
@@ -23,7 +23,7 @@
#include <sys/wait.h>
#include <unistd.h>
-#include <base/stringprintf.h>
+#include <android-base/stringprintf.h>
#include <cutils/android_reboot.h>
#include <cutils/list.h>
#include <cutils/sockets.h>
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index 75924cb..249739b 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -22,7 +22,7 @@
#include <stdlib.h>
#include <string.h>
-#include <base/stringprintf.h>
+#include <android-base/stringprintf.h>
#include <private/android_filesystem_config.h>
#include <selinux/selinux.h>
diff --git a/init/util.cpp b/init/util.cpp
index b316d6e..aefdf8f 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -32,12 +32,12 @@
#include <sys/socket.h>
#include <sys/un.h>
-#include <base/file.h>
-#include <base/strings.h>
+#include <android-base/file.h>
+#include <android-base/strings.h>
/* for ANDROID_SOCKET_* */
#include <cutils/sockets.h>
-#include <base/stringprintf.h>
+#include <android-base/stringprintf.h>
#include <private/android_filesystem_config.h>
@@ -105,8 +105,12 @@
int fd, ret;
char *filecon;
- if (socketcon)
- setsockcreatecon(socketcon);
+ if (socketcon) {
+ if (setsockcreatecon(socketcon) == -1) {
+ ERROR("setsockcreatecon(\"%s\") failed: %s\n", socketcon, strerror(errno));
+ return -1;
+ }
+ }
fd = socket(PF_UNIX, type, 0);
if (fd < 0) {
diff --git a/libbacktrace/Android.mk b/libbacktrace/Android.mk
index 9c6742e..5d3dd86 100644
--- a/libbacktrace/Android.mk
+++ b/libbacktrace/Android.mk
@@ -68,6 +68,7 @@
# Use static llvm libraries on host to remove dependency on 32-bit llvm shared library
# which is not included in the prebuilt.
libbacktrace_static_libraries_host := \
+ libcutils \
libLLVMObject \
libLLVMBitReader \
libLLVMMC \
diff --git a/libbacktrace/Backtrace.cpp b/libbacktrace/Backtrace.cpp
index 9ead452..3c8f879 100644
--- a/libbacktrace/Backtrace.cpp
+++ b/libbacktrace/Backtrace.cpp
@@ -22,13 +22,11 @@
#include <string>
-#include <base/stringprintf.h>
+#include <android-base/stringprintf.h>
#include <backtrace/Backtrace.h>
#include <backtrace/BacktraceMap.h>
-#include <cutils/threads.h>
-
#include "BacktraceLog.h"
#include "BacktraceOffline.h"
#include "thread_utils.h"
@@ -93,16 +91,26 @@
}
std::string Backtrace::FormatFrameData(const backtrace_frame_data_t* frame) {
- const char* map_name;
- if (BacktraceMap::IsValid(frame->map) && !frame->map.name.empty()) {
- map_name = frame->map.name.c_str();
+ uintptr_t relative_pc;
+ std::string map_name;
+ if (BacktraceMap::IsValid(frame->map)) {
+ relative_pc = BacktraceMap::GetRelativePc(frame->map, frame->pc);
+ if (!frame->map.name.empty()) {
+ map_name = frame->map.name.c_str();
+ if (map_name[0] == '[' && map_name[map_name.size() - 1] == ']') {
+ map_name.resize(map_name.size() - 1);
+ map_name += StringPrintf(":%" PRIPTR "]", frame->map.start);
+ }
+ } else {
+ map_name = StringPrintf("<anonymous:%" PRIPTR ">", frame->map.start);
+ }
} else {
map_name = "<unknown>";
+ relative_pc = frame->pc;
}
- uintptr_t relative_pc = BacktraceMap::GetRelativePc(frame->map, frame->pc);
-
- std::string line(StringPrintf("#%02zu pc %" PRIPTR " %s", frame->num, relative_pc, map_name));
+ std::string line(StringPrintf("#%02zu pc %" PRIPTR " ", frame->num, relative_pc));
+ line += map_name;
// Special handling for non-zero offset maps, we need to print that
// information.
if (frame->map.offset != 0) {
diff --git a/libbacktrace/BacktraceCurrent.cpp b/libbacktrace/BacktraceCurrent.cpp
index d339550..8e22366 100644
--- a/libbacktrace/BacktraceCurrent.cpp
+++ b/libbacktrace/BacktraceCurrent.cpp
@@ -29,8 +29,6 @@
#include <backtrace/Backtrace.h>
#include <backtrace/BacktraceMap.h>
-#include <cutils/threads.h>
-
#include "BacktraceCurrent.h"
#include "BacktraceLog.h"
#include "ThreadEntry.h"
diff --git a/libbacktrace/BacktraceOffline.cpp b/libbacktrace/BacktraceOffline.cpp
index 27dfb83..abc186b 100644
--- a/libbacktrace/BacktraceOffline.cpp
+++ b/libbacktrace/BacktraceOffline.cpp
@@ -22,7 +22,9 @@
}
#include <stdint.h>
+#include <stdio.h>
#include <string.h>
+#include <sys/stat.h>
#include <sys/types.h>
#include <ucontext.h>
#include <unistd.h>
@@ -616,7 +618,30 @@
return debug_frame;
}
+static bool IsValidElfPath(const std::string& filename) {
+ static const char elf_magic[] = {0x7f, 'E', 'L', 'F'};
+
+ struct stat st;
+ if (stat(filename.c_str(), &st) != 0 || !S_ISREG(st.st_mode)) {
+ return false;
+ }
+ FILE* fp = fopen(filename.c_str(), "reb");
+ if (fp == nullptr) {
+ return false;
+ }
+ char buf[4];
+ if (fread(buf, 4, 1, fp) != 1) {
+ fclose(fp);
+ return false;
+ }
+ fclose(fp);
+ return memcmp(buf, elf_magic, 4) == 0;
+}
+
static DebugFrameInfo* ReadDebugFrameFromFile(const std::string& filename) {
+ if (!IsValidElfPath(filename)) {
+ return nullptr;
+ }
auto owning_binary = llvm::object::createBinary(llvm::StringRef(filename));
if (owning_binary.getError()) {
return nullptr;
diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp
index 9ebd639..23636db 100644
--- a/libbacktrace/backtrace_test.cpp
+++ b/libbacktrace/backtrace_test.cpp
@@ -42,7 +42,7 @@
#include <backtrace/Backtrace.h>
#include <backtrace/BacktraceMap.h>
-#include <base/stringprintf.h>
+#include <android-base/stringprintf.h>
#include <cutils/atomic.h>
#include <cutils/threads.h>
@@ -775,16 +775,29 @@
backtrace->FormatFrameData(&frame));
// Check map name empty, but exists.
- frame.map.start = 1;
- frame.map.end = 1;
+ frame.pc = 0xb0020;
+ frame.map.start = 0xb0000;
+ frame.map.end = 0xbffff;
frame.map.load_base = 0;
#if defined(__LP64__)
- EXPECT_EQ("#01 pc 0000000000000001 <unknown>",
+ EXPECT_EQ("#01 pc 0000000000000020 <anonymous:00000000000b0000>",
#else
- EXPECT_EQ("#01 pc 00000001 <unknown>",
+ EXPECT_EQ("#01 pc 00000020 <anonymous:000b0000>",
#endif
backtrace->FormatFrameData(&frame));
+ // Check map name begins with a [.
+ frame.pc = 0xc0020;
+ frame.map.start = 0xc0000;
+ frame.map.end = 0xcffff;
+ frame.map.load_base = 0;
+ frame.map.name = "[anon:thread signal stack]";
+#if defined(__LP64__)
+ EXPECT_EQ("#01 pc 0000000000000020 [anon:thread signal stack:00000000000c0000]",
+#else
+ EXPECT_EQ("#01 pc 00000020 [anon:thread signal stack:000c0000]",
+#endif
+ backtrace->FormatFrameData(&frame));
// Check relative pc is set and map name is set.
frame.pc = 0x12345679;
@@ -1448,4 +1461,3 @@
ASSERT_EQ(waitpid(pid, nullptr, 0), pid);
}
#endif
-
diff --git a/libbacktrace/thread_utils.h b/libbacktrace/thread_utils.h
index df83581..9590657 100644
--- a/libbacktrace/thread_utils.h
+++ b/libbacktrace/thread_utils.h
@@ -19,6 +19,10 @@
#include <unistd.h>
+#if !defined(__ANDROID__)
+#include <cutils/threads.h>
+#endif
+
__BEGIN_DECLS
int tgkill(int tgid, int tid, int sig);
diff --git a/libbinderwrapper/binder_wrapper.cc b/libbinderwrapper/binder_wrapper.cc
index 0b5ff96..ca9c1df 100644
--- a/libbinderwrapper/binder_wrapper.cc
+++ b/libbinderwrapper/binder_wrapper.cc
@@ -50,4 +50,11 @@
return instance_;
}
+// static
+BinderWrapper* BinderWrapper::GetOrCreateInstance() {
+ if (!instance_)
+ instance_ = new RealBinderWrapper();
+ return instance_;
+}
+
} // namespace android
diff --git a/libcutils/Android.mk b/libcutils/Android.mk
index 3ca544f..dd08108 100644
--- a/libcutils/Android.mk
+++ b/libcutils/Android.mk
@@ -16,28 +16,27 @@
LOCAL_PATH := $(my-dir)
include $(CLEAR_VARS)
-commonSources := \
- hashmap.c \
- atomic.c.arm \
- native_handle.c \
- config_utils.c \
- load_file.c \
- strlcpy.c \
- open_memstream.c \
- strdup16to8.c \
- strdup8to16.c \
- record_stream.c \
- process_name.c \
- threads.c \
- sched_policy.c \
- iosched_policy.c \
- str_parms.c \
- fs_config.c
+libcutils_common_sources := \
+ hashmap.c \
+ atomic.c.arm \
+ native_handle.c \
+ config_utils.c \
+ load_file.c \
+ strlcpy.c \
+ open_memstream.c \
+ strdup16to8.c \
+ strdup8to16.c \
+ record_stream.c \
+ process_name.c \
+ threads.c \
+ sched_policy.c \
+ iosched_policy.c \
+ fs_config.c
# some files must not be compiled when building against Mingw
# they correspond to features not used by our host development tools
# which are also hard or even impossible to port to native Win32
-nonWindowsSources := \
+libcutils_nonwindows_sources := \
fs.c \
multiuser.c \
socket_inaddr_any_server.c \
@@ -47,8 +46,9 @@
socket_loopback_server.c \
socket_network_client.c \
sockets.c \
+ str_parms.c \
-nonWindowsHostSources := \
+libcutils_nonwindows_host_sources := \
ashmem-host.c \
trace-host.c
@@ -56,24 +56,22 @@
# Shared and static library for host
# ========================================================
LOCAL_MODULE := libcutils
-LOCAL_SRC_FILES := $(commonSources) dlmalloc_stubs.c
-LOCAL_SRC_FILES_darwin := $(nonWindowsSources) $(nonWindowsHostSources)
-LOCAL_SRC_FILES_linux := $(nonWindowsSources) $(nonWindowsHostSources)
+LOCAL_SRC_FILES := $(libcutils_common_sources) dlmalloc_stubs.c
+LOCAL_SRC_FILES_darwin := $(libcutils_nonwindows_sources) $(libcutils_nonwindows_host_sources)
+LOCAL_SRC_FILES_linux := $(libcutils_nonwindows_sources) $(libcutils_nonwindows_host_sources)
LOCAL_STATIC_LIBRARIES := liblog
-LOCAL_CFLAGS_darwin := -Werror -Wall -Wextra
-LOCAL_CFLAGS_linux := -Werror -Wall -Wextra
+LOCAL_CFLAGS := -Werror -Wall -Wextra
LOCAL_MULTILIB := both
LOCAL_MODULE_HOST_OS := darwin linux windows
include $(BUILD_HOST_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := libcutils
-LOCAL_SRC_FILES := $(commonSources) dlmalloc_stubs.c
-LOCAL_SRC_FILES_darwin := $(nonWindowsSources) $(nonWindowsHostSources)
-LOCAL_SRC_FILES_linux := $(nonWindowsSources) $(nonWindowsHostSources)
+LOCAL_SRC_FILES := $(libcutils_common_sources) dlmalloc_stubs.c
+LOCAL_SRC_FILES_darwin := $(libcutils_nonwindows_sources) $(libcutils_nonwindows_host_sources)
+LOCAL_SRC_FILES_linux := $(libcutils_nonwindows_sources) $(libcutils_nonwindows_host_sources)
LOCAL_SHARED_LIBRARIES := liblog
-LOCAL_CFLAGS_darwin := -Werror -Wall -Wextra
-LOCAL_CFLAGS_linux := -Werror -Wall -Wextra
+LOCAL_CFLAGS := -Werror -Wall -Wextra
LOCAL_MULTILIB := both
include $(BUILD_HOST_SHARED_LIBRARY)
@@ -84,8 +82,8 @@
include $(CLEAR_VARS)
LOCAL_MODULE := libcutils
-LOCAL_SRC_FILES := $(commonSources) \
- $(nonWindowsSources) \
+LOCAL_SRC_FILES := $(libcutils_common_sources) \
+ $(libcutils_nonwindows_sources) \
android_reboot.c \
ashmem-dev.c \
debugger.c \
diff --git a/libcutils/ashmem-host.c b/libcutils/ashmem-host.c
index abc4f94..15dd43e 100644
--- a/libcutils/ashmem-host.c
+++ b/libcutils/ashmem-host.c
@@ -43,11 +43,16 @@
char template[PATH_MAX];
snprintf(template, sizeof(template), "/tmp/android-ashmem-%d-XXXXXXXXX", getpid());
int fd = mkstemp(template);
- if (fd != -1 && TEMP_FAILURE_RETRY(ftruncate(fd, size)) != -1 && unlink(template) != -1) {
- return fd;
+ if (fd == -1) return -1;
+
+ unlink(template);
+
+ if (TEMP_FAILURE_RETRY(ftruncate(fd, size)) == -1) {
+ close(fd);
+ return -1;
}
- close(fd);
- return -1;
+
+ return fd;
}
int ashmem_set_prot_region(int fd __unused, int prot __unused)
diff --git a/libcutils/fs_config.c b/libcutils/fs_config.c
index 2a178aa..6d50adc 100644
--- a/libcutils/fs_config.c
+++ b/libcutils/fs_config.c
@@ -88,6 +88,8 @@
{ 00771, AID_SHARED_RELRO, AID_SHARED_RELRO, 0, "data/misc/shared_relro" },
{ 00775, AID_MEDIA_RW, AID_MEDIA_RW, 0, "data/media" },
{ 00775, AID_MEDIA_RW, AID_MEDIA_RW, 0, "data/media/Music" },
+ { 00750, AID_ROOT, AID_SHELL, 0, "data/nativetest" },
+ { 00750, AID_ROOT, AID_SHELL, 0, "data/nativetest64" },
{ 00771, AID_SYSTEM, AID_SYSTEM, 0, "data" },
{ 00755, AID_ROOT, AID_SYSTEM, 0, "mnt" },
{ 00755, AID_ROOT, AID_ROOT, 0, "root" },
@@ -125,15 +127,15 @@
{ 00644, AID_MEDIA_RW, AID_MEDIA_RW, 0, "data/media/*" },
{ 00644, AID_SYSTEM, AID_SYSTEM, 0, "data/app-private/*" },
{ 00644, AID_APP, AID_APP, 0, "data/data/*" },
+ { 00640, AID_ROOT, AID_SHELL, 0, "data/nativetest/tests.txt" },
+ { 00640, AID_ROOT, AID_SHELL, 0, "data/nativetest64/tests.txt" },
{ 00750, AID_ROOT, AID_SHELL, 0, "data/nativetest/*" },
{ 00750, AID_ROOT, AID_SHELL, 0, "data/nativetest64/*" },
- /* the following four files are INTENTIONALLY set-uid, but they
+ /* the following two files are INTENTIONALLY set-uid, but they
* are NOT included on user builds. */
{ 04750, AID_ROOT, AID_SHELL, 0, "system/xbin/su" },
- { 06755, AID_ROOT, AID_ROOT, 0, "system/xbin/librank" },
{ 06755, AID_ROOT, AID_ROOT, 0, "system/xbin/procmem" },
- { 04770, AID_ROOT, AID_RADIO, 0, "system/bin/pppd-ril" },
/* the following files have enhanced capabilities and ARE included in user builds. */
{ 00750, AID_ROOT, AID_SHELL, CAP_MASK_LONG(CAP_SETUID) | CAP_MASK_LONG(CAP_SETGID), "system/bin/run-as" },
diff --git a/libcutils/socket_inaddr_any_server.c b/libcutils/socket_inaddr_any_server.c
index 7f0ccb8..e1b7d84 100644
--- a/libcutils/socket_inaddr_any_server.c
+++ b/libcutils/socket_inaddr_any_server.c
@@ -34,21 +34,21 @@
/* open listen() port on any interface */
int socket_inaddr_any_server(int port, int type)
{
- struct sockaddr_in addr;
+ struct sockaddr_in6 addr;
int s, n;
memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_port = htons(port);
- addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ addr.sin6_family = AF_INET6;
+ addr.sin6_port = htons(port);
+ addr.sin6_addr = in6addr_any;
- s = socket(AF_INET, type, 0);
- if(s < 0) return -1;
+ s = socket(AF_INET6, type, 0);
+ if (s < 0) return -1;
n = 1;
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *) &n, sizeof(n));
- if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
close(s);
return -1;
}
diff --git a/libcutils/trace-dev.c b/libcutils/trace-dev.c
index a06987e..f025256 100644
--- a/libcutils/trace-dev.c
+++ b/libcutils/trace-dev.c
@@ -104,7 +104,7 @@
if (sys_debuggable || atrace_is_debuggable) {
// Check whether tracing is enabled for this process.
- FILE * file = fopen("/proc/self/cmdline", "r");
+ FILE * file = fopen("/proc/self/cmdline", "re");
if (file) {
char cmdline[4096];
if (fgets(cmdline, sizeof(cmdline), file)) {
@@ -173,7 +173,7 @@
static void atrace_init_once()
{
- atrace_marker_fd = open("/sys/kernel/debug/tracing/trace_marker", O_WRONLY);
+ atrace_marker_fd = open("/sys/kernel/debug/tracing/trace_marker", O_WRONLY | O_CLOEXEC);
if (atrace_marker_fd == -1) {
ALOGE("Error opening trace file: %s (%d)", strerror(errno), errno);
atrace_enabled_tags = 0;
diff --git a/liblog/Android.bp b/liblog/Android.bp
index 34e7f92..ee883f0 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -52,6 +52,7 @@
},
windows: {
srcs: ["uio.c"],
+ enabled: true,
},
not_windows: {
srcs: ["event_tag_map.c"],
@@ -70,6 +71,7 @@
// $(LOCAL_PATH)/event.logtags)
// so make sure we do not regret hard-coding it as follows:
"-DLIBLOG_LOG_TAG=1005",
+ "-DSNET_EVENT_LOG_TAG=1397638484",
],
compile_multilib: "both",
stl: "none",
diff --git a/liblog/Android.mk b/liblog/Android.mk
index a183db8..4ab5006 100644
--- a/liblog/Android.mk
+++ b/liblog/Android.mk
@@ -22,7 +22,7 @@
# 's/^\([0-9]*\)[ \t]*liblog[ \t].*/-DLIBLOG_LOG_TAG=\1/p' \
# $(LOCAL_PATH)/event.logtags)
# so make sure we do not regret hard-coding it as follows:
-liblog_cflags := -DLIBLOG_LOG_TAG=1005
+liblog_cflags := -DLIBLOG_LOG_TAG=1005 -DSNET_EVENT_LOG_TAG=1397638484
liblog_host_sources := logd_write.c log_event_write.c fake_log_device.c event.logtags
liblog_target_sources := logd_write.c log_event_write.c event_tag_map.c log_time.cpp log_is_loggable.c
diff --git a/liblog/README b/liblog/README
index f29ac04..df1e68c 100644
--- a/liblog/README
+++ b/liblog/README
@@ -116,6 +116,10 @@
code, otherwise the android_logger_list_read call will block for new
entries.
+ The ANDROID_LOG_WRAP mode flag to the android_logger_list_alloc_time
+ signals logd to quiesce the reader until the buffer is about to prune
+ at the start time then proceed to dumping content.
+
The ANDROID_LOG_PSTORE mode flag to the android_logger_open is used to
switch from the active logs to the persistent logs from before the last
reboot.
diff --git a/liblog/fake_log_device.c b/liblog/fake_log_device.c
index 8a8ece2..cb80ee6 100644
--- a/liblog/fake_log_device.c
+++ b/liblog/fake_log_device.c
@@ -99,6 +99,10 @@
static void 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(&fakeLogDeviceLock);
}
@@ -106,9 +110,12 @@
{
pthread_mutex_unlock(&fakeLogDeviceLock);
}
+
#else // !defined(_WIN32)
+
#define lock() ((void)0)
#define unlock() ((void)0)
+
#endif // !defined(_WIN32)
diff --git a/liblog/log_is_loggable.c b/liblog/log_is_loggable.c
index 814d96d..0f81efc 100644
--- a/liblog/log_is_loggable.c
+++ b/liblog/log_is_loggable.c
@@ -23,12 +23,42 @@
#include <android/log.h>
+static pthread_mutex_t lock_loggable = PTHREAD_MUTEX_INITIALIZER;
+
+static int 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.
+ */
+ /*
+ * Any contention, and we can turn around and use the non-cached method
+ * in less time than the system call associated with a mutex to deal with
+ * the contention.
+ */
+ return pthread_mutex_trylock(&lock_loggable);
+}
+
+static void unlock()
+{
+ pthread_mutex_unlock(&lock_loggable);
+}
+
struct cache {
const prop_info *pinfo;
uint32_t serial;
- char c;
+ unsigned char c;
};
+static int check_cache(struct cache *cache)
+{
+ return cache->pinfo
+ && __system_property_serial(cache->pinfo) != cache->serial;
+}
+
+#define BOOLEAN_TRUE 0xFF
+#define BOOLEAN_FALSE 0xFE
+
static void refresh_cache(struct cache *cache, const char *key)
{
uint32_t serial;
@@ -39,6 +69,7 @@
if (!cache->pinfo) {
return;
}
+ cache->serial = -1;
}
serial = __system_property_serial(cache->pinfo);
if (serial == cache->serial) {
@@ -46,12 +77,19 @@
}
cache->serial = serial;
__system_property_read(cache->pinfo, 0, buf);
- cache->c = buf[0];
+ switch(buf[0]) {
+ case 't': case 'T':
+ cache->c = strcasecmp(buf + 1, "rue") ? buf[0] : BOOLEAN_TRUE;
+ break;
+ case 'f': case 'F':
+ cache->c = strcasecmp(buf + 1, "alse") ? buf[0] : BOOLEAN_FALSE;
+ break;
+ default:
+ cache->c = buf[0];
+ }
}
-static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
-
-static int __android_log_level(const char *tag, int def)
+static int __android_log_level(const char *tag, int default_prio)
{
/* sizeof() is used on this array below */
static const char log_namespace[] = "persist.log.tag.";
@@ -59,7 +97,7 @@
/* calculate the size of our key temporary buffer */
const size_t taglen = (tag && *tag) ? strlen(tag) : 0;
/* sizeof(log_namespace) = strlen(log_namespace) + 1 */
- char key[sizeof(log_namespace) + taglen];
+ char key[sizeof(log_namespace) + taglen]; /* may be > PROPERTY_KEY_MAX */
char *kp;
size_t i;
char c = 0;
@@ -74,49 +112,77 @@
*/
static char *last_tag;
static uint32_t global_serial;
- uint32_t current_global_serial;
- static struct cache tag_cache[2] = {
- { NULL, -1, 0 },
- { NULL, -1, 0 }
- };
- static struct cache global_cache[2] = {
- { NULL, -1, 0 },
- { NULL, -1, 0 }
- };
+ /* some compilers erroneously see uninitialized use. !not_locked */
+ uint32_t current_global_serial = 0;
+ static struct cache tag_cache[2];
+ static struct cache global_cache[2];
+ int change_detected;
+ int global_change_detected;
+ int not_locked;
strcpy(key, log_namespace);
- pthread_mutex_lock(&lock);
+ global_change_detected = change_detected = not_locked = lock();
- current_global_serial = __system_property_area_serial();
+ if (!not_locked) {
+ /*
+ * check all known serial numbers to changes.
+ */
+ for (i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) {
+ if (check_cache(&tag_cache[i])) {
+ change_detected = 1;
+ }
+ }
+ for (i = 0; i < (sizeof(global_cache) / sizeof(global_cache[0])); ++i) {
+ if (check_cache(&global_cache[i])) {
+ global_change_detected = 1;
+ }
+ }
+
+ current_global_serial = __system_property_area_serial();
+ if (current_global_serial != global_serial) {
+ change_detected = 1;
+ global_change_detected = 1;
+ }
+ }
if (taglen) {
- uint32_t current_local_serial = current_global_serial;
-
- if (!last_tag || (last_tag[0] != tag[0]) || strcmp(last_tag + 1, tag + 1)) {
- /* invalidate log.tag.<tag> cache */
- for(i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) {
- tag_cache[i].pinfo = NULL;
- tag_cache[i].serial = -1;
- tag_cache[i].c = '\0';
+ int local_change_detected = change_detected;
+ if (!not_locked) {
+ if (!last_tag
+ || (last_tag[0] != tag[0])
+ || strcmp(last_tag + 1, tag + 1)) {
+ /* invalidate log.tag.<tag> cache */
+ for (i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) {
+ tag_cache[i].pinfo = NULL;
+ tag_cache[i].c = '\0';
+ }
+ free(last_tag);
+ last_tag = NULL;
+ local_change_detected = 1;
}
- free(last_tag);
- last_tag = NULL;
- current_global_serial = -1;
- }
- if (!last_tag) {
- last_tag = strdup(tag);
+ if (!last_tag) {
+ last_tag = strdup(tag);
+ }
}
strcpy(key + sizeof(log_namespace) - 1, tag);
kp = key;
- for(i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) {
- if (current_local_serial != global_serial) {
- refresh_cache(&tag_cache[i], kp);
+ for (i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) {
+ struct cache *cache = &tag_cache[i];
+ struct cache temp_cache;
+
+ if (not_locked) {
+ temp_cache.pinfo = NULL;
+ temp_cache.c = '\0';
+ cache = &temp_cache;
+ }
+ if (local_change_detected) {
+ refresh_cache(cache, kp);
}
- if (tag_cache[i].c) {
- c = tag_cache[i].c;
+ if (cache->c) {
+ c = cache->c;
break;
}
@@ -133,19 +199,31 @@
case 'F': /* Not officially supported */
case 'A':
case 'S':
+ case BOOLEAN_FALSE: /* Not officially supported */
break;
default:
/* clear '.' after log.tag */
key[sizeof(log_namespace) - 2] = '\0';
kp = key;
- for(i = 0; i < (sizeof(global_cache) / sizeof(global_cache[0])); ++i) {
- if (current_global_serial != global_serial) {
- refresh_cache(&global_cache[i], kp);
+ for (i = 0; i < (sizeof(global_cache) / sizeof(global_cache[0])); ++i) {
+ struct cache *cache = &global_cache[i];
+ struct cache temp_cache;
+
+ if (not_locked) {
+ temp_cache = *cache;
+ if (temp_cache.pinfo != cache->pinfo) { /* check atomic */
+ temp_cache.pinfo = NULL;
+ temp_cache.c = '\0';
+ }
+ cache = &temp_cache;
+ }
+ if (global_change_detected) {
+ refresh_cache(cache, kp);
}
- if (global_cache[i].c) {
- c = global_cache[i].c;
+ if (cache->c) {
+ c = cache->c;
break;
}
@@ -154,9 +232,10 @@
break;
}
- global_serial = current_global_serial;
-
- pthread_mutex_unlock(&lock);
+ if (!not_locked) {
+ global_serial = current_global_serial;
+ unlock();
+ }
switch (toupper(c)) {
case 'V': return ANDROID_LOG_VERBOSE;
@@ -166,38 +245,116 @@
case 'E': return ANDROID_LOG_ERROR;
case 'F': /* FALLTHRU */ /* Not officially supported */
case 'A': return ANDROID_LOG_FATAL;
+ case BOOLEAN_FALSE: /* FALLTHRU */ /* Not Officially supported */
case 'S': return -1; /* ANDROID_LOG_SUPPRESS */
}
- return def;
+ return default_prio;
}
-int __android_log_is_loggable(int prio, const char *tag, int def)
+int __android_log_is_loggable(int prio, const char *tag, int default_prio)
{
- int logLevel = __android_log_level(tag, def);
+ int logLevel = __android_log_level(tag, default_prio);
return logLevel >= 0 && prio >= logLevel;
}
-char android_log_timestamp()
+/*
+ * For properties that are read often, but generally remain constant.
+ * Since a change is rare, we will accept a trylock failure gracefully.
+ * Use a separate lock from is_loggable to keep contention down b/25563384.
+ */
+struct cache2 {
+ pthread_mutex_t lock;
+ uint32_t serial;
+ const char *key_persist;
+ struct cache cache_persist;
+ const char *key_ro;
+ struct cache cache_ro;
+ unsigned char (*const evaluate)(const struct cache2 *self);
+};
+
+static inline unsigned char do_cache2(struct cache2 *self)
{
- static struct cache r_time_cache = { NULL, -1, 0 };
- static struct cache p_time_cache = { NULL, -1, 0 };
- static uint32_t serial;
uint32_t current_serial;
- char retval;
+ int change_detected;
+ unsigned char c;
- pthread_mutex_lock(&lock);
+ if (pthread_mutex_trylock(&self->lock)) {
+ /* We are willing to accept some race in this context */
+ return self->evaluate(self);
+ }
+ change_detected = check_cache(&self->cache_persist)
+ || check_cache(&self->cache_ro);
current_serial = __system_property_area_serial();
- if (current_serial != serial) {
- refresh_cache(&r_time_cache, "ro.logd.timestamp");
- refresh_cache(&p_time_cache, "persist.logd.timestamp");
- serial = current_serial;
+ if (current_serial != self->serial) {
+ change_detected = 1;
}
- if (!(retval = p_time_cache.c)) {
- retval = r_time_cache.c;
+ if (change_detected) {
+ refresh_cache(&self->cache_persist, self->key_persist);
+ refresh_cache(&self->cache_ro, self->key_ro);
+ self->serial = current_serial;
+ }
+ c = self->evaluate(self);
+
+ pthread_mutex_unlock(&self->lock);
+
+ return c;
+}
+
+static unsigned char evaluate_persist_ro(const struct cache2 *self)
+{
+ unsigned char c = self->cache_persist.c;
+
+ if (c) {
+ return c;
}
- pthread_mutex_unlock(&lock);
+ return self->cache_ro.c;
+}
- return tolower(retval ?: 'r');
+/*
+ * Timestamp state generally remains constant, but can change at any time
+ * to handle developer requirements.
+ */
+clockid_t android_log_clockid()
+{
+ static struct cache2 clockid = {
+ PTHREAD_MUTEX_INITIALIZER,
+ 0,
+ "persist.logd.timestamp",
+ { NULL, -1, '\0' },
+ "ro.logd.timestamp",
+ { NULL, -1, '\0' },
+ evaluate_persist_ro
+ };
+
+ return (tolower(do_cache2(&clockid)) == 'm')
+ ? CLOCK_MONOTONIC
+ : CLOCK_REALTIME;
+}
+
+/*
+ * Security state generally remains constant, but the DO must be able
+ * to turn off logging should it become spammy after an attack is detected.
+ */
+static unsigned char evaluate_security(const struct cache2 *self)
+{
+ unsigned char c = self->cache_ro.c;
+
+ return (c != BOOLEAN_FALSE) && c && (self->cache_persist.c == BOOLEAN_TRUE);
+}
+
+int __android_log_security()
+{
+ static struct cache2 security = {
+ PTHREAD_MUTEX_INITIALIZER,
+ 0,
+ "persist.logd.security",
+ { NULL, -1, BOOLEAN_FALSE },
+ "ro.device_owner",
+ { NULL, -1, BOOLEAN_FALSE },
+ evaluate_security
+ };
+
+ return do_cache2(&security);
}
diff --git a/liblog/log_read.c b/liblog/log_read.c
index cfc8a7a..1aff272 100644
--- a/liblog/log_read.c
+++ b/liblog/log_read.c
@@ -208,6 +208,7 @@
[LOG_ID_EVENTS] = "events",
[LOG_ID_SYSTEM] = "system",
[LOG_ID_CRASH] = "crash",
+ [LOG_ID_SECURITY] = "security",
[LOG_ID_KERNEL] = "kernel",
};
@@ -500,6 +501,14 @@
remaining -= n;
cp += n;
}
+
+ if (logger_list->pid) {
+ n = snprintf(cp, remaining, " pid=%u", logger_list->pid);
+ n = min(n, remaining);
+ remaining -= n;
+ cp += n;
+ }
+
return send_log_msg(NULL, NULL, buf, len);
}
@@ -634,6 +643,7 @@
android_log_header_t l;
} buf;
static uint8_t preread_count;
+ bool is_system;
memset(log_msg, 0, sizeof(*log_msg));
@@ -690,12 +700,15 @@
}
uid = get_best_effective_uid();
- if (!uid_has_log_permission(uid) && (uid != buf.p.uid)) {
+ is_system = uid_has_log_permission(uid);
+ if (!is_system && (uid != buf.p.uid)) {
break;
}
ret = TEMP_FAILURE_RETRY(read(logger_list->sock,
- log_msg->entry_v3.msg,
+ is_system ?
+ log_msg->entry_v4.msg :
+ log_msg->entry_v3.msg,
buf.p.len - sizeof(buf)));
if (ret < 0) {
return -errno;
@@ -704,13 +717,18 @@
return -EIO;
}
- log_msg->entry_v3.len = buf.p.len - sizeof(buf);
- log_msg->entry_v3.hdr_size = sizeof(log_msg->entry_v3);
- log_msg->entry_v3.pid = buf.p.pid;
- log_msg->entry_v3.tid = buf.l.tid;
- log_msg->entry_v3.sec = buf.l.realtime.tv_sec;
- log_msg->entry_v3.nsec = buf.l.realtime.tv_nsec;
- log_msg->entry_v3.lid = buf.l.id;
+ log_msg->entry_v4.len = buf.p.len - sizeof(buf);
+ log_msg->entry_v4.hdr_size = is_system ?
+ sizeof(log_msg->entry_v4) :
+ sizeof(log_msg->entry_v3);
+ log_msg->entry_v4.pid = buf.p.pid;
+ log_msg->entry_v4.tid = buf.l.tid;
+ log_msg->entry_v4.sec = buf.l.realtime.tv_sec;
+ log_msg->entry_v4.nsec = buf.l.realtime.tv_nsec;
+ log_msg->entry_v4.lid = buf.l.id;
+ if (is_system) {
+ log_msg->entry_v4.uid = buf.p.uid;
+ }
return ret;
}
@@ -797,6 +815,14 @@
}
if (logger_list->start.tv_sec || logger_list->start.tv_nsec) {
+ if (logger_list->mode & ANDROID_LOG_WRAP) {
+ // ToDo: alternate API to allow timeout to be adjusted.
+ ret = snprintf(cp, remaining, " timeout=%u",
+ ANDROID_LOG_WRAP_DEFAULT_TIMEOUT);
+ ret = min(ret, remaining);
+ remaining -= ret;
+ cp += ret;
+ }
ret = snprintf(cp, remaining, " start=%" PRIu32 ".%09" PRIu32,
logger_list->start.tv_sec,
logger_list->start.tv_nsec);
@@ -862,18 +888,10 @@
sigaction(SIGALRM, &old_sigaction, NULL);
}
- if (ret <= 0) {
- if ((ret == -1) && e) {
- return -e;
- }
- return ret;
+ if ((ret == -1) && e) {
+ return -e;
}
-
- logger_for_each(logger, logger_list) {
- if (log_msg->entry.lid == logger->id) {
- return ret;
- }
- }
+ return ret;
}
/* NOTREACH */
return ret;
diff --git a/liblog/logd_write.c b/liblog/logd_write.c
index bdee28f..ec86e6b 100644
--- a/liblog/logd_write.c
+++ b/liblog/logd_write.c
@@ -39,6 +39,7 @@
#include <android/set_abort_message.h>
#endif
+#include <log/event_tag_map.h>
#include <log/logd.h>
#include <log/logger.h>
#include <log/log_read.h>
@@ -54,16 +55,43 @@
static int __write_to_log_init(log_id_t, struct iovec *vec, size_t nr);
static int (*write_to_log)(log_id_t, struct iovec *vec, size_t nr) = __write_to_log_init;
-#if !defined(_WIN32)
-static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER;
-#endif
#ifndef __unused
#define __unused __attribute__((__unused__))
#endif
+#if !defined(_WIN32)
+static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER;
+
+static void 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);
+}
+
+static int trylock()
+{
+ return pthread_mutex_trylock(&log_init_lock);
+}
+
+static void unlock()
+{
+ pthread_mutex_unlock(&log_init_lock);
+}
+
+#else /* !defined(_WIN32) */
+
+#define lock() ((void)0)
+#define trylock() (0) /* success */
+#define unlock() ((void)0)
+
+#endif /* !defined(_WIN32) */
+
#if FAKE_LOG_DEVICE
-static int log_fds[(int)LOG_ID_MAX] = { -1, -1, -1, -1, -1 };
+static int log_fds[(int)LOG_ID_MAX] = { -1, -1, -1, -1, -1, -1 };
#else
static int logd_fd = -1;
static int pstore_fd = -1;
@@ -160,6 +188,7 @@
static uid_t last_uid = AID_ROOT; /* logd *always* starts up as AID_ROOT */
static pid_t last_pid = (pid_t) -1;
static atomic_int_fast32_t dropped;
+ static atomic_int_fast32_t dropped_security;
if (!nr) {
return -EINVAL;
@@ -171,6 +200,101 @@
if (last_pid == (pid_t) -1) {
last_pid = getpid();
}
+ if (log_id == LOG_ID_SECURITY) {
+ if (vec[0].iov_len < 4) {
+ return -EINVAL;
+ }
+ if ((last_uid != AID_SYSTEM) && (last_uid != AID_ROOT)) {
+ uid_t uid = geteuid();
+ if ((uid != AID_SYSTEM) && (uid != AID_ROOT)) {
+ gid_t gid = getgid();
+ if ((gid != AID_SYSTEM) && (gid != AID_ROOT)) {
+ gid = getegid();
+ if ((gid != AID_SYSTEM) && (gid != AID_ROOT)) {
+ return -EPERM;
+ }
+ }
+ }
+ }
+ if (!__android_log_security()) {
+ atomic_store(&dropped_security, 0);
+ return -EPERM;
+ }
+ } else if (log_id == LOG_ID_EVENTS) {
+ if (vec[0].iov_len < 4) {
+ return -EINVAL;
+ }
+ if (((uint32_t *)vec[0].iov_base)[0] != htole32(SNET_EVENT_LOG_TAG)) {
+ static atomic_uintptr_t map;
+ int ret;
+ const char *tag = NULL;
+ EventTagMap *m, *f = NULL;
+
+ m = (EventTagMap *)atomic_load(&map);
+ if (!m) {
+ ret = trylock();
+ m = (EventTagMap *)atomic_load(&map); /* trylock flush cache */
+ if (!m) {
+ m = android_openEventTagMap(EVENT_TAG_MAP_FILE);
+ if (ret) { /* trylock failed, local copy, mark for close */
+ f = m;
+ } else {
+ if (!m) { /* One chance to open map file */
+ m = (EventTagMap *)(uintptr_t)-1LL;
+ }
+ atomic_store(&map, (uintptr_t)m);
+ }
+ }
+ if (!ret) { /* trylock succeeded, unlock */
+ unlock();
+ }
+ }
+ if (m && (m != (EventTagMap *)(uintptr_t)-1LL)) {
+ tag = android_lookupEventTag(
+ m,
+ htole32(((uint32_t *)vec[0].iov_base)[0]));
+ }
+ ret = __android_log_is_loggable(ANDROID_LOG_INFO,
+ tag,
+ ANDROID_LOG_VERBOSE);
+ if (f) { /* local copy marked for close */
+ android_closeEventTagMap(f);
+ }
+ if (!ret) {
+ return -EPERM;
+ }
+ }
+ } else {
+ /* Validate the incoming tag, tag content can not split across iovec */
+ char prio = ANDROID_LOG_VERBOSE;
+ const char *tag = vec[0].iov_base;
+ size_t len = vec[0].iov_len;
+ if (!tag) {
+ len = 0;
+ }
+ if (len > 0) {
+ prio = *tag;
+ if (len > 1) {
+ --len;
+ ++tag;
+ } else {
+ len = vec[1].iov_len;
+ tag = ((const char *)vec[1].iov_base);
+ if (!tag) {
+ len = 0;
+ }
+ }
+ }
+ /* tag must be nul terminated */
+ if (strnlen(tag, len) >= len) {
+ tag = NULL;
+ }
+
+ if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
+ return -EPERM;
+ }
+ }
+
/*
* struct {
* // what we provide to pstore
@@ -191,7 +315,7 @@
* };
*/
- clock_gettime(CLOCK_REALTIME, &ts);
+ clock_gettime(android_log_clockid(), &ts);
pmsg_header.magic = LOGGER_MAGIC;
pmsg_header.len = sizeof(pmsg_header) + sizeof(header);
@@ -208,10 +332,31 @@
newVec[1].iov_len = sizeof(header);
if (logd_fd > 0) {
- int32_t snapshot = atomic_exchange_explicit(&dropped, 0, memory_order_relaxed);
+ int32_t snapshot = atomic_exchange_explicit(&dropped_security, 0,
+ memory_order_relaxed);
if (snapshot) {
android_log_event_int_t buffer;
+ header.id = LOG_ID_SECURITY;
+ buffer.header.tag = htole32(LIBLOG_LOG_TAG);
+ buffer.payload.type = EVENT_TYPE_INT;
+ buffer.payload.data = htole32(snapshot);
+
+ newVec[2].iov_base = &buffer;
+ newVec[2].iov_len = sizeof(buffer);
+
+ ret = TEMP_FAILURE_RETRY(writev(logd_fd, newVec + 1, 2));
+ if (ret != (ssize_t)(sizeof(header) + sizeof(buffer))) {
+ atomic_fetch_add_explicit(&dropped_security, snapshot,
+ memory_order_relaxed);
+ }
+ }
+ snapshot = atomic_exchange_explicit(&dropped, 0, memory_order_relaxed);
+ if (snapshot && __android_log_is_loggable(ANDROID_LOG_INFO,
+ "liblog",
+ ANDROID_LOG_VERBOSE)) {
+ android_log_event_int_t buffer;
+
header.id = LOG_ID_EVENTS;
buffer.header.tag = htole32(LIBLOG_LOG_TAG);
buffer.payload.type = EVENT_TYPE_INT;
@@ -222,7 +367,8 @@
ret = TEMP_FAILURE_RETRY(writev(logd_fd, newVec + 1, 2));
if (ret != (ssize_t)(sizeof(header) + sizeof(buffer))) {
- atomic_fetch_add_explicit(&dropped, snapshot, memory_order_relaxed);
+ atomic_fetch_add_explicit(&dropped, snapshot,
+ memory_order_relaxed);
}
}
}
@@ -273,15 +419,11 @@
if (ret < 0) {
ret = -errno;
if (ret == -ENOTCONN) {
-#if !defined(_WIN32)
- pthread_mutex_lock(&log_init_lock);
-#endif
+ lock();
close(logd_fd);
logd_fd = -1;
ret = __write_to_log_initialize();
-#if !defined(_WIN32)
- pthread_mutex_unlock(&log_init_lock);
-#endif
+ unlock();
if (ret < 0) {
return ret;
@@ -298,6 +440,10 @@
ret -= sizeof(header);
} else if (ret == -EAGAIN) {
atomic_fetch_add_explicit(&dropped, 1, memory_order_relaxed);
+ if (log_id == LOG_ID_SECURITY) {
+ atomic_fetch_add_explicit(&dropped_security, 1,
+ memory_order_relaxed);
+ }
}
#endif
@@ -311,6 +457,7 @@
[LOG_ID_EVENTS] = "events",
[LOG_ID_SYSTEM] = "system",
[LOG_ID_CRASH] = "crash",
+ [LOG_ID_SECURITY] = "security",
[LOG_ID_KERNEL] = "kernel",
};
@@ -325,18 +472,14 @@
static int __write_to_log_init(log_id_t log_id, struct iovec *vec, size_t nr)
{
-#if !defined(_WIN32)
- pthread_mutex_lock(&log_init_lock);
-#endif
+ lock();
if (write_to_log == __write_to_log_init) {
int ret;
ret = __write_to_log_initialize();
if (ret < 0) {
-#if !defined(_WIN32)
- pthread_mutex_unlock(&log_init_lock);
-#endif
+ unlock();
#if (FAKE_LOG_DEVICE == 0)
if (pstore_fd >= 0) {
__write_to_log_daemon(log_id, vec, nr);
@@ -348,9 +491,7 @@
write_to_log = __write_to_log_daemon;
}
-#if !defined(_WIN32)
- pthread_mutex_unlock(&log_init_lock);
-#endif
+ unlock();
return write_to_log(log_id, vec, nr);
}
@@ -472,6 +613,18 @@
return write_to_log(LOG_ID_EVENTS, vec, 2);
}
+int __android_log_security_bwrite(int32_t tag, const void *payload, size_t len)
+{
+ struct iovec vec[2];
+
+ vec[0].iov_base = &tag;
+ vec[0].iov_len = sizeof(tag);
+ vec[1].iov_base = (void*)payload;
+ vec[1].iov_len = len;
+
+ return write_to_log(LOG_ID_SECURITY, vec, 2);
+}
+
/*
* Like __android_log_bwrite, but takes the type as well. Doesn't work
* for the general case where we're generating lists of stuff, but very
diff --git a/liblog/logprint.c b/liblog/logprint.c
index 0ea2269..bd36cdd 100644
--- a/liblog/logprint.c
+++ b/liblog/logprint.c
@@ -32,6 +32,7 @@
#include <cutils/list.h>
#include <log/logd.h>
#include <log/logprint.h>
+#include <private/android_filesystem_config.h>
#define MS_PER_NSEC 1000000
#define US_PER_NSEC 1000
@@ -56,6 +57,7 @@
bool zone_output;
bool epoch_output;
bool monotonic_output;
+ bool uid_output;
};
/*
@@ -203,7 +205,8 @@
p_ret->year_output = false;
p_ret->zone_output = false;
p_ret->epoch_output = false;
- p_ret->monotonic_output = false;
+ p_ret->monotonic_output = android_log_clockid() == CLOCK_MONOTONIC;
+ p_ret->uid_output = false;
return p_ret;
}
@@ -258,6 +261,9 @@
case FORMAT_MODIFIER_MONOTONIC:
p_format->monotonic_output = true;
return 0;
+ case FORMAT_MODIFIER_UID:
+ p_format->uid_output = true;
+ return 0;
default:
break;
}
@@ -290,6 +296,7 @@
else if (strcmp(formatString, "zone") == 0) format = FORMAT_MODIFIER_ZONE;
else if (strcmp(formatString, "epoch") == 0) format = FORMAT_MODIFIER_EPOCH;
else if (strcmp(formatString, "monotonic") == 0) format = FORMAT_MODIFIER_MONOTONIC;
+ else if (strcmp(formatString, "uid") == 0) format = FORMAT_MODIFIER_UID;
else {
extern char *tzname[2];
static const char gmt[] = "GMT";
@@ -451,6 +458,7 @@
{
entry->tv_sec = buf->sec;
entry->tv_nsec = buf->nsec;
+ entry->uid = -1;
entry->pid = buf->pid;
entry->tid = buf->tid;
@@ -482,6 +490,9 @@
struct logger_entry_v2 *buf2 = (struct logger_entry_v2 *)buf;
if (buf2->hdr_size) {
msg = ((char *)buf2) + buf2->hdr_size;
+ if (buf2->hdr_size >= sizeof(struct logger_entry_v4)) {
+ entry->uid = ((struct logger_entry_v4 *)buf)->uid;
+ }
}
for (i = 1; i < buf->len; i++) {
if (msg[i] == '\0') {
@@ -500,14 +511,14 @@
}
if (msgEnd == -1) {
/* incoming message not null-terminated; force it */
- msgEnd = buf->len - 1;
+ msgEnd = buf->len - 1; /* may result in msgEnd < msgStart */
msg[msgEnd] = '\0';
}
entry->priority = msg[0];
entry->tag = msg + 1;
entry->message = msg + msgStart;
- entry->messageLen = msgEnd - msgStart;
+ entry->messageLen = (msgEnd < msgStart) ? 0 : (msgEnd - msgStart);
return 0;
}
@@ -734,16 +745,25 @@
entry->tv_sec = buf->sec;
entry->tv_nsec = buf->nsec;
entry->priority = ANDROID_LOG_INFO;
+ entry->uid = -1;
entry->pid = buf->pid;
entry->tid = buf->tid;
/*
- * Pull the tag out.
+ * Pull the tag out, fill in some additional details based on incoming
+ * buffer version (v3 adds lid, v4 adds uid).
*/
eventData = (const unsigned char*) buf->msg;
struct logger_entry_v2 *buf2 = (struct logger_entry_v2 *)buf;
if (buf2->hdr_size) {
eventData = ((unsigned char *)buf2) + buf2->hdr_size;
+ if ((buf2->hdr_size >= sizeof(struct logger_entry_v3)) &&
+ (((struct logger_entry_v3 *)buf)->lid == LOG_ID_SECURITY)) {
+ entry->priority = ANDROID_LOG_WARN;
+ }
+ if (buf2->hdr_size >= sizeof(struct logger_entry_v4)) {
+ entry->uid = ((struct logger_entry_v4 *)buf)->uid;
+ }
}
inCount = buf->len;
if (inCount < 4)
@@ -908,7 +928,7 @@
} else if (*message == '\b') {
strcpy(buf, "\\b");
} else if (*message == '\t') {
- strcpy(buf, "\\t");
+ strcpy(buf, "\t"); // Do not escape tabs
} else if (*message == '\v') {
strcpy(buf, "\\v");
} else if (*message == '\f') {
@@ -1238,7 +1258,7 @@
char prefixBuf[128], suffixBuf[128];
char priChar;
int prefixSuffixIsHeaderFooter = 0;
- char *ret = NULL;
+ char *ret;
time_t now;
unsigned long nsec;
@@ -1261,10 +1281,13 @@
now = entry->tv_sec;
nsec = entry->tv_nsec;
if (p_format->monotonic_output) {
- struct timespec time;
- convertMonotonic(&time, entry);
- now = time.tv_sec;
- nsec = time.tv_nsec;
+ // prevent convertMonotonic from being called if logd is monotonic
+ if (android_log_clockid() != CLOCK_MONOTONIC) {
+ struct timespec time;
+ convertMonotonic(&time, entry);
+ now = time.tv_sec;
+ nsec = time.tv_nsec;
+ }
}
if (now < 0) {
nsec = NS_PER_SEC - nsec;
@@ -1307,6 +1330,30 @@
suffixLen = MIN(suffixLen, sizeof(suffixBuf));
}
+ char uid[16];
+ uid[0] = '\0';
+ if (p_format->uid_output) {
+ if (entry->uid >= 0) {
+ const struct android_id_info *info = android_ids;
+ size_t i;
+
+ for (i = 0; i < android_id_count; ++i) {
+ if (info->aid == (unsigned int)entry->uid) {
+ break;
+ }
+ ++info;
+ }
+ if ((i < android_id_count) && (strlen(info->name) <= 5)) {
+ snprintf(uid, sizeof(uid), "%5s:", info->name);
+ } else {
+ // Not worth parsing package list, names all longer than 5
+ snprintf(uid, sizeof(uid), "%5d:", entry->uid);
+ }
+ } else {
+ snprintf(uid, sizeof(uid), " ");
+ }
+ }
+
switch (p_format->format) {
case FORMAT_TAG:
len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
@@ -1319,11 +1366,11 @@
" (%s)\n", entry->tag);
suffixLen += MIN(len, sizeof(suffixBuf) - suffixLen);
len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
- "%c(%5d) ", priChar, entry->pid);
+ "%c(%s%5d) ", priChar, uid, entry->pid);
break;
case FORMAT_THREAD:
len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
- "%c(%5d:%5d) ", priChar, entry->pid, entry->tid);
+ "%c(%s%5d:%5d) ", priChar, uid, entry->pid, entry->tid);
strcpy(suffixBuf + suffixLen, "\n");
++suffixLen;
break;
@@ -1335,21 +1382,26 @@
break;
case FORMAT_TIME:
len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
- "%s %c/%-8s(%5d): ", timeBuf, priChar, entry->tag, entry->pid);
+ "%s %c/%-8s(%s%5d): ", timeBuf, priChar, entry->tag,
+ uid, entry->pid);
strcpy(suffixBuf + suffixLen, "\n");
++suffixLen;
break;
case FORMAT_THREADTIME:
+ ret = strchr(uid, ':');
+ if (ret) {
+ *ret = ' ';
+ }
len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
- "%s %5d %5d %c %-8s: ", timeBuf,
- entry->pid, entry->tid, priChar, entry->tag);
+ "%s %s%5d %5d %c %-8s: ", timeBuf,
+ uid, entry->pid, entry->tid, priChar, entry->tag);
strcpy(suffixBuf + suffixLen, "\n");
++suffixLen;
break;
case FORMAT_LONG:
len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
- "[ %s %5d:%5d %c/%-8s ]\n",
- timeBuf, entry->pid, entry->tid, priChar, entry->tag);
+ "[ %s %s%5d:%5d %c/%-8s ]\n",
+ timeBuf, uid, entry->pid, entry->tid, priChar, entry->tag);
strcpy(suffixBuf + suffixLen, "\n\n");
suffixLen += 2;
prefixSuffixIsHeaderFooter = 1;
@@ -1357,7 +1409,7 @@
case FORMAT_BRIEF:
default:
len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
- "%c/%-8s(%5d): ", priChar, entry->tag, entry->pid);
+ "%c/%-8s(%s%5d): ", priChar, entry->tag, uid, entry->pid);
strcpy(suffixBuf + suffixLen, "\n");
++suffixLen;
break;
diff --git a/liblog/tests/liblog_benchmark.cpp b/liblog/tests/liblog_benchmark.cpp
index b594634..01fb50f 100644
--- a/liblog/tests/liblog_benchmark.cpp
+++ b/liblog/tests/liblog_benchmark.cpp
@@ -14,11 +14,17 @@
* limitations under the License.
*/
+#include <fcntl.h>
+#include <sys/endian.h>
#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
#include <cutils/sockets.h>
#include <log/log.h>
#include <log/logger.h>
#include <log/log_read.h>
+#include <private/android_logger.h>
#include "benchmark.h"
@@ -85,6 +91,380 @@
BENCHMARK(BM_clock_overhead);
/*
+ * Measure the time it takes to submit the android logging data to pstore
+ */
+static void BM_pmsg_short(int iters) {
+
+ int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));
+ if (pstore_fd < 0) {
+ return;
+ }
+
+ /*
+ * struct {
+ * // what we provide to pstore
+ * android_pmsg_log_header_t pmsg_header;
+ * // 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;
+ * };
+ * };
+ */
+
+ struct timespec ts;
+ clock_gettime(android_log_clockid(), &ts);
+
+ android_pmsg_log_header_t pmsg_header;
+ pmsg_header.magic = LOGGER_MAGIC;
+ pmsg_header.len = sizeof(android_pmsg_log_header_t)
+ + sizeof(android_log_header_t);
+ pmsg_header.uid = getuid();
+ pmsg_header.pid = getpid();
+
+ android_log_header_t header;
+ header.tid = gettid();
+ header.realtime.tv_sec = ts.tv_sec;
+ header.realtime.tv_nsec = ts.tv_nsec;
+
+ static const unsigned nr = 1;
+ static const unsigned header_length = 2;
+ struct iovec newVec[nr + header_length];
+
+ newVec[0].iov_base = (unsigned char *) &pmsg_header;
+ newVec[0].iov_len = sizeof(pmsg_header);
+ newVec[1].iov_base = (unsigned char *) &header;
+ newVec[1].iov_len = sizeof(header);
+
+ android_log_event_int_t buffer;
+
+ header.id = LOG_ID_EVENTS;
+ buffer.header.tag = 0;
+ buffer.payload.type = EVENT_TYPE_INT;
+ uint32_t snapshot = 0;
+ buffer.payload.data = htole32(snapshot);
+
+ newVec[2].iov_base = &buffer;
+ newVec[2].iov_len = sizeof(buffer);
+
+ StartBenchmarkTiming();
+ for (int i = 0; i < iters; ++i) {
+ ++snapshot;
+ buffer.payload.data = htole32(snapshot);
+ writev(pstore_fd, newVec, nr);
+ }
+ StopBenchmarkTiming();
+ close(pstore_fd);
+}
+BENCHMARK(BM_pmsg_short);
+
+/*
+ * Measure the time it takes to submit the android logging data to pstore
+ * best case aligned single block.
+ */
+static void BM_pmsg_short_aligned(int iters) {
+
+ int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));
+ if (pstore_fd < 0) {
+ return;
+ }
+
+ /*
+ * struct {
+ * // what we provide to pstore
+ * android_pmsg_log_header_t pmsg_header;
+ * // 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;
+ * };
+ * };
+ */
+
+ struct timespec ts;
+ clock_gettime(android_log_clockid(), &ts);
+
+ struct packet {
+ android_pmsg_log_header_t pmsg_header;
+ android_log_header_t header;
+ android_log_event_int_t payload;
+ };
+ char buf[sizeof(struct packet) + 8] __aligned(8);
+ memset(buf, 0, sizeof(buf));
+ struct packet *buffer = (struct packet*)(((uintptr_t)buf + 7) & ~7);
+ if (((uintptr_t)&buffer->pmsg_header) & 7) {
+ fprintf (stderr, "&buffer=0x%p iters=%d\n", &buffer->pmsg_header, iters);
+ }
+
+ buffer->pmsg_header.magic = LOGGER_MAGIC;
+ buffer->pmsg_header.len = sizeof(android_pmsg_log_header_t)
+ + sizeof(android_log_header_t);
+ buffer->pmsg_header.uid = getuid();
+ buffer->pmsg_header.pid = getpid();
+
+ buffer->header.tid = gettid();
+ buffer->header.realtime.tv_sec = ts.tv_sec;
+ buffer->header.realtime.tv_nsec = ts.tv_nsec;
+
+ buffer->header.id = LOG_ID_EVENTS;
+ buffer->payload.header.tag = 0;
+ buffer->payload.payload.type = EVENT_TYPE_INT;
+ uint32_t snapshot = 0;
+ buffer->payload.payload.data = htole32(snapshot);
+
+ StartBenchmarkTiming();
+ for (int i = 0; i < iters; ++i) {
+ ++snapshot;
+ buffer->payload.payload.data = htole32(snapshot);
+ write(pstore_fd, &buffer->pmsg_header,
+ sizeof(android_pmsg_log_header_t) +
+ sizeof(android_log_header_t) +
+ sizeof(android_log_event_int_t));
+ }
+ StopBenchmarkTiming();
+ close(pstore_fd);
+}
+BENCHMARK(BM_pmsg_short_aligned);
+
+/*
+ * Measure the time it takes to submit the android logging data to pstore
+ * best case aligned single block.
+ */
+static void BM_pmsg_short_unaligned1(int iters) {
+
+ int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));
+ if (pstore_fd < 0) {
+ return;
+ }
+
+ /*
+ * struct {
+ * // what we provide to pstore
+ * android_pmsg_log_header_t pmsg_header;
+ * // 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;
+ * };
+ * };
+ */
+
+ struct timespec ts;
+ clock_gettime(android_log_clockid(), &ts);
+
+ struct packet {
+ android_pmsg_log_header_t pmsg_header;
+ android_log_header_t header;
+ android_log_event_int_t payload;
+ };
+ char buf[sizeof(struct packet) + 8] __aligned(8);
+ memset(buf, 0, sizeof(buf));
+ struct packet *buffer = (struct packet*)((((uintptr_t)buf + 7) & ~7) + 1);
+ if ((((uintptr_t)&buffer->pmsg_header) & 7) != 1) {
+ fprintf (stderr, "&buffer=0x%p iters=%d\n", &buffer->pmsg_header, iters);
+ }
+
+ buffer->pmsg_header.magic = LOGGER_MAGIC;
+ buffer->pmsg_header.len = sizeof(android_pmsg_log_header_t)
+ + sizeof(android_log_header_t);
+ buffer->pmsg_header.uid = getuid();
+ buffer->pmsg_header.pid = getpid();
+
+ buffer->header.tid = gettid();
+ buffer->header.realtime.tv_sec = ts.tv_sec;
+ buffer->header.realtime.tv_nsec = ts.tv_nsec;
+
+ buffer->header.id = LOG_ID_EVENTS;
+ buffer->payload.header.tag = 0;
+ buffer->payload.payload.type = EVENT_TYPE_INT;
+ uint32_t snapshot = 0;
+ buffer->payload.payload.data = htole32(snapshot);
+
+ StartBenchmarkTiming();
+ for (int i = 0; i < iters; ++i) {
+ ++snapshot;
+ buffer->payload.payload.data = htole32(snapshot);
+ write(pstore_fd, &buffer->pmsg_header,
+ sizeof(android_pmsg_log_header_t) +
+ sizeof(android_log_header_t) +
+ sizeof(android_log_event_int_t));
+ }
+ StopBenchmarkTiming();
+ close(pstore_fd);
+}
+BENCHMARK(BM_pmsg_short_unaligned1);
+
+/*
+ * Measure the time it takes to submit the android logging data to pstore
+ * best case aligned single block.
+ */
+static void BM_pmsg_long_aligned(int iters) {
+
+ int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));
+ if (pstore_fd < 0) {
+ return;
+ }
+
+ /*
+ * struct {
+ * // what we provide to pstore
+ * android_pmsg_log_header_t pmsg_header;
+ * // 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;
+ * };
+ * };
+ */
+
+ struct timespec ts;
+ clock_gettime(android_log_clockid(), &ts);
+
+ struct packet {
+ android_pmsg_log_header_t pmsg_header;
+ android_log_header_t header;
+ android_log_event_int_t payload;
+ };
+ char buf[sizeof(struct packet) + 8 + LOGGER_ENTRY_MAX_PAYLOAD] __aligned(8);
+ memset(buf, 0, sizeof(buf));
+ struct packet *buffer = (struct packet*)(((uintptr_t)buf + 7) & ~7);
+ if (((uintptr_t)&buffer->pmsg_header) & 7) {
+ fprintf (stderr, "&buffer=0x%p iters=%d\n", &buffer->pmsg_header, iters);
+ }
+
+ buffer->pmsg_header.magic = LOGGER_MAGIC;
+ buffer->pmsg_header.len = sizeof(android_pmsg_log_header_t)
+ + sizeof(android_log_header_t);
+ buffer->pmsg_header.uid = getuid();
+ buffer->pmsg_header.pid = getpid();
+
+ buffer->header.tid = gettid();
+ buffer->header.realtime.tv_sec = ts.tv_sec;
+ buffer->header.realtime.tv_nsec = ts.tv_nsec;
+
+ buffer->header.id = LOG_ID_EVENTS;
+ buffer->payload.header.tag = 0;
+ buffer->payload.payload.type = EVENT_TYPE_INT;
+ uint32_t snapshot = 0;
+ buffer->payload.payload.data = htole32(snapshot);
+
+ StartBenchmarkTiming();
+ for (int i = 0; i < iters; ++i) {
+ ++snapshot;
+ buffer->payload.payload.data = htole32(snapshot);
+ write(pstore_fd, &buffer->pmsg_header, LOGGER_ENTRY_MAX_PAYLOAD);
+ }
+ StopBenchmarkTiming();
+ close(pstore_fd);
+}
+BENCHMARK(BM_pmsg_long_aligned);
+
+/*
+ * Measure the time it takes to submit the android logging data to pstore
+ * best case aligned single block.
+ */
+static void BM_pmsg_long_unaligned1(int iters) {
+
+ int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));
+ if (pstore_fd < 0) {
+ return;
+ }
+
+ /*
+ * struct {
+ * // what we provide to pstore
+ * android_pmsg_log_header_t pmsg_header;
+ * // 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;
+ * };
+ * };
+ */
+
+ struct timespec ts;
+ clock_gettime(android_log_clockid(), &ts);
+
+ struct packet {
+ android_pmsg_log_header_t pmsg_header;
+ android_log_header_t header;
+ android_log_event_int_t payload;
+ };
+ char buf[sizeof(struct packet) + 8 + LOGGER_ENTRY_MAX_PAYLOAD] __aligned(8);
+ memset(buf, 0, sizeof(buf));
+ struct packet *buffer = (struct packet*)((((uintptr_t)buf + 7) & ~7) + 1);
+ if ((((uintptr_t)&buffer->pmsg_header) & 7) != 1) {
+ fprintf (stderr, "&buffer=0x%p iters=%d\n", &buffer->pmsg_header, iters);
+ }
+
+ buffer->pmsg_header.magic = LOGGER_MAGIC;
+ buffer->pmsg_header.len = sizeof(android_pmsg_log_header_t)
+ + sizeof(android_log_header_t);
+ buffer->pmsg_header.uid = getuid();
+ buffer->pmsg_header.pid = getpid();
+
+ buffer->header.tid = gettid();
+ buffer->header.realtime.tv_sec = ts.tv_sec;
+ buffer->header.realtime.tv_nsec = ts.tv_nsec;
+
+ buffer->header.id = LOG_ID_EVENTS;
+ buffer->payload.header.tag = 0;
+ buffer->payload.payload.type = EVENT_TYPE_INT;
+ uint32_t snapshot = 0;
+ buffer->payload.payload.data = htole32(snapshot);
+
+ StartBenchmarkTiming();
+ for (int i = 0; i < iters; ++i) {
+ ++snapshot;
+ buffer->payload.payload.data = htole32(snapshot);
+ write(pstore_fd, &buffer->pmsg_header, LOGGER_ENTRY_MAX_PAYLOAD);
+ }
+ StopBenchmarkTiming();
+ close(pstore_fd);
+}
+BENCHMARK(BM_pmsg_long_unaligned1);
+
+/*
* Measure the time it takes to submit the android logging call using
* discrete acquisition under light load. Expect this to be a dozen or so
* syscall periods (40us).
@@ -280,3 +660,31 @@
StopBenchmarkTiming();
}
BENCHMARK(BM_is_loggable);
+
+/*
+ * Measure the time it takes for android_log_clockid.
+ */
+static void BM_clockid(int iters) {
+ StartBenchmarkTiming();
+
+ for (int i = 0; i < iters; ++i) {
+ android_log_clockid();
+ }
+
+ StopBenchmarkTiming();
+}
+BENCHMARK(BM_clockid);
+
+/*
+ * Measure the time it takes for __android_log_security.
+ */
+static void BM_security(int iters) {
+ StartBenchmarkTiming();
+
+ for (int i = 0; i < iters; ++i) {
+ __android_log_security();
+ }
+
+ StopBenchmarkTiming();
+}
+BENCHMARK(BM_security);
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index c987041..50afc5f 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -25,6 +25,7 @@
#include <log/logger.h>
#include <log/log_read.h>
#include <log/logprint.h>
+#include <private/android_logger.h>
// enhanced version of LOG_FAILURE_RETRY to add support for EAGAIN and
// non-syscall libs. Since we are only using this in the emergency of
@@ -164,6 +165,133 @@
android_logger_list_close(logger_list);
}
+TEST(liblog, __security) {
+ static const char persist_key[] = "persist.logd.security";
+ static const char readonly_key[] = "ro.device_owner";
+ static const char nothing_val[] = "_NOTHING_TO_SEE_HERE_";
+ char persist[PROP_VALUE_MAX];
+ char readonly[PROP_VALUE_MAX];
+
+ property_get(persist_key, persist, "");
+ property_get(readonly_key, readonly, nothing_val);
+
+ if (!strcmp(readonly, nothing_val)) {
+ EXPECT_FALSE(__android_log_security());
+ fprintf(stderr, "Warning, setting ro.device_owner to a domain\n");
+ property_set(readonly_key, "com.google.android.SecOps.DeviceOwner");
+ } else if (!strcasecmp(readonly, "false") || !readonly[0]) {
+ EXPECT_FALSE(__android_log_security());
+ return;
+ }
+
+ if (!strcasecmp(persist, "true")) {
+ EXPECT_TRUE(__android_log_security());
+ } else {
+ EXPECT_FALSE(__android_log_security());
+ }
+ property_set(persist_key, "TRUE");
+ EXPECT_TRUE(__android_log_security());
+ property_set(persist_key, "FALSE");
+ EXPECT_FALSE(__android_log_security());
+ property_set(persist_key, "true");
+ EXPECT_TRUE(__android_log_security());
+ property_set(persist_key, "false");
+ EXPECT_FALSE(__android_log_security());
+ property_set(persist_key, "");
+ EXPECT_FALSE(__android_log_security());
+ property_set(persist_key, persist);
+}
+
+TEST(liblog, __security_buffer) {
+ struct logger_list *logger_list;
+ android_event_long_t buffer;
+
+ static const char persist_key[] = "persist.logd.security";
+ char persist[PROP_VALUE_MAX];
+ bool set_persist = false;
+ bool allow_security = false;
+
+ if (__android_log_security()) {
+ allow_security = true;
+ } else {
+ property_get(persist_key, persist, "");
+ if (strcasecmp(persist, "true")) {
+ property_set(persist_key, "TRUE");
+ if (__android_log_security()) {
+ allow_security = true;
+ set_persist = true;
+ } else {
+ property_set(persist_key, persist);
+ }
+ }
+ }
+
+ if (!allow_security) {
+ fprintf(stderr, "WARNING: "
+ "security buffer disabled, bypassing end-to-end test\n");
+
+ log_time ts(CLOCK_MONOTONIC);
+
+ buffer.type = EVENT_TYPE_LONG;
+ buffer.data = *(static_cast<uint64_t *>((void *)&ts));
+
+ // expect failure!
+ ASSERT_GE(0, __android_log_security_bwrite(0, &buffer, sizeof(buffer)));
+
+ return;
+ }
+
+ pid_t pid = getpid();
+
+ ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+ LOG_ID_SECURITY, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK,
+ 1000, pid)));
+
+ log_time ts(CLOCK_MONOTONIC);
+
+ buffer.type = EVENT_TYPE_LONG;
+ buffer.data = *(static_cast<uint64_t *>((void *)&ts));
+
+ ASSERT_LT(0, __android_log_security_bwrite(0, &buffer, sizeof(buffer)));
+ usleep(1000000);
+
+ int count = 0;
+
+ for (;;) {
+ log_msg log_msg;
+ if (android_logger_list_read(logger_list, &log_msg) <= 0) {
+ break;
+ }
+
+ ASSERT_EQ(log_msg.entry.pid, pid);
+
+ if ((log_msg.entry.len != (4 + 1 + 8))
+ || (log_msg.id() != LOG_ID_SECURITY)) {
+ continue;
+ }
+
+ char *eventData = log_msg.msg();
+
+ if (eventData[4] != EVENT_TYPE_LONG) {
+ continue;
+ }
+
+ log_time tx(eventData + 4 + 1);
+ if (ts == tx) {
+ ++count;
+ }
+ }
+
+ if (set_persist) {
+ property_set(persist_key, persist);
+ }
+
+ android_logger_list_close(logger_list);
+
+ EXPECT_EQ(1, count);
+
+}
+
static unsigned signaled;
log_time signal_time;
@@ -305,8 +433,9 @@
}
static const char max_payload_tag[] = "TEST_max_payload_XXXX";
-static const char max_payload_buf[LOGGER_ENTRY_MAX_PAYLOAD
- - sizeof(max_payload_tag) - 1] = "LEONATO\n\
+#define SIZEOF_MAX_PAYLOAD_BUF (LOGGER_ENTRY_MAX_PAYLOAD - \
+ sizeof(max_payload_tag) - 1)
+static const char max_payload_buf[] = "LEONATO\n\
I learn in this letter that Don Peter of Arragon\n\
comes this night to Messina\n\
MESSENGER\n\
@@ -432,7 +561,7 @@
trouble: the fashion of the world is to avoid\n\
cost, and you encounter it\n\
LEONATO\n\
-Never came trouble to my house in the likeness";
+Never came trouble to my house in the likeness of your grace";
TEST(liblog, max_payload) {
pid_t pid = getpid();
@@ -491,7 +620,7 @@
EXPECT_EQ(true, matches);
- EXPECT_LE(sizeof(max_payload_buf), static_cast<size_t>(max_len));
+ EXPECT_LE(SIZEOF_MAX_PAYLOAD_BUF, static_cast<size_t>(max_len));
}
TEST(liblog, too_big_payload) {
@@ -611,11 +740,12 @@
struct logger * logger;
EXPECT_TRUE(NULL != (logger = android_logger_open(logger_list, id)));
EXPECT_EQ(id, android_logger_get_id(logger));
+ EXPECT_LT(0, android_logger_get_log_size(logger));
/* crash buffer is allowed to be empty, that is actually healthy! */
- if (android_logger_get_log_size(logger) || strcmp("crash", name)) {
- EXPECT_LT(0, android_logger_get_log_size(logger));
+ if (android_logger_get_log_readable_size(logger) ||
+ (strcmp("crash", name) && strcmp("security", name))) {
+ EXPECT_LT(0, android_logger_get_log_readable_size(logger));
}
- EXPECT_LT(0, android_logger_get_log_readable_size(logger));
EXPECT_LT(0, android_logger_get_log_version(logger));
}
@@ -736,13 +866,27 @@
continue;
}
fprintf(stderr, "i=%zu j=%zu\r", i, j);
+ bool android_log_is_loggable = __android_log_is_loggable(
+ levels[i].level, tag, levels[j].level);
if ((levels[i].level < levels[j].level)
|| (levels[j].level == -1)) {
- EXPECT_FALSE(__android_log_is_loggable(levels[i].level, tag,
- levels[j].level));
+ if (android_log_is_loggable) {
+ fprintf(stderr, "\n");
+ }
+ EXPECT_FALSE(android_log_is_loggable);
+ for(size_t k = 1000; k; --k) {
+ EXPECT_FALSE(__android_log_is_loggable(
+ levels[i].level, tag, levels[j].level));
+ }
} else {
- EXPECT_TRUE(__android_log_is_loggable(levels[i].level, tag,
- levels[j].level));
+ if (!android_log_is_loggable) {
+ fprintf(stderr, "\n");
+ }
+ EXPECT_TRUE(android_log_is_loggable);
+ for(size_t k = 1000; k; --k) {
+ EXPECT_TRUE(__android_log_is_loggable(
+ levels[i].level, tag, levels[j].level));
+ }
}
}
}
@@ -761,30 +905,58 @@
fprintf(stderr, "i=%zu j=%zu property_set(\"%s\",\"%s\")\r",
i, j, key, buf);
property_set(key, buf);
+ bool android_log_is_loggable = __android_log_is_loggable(
+ levels[i].level, tag, ANDROID_LOG_DEBUG);
if ((levels[i].level < levels[j].level)
|| (levels[j].level == -1)
|| ((levels[i].level < ANDROID_LOG_DEBUG)
&& (levels[j].level == -2))) {
- EXPECT_FALSE(__android_log_is_loggable(levels[i].level, tag,
- ANDROID_LOG_DEBUG));
+ if (android_log_is_loggable) {
+ fprintf(stderr, "\n");
+ }
+ EXPECT_FALSE(android_log_is_loggable);
+ for(size_t k = 1000; k; --k) {
+ EXPECT_FALSE(__android_log_is_loggable(
+ levels[i].level, tag, ANDROID_LOG_DEBUG));
+ }
} else {
- EXPECT_TRUE(__android_log_is_loggable(levels[i].level, tag,
- ANDROID_LOG_DEBUG));
+ if (!android_log_is_loggable) {
+ fprintf(stderr, "\n");
+ }
+ EXPECT_TRUE(android_log_is_loggable);
+ for(size_t k = 1000; k; --k) {
+ EXPECT_TRUE(__android_log_is_loggable(
+ levels[i].level, tag, ANDROID_LOG_DEBUG));
+ }
}
property_set(key, "");
fprintf(stderr, "i=%zu j=%zu property_set(\"%s\",\"%s\")\r",
i, j, key + base_offset, buf);
property_set(key + base_offset, buf);
+ android_log_is_loggable = __android_log_is_loggable(
+ levels[i].level, tag, ANDROID_LOG_DEBUG);
if ((levels[i].level < levels[j].level)
|| (levels[j].level == -1)
|| ((levels[i].level < ANDROID_LOG_DEBUG)
&& (levels[j].level == -2))) {
- EXPECT_FALSE(__android_log_is_loggable(levels[i].level, tag,
- ANDROID_LOG_DEBUG));
+ if (android_log_is_loggable) {
+ fprintf(stderr, "\n");
+ }
+ EXPECT_FALSE(android_log_is_loggable);
+ for(size_t k = 1000; k; --k) {
+ EXPECT_FALSE(__android_log_is_loggable(
+ levels[i].level, tag, ANDROID_LOG_DEBUG));
+ }
} else {
- EXPECT_TRUE(__android_log_is_loggable(levels[i].level, tag,
- ANDROID_LOG_DEBUG));
+ if (!android_log_is_loggable) {
+ fprintf(stderr, "\n");
+ }
+ EXPECT_TRUE(android_log_is_loggable);
+ for(size_t k = 1000; k; --k) {
+ EXPECT_TRUE(__android_log_is_loggable(
+ levels[i].level, tag, ANDROID_LOG_DEBUG));
+ }
}
property_set(key + base_offset, "");
@@ -793,30 +965,58 @@
fprintf(stderr, "i=%zu j=%zu property_set(\"%s\",\"%s\")\r",
i, j, key, buf);
property_set(key, buf);
+ android_log_is_loggable = __android_log_is_loggable(
+ levels[i].level, tag, ANDROID_LOG_DEBUG);
if ((levels[i].level < levels[j].level)
|| (levels[j].level == -1)
|| ((levels[i].level < ANDROID_LOG_DEBUG)
&& (levels[j].level == -2))) {
- EXPECT_FALSE(__android_log_is_loggable(levels[i].level, tag,
- ANDROID_LOG_DEBUG));
+ if (android_log_is_loggable) {
+ fprintf(stderr, "\n");
+ }
+ EXPECT_FALSE(android_log_is_loggable);
+ for(size_t k = 1000; k; --k) {
+ EXPECT_FALSE(__android_log_is_loggable(
+ levels[i].level, tag, ANDROID_LOG_DEBUG));
+ }
} else {
- EXPECT_TRUE(__android_log_is_loggable(levels[i].level, tag,
- ANDROID_LOG_DEBUG));
+ if (!android_log_is_loggable) {
+ fprintf(stderr, "\n");
+ }
+ EXPECT_TRUE(android_log_is_loggable);
+ for(size_t k = 1000; k; --k) {
+ EXPECT_TRUE(__android_log_is_loggable(
+ levels[i].level, tag, ANDROID_LOG_DEBUG));
+ }
}
property_set(key, "");
fprintf(stderr, "i=%zu j=%zu property_set(\"%s\",\"%s\")\r",
i, j, key + base_offset, buf);
property_set(key + base_offset, buf);
+ android_log_is_loggable = __android_log_is_loggable(
+ levels[i].level, tag, ANDROID_LOG_DEBUG);
if ((levels[i].level < levels[j].level)
|| (levels[j].level == -1)
|| ((levels[i].level < ANDROID_LOG_DEBUG)
&& (levels[j].level == -2))) {
- EXPECT_FALSE(__android_log_is_loggable(levels[i].level, tag,
- ANDROID_LOG_DEBUG));
+ if (android_log_is_loggable) {
+ fprintf(stderr, "\n");
+ }
+ EXPECT_FALSE(android_log_is_loggable);
+ for(size_t k = 1000; k; --k) {
+ EXPECT_FALSE(__android_log_is_loggable(
+ levels[i].level, tag, ANDROID_LOG_DEBUG));
+ }
} else {
- EXPECT_TRUE(__android_log_is_loggable(levels[i].level, tag,
- ANDROID_LOG_DEBUG));
+ if (!android_log_is_loggable) {
+ fprintf(stderr, "\n");
+ }
+ EXPECT_TRUE(android_log_is_loggable);
+ for(size_t k = 1000; k; --k) {
+ EXPECT_TRUE(__android_log_is_loggable(
+ levels[i].level, tag, ANDROID_LOG_DEBUG));
+ }
}
property_set(key + base_offset, "");
}
@@ -839,30 +1039,58 @@
fprintf(stderr, "i=%zu j=%zu property_set(\"%s\",\"%s\")\r",
i, j, key, buf);
property_set(key, buf);
+ bool android_log_is_loggable = __android_log_is_loggable(
+ levels[i].level, tag, ANDROID_LOG_DEBUG);
if ((levels[i].level < levels[j].level)
|| (levels[j].level == -1)
|| ((levels[i].level < ANDROID_LOG_INFO) // Yes INFO
&& (levels[j].level == -2))) {
- EXPECT_FALSE(__android_log_is_loggable(levels[i].level, tag,
- ANDROID_LOG_DEBUG));
+ if (android_log_is_loggable) {
+ fprintf(stderr, "\n");
+ }
+ EXPECT_FALSE(android_log_is_loggable);
+ for(size_t k = 1000; k; --k) {
+ EXPECT_FALSE(__android_log_is_loggable(
+ levels[i].level, tag, ANDROID_LOG_DEBUG));
+ }
} else {
- EXPECT_TRUE(__android_log_is_loggable(levels[i].level, tag,
- ANDROID_LOG_DEBUG));
+ if (!android_log_is_loggable) {
+ fprintf(stderr, "\n");
+ }
+ EXPECT_TRUE(android_log_is_loggable);
+ for(size_t k = 1000; k; --k) {
+ EXPECT_TRUE(__android_log_is_loggable(
+ levels[i].level, tag, ANDROID_LOG_DEBUG));
+ }
}
property_set(key, "");
fprintf(stderr, "i=%zu j=%zu property_set(\"%s\",\"%s\")\r",
i, j, key + base_offset, buf);
property_set(key + base_offset, buf);
+ android_log_is_loggable = __android_log_is_loggable(
+ levels[i].level, tag, ANDROID_LOG_DEBUG);
if ((levels[i].level < levels[j].level)
|| (levels[j].level == -1)
|| ((levels[i].level < ANDROID_LOG_INFO) // Yes INFO
&& (levels[j].level == -2))) {
- EXPECT_FALSE(__android_log_is_loggable(levels[i].level, tag,
- ANDROID_LOG_DEBUG));
+ if (android_log_is_loggable) {
+ fprintf(stderr, "\n");
+ }
+ EXPECT_FALSE(android_log_is_loggable);
+ for(size_t k = 1000; k; --k) {
+ EXPECT_FALSE(__android_log_is_loggable(
+ levels[i].level, tag, ANDROID_LOG_DEBUG));
+ }
} else {
- EXPECT_TRUE(__android_log_is_loggable(levels[i].level, tag,
- ANDROID_LOG_DEBUG));
+ if (!android_log_is_loggable) {
+ fprintf(stderr, "\n");
+ }
+ EXPECT_TRUE(android_log_is_loggable);
+ for(size_t k = 1000; k; --k) {
+ EXPECT_TRUE(__android_log_is_loggable(
+ levels[i].level, tag, ANDROID_LOG_DEBUG));
+ }
}
property_set(key + base_offset, "");
}
@@ -968,7 +1196,7 @@
const int TAG = 123456782;
const char SUBTAG[] = "test-subtag";
const int UID = -1;
- const int DATA_LEN = sizeof(max_payload_buf);
+ const int DATA_LEN = SIZEOF_MAX_PAYLOAD_BUF;
struct logger_list *logger_list;
pid_t pid = getpid();
@@ -1039,8 +1267,8 @@
}
eventData += dataLen;
- // 4 bytes for the tag, and 512 bytes for the log since the max_payload_buf should be
- // truncated.
+ // 4 bytes for the tag, and 512 bytes for the log since the
+ // max_payload_buf should be truncated.
ASSERT_EQ(4 + 512, eventData - original);
++count;
diff --git a/libnativeloader/Android.mk b/libnativeloader/Android.mk
new file mode 100644
index 0000000..5e65c4c
--- /dev/null
+++ b/libnativeloader/Android.mk
@@ -0,0 +1,54 @@
+LOCAL_PATH:= $(call my-dir)
+
+NATIVE_LOADER_COMMON_SRC_FILES := \
+ native_loader.cpp
+
+# Shared library for target
+# ========================================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE:= libnativeloader
+
+LOCAL_SRC_FILES:= $(NATIVE_LOADER_COMMON_SRC_FILES)
+LOCAL_SHARED_LIBRARIES := libnativehelper liblog libcutils
+LOCAL_STATIC_LIBRARIES := libbase
+LOCAL_CLANG := true
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CPPFLAGS := -std=gnu++14 -fvisibility=hidden
+LOCAL_LDFLAGS := -ldl
+LOCAL_MULTILIB := both
+
+include $(BUILD_SHARED_LIBRARY)
+
+# Shared library for host
+# ========================================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE:= libnativeloader
+
+LOCAL_SRC_FILES:= $(NATIVE_LOADER_COMMON_SRC_FILES)
+LOCAL_SHARED_LIBRARIES := libnativehelper liblog libcutils
+LOCAL_STATIC_LIBRARIES := libbase
+LOCAL_CLANG := true
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CPPFLAGS := -std=gnu++14 -fvisibility=hidden
+LOCAL_LDFLAGS := -ldl
+LOCAL_MULTILIB := both
+
+include $(BUILD_HOST_SHARED_LIBRARY)
+
+# Static library for host
+# ========================================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE:= libnativeloader
+
+LOCAL_SRC_FILES:= $(NATIVE_LOADER_COMMON_SRC_FILES)
+LOCAL_STATIC_LIBRARIES := libnativehelper libcutils liblog libbase
+LOCAL_CLANG := true
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CPPFLAGS := -std=gnu++14 -fvisibility=hidden
+LOCAL_LDFLAGS := -ldl
+LOCAL_MULTILIB := both
+
+include $(BUILD_HOST_STATIC_LIBRARY)
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
new file mode 100644
index 0000000..403a4f4
--- /dev/null
+++ b/libnativeloader/native_loader.cpp
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "nativeloader/native_loader.h"
+#include "ScopedUtfChars.h"
+
+#include <dlfcn.h>
+#ifdef __ANDROID__
+#include <android/dlext.h>
+#include "cutils/properties.h"
+#endif
+
+#include <algorithm>
+#include <vector>
+#include <string>
+#include <mutex>
+
+#include "android-base/macros.h"
+#include "android-base/strings.h"
+
+namespace android {
+
+#ifdef __ANDROID__
+// TODO(dimitry): move this to system properties.
+static const char* kPublicNativeLibraries = "libandroid.so:"
+ "libc.so:"
+ "libdl.so:"
+ "libEGL.so:"
+ "libGLESv1_CM.so:"
+ "libGLESv2.so:"
+ "libGLESv3.so:"
+ "libjnigraphics.so:"
+ "liblog.so:"
+ "libmediandk.so:"
+ "libm.so:"
+ "libOpenMAXAL.so:"
+ "libOpenSLES.so:"
+ "libstdc++.so:"
+ "libz.so";
+
+class LibraryNamespaces {
+ public:
+ LibraryNamespaces() : initialized_(false) { }
+
+ android_namespace_t* GetOrCreate(JNIEnv* env, jobject class_loader,
+ bool is_shared,
+ jstring java_library_path,
+ jstring java_permitted_path) {
+ ScopedUtfChars library_path(env, java_library_path);
+
+ std::string permitted_path;
+ if (java_permitted_path != nullptr) {
+ ScopedUtfChars path(env, java_permitted_path);
+ permitted_path = path.c_str();
+ }
+
+ if (!initialized_ && !InitPublicNamespace(library_path.c_str())) {
+ return nullptr;
+ }
+
+ std::lock_guard<std::mutex> guard(mutex_);
+
+ auto it = FindNamespaceByClassLoader(env, class_loader);
+
+ if (it != namespaces_.end()) {
+ return it->second;
+ }
+
+ uint64_t namespace_type = ANDROID_NAMESPACE_TYPE_ISOLATED;
+ if (is_shared) {
+ namespace_type |= ANDROID_NAMESPACE_TYPE_SHARED;
+ }
+
+ android_namespace_t* ns =
+ android_create_namespace("classloader-namespace",
+ nullptr,
+ library_path.c_str(),
+ namespace_type,
+ java_permitted_path != nullptr ?
+ permitted_path.c_str() :
+ nullptr);
+
+ namespaces_.push_back(std::make_pair(env->NewWeakGlobalRef(class_loader), ns));
+
+ return ns;
+ }
+
+ private:
+ bool InitPublicNamespace(const char* library_path) {
+ // Make sure all the public libraries are loaded
+ std::vector<std::string> sonames = android::base::Split(kPublicNativeLibraries, ":");
+ for (const auto& soname : sonames) {
+ if (dlopen(soname.c_str(), RTLD_NOW | RTLD_NODELETE) == nullptr) {
+ return false;
+ }
+ }
+
+ // Some apps call dlopen from generated code unknown to linker in which
+ // case linker uses anonymous namespace. See b/25844435 for details.
+ initialized_ = android_init_namespaces(kPublicNativeLibraries, library_path);
+
+ return initialized_;
+ }
+
+ std::vector<std::pair<jweak, android_namespace_t*>>::const_iterator
+ FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
+ return std::find_if(namespaces_.begin(), namespaces_.end(),
+ [&](const std::pair<jweak, android_namespace_t*>& value) {
+ return env->IsSameObject(value.first, class_loader);
+ });
+ }
+
+ bool initialized_;
+ std::mutex mutex_;
+ std::vector<std::pair<jweak, android_namespace_t*>> namespaces_;
+
+ DISALLOW_COPY_AND_ASSIGN(LibraryNamespaces);
+};
+
+static LibraryNamespaces* g_namespaces = new LibraryNamespaces;
+#endif
+
+
+void* OpenNativeLibrary(JNIEnv* env, int32_t target_sdk_version, const char* path,
+ jobject class_loader, bool is_shared, jstring java_library_path,
+ jstring java_permitted_path) {
+#if defined(__ANDROID__)
+ if (target_sdk_version == 0 || class_loader == nullptr) {
+ return dlopen(path, RTLD_NOW);
+ }
+
+ android_namespace_t* ns =
+ g_namespaces->GetOrCreate(env, class_loader, is_shared,
+ java_library_path, java_permitted_path);
+
+ if (ns == nullptr) {
+ return nullptr;
+ }
+
+ android_dlextinfo extinfo;
+ extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE;
+ extinfo.library_namespace = ns;
+
+ return android_dlopen_ext(path, RTLD_NOW, &extinfo);
+#else
+ UNUSED(env, target_sdk_version, class_loader, is_shared,
+ java_library_path, java_permitted_path);
+ return dlopen(path, RTLD_NOW);
+#endif
+}
+
+}; // android namespace
diff --git a/packagelistparser/Android.mk b/libpackagelistparser/Android.mk
similarity index 74%
rename from packagelistparser/Android.mk
rename to libpackagelistparser/Android.mk
index 802a3cb..c8be050 100644
--- a/packagelistparser/Android.mk
+++ b/libpackagelistparser/Android.mk
@@ -6,9 +6,9 @@
LOCAL_MODULE := libpackagelistparser
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := packagelistparser.c
-LOCAL_COPY_HEADERS_TO := packagelistparser
-LOCAL_COPY_HEADERS := packagelistparser.h
LOCAL_SHARED_LIBRARIES := liblog
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
LOCAL_CLANG := true
LOCAL_SANITIZE := integer
@@ -22,9 +22,9 @@
LOCAL_MODULE := libpackagelistparser
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := packagelistparser.c
-LOCAL_COPY_HEADERS_TO := packagelistparser
-LOCAL_COPY_HEADERS := packagelistparser.h
LOCAL_STATIC_LIBRARIES := liblog
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
LOCAL_CLANG := true
LOCAL_SANITIZE := integer
diff --git a/packagelistparser/packagelistparser.h b/libpackagelistparser/include/packagelistparser/packagelistparser.h
similarity index 100%
rename from packagelistparser/packagelistparser.h
rename to libpackagelistparser/include/packagelistparser/packagelistparser.h
diff --git a/packagelistparser/packagelistparser.c b/libpackagelistparser/packagelistparser.c
similarity index 98%
rename from packagelistparser/packagelistparser.c
rename to libpackagelistparser/packagelistparser.c
index 3e2539c..16052e2 100644
--- a/packagelistparser/packagelistparser.c
+++ b/libpackagelistparser/packagelistparser.c
@@ -29,7 +29,7 @@
#define LOG_TAG "packagelistparser"
#include <utils/Log.h>
-#include "packagelistparser.h"
+#include <packagelistparser/packagelistparser.h>
#define CLOGE(fmt, ...) \
do {\
diff --git a/libpixelflinger/Android.mk b/libpixelflinger/Android.mk
index 11e7988..9a937f8 100644
--- a/libpixelflinger/Android.mk
+++ b/libpixelflinger/Android.mk
@@ -14,8 +14,6 @@
codeflinger/load_store.cpp \
codeflinger/blending.cpp \
codeflinger/texturing.cpp \
- codeflinger/tinyutils/SharedBuffer.cpp \
- codeflinger/tinyutils/VectorImpl.cpp \
fixed.cpp.arm \
picker.cpp.arm \
pixelflinger.cpp.arm \
@@ -52,6 +50,14 @@
arch-mips/t32cb16blend.S \
endif
+
+PIXELFLINGER_SRC_FILES_mips64 := \
+ codeflinger/MIPSAssembler.cpp \
+ codeflinger/MIPS64Assembler.cpp \
+ codeflinger/mips64_disassem.c \
+ arch-mips64/col32cb16blend.S \
+ arch-mips64/t32cb16blend.S \
+
#
# Shared library
#
@@ -61,10 +67,12 @@
LOCAL_SRC_FILES_arm := $(PIXELFLINGER_SRC_FILES_arm)
LOCAL_SRC_FILES_arm64 := $(PIXELFLINGER_SRC_FILES_arm64)
LOCAL_SRC_FILES_mips := $(PIXELFLINGER_SRC_FILES_mips)
+LOCAL_SRC_FILES_mips64 := $(PIXELFLINGER_SRC_FILES_mips64)
LOCAL_CFLAGS := $(PIXELFLINGER_CFLAGS)
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_C_INCLUDES += $(LOCAL_EXPORT_C_INCLUDE_DIRS)
-LOCAL_SHARED_LIBRARIES := libcutils liblog
+LOCAL_C_INCLUDES += $(LOCAL_EXPORT_C_INCLUDE_DIRS) \
+ external/safe-iop/include
+LOCAL_SHARED_LIBRARIES := libcutils liblog libutils
# Really this should go away entirely or at least not depend on
# libhardware, but this at least gets us built.
diff --git a/libpixelflinger/arch-mips/col32cb16blend.S b/libpixelflinger/arch-mips/col32cb16blend.S
new file mode 100644
index 0000000..5d18e55
--- /dev/null
+++ b/libpixelflinger/arch-mips/col32cb16blend.S
@@ -0,0 +1,134 @@
+/*
+** Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+ .macro pixel dreg src f sR sG sB shift
+
+#if __mips==32 && __mips_isa_rev>=2
+ /* extract red */
+ ext $t4,\src,\shift+11,5
+ mul $t4,$t4,\f
+
+ /* extract green */
+ ext $t5,\src,\shift+5,6
+ mul $t5,$t5,\f
+
+ /* extract blue */
+ ext $t6,\src,\shift,5
+ mul $t6,$t6,\f
+#else
+ /* extract red */
+ srl $t4,\src,\shift+11
+ andi $t4, 0x1f
+ mul $t4,$t4,\f
+
+ /* extract green */
+ srl $t5,\src,\shift+5
+ andi $t5, 0x3f
+ mul $t5,$t5,\f
+
+ /* extract blue */
+ srl $t6,\src,\shift
+ andi $t6, 0x1f
+ mul $t6,$t6,\f
+#endif
+
+ srl $t4,$t4,8
+ srl $t5,$t5,8
+ srl $t6,$t6,8
+ addu $t4,$t4,\sR
+ addu $t5,$t5,\sG
+ addu \dreg,$t6,\sB
+ sll $t4,$t4,11
+ sll $t5,$t5,5
+ or \dreg,\dreg,$t4
+ or \dreg,\dreg,$t5
+ andi \dreg, 0xffff
+ .endm
+
+ .text
+ .align
+
+ .global scanline_col32cb16blend_mips
+ .ent scanline_col32cb16blend_mips
+scanline_col32cb16blend_mips:
+
+ /* check if count is zero */
+ srl $v0,$a1,24 /* sA */
+ beqz $a2,done
+ li $t4, 0x100
+ srl $v1,$v0,7
+ addu $v0,$v1,$v0
+ subu $v0,$t4,$v0 /* f */
+#if __mips==32 && __mips_isa_rev>=2
+ ext $a3,$a1,3,5 /* sR */
+ ext $t0,$a1,10,6 /* sG */
+ ext $t1,$a1,19,5 /* sB */
+#else
+ srl $a3, $a1, 3
+ andi $a3, 0x1f /* sR */
+ srl $t0, $a1, 10
+ andi $t0, 0x3f /* sG */
+ srl $t1, $a1, 19
+ andi $t1, 0x1f /* sB */
+#endif
+
+ /* check if cnt is at least 4 */
+ addiu $a2,$a2,-4
+ bltz $a2,tail
+
+loop_4pixels:
+ lw $t7,0($a0)
+ lw $t8,4($a0)
+ addiu $a0,$a0,8
+ addiu $a2,$a2,-4
+ pixel $t2 $t7 $v0 $a3 $t0 $t1 0
+ pixel $t3 $t7 $v0 $a3 $t0 $t1 16
+#if __mips==32 && __mips_isa_rev>=2
+ ins $t2,$t3,16,16
+#else
+ sll $t3, 16
+ or $t2, $t2, $t3
+#endif
+ pixel $t7 $t8 $v0 $a3 $t0 $t1 0
+ pixel $t3 $t8 $v0 $a3 $t0 $t1 16
+#if __mips==32 && __mips_isa_rev>=2
+ ins $t7,$t3,16,16
+#else
+ sll $t3, 16
+ or $t7, $t7, $t3
+#endif
+ sw $t2,-8($a0)
+ sw $t7,-4($a0)
+ bgez $a2, loop_4pixels
+
+tail:
+ /* the pixel count underran, restore it now */
+ addiu $a2,$a2,4
+
+ /* handle the last 0..3 pixels */
+ beqz $a2,done
+
+loop_1pixel:
+ lhu $t7,0($a0)
+ addiu $a0,$a0,2
+ addiu $a2,$a2,-1
+ pixel $t2 $t7 $v0 $a3 $t0 $t1 0
+ sh $t2, -2($a0)
+ bnez $a2,loop_1pixel
+
+done:
+ j $ra
+ .end scanline_col32cb16blend_mips
diff --git a/libpixelflinger/arch-mips/t32cb16blend.S b/libpixelflinger/arch-mips/t32cb16blend.S
index c911fbb..236a2c9 100644
--- a/libpixelflinger/arch-mips/t32cb16blend.S
+++ b/libpixelflinger/arch-mips/t32cb16blend.S
@@ -33,232 +33,241 @@
*/
#if __mips==32 && __mips_isa_rev>=2
- .macro pixel dreg src fb shift
- /*
- * sA = s >> 24
- * f = 0x100 - (sA + (sA>>7))
- */
-DBG .set noat
-DBG rdhwr $at,$2
-DBG .set at
+ .macro pixel dreg src fb shift
+ /*
+ * sA = s >> 24
+ * f = 0x100 - (sA + (sA>>7))
+ */
+DBG .set noat
+DBG rdhwr $at,$2
+DBG .set at
- srl $t7,\src,24
- srl $t6,$t7,7
- addu $t7,$t6
- li $t6,0x100
- subu $t7,$t6,$t7
+ srl $t7,\src,24
+ srl $t6,$t7,7
+ addu $t7,$t6
+ li $t6,0x100
+ subu $t7,$t6,$t7
- /* red */
- ext $t8,\dreg,\shift+6+5,5 # dst[\shift:15..11]
- mul $t6,$t8,$t7
- ext $t0,\dreg,\shift+5,6 # start green extraction dst[\shift:10..5]
- ext $t8,\src,3,5 # src[7..3]
- srl $t6,8
- addu $t8,$t6
- ins \fb,$t8,\shift+6+5,5 # dst[\shift:15..11]
+ /* red */
+ ext $t8,\dreg,\shift+6+5,5 # dst[\shift:15..11]
+ mul $t6,$t8,$t7
+ ext $t0,\dreg,\shift+5,6 # start green extraction dst[\shift:10..5]
+ ext $t8,\src,3,5 # src[7..3]
+ srl $t6,8
+ addu $t8,$t6
+.if \shift!=0
+ sll $t8,\shift+11
+ or \fb,$t8
+.else
+ sll \fb,$t8,11
+.endif
- /* green */
- mul $t8,$t0,$t7
- ext $t0,\dreg,\shift,5 # start blue extraction dst[\shift:4..0]
- ext $t6,\src,2+8,6 # src[15..10]
- srl $t8,8
- addu $t8,$t6
+ /* green */
+ mul $t8,$t0,$t7
+ ext $t0,\dreg,\shift,5 # start blue extraction dst[\shift:4..0]
+ ext $t6,\src,2+8,6 # src[15..10]
+ srl $t8,8
+ addu $t8,$t6
- /* blue */
- mul $t0,$t0,$t7
- ins \fb,$t8,\shift+5,6 # finish green insertion dst[\shift:10..5]
- ext $t6,\src,(3+8+8),5
- srl $t8,$t0,8
- addu $t8,$t6
- ins \fb,$t8,\shift,5
+ /* blue */
+ mul $t0,$t0,$t7
+ sll $t8, $t8, \shift+5
+ or \fb, \fb, $t8
+ ext $t6,\src,(3+8+8),5
+ srl $t8,$t0,8
+ addu $t8,$t6
+ sll $t8, $t8, \shift
+ or \fb, \fb, $t8
-DBG .set noat
-DBG rdhwr $t8,$2
-DBG subu $t8,$at
-DBG sltu $at,$t8,$v0
-DBG movn $v0,$t8,$at
-DBG sgtu $at,$t8,$v1
-DBG movn $v1,$t8,$at
-DBG .set at
- .endm
+DBG .set noat
+DBG rdhwr $t8,$2
+DBG subu $t8,$at
+DBG sltu $at,$t8,$v0
+DBG movn $v0,$t8,$at
+DBG sgtu $at,$t8,$v1
+DBG movn $v1,$t8,$at
+DBG .set at
+ .endm
#else
- .macro pixel dreg src fb shift
- /*
- * sA = s >> 24
- * f = 0x100 - (sA + (sA>>7))
- */
-DBG .set push
-DBG .set noat
-DBG .set mips32r2
-DBG rdhwr $at,$2
-DBG .set pop
+ .macro pixel dreg src fb shift
+ /*
+ * sA = s >> 24
+ * f = 0x100 - (sA + (sA>>7))
+ */
+DBG .set push
+DBG .set noat
+DBG .set mips32r2
+DBG rdhwr $at,$2
+DBG .set pop
- srl $t7,\src,24
- srl $t6,$t7,7
- addu $t7,$t6
- li $t6,0x100
- subu $t7,$t6,$t7
+ srl $t7,\src,24
+ srl $t6,$t7,7
+ addu $t7,$t6
+ li $t6,0x100
+ subu $t7,$t6,$t7
- /*
- * red
- * dR = (d >> (6 + 5)) & 0x1f;
- * dR = (f*dR)>>8
- * sR = (s >> ( 3)) & 0x1f;
- * sR += dR
- * fb |= sR << 11
- */
- srl $t8,\dreg,\shift+6+5
+ /*
+ * red
+ * dR = (d >> (6 + 5)) & 0x1f;
+ * dR = (f*dR)>>8
+ * sR = (s >> ( 3)) & 0x1f;
+ * sR += dR
+ * fb |= sR << 11
+ */
+ srl $t8,\dreg,\shift+6+5
.if \shift==0
- and $t8,0x1f
+ and $t8,0x1f
.endif
- mul $t8,$t8,$t7
- srl $t6,\src,3
- and $t6,0x1f
- srl $t8,8
- addu $t8,$t6
+ mul $t8,$t8,$t7
+ srl $t6,\src,3
+ and $t6,0x1f
+ srl $t8,8
+ addu $t8,$t6
.if \shift!=0
- sll $t8,\shift+11
- or \fb,$t8
+ sll $t8,\shift+11
+ or \fb,$t8
.else
- sll \fb,$t8,11
+ sll \fb,$t8,11
.endif
/*
- * green
- * dG = (d >> 5) & 0x3f
- * dG = (f*dG) >> 8
- * sG = (s >> ( 8+2))&0x3F;
- */
- srl $t8,\dreg,\shift+5
- and $t8,0x3f
- mul $t8,$t8,$t7
- srl $t6,\src,8+2
- and $t6,0x3f
- srl $t8,8
- addu $t8,$t6
- sll $t8,\shift + 5
- or \fb,$t8
+ * green
+ * dG = (d >> 5) & 0x3f
+ * dG = (f*dG) >> 8
+ * sG = (s >> ( 8+2))&0x3F;
+ */
+ srl $t8,\dreg,\shift+5
+ and $t8,0x3f
+ mul $t8,$t8,$t7
+ srl $t6,\src,8+2
+ and $t6,0x3f
+ srl $t8,8
+ addu $t8,$t6
+ sll $t8,\shift + 5
+ or \fb,$t8
- /* blue */
+ /* blue */
.if \shift!=0
- srl $t8,\dreg,\shift
- and $t8,0x1f
+ srl $t8,\dreg,\shift
+ and $t8,0x1f
.else
- and $t8,\dreg,0x1f
+ and $t8,\dreg,0x1f
.endif
- mul $t8,$t8,$t7
- srl $t6,\src,(8+8+3)
- and $t6,0x1f
- srl $t8,8
- addu $t8,$t6
+ mul $t8,$t8,$t7
+ srl $t6,\src,(8+8+3)
+ and $t6,0x1f
+ srl $t8,8
+ addu $t8,$t6
.if \shift!=0
- sll $t8,\shift
+ sll $t8,\shift
.endif
- or \fb,$t8
-DBG .set push
-DBG .set noat
-DBG .set mips32r2
-DBG rdhwr $t8,$2
-DBG subu $t8,$at
-DBG sltu $at,$t8,$v0
-DBG movn $v0,$t8,$at
-DBG sgtu $at,$t8,$v1
-DBG movn $v1,$t8,$at
-DBG .set pop
- .endm
+ or \fb,$t8
+DBG .set push
+DBG .set noat
+DBG .set mips32r2
+DBG rdhwr $t8,$2
+DBG subu $t8,$at
+DBG sltu $at,$t8,$v0
+DBG movn $v0,$t8,$at
+DBG sgtu $at,$t8,$v1
+DBG movn $v1,$t8,$at
+DBG .set pop
+ .endm
#endif
- .text
- .align
+ .text
+ .align
- .global scanline_t32cb16blend_mips
- .ent scanline_t32cb16blend_mips
+ .global scanline_t32cb16blend_mips
+ .ent scanline_t32cb16blend_mips
scanline_t32cb16blend_mips:
-DBG li $v0,0xffffffff
-DBG li $v1,0
- /* Align the destination if necessary */
- and $t0,$a0,3
- beqz $t0,aligned
+DBG li $v0,0xffffffff
+DBG li $v1,0
+ /* Align the destination if necessary */
+ and $t0,$a0,3
+ beqz $t0,aligned
- /* as long as there is at least one pixel */
- beqz $a2,done
+ /* as long as there is at least one pixel */
+ beqz $a2,done
- lw $t4,($a1)
- addu $a0,2
- addu $a1,4
- beqz $t4,1f
- lhu $t3,-2($a0)
- pixel $t3,$t4,$t1,0
- sh $t1,-2($a0)
-1: subu $a2,1
+ lw $t4,($a1)
+ addu $a0,2
+ addu $a1,4
+ beqz $t4,1f
+ lhu $t3,-2($a0)
+ pixel $t3,$t4,$t1,0
+ sh $t1,-2($a0)
+1: subu $a2,1
aligned:
- /* Check to see if its worth unrolling the loop */
- subu $a2,4
- bltz $a2,tail
+ /* Check to see if its worth unrolling the loop */
+ subu $a2,4
+ bltz $a2,tail
- /* Process 4 pixels at a time */
+ /* Process 4 pixels at a time */
fourpixels:
- /* 1st pair of pixels */
- lw $t4,0($a1)
- lw $t5,4($a1)
- addu $a0,8
- addu $a1,16
+ /* 1st pair of pixels */
+ lw $t4,0($a1)
+ lw $t5,4($a1)
+ addu $a0,8
+ addu $a1,16
- /* both are zero, skip this pair */
- or $t3,$t4,$t5
- beqz $t3,1f
+ /* both are zero, skip this pair */
+ or $t3,$t4,$t5
+ beqz $t3,1f
- /* load the destination */
- lw $t3,-8($a0)
+ /* load the destination */
+ lw $t3,-8($a0)
- pixel $t3,$t4,$t1,0
- pixel $t3,$t5,$t1,16
- sw $t1,-8($a0)
+ pixel $t3,$t4,$t1,0
+ andi $t1, 0xFFFF
+ pixel $t3,$t5,$t1,16
+ sw $t1,-8($a0)
1:
- /* 2nd pair of pixels */
- lw $t4,-8($a1)
- lw $t5,-4($a1)
+ /* 2nd pair of pixels */
+ lw $t4,-8($a1)
+ lw $t5,-4($a1)
- /* both are zero, skip this pair */
- or $t3,$t4,$t5
- beqz $t3,1f
+ /* both are zero, skip this pair */
+ or $t3,$t4,$t5
+ beqz $t3,1f
- /* load the destination */
- lw $t3,-4($a0)
+ /* load the destination */
+ lw $t3,-4($a0)
- pixel $t3,$t4,$t1,0
- pixel $t3,$t5,$t1,16
- sw $t1,-4($a0)
+ pixel $t3,$t4,$t1,0
+ andi $t1, 0xFFFF
+ pixel $t3,$t5,$t1,16
+ sw $t1,-4($a0)
-1: subu $a2,4
- bgtz $a2,fourpixels
+1: subu $a2,4
+ bgtz $a2,fourpixels
tail:
- /* the pixel count underran, restore it now */
- addu $a2,4
+ /* the pixel count underran, restore it now */
+ addu $a2,4
- /* handle the last 0..3 pixels */
- beqz $a2,done
+ /* handle the last 0..3 pixels */
+ beqz $a2,done
onepixel:
- lw $t4,($a1)
- addu $a0,2
- addu $a1,4
- beqz $t4,1f
- lhu $t3,-2($a0)
- pixel $t3,$t4,$t1,0
- sh $t1,-2($a0)
-1: subu $a2,1
- bnez $a2,onepixel
+ lw $t4,($a1)
+ addu $a0,2
+ addu $a1,4
+ beqz $t4,1f
+ lhu $t3,-2($a0)
+ pixel $t3,$t4,$t1,0
+ sh $t1,-2($a0)
+1: subu $a2,1
+ bnez $a2,onepixel
done:
-DBG .set push
-DBG .set mips32r2
-DBG rdhwr $a0,$3
-DBG mul $v0,$a0
-DBG mul $v1,$a0
-DBG .set pop
- j $ra
- .end scanline_t32cb16blend_mips
+DBG .set push
+DBG .set mips32r2
+DBG rdhwr $a0,$3
+DBG mul $v0,$a0
+DBG mul $v1,$a0
+DBG .set pop
+ j $ra
+ .end scanline_t32cb16blend_mips
diff --git a/libpixelflinger/arch-mips64/col32cb16blend.S b/libpixelflinger/arch-mips64/col32cb16blend.S
new file mode 100644
index 0000000..fea4491
--- /dev/null
+++ b/libpixelflinger/arch-mips64/col32cb16blend.S
@@ -0,0 +1,108 @@
+/*
+** Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+ .macro pixel dreg src f sR sG sB shift
+
+ /* extract red */
+.if \shift < 32
+ dext $t0,\src,\shift+11,5
+.else
+ dextu $t0,\src,\shift+11,5
+.endif
+ mul $t0,$t0,\f
+
+ /* extract green */
+.if \shift < 32
+ dext $t1,\src,\shift+5,6
+.else
+ dextu $t1,\src,\shift+5,6
+.endif
+ mul $t1,$t1,\f
+
+ /* extract blue */
+.if \shift < 32
+ dext $t2,\src,\shift,5
+.else
+ dextu $t2,\src,\shift,5
+.endif
+ mul $t2,$t2,\f
+
+ srl $t0,$t0,8
+ srl $t1,$t1,8
+ srl $t2,$t2,8
+ addu $t0,$t0,\sR
+ addu $t1,$t1,\sG
+ addu \dreg,$t2,\sB
+ sll $t0,$t0,11
+ sll $t1,$t1,5
+ or \dreg,\dreg,$t0
+ or \dreg,\dreg,$t1
+ .endm
+
+ .text
+ .align
+
+ .global scanline_col32cb16blend_mips64
+ .ent scanline_col32cb16blend_mips64
+scanline_col32cb16blend_mips64:
+
+ /* check if count is zero */
+ srl $v0,$a1,24 /* sA */
+ beqz $a2,done
+ li $t0, 0x100
+ srl $v1,$v0,7
+ addu $v0,$v1,$v0
+ subu $v0,$t0,$v0 /* f */
+ ext $a3,$a1,3,5 /* sR */
+ ext $a4,$a1,10,6 /* sG */
+ ext $a5,$a1,19,5 /* sB */
+
+ /* check if cnt is at least 4 */
+ addiu $a2,$a2,-4
+ bltz $a2,tail
+
+loop_4pixels:
+ ld $t3,0($a0)
+ daddiu $a0,$a0,8
+ addiu $a2,$a2,-4
+ pixel $a6 $t3 $v0 $a3 $a4 $a5 0
+ pixel $a7 $t3 $v0 $a3 $a4 $a5 16
+ pixel $t8 $t3 $v0 $a3 $a4 $a5 32
+ pixel $t9 $t3 $v0 $a3 $a4 $a5 48
+ dins $a6,$a7,16,16
+ dinsu $a6,$t8,32,16
+ dinsu $a6,$t9,48,16
+ sd $a6,-8($a0)
+ bgez $a2, loop_4pixels
+
+tail:
+ /* the pixel count underran, restore it now */
+ addiu $a2,$a2,4
+
+ /* handle the last 0..3 pixels */
+ beqz $a2,done
+
+loop_1pixel:
+ lhu $t3,0($a0)
+ daddiu $a0,$a0,2
+ addiu $a2,$a2,-1
+ pixel $a6 $t3 $v0 $a3 $a4 $a5 0
+ sh $a6, -2($a0)
+ bnez $a2,loop_1pixel
+
+done:
+ j $ra
+ .end scanline_col32cb16blend_mips64
diff --git a/libpixelflinger/arch-mips64/t32cb16blend.S b/libpixelflinger/arch-mips64/t32cb16blend.S
new file mode 100644
index 0000000..d2f4d49
--- /dev/null
+++ b/libpixelflinger/arch-mips64/t32cb16blend.S
@@ -0,0 +1,172 @@
+/*
+** Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifdef DEBUG
+#define DBG
+#else
+#define DBG #
+#endif
+
+/*
+ * blend one of 2 16bpp RGB pixels held in dreg selected by shift
+ * with the 32bpp ABGR pixel held in src and store the result in fb
+ *
+ * Assumes that the dreg data is little endian and that
+ * the the second pixel (shift==16) will be merged into
+ * the fb result
+ *
+ * Uses $a4,$t2,$t3,$t8
+ */
+
+ .macro pixel dreg src fb shift
+ /*
+ * sA = s >> 24
+ * f = 0x100 - (sA + (sA>>7))
+ */
+ srl $t3,\src,24
+ srl $t2,$t3,7
+ addu $t3,$t2
+ li $t2,0x100
+ subu $t3,$t2,$t3
+
+ /* red */
+ ext $t8,\dreg,\shift+6+5,5 # dst[\shift:15..11]
+ mul $t2,$t8,$t3
+ ext $a4,\dreg,\shift+5,6 # start green extraction dst[\shift:10..5]
+ ext $t8,\src,3,5 # src[7..3]
+ srl $t2,8
+ addu $t8,$t2
+.if \shift!=0
+ sll $t8,\shift+11 # dst[\shift:15..11]
+ or \fb,$t8
+.else
+ sll \fb,$t8,11
+.endif
+
+ /* green */
+ mul $t8,$a4,$t3
+ ext $a4,\dreg,\shift,5 # start blue extraction dst[\shift:4..0]
+ ext $t2,\src,2+8,6 # src[15..10]
+ srl $t8,8
+ addu $t8,$t2
+
+ /* blue */
+ mul $a4,$a4,$t3
+ sll $t8, $t8, \shift+5 # finish green insertion dst[\shift:10..5]
+ or \fb, \fb, $t8
+ ext $t2,\src,(3+8+8),5
+ srl $t8,$a4,8
+ addu $t8,$t2
+ sll $t8, $t8, \shift
+ or \fb, \fb, $t8
+ .endm
+
+ .text
+ .align
+
+ .global scanline_t32cb16blend_mips64
+ .ent scanline_t32cb16blend_mips64
+scanline_t32cb16blend_mips64:
+ daddiu $sp, $sp, -40
+DBG li $v0,0xffffffff
+DBG li $v1,0
+ /* Align the destination if necessary */
+ and $a4,$a0,3
+ beqz $a4,aligned
+
+ /* as long as there is at least one pixel */
+ beqz $a2,done
+
+ lw $t0,($a1)
+ daddu $a0,2
+ daddu $a1,4
+ beqz $t0,1f
+ lhu $a7,-2($a0)
+ pixel $a7,$t0,$a5,0
+ sh $a5,-2($a0)
+1: subu $a2,1
+
+aligned:
+ /* Check to see if its worth unrolling the loop */
+ subu $a2,4
+ bltz $a2,tail
+
+ /* Process 4 pixels at a time */
+fourpixels:
+ /* 1st pair of pixels */
+ lw $t0,0($a1)
+ lw $t1,4($a1)
+ daddu $a0,8
+ daddu $a1,16
+
+ /* both are zero, skip this pair */
+ or $a7,$t0,$t1
+ beqz $a7,1f
+
+ /* load the destination */
+ lw $a7,-8($a0)
+
+ pixel $a7,$t0,$a5,0
+ andi $a5, 0xFFFF
+ pixel $a7,$t1,$a5,16
+ sw $a5,-8($a0)
+
+1:
+ /* 2nd pair of pixels */
+ lw $t0,-8($a1)
+ lw $t1,-4($a1)
+
+ /* both are zero, skip this pair */
+ or $a7,$t0,$t1
+ beqz $a7,1f
+
+ /* load the destination */
+ lw $a7,-4($a0)
+
+ pixel $a7,$t0,$a5,0
+ andi $a5, 0xFFFF
+ pixel $a7,$t1,$a5,16
+ sw $a5,-4($a0)
+
+1: subu $a2,4
+ bgtz $a2,fourpixels
+
+tail:
+ /* the pixel count underran, restore it now */
+ addu $a2,4
+
+ /* handle the last 0..3 pixels */
+ beqz $a2,done
+onepixel:
+ lw $t0,($a1)
+ daddu $a0,2
+ daddu $a1,4
+ beqz $t0,1f
+ lhu $a7,-2($a0)
+ pixel $a7,$t0,$a5,0
+ sh $a5,-2($a0)
+1: subu $a2,1
+ bnez $a2,onepixel
+done:
+DBG .set push
+DBG .set mips32r2
+DBG rdhwr $a0,$3
+DBG mul $v0,$a0
+DBG mul $v1,$a0
+DBG .set pop
+ daddiu $sp, $sp, 40
+ j $ra
+ .end scanline_t32cb16blend_mips64
diff --git a/libpixelflinger/codeflinger/ARMAssembler.h b/libpixelflinger/codeflinger/ARMAssembler.h
index c03dd9a..e0c7646 100644
--- a/libpixelflinger/codeflinger/ARMAssembler.h
+++ b/libpixelflinger/codeflinger/ARMAssembler.h
@@ -21,9 +21,9 @@
#include <stdint.h>
#include <sys/types.h>
-#include "tinyutils/Vector.h"
-#include "tinyutils/KeyedVector.h"
#include "tinyutils/smartpointer.h"
+#include "utils/Vector.h"
+#include "utils/KeyedVector.h"
#include "ARMAssemblerInterface.h"
#include "CodeCache.h"
diff --git a/libpixelflinger/codeflinger/ARMAssemblerInterface.h b/libpixelflinger/codeflinger/ARMAssemblerInterface.h
index 40cbfcf..72935ac 100644
--- a/libpixelflinger/codeflinger/ARMAssemblerInterface.h
+++ b/libpixelflinger/codeflinger/ARMAssemblerInterface.h
@@ -63,7 +63,7 @@
};
enum {
- CODEGEN_ARCH_ARM = 1, CODEGEN_ARCH_MIPS, CODEGEN_ARCH_ARM64
+ CODEGEN_ARCH_ARM = 1, CODEGEN_ARCH_MIPS, CODEGEN_ARCH_ARM64, CODEGEN_ARCH_MIPS64
};
// -----------------------------------------------------------------------
@@ -115,7 +115,8 @@
// data processing...
enum {
opAND, opEOR, opSUB, opRSB, opADD, opADC, opSBC, opRSC,
- opTST, opTEQ, opCMP, opCMN, opORR, opMOV, opBIC, opMVN
+ opTST, opTEQ, opCMP, opCMN, opORR, opMOV, opBIC, opMVN,
+ opADD64, opSUB64
};
virtual void
diff --git a/libpixelflinger/codeflinger/Arm64Assembler.h b/libpixelflinger/codeflinger/Arm64Assembler.h
index 8479270..c9be116 100644
--- a/libpixelflinger/codeflinger/Arm64Assembler.h
+++ b/libpixelflinger/codeflinger/Arm64Assembler.h
@@ -32,9 +32,9 @@
#include <stdint.h>
#include <sys/types.h>
-#include "tinyutils/Vector.h"
-#include "tinyutils/KeyedVector.h"
#include "tinyutils/smartpointer.h"
+#include "utils/Vector.h"
+#include "utils/KeyedVector.h"
#include "tinyutils/smartpointer.h"
#include "codeflinger/ARMAssemblerInterface.h"
diff --git a/libpixelflinger/codeflinger/CodeCache.h b/libpixelflinger/codeflinger/CodeCache.h
index fa67dd0..0fb6fd5 100644
--- a/libpixelflinger/codeflinger/CodeCache.h
+++ b/libpixelflinger/codeflinger/CodeCache.h
@@ -23,7 +23,7 @@
#include <pthread.h>
#include <sys/types.h>
-#include "tinyutils/KeyedVector.h"
+#include "utils/KeyedVector.h"
#include "tinyutils/smartpointer.h"
namespace android {
diff --git a/libpixelflinger/codeflinger/GGLAssembler.cpp b/libpixelflinger/codeflinger/GGLAssembler.cpp
index 325caba..346779f 100644
--- a/libpixelflinger/codeflinger/GGLAssembler.cpp
+++ b/libpixelflinger/codeflinger/GGLAssembler.cpp
@@ -893,7 +893,8 @@
return;
}
- if (getCodegenArch() == CODEGEN_ARCH_MIPS) {
+ if ((getCodegenArch() == CODEGEN_ARCH_MIPS) ||
+ (getCodegenArch() == CODEGEN_ARCH_MIPS64)) {
// MIPS can do 16-bit imm in 1 instr, 32-bit in 3 instr
// the below ' while (mask)' code is buggy on mips
// since mips returns true on isValidImmediate()
@@ -1057,7 +1058,8 @@
RegisterAllocator::RegisterFile::RegisterFile(int codegen_arch)
: mRegs(0), mTouched(0), mStatus(0), mArch(codegen_arch), mRegisterOffset(0)
{
- if (mArch == ARMAssemblerInterface::CODEGEN_ARCH_MIPS) {
+ if ((mArch == ARMAssemblerInterface::CODEGEN_ARCH_MIPS) ||
+ (mArch == ARMAssemblerInterface::CODEGEN_ARCH_MIPS64)) {
mRegisterOffset = 2; // ARM has regs 0..15, MIPS offset to 2..17
}
reserve(ARMAssemblerInterface::SP);
@@ -1067,7 +1069,8 @@
RegisterAllocator::RegisterFile::RegisterFile(const RegisterFile& rhs, int codegen_arch)
: mRegs(rhs.mRegs), mTouched(rhs.mTouched), mArch(codegen_arch), mRegisterOffset(0)
{
- if (mArch == ARMAssemblerInterface::CODEGEN_ARCH_MIPS) {
+ if ((mArch == ARMAssemblerInterface::CODEGEN_ARCH_MIPS) ||
+ (mArch == ARMAssemblerInterface::CODEGEN_ARCH_MIPS64)) {
mRegisterOffset = 2; // ARM has regs 0..15, MIPS offset to 2..17
}
}
diff --git a/libpixelflinger/codeflinger/MIPS64Assembler.cpp b/libpixelflinger/codeflinger/MIPS64Assembler.cpp
new file mode 100644
index 0000000..672040b
--- /dev/null
+++ b/libpixelflinger/codeflinger/MIPS64Assembler.cpp
@@ -0,0 +1,1451 @@
+/* libs/pixelflinger/codeflinger/MIPS64Assembler.cpp
+**
+** Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+
+/* MIPS64 assembler and ARM->MIPS64 assembly translator
+**
+** The approach is utilize MIPSAssembler generator, using inherited MIPS64Assembler
+** that overrides just the specific MIPS64r6 instructions.
+** For now ArmToMips64Assembler is copied over from ArmToMipsAssembler class,
+** changing some MIPS64r6 related stuff.
+**
+*/
+
+
+#define LOG_TAG "MIPS64Assembler"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <cutils/log.h>
+#include <cutils/properties.h>
+
+#if defined(WITH_LIB_HARDWARE)
+#include <hardware_legacy/qemu_tracing.h>
+#endif
+
+#include <private/pixelflinger/ggl_context.h>
+
+#include "MIPS64Assembler.h"
+#include "CodeCache.h"
+#include "mips64_disassem.h"
+
+
+#define NOT_IMPLEMENTED() LOG_ALWAYS_FATAL("Arm instruction %s not yet implemented\n", __func__)
+
+
+// ----------------------------------------------------------------------------
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#pragma mark ArmToMips64Assembler...
+#endif
+
+ArmToMips64Assembler::ArmToMips64Assembler(const sp<Assembly>& assembly,
+ char *abuf, int linesz, int instr_count)
+ : ARMAssemblerInterface(),
+ mArmDisassemblyBuffer(abuf),
+ mArmLineLength(linesz),
+ mArmInstrCount(instr_count),
+ mInum(0),
+ mAssembly(assembly)
+{
+ mMips = new MIPS64Assembler(assembly, this);
+ mArmPC = (uint32_t **) malloc(ARM_MAX_INSTUCTIONS * sizeof(uint32_t *));
+ init_conditional_labels();
+}
+
+ArmToMips64Assembler::ArmToMips64Assembler(void* assembly)
+ : ARMAssemblerInterface(),
+ mArmDisassemblyBuffer(NULL),
+ mInum(0),
+ mAssembly(NULL)
+{
+ mMips = new MIPS64Assembler(assembly, this);
+ mArmPC = (uint32_t **) malloc(ARM_MAX_INSTUCTIONS * sizeof(uint32_t *));
+ init_conditional_labels();
+}
+
+ArmToMips64Assembler::~ArmToMips64Assembler()
+{
+ delete mMips;
+ free((void *) mArmPC);
+}
+
+uint32_t* ArmToMips64Assembler::pc() const
+{
+ return mMips->pc();
+}
+
+uint32_t* ArmToMips64Assembler::base() const
+{
+ return mMips->base();
+}
+
+void ArmToMips64Assembler::reset()
+{
+ cond.labelnum = 0;
+ mInum = 0;
+ mMips->reset();
+}
+
+int ArmToMips64Assembler::getCodegenArch()
+{
+ return CODEGEN_ARCH_MIPS64;
+}
+
+void ArmToMips64Assembler::comment(const char* string)
+{
+ mMips->comment(string);
+}
+
+void ArmToMips64Assembler::label(const char* theLabel)
+{
+ mMips->label(theLabel);
+}
+
+void ArmToMips64Assembler::disassemble(const char* name)
+{
+ mMips->disassemble(name);
+}
+
+void ArmToMips64Assembler::init_conditional_labels()
+{
+ int i;
+ for (i=0;i<99; ++i) {
+ sprintf(cond.label[i], "cond_%d", i);
+ }
+}
+
+
+
+#if 0
+#pragma mark -
+#pragma mark Prolog/Epilog & Generate...
+#endif
+
+void ArmToMips64Assembler::prolog()
+{
+ mArmPC[mInum++] = pc(); // save starting PC for this instr
+
+ mMips->DADDIU(R_sp, R_sp, -(5 * 8));
+ mMips->SD(R_s0, R_sp, 0);
+ mMips->SD(R_s1, R_sp, 8);
+ mMips->SD(R_s2, R_sp, 16);
+ mMips->SD(R_s3, R_sp, 24);
+ mMips->SD(R_s4, R_sp, 32);
+ mMips->MOVE(R_v0, R_a0); // move context * passed in a0 to v0 (arm r0)
+}
+
+void ArmToMips64Assembler::epilog(uint32_t touched)
+{
+ mArmPC[mInum++] = pc(); // save starting PC for this instr
+
+ mMips->LD(R_s0, R_sp, 0);
+ mMips->LD(R_s1, R_sp, 8);
+ mMips->LD(R_s2, R_sp, 16);
+ mMips->LD(R_s3, R_sp, 24);
+ mMips->LD(R_s4, R_sp, 32);
+ mMips->DADDIU(R_sp, R_sp, (5 * 8));
+ mMips->JR(R_ra);
+
+}
+
+int ArmToMips64Assembler::generate(const char* name)
+{
+ return mMips->generate(name);
+}
+
+void ArmToMips64Assembler::fix_branches()
+{
+ mMips->fix_branches();
+}
+
+uint32_t* ArmToMips64Assembler::pcForLabel(const char* label)
+{
+ return mMips->pcForLabel(label);
+}
+
+void ArmToMips64Assembler::set_condition(int mode, int R1, int R2) {
+ if (mode == 2) {
+ cond.type = SBIT_COND;
+ } else {
+ cond.type = CMP_COND;
+ }
+ cond.r1 = R1;
+ cond.r2 = R2;
+}
+
+//----------------------------------------------------------
+
+#if 0
+#pragma mark -
+#pragma mark Addressing modes & shifters...
+#endif
+
+
+// do not need this for MIPS, but it is in the Interface (virtual)
+int ArmToMips64Assembler::buildImmediate(
+ uint32_t immediate, uint32_t& rot, uint32_t& imm)
+{
+ // for MIPS, any 32-bit immediate is OK
+ rot = 0;
+ imm = immediate;
+ return 0;
+}
+
+// shifters...
+
+bool ArmToMips64Assembler::isValidImmediate(uint32_t immediate)
+{
+ // for MIPS, any 32-bit immediate is OK
+ return true;
+}
+
+uint32_t ArmToMips64Assembler::imm(uint32_t immediate)
+{
+ amode.value = immediate;
+ return AMODE_IMM;
+}
+
+uint32_t ArmToMips64Assembler::reg_imm(int Rm, int type, uint32_t shift)
+{
+ amode.reg = Rm;
+ amode.stype = type;
+ amode.value = shift;
+ return AMODE_REG_IMM;
+}
+
+uint32_t ArmToMips64Assembler::reg_rrx(int Rm)
+{
+ // reg_rrx mode is not used in the GLLAssember code at this time
+ return AMODE_UNSUPPORTED;
+}
+
+uint32_t ArmToMips64Assembler::reg_reg(int Rm, int type, int Rs)
+{
+ // reg_reg mode is not used in the GLLAssember code at this time
+ return AMODE_UNSUPPORTED;
+}
+
+
+// addressing modes...
+// LDR(B)/STR(B)/PLD (immediate and Rm can be negative, which indicate U=0)
+uint32_t ArmToMips64Assembler::immed12_pre(int32_t immed12, int W)
+{
+ LOG_ALWAYS_FATAL_IF(abs(immed12) >= 0x800,
+ "LDR(B)/STR(B)/PLD immediate too big (%08x)",
+ immed12);
+ amode.value = immed12;
+ amode.writeback = W;
+ return AMODE_IMM_12_PRE;
+}
+
+uint32_t ArmToMips64Assembler::immed12_post(int32_t immed12)
+{
+ LOG_ALWAYS_FATAL_IF(abs(immed12) >= 0x800,
+ "LDR(B)/STR(B)/PLD immediate too big (%08x)",
+ immed12);
+
+ amode.value = immed12;
+ return AMODE_IMM_12_POST;
+}
+
+uint32_t ArmToMips64Assembler::reg_scale_pre(int Rm, int type,
+ uint32_t shift, int W)
+{
+ LOG_ALWAYS_FATAL_IF(W | type | shift, "reg_scale_pre adv modes not yet implemented");
+
+ amode.reg = Rm;
+ // amode.stype = type; // more advanced modes not used in GGLAssembler yet
+ // amode.value = shift;
+ // amode.writeback = W;
+ return AMODE_REG_SCALE_PRE;
+}
+
+uint32_t ArmToMips64Assembler::reg_scale_post(int Rm, int type, uint32_t shift)
+{
+ LOG_ALWAYS_FATAL("adr mode reg_scale_post not yet implemented\n");
+ return AMODE_UNSUPPORTED;
+}
+
+// LDRH/LDRSB/LDRSH/STRH (immediate and Rm can be negative, which indicate U=0)
+uint32_t ArmToMips64Assembler::immed8_pre(int32_t immed8, int W)
+{
+ LOG_ALWAYS_FATAL("adr mode immed8_pre not yet implemented\n");
+
+ LOG_ALWAYS_FATAL_IF(abs(immed8) >= 0x100,
+ "LDRH/LDRSB/LDRSH/STRH immediate too big (%08x)",
+ immed8);
+ return AMODE_IMM_8_PRE;
+}
+
+uint32_t ArmToMips64Assembler::immed8_post(int32_t immed8)
+{
+ LOG_ALWAYS_FATAL_IF(abs(immed8) >= 0x100,
+ "LDRH/LDRSB/LDRSH/STRH immediate too big (%08x)",
+ immed8);
+ amode.value = immed8;
+ return AMODE_IMM_8_POST;
+}
+
+uint32_t ArmToMips64Assembler::reg_pre(int Rm, int W)
+{
+ LOG_ALWAYS_FATAL_IF(W, "reg_pre writeback not yet implemented");
+ amode.reg = Rm;
+ return AMODE_REG_PRE;
+}
+
+uint32_t ArmToMips64Assembler::reg_post(int Rm)
+{
+ LOG_ALWAYS_FATAL("adr mode reg_post not yet implemented\n");
+ return AMODE_UNSUPPORTED;
+}
+
+
+
+// ----------------------------------------------------------------------------
+
+#if 0
+#pragma mark -
+#pragma mark Data Processing...
+#endif
+
+
+static const char * const dpOpNames[] = {
+ "AND", "EOR", "SUB", "RSB", "ADD", "ADC", "SBC", "RSC",
+ "TST", "TEQ", "CMP", "CMN", "ORR", "MOV", "BIC", "MVN"
+};
+
+// check if the operand registers from a previous CMP or S-bit instruction
+// would be overwritten by this instruction. If so, move the value to a
+// safe register.
+// Note that we cannot tell at _this_ instruction time if a future (conditional)
+// instruction will _also_ use this value (a defect of the simple 1-pass, one-
+// instruction-at-a-time translation). Therefore we must be conservative and
+// save the value before it is overwritten. This costs an extra MOVE instr.
+
+void ArmToMips64Assembler::protectConditionalOperands(int Rd)
+{
+ if (Rd == cond.r1) {
+ mMips->MOVE(R_cmp, cond.r1);
+ cond.r1 = R_cmp;
+ }
+ if (cond.type == CMP_COND && Rd == cond.r2) {
+ mMips->MOVE(R_cmp2, cond.r2);
+ cond.r2 = R_cmp2;
+ }
+}
+
+
+// interprets the addressing mode, and generates the common code
+// used by the majority of data-processing ops. Many MIPS instructions
+// have a register-based form and a different immediate form. See
+// opAND below for an example. (this could be inlined)
+//
+// this works with the imm(), reg_imm() methods above, which are directly
+// called by the GLLAssembler.
+// note: _signed parameter defaults to false (un-signed)
+// note: tmpReg parameter defaults to 1, MIPS register AT
+int ArmToMips64Assembler::dataProcAdrModes(int op, int& source, bool _signed, int tmpReg)
+{
+ if (op < AMODE_REG) {
+ source = op;
+ return SRC_REG;
+ } else if (op == AMODE_IMM) {
+ if ((!_signed && amode.value > 0xffff)
+ || (_signed && ((int)amode.value < -32768 || (int)amode.value > 32767) )) {
+ mMips->LUI(tmpReg, (amode.value >> 16));
+ if (amode.value & 0x0000ffff) {
+ mMips->ORI(tmpReg, tmpReg, (amode.value & 0x0000ffff));
+ }
+ source = tmpReg;
+ return SRC_REG;
+ } else {
+ source = amode.value;
+ return SRC_IMM;
+ }
+ } else if (op == AMODE_REG_IMM) {
+ switch (amode.stype) {
+ case LSL: mMips->SLL(tmpReg, amode.reg, amode.value); break;
+ case LSR: mMips->SRL(tmpReg, amode.reg, amode.value); break;
+ case ASR: mMips->SRA(tmpReg, amode.reg, amode.value); break;
+ case ROR: mMips->ROTR(tmpReg, amode.reg, amode.value); break;
+ }
+ source = tmpReg;
+ return SRC_REG;
+ } else { // adr mode RRX is not used in GGL Assembler at this time
+ // we are screwed, this should be exception, assert-fail or something
+ LOG_ALWAYS_FATAL("adr mode reg_rrx not yet implemented\n");
+ return SRC_ERROR;
+ }
+}
+
+
+void ArmToMips64Assembler::dataProcessing(int opcode, int cc,
+ int s, int Rd, int Rn, uint32_t Op2)
+{
+ int src; // src is modified by dataProcAdrModes() - passed as int&
+
+ if (cc != AL) {
+ protectConditionalOperands(Rd);
+ // the branch tests register(s) set by prev CMP or instr with 'S' bit set
+ // inverse the condition to jump past this conditional instruction
+ ArmToMips64Assembler::B(cc^1, cond.label[++cond.labelnum]);
+ } else {
+ mArmPC[mInum++] = pc(); // save starting PC for this instr
+ }
+
+ switch (opcode) {
+ case opAND:
+ if (dataProcAdrModes(Op2, src) == SRC_REG) {
+ mMips->AND(Rd, Rn, src);
+ } else { // adr mode was SRC_IMM
+ mMips->ANDI(Rd, Rn, src);
+ }
+ break;
+
+ case opADD:
+ // set "signed" to true for adr modes
+ if (dataProcAdrModes(Op2, src, true) == SRC_REG) {
+ mMips->ADDU(Rd, Rn, src);
+ } else { // adr mode was SRC_IMM
+ mMips->ADDIU(Rd, Rn, src);
+ }
+ break;
+
+ case opSUB:
+ // set "signed" to true for adr modes
+ if (dataProcAdrModes(Op2, src, true) == SRC_REG) {
+ mMips->SUBU(Rd, Rn, src);
+ } else { // adr mode was SRC_IMM
+ mMips->SUBIU(Rd, Rn, src);
+ }
+ break;
+
+ case opADD64:
+ // set "signed" to true for adr modes
+ if (dataProcAdrModes(Op2, src, true) == SRC_REG) {
+ mMips->DADDU(Rd, Rn, src);
+ } else { // adr mode was SRC_IMM
+ mMips->DADDIU(Rd, Rn, src);
+ }
+ break;
+
+ case opSUB64:
+ // set "signed" to true for adr modes
+ if (dataProcAdrModes(Op2, src, true) == SRC_REG) {
+ mMips->DSUBU(Rd, Rn, src);
+ } else { // adr mode was SRC_IMM
+ mMips->DSUBIU(Rd, Rn, src);
+ }
+ break;
+
+ case opEOR:
+ if (dataProcAdrModes(Op2, src) == SRC_REG) {
+ mMips->XOR(Rd, Rn, src);
+ } else { // adr mode was SRC_IMM
+ mMips->XORI(Rd, Rn, src);
+ }
+ break;
+
+ case opORR:
+ if (dataProcAdrModes(Op2, src) == SRC_REG) {
+ mMips->OR(Rd, Rn, src);
+ } else { // adr mode was SRC_IMM
+ mMips->ORI(Rd, Rn, src);
+ }
+ break;
+
+ case opBIC:
+ if (dataProcAdrModes(Op2, src) == SRC_IMM) {
+ // if we are 16-bit imnmediate, load to AT reg
+ mMips->ORI(R_at, 0, src);
+ src = R_at;
+ }
+ mMips->NOT(R_at, src);
+ mMips->AND(Rd, Rn, R_at);
+ break;
+
+ case opRSB:
+ if (dataProcAdrModes(Op2, src) == SRC_IMM) {
+ // if we are 16-bit imnmediate, load to AT reg
+ mMips->ORI(R_at, 0, src);
+ src = R_at;
+ }
+ mMips->SUBU(Rd, src, Rn); // subu with the parameters reversed
+ break;
+
+ case opMOV:
+ if (Op2 < AMODE_REG) { // op2 is reg # in this case
+ mMips->MOVE(Rd, Op2);
+ } else if (Op2 == AMODE_IMM) {
+ if (amode.value > 0xffff) {
+ mMips->LUI(Rd, (amode.value >> 16));
+ if (amode.value & 0x0000ffff) {
+ mMips->ORI(Rd, Rd, (amode.value & 0x0000ffff));
+ }
+ } else {
+ mMips->ORI(Rd, 0, amode.value);
+ }
+ } else if (Op2 == AMODE_REG_IMM) {
+ switch (amode.stype) {
+ case LSL: mMips->SLL(Rd, amode.reg, amode.value); break;
+ case LSR: mMips->SRL(Rd, amode.reg, amode.value); break;
+ case ASR: mMips->SRA(Rd, amode.reg, amode.value); break;
+ case ROR: mMips->ROTR(Rd, amode.reg, amode.value); break;
+ }
+ }
+ else {
+ // adr mode RRX is not used in GGL Assembler at this time
+ mMips->UNIMPL();
+ }
+ break;
+
+ case opMVN: // this is a 1's complement: NOT
+ if (Op2 < AMODE_REG) { // op2 is reg # in this case
+ mMips->NOR(Rd, Op2, 0); // NOT is NOR with 0
+ break;
+ } else if (Op2 == AMODE_IMM) {
+ if (amode.value > 0xffff) {
+ mMips->LUI(Rd, (amode.value >> 16));
+ if (amode.value & 0x0000ffff) {
+ mMips->ORI(Rd, Rd, (amode.value & 0x0000ffff));
+ }
+ } else {
+ mMips->ORI(Rd, 0, amode.value);
+ }
+ } else if (Op2 == AMODE_REG_IMM) {
+ switch (amode.stype) {
+ case LSL: mMips->SLL(Rd, amode.reg, amode.value); break;
+ case LSR: mMips->SRL(Rd, amode.reg, amode.value); break;
+ case ASR: mMips->SRA(Rd, amode.reg, amode.value); break;
+ case ROR: mMips->ROTR(Rd, amode.reg, amode.value); break;
+ }
+ }
+ else {
+ // adr mode RRX is not used in GGL Assembler at this time
+ mMips->UNIMPL();
+ }
+ mMips->NOR(Rd, Rd, 0); // NOT is NOR with 0
+ break;
+
+ case opCMP:
+ // Either operand of a CMP instr could get overwritten by a subsequent
+ // conditional instruction, which is ok, _UNLESS_ there is a _second_
+ // conditional instruction. Under MIPS, this requires doing the comparison
+ // again (SLT), and the original operands must be available. (and this
+ // pattern of multiple conditional instructions from same CMP _is_ used
+ // in GGL-Assembler)
+ //
+ // For now, if a conditional instr overwrites the operands, we will
+ // move them to dedicated temp regs. This is ugly, and inefficient,
+ // and should be optimized.
+ //
+ // WARNING: making an _Assumption_ that CMP operand regs will NOT be
+ // trashed by intervening NON-conditional instructions. In the general
+ // case this is legal, but it is NOT currently done in GGL-Assembler.
+
+ cond.type = CMP_COND;
+ cond.r1 = Rn;
+ if (dataProcAdrModes(Op2, src, false, R_cmp2) == SRC_REG) {
+ cond.r2 = src;
+ } else { // adr mode was SRC_IMM
+ mMips->ORI(R_cmp2, R_zero, src);
+ cond.r2 = R_cmp2;
+ }
+
+ break;
+
+
+ case opTST:
+ case opTEQ:
+ case opCMN:
+ case opADC:
+ case opSBC:
+ case opRSC:
+ mMips->UNIMPL(); // currently unused in GGL Assembler code
+ break;
+ }
+
+ if (cc != AL) {
+ mMips->label(cond.label[cond.labelnum]);
+ }
+ if (s && opcode != opCMP) {
+ cond.type = SBIT_COND;
+ cond.r1 = Rd;
+ }
+}
+
+
+
+#if 0
+#pragma mark -
+#pragma mark Multiply...
+#endif
+
+// multiply, accumulate
+void ArmToMips64Assembler::MLA(int cc, int s,
+ int Rd, int Rm, int Rs, int Rn) {
+
+ //ALOGW("MLA");
+ mArmPC[mInum++] = pc(); // save starting PC for this instr
+
+ mMips->MUL(R_at, Rm, Rs);
+ mMips->ADDU(Rd, R_at, Rn);
+ if (s) {
+ cond.type = SBIT_COND;
+ cond.r1 = Rd;
+ }
+}
+
+void ArmToMips64Assembler::MUL(int cc, int s,
+ int Rd, int Rm, int Rs) {
+ mArmPC[mInum++] = pc();
+ mMips->MUL(Rd, Rm, Rs);
+ if (s) {
+ cond.type = SBIT_COND;
+ cond.r1 = Rd;
+ }
+}
+
+void ArmToMips64Assembler::UMULL(int cc, int s,
+ int RdLo, int RdHi, int Rm, int Rs) {
+ mArmPC[mInum++] = pc();
+ mMips->MUH(RdHi, Rm, Rs);
+ mMips->MUL(RdLo, Rm, Rs);
+
+ if (s) {
+ cond.type = SBIT_COND;
+ cond.r1 = RdHi; // BUG...
+ LOG_ALWAYS_FATAL("Condition on UMULL must be on 64-bit result\n");
+ }
+}
+
+void ArmToMips64Assembler::UMUAL(int cc, int s,
+ int RdLo, int RdHi, int Rm, int Rs) {
+ LOG_FATAL_IF(RdLo==Rm || RdHi==Rm || RdLo==RdHi,
+ "UMUAL(r%u,r%u,r%u,r%u)", RdLo,RdHi,Rm,Rs);
+ // *mPC++ = (cc<<28) | (1<<23) | (1<<21) | (s<<20) |
+ // (RdHi<<16) | (RdLo<<12) | (Rs<<8) | 0x90 | Rm;
+ mArmPC[mInum++] = pc();
+ mMips->NOP2();
+ NOT_IMPLEMENTED();
+ if (s) {
+ cond.type = SBIT_COND;
+ cond.r1 = RdHi; // BUG...
+ LOG_ALWAYS_FATAL("Condition on UMULL must be on 64-bit result\n");
+ }
+}
+
+void ArmToMips64Assembler::SMULL(int cc, int s,
+ int RdLo, int RdHi, int Rm, int Rs) {
+ LOG_FATAL_IF(RdLo==Rm || RdHi==Rm || RdLo==RdHi,
+ "SMULL(r%u,r%u,r%u,r%u)", RdLo,RdHi,Rm,Rs);
+ // *mPC++ = (cc<<28) | (1<<23) | (1<<22) | (s<<20) |
+ // (RdHi<<16) | (RdLo<<12) | (Rs<<8) | 0x90 | Rm;
+ mArmPC[mInum++] = pc();
+ mMips->NOP2();
+ NOT_IMPLEMENTED();
+ if (s) {
+ cond.type = SBIT_COND;
+ cond.r1 = RdHi; // BUG...
+ LOG_ALWAYS_FATAL("Condition on SMULL must be on 64-bit result\n");
+ }
+}
+void ArmToMips64Assembler::SMUAL(int cc, int s,
+ int RdLo, int RdHi, int Rm, int Rs) {
+ LOG_FATAL_IF(RdLo==Rm || RdHi==Rm || RdLo==RdHi,
+ "SMUAL(r%u,r%u,r%u,r%u)", RdLo,RdHi,Rm,Rs);
+ // *mPC++ = (cc<<28) | (1<<23) | (1<<22) | (1<<21) | (s<<20) |
+ // (RdHi<<16) | (RdLo<<12) | (Rs<<8) | 0x90 | Rm;
+ mArmPC[mInum++] = pc();
+ mMips->NOP2();
+ NOT_IMPLEMENTED();
+ if (s) {
+ cond.type = SBIT_COND;
+ cond.r1 = RdHi; // BUG...
+ LOG_ALWAYS_FATAL("Condition on SMUAL must be on 64-bit result\n");
+ }
+}
+
+
+
+#if 0
+#pragma mark -
+#pragma mark Branches...
+#endif
+
+// branches...
+
+void ArmToMips64Assembler::B(int cc, const char* label)
+{
+ mArmPC[mInum++] = pc();
+ if (cond.type == SBIT_COND) { cond.r2 = R_zero; }
+
+ switch(cc) {
+ case EQ: mMips->BEQ(cond.r1, cond.r2, label); break;
+ case NE: mMips->BNE(cond.r1, cond.r2, label); break;
+ case HS: mMips->BGEU(cond.r1, cond.r2, label); break;
+ case LO: mMips->BLTU(cond.r1, cond.r2, label); break;
+ case MI: mMips->BLT(cond.r1, cond.r2, label); break;
+ case PL: mMips->BGE(cond.r1, cond.r2, label); break;
+
+ case HI: mMips->BGTU(cond.r1, cond.r2, label); break;
+ case LS: mMips->BLEU(cond.r1, cond.r2, label); break;
+ case GE: mMips->BGE(cond.r1, cond.r2, label); break;
+ case LT: mMips->BLT(cond.r1, cond.r2, label); break;
+ case GT: mMips->BGT(cond.r1, cond.r2, label); break;
+ case LE: mMips->BLE(cond.r1, cond.r2, label); break;
+ case AL: mMips->B(label); break;
+ case NV: /* B Never - no instruction */ break;
+
+ case VS:
+ case VC:
+ default:
+ LOG_ALWAYS_FATAL("Unsupported cc: %02x\n", cc);
+ break;
+ }
+}
+
+void ArmToMips64Assembler::BL(int cc, const char* label)
+{
+ LOG_ALWAYS_FATAL("branch-and-link not supported yet\n");
+ mArmPC[mInum++] = pc();
+}
+
+// no use for Branches with integer PC, but they're in the Interface class ....
+void ArmToMips64Assembler::B(int cc, uint32_t* to_pc)
+{
+ LOG_ALWAYS_FATAL("branch to absolute PC not supported, use Label\n");
+ mArmPC[mInum++] = pc();
+}
+
+void ArmToMips64Assembler::BL(int cc, uint32_t* to_pc)
+{
+ LOG_ALWAYS_FATAL("branch to absolute PC not supported, use Label\n");
+ mArmPC[mInum++] = pc();
+}
+
+void ArmToMips64Assembler::BX(int cc, int Rn)
+{
+ LOG_ALWAYS_FATAL("branch to absolute PC not supported, use Label\n");
+ mArmPC[mInum++] = pc();
+}
+
+
+
+#if 0
+#pragma mark -
+#pragma mark Data Transfer...
+#endif
+
+// data transfer...
+void ArmToMips64Assembler::LDR(int cc, int Rd, int Rn, uint32_t offset)
+{
+ mArmPC[mInum++] = pc();
+ // work-around for ARM default address mode of immed12_pre(0)
+ if (offset > AMODE_UNSUPPORTED) offset = 0;
+ switch (offset) {
+ case 0:
+ amode.value = 0;
+ amode.writeback = 0;
+ // fall thru to next case ....
+ case AMODE_IMM_12_PRE:
+ if (Rn == ARMAssemblerInterface::SP) {
+ Rn = R_sp; // convert LDR via Arm SP to LW via Mips SP
+ }
+ mMips->LW(Rd, Rn, amode.value);
+ if (amode.writeback) { // OPTIONAL writeback on pre-index mode
+ mMips->DADDIU(Rn, Rn, amode.value);
+ }
+ break;
+ case AMODE_IMM_12_POST:
+ if (Rn == ARMAssemblerInterface::SP) {
+ Rn = R_sp; // convert STR thru Arm SP to STR thru Mips SP
+ }
+ mMips->LW(Rd, Rn, 0);
+ mMips->DADDIU(Rn, Rn, amode.value);
+ break;
+ case AMODE_REG_SCALE_PRE:
+ // we only support simple base + index, no advanced modes for this one yet
+ mMips->DADDU(R_at, Rn, amode.reg);
+ mMips->LW(Rd, R_at, 0);
+ break;
+ }
+}
+
+void ArmToMips64Assembler::LDRB(int cc, int Rd, int Rn, uint32_t offset)
+{
+ mArmPC[mInum++] = pc();
+ // work-around for ARM default address mode of immed12_pre(0)
+ if (offset > AMODE_UNSUPPORTED) offset = 0;
+ switch (offset) {
+ case 0:
+ amode.value = 0;
+ amode.writeback = 0;
+ // fall thru to next case ....
+ case AMODE_IMM_12_PRE:
+ mMips->LBU(Rd, Rn, amode.value);
+ if (amode.writeback) { // OPTIONAL writeback on pre-index mode
+ mMips->DADDIU(Rn, Rn, amode.value);
+ }
+ break;
+ case AMODE_IMM_12_POST:
+ mMips->LBU(Rd, Rn, 0);
+ mMips->DADDIU(Rn, Rn, amode.value);
+ break;
+ case AMODE_REG_SCALE_PRE:
+ // we only support simple base + index, no advanced modes for this one yet
+ mMips->DADDU(R_at, Rn, amode.reg);
+ mMips->LBU(Rd, R_at, 0);
+ break;
+ }
+
+}
+
+void ArmToMips64Assembler::STR(int cc, int Rd, int Rn, uint32_t offset)
+{
+ mArmPC[mInum++] = pc();
+ // work-around for ARM default address mode of immed12_pre(0)
+ if (offset > AMODE_UNSUPPORTED) offset = 0;
+ switch (offset) {
+ case 0:
+ amode.value = 0;
+ amode.writeback = 0;
+ // fall thru to next case ....
+ case AMODE_IMM_12_PRE:
+ if (Rn == ARMAssemblerInterface::SP) {
+ Rn = R_sp; // convert STR thru Arm SP to SW thru Mips SP
+ }
+ if (amode.writeback) { // OPTIONAL writeback on pre-index mode
+ // If we will writeback, then update the index reg, then store.
+ // This correctly handles stack-push case.
+ mMips->DADDIU(Rn, Rn, amode.value);
+ mMips->SW(Rd, Rn, 0);
+ } else {
+ // No writeback so store offset by value
+ mMips->SW(Rd, Rn, amode.value);
+ }
+ break;
+ case AMODE_IMM_12_POST:
+ mMips->SW(Rd, Rn, 0);
+ mMips->DADDIU(Rn, Rn, amode.value); // post index always writes back
+ break;
+ case AMODE_REG_SCALE_PRE:
+ // we only support simple base + index, no advanced modes for this one yet
+ mMips->DADDU(R_at, Rn, amode.reg);
+ mMips->SW(Rd, R_at, 0);
+ break;
+ }
+}
+
+void ArmToMips64Assembler::STRB(int cc, int Rd, int Rn, uint32_t offset)
+{
+ mArmPC[mInum++] = pc();
+ // work-around for ARM default address mode of immed12_pre(0)
+ if (offset > AMODE_UNSUPPORTED) offset = 0;
+ switch (offset) {
+ case 0:
+ amode.value = 0;
+ amode.writeback = 0;
+ // fall thru to next case ....
+ case AMODE_IMM_12_PRE:
+ mMips->SB(Rd, Rn, amode.value);
+ if (amode.writeback) { // OPTIONAL writeback on pre-index mode
+ mMips->DADDIU(Rn, Rn, amode.value);
+ }
+ break;
+ case AMODE_IMM_12_POST:
+ mMips->SB(Rd, Rn, 0);
+ mMips->DADDIU(Rn, Rn, amode.value);
+ break;
+ case AMODE_REG_SCALE_PRE:
+ // we only support simple base + index, no advanced modes for this one yet
+ mMips->DADDU(R_at, Rn, amode.reg);
+ mMips->SB(Rd, R_at, 0);
+ break;
+ }
+}
+
+void ArmToMips64Assembler::LDRH(int cc, int Rd, int Rn, uint32_t offset)
+{
+ mArmPC[mInum++] = pc();
+ // work-around for ARM default address mode of immed8_pre(0)
+ if (offset > AMODE_UNSUPPORTED) offset = 0;
+ switch (offset) {
+ case 0:
+ amode.value = 0;
+ // fall thru to next case ....
+ case AMODE_IMM_8_PRE: // no support yet for writeback
+ mMips->LHU(Rd, Rn, amode.value);
+ break;
+ case AMODE_IMM_8_POST:
+ mMips->LHU(Rd, Rn, 0);
+ mMips->DADDIU(Rn, Rn, amode.value);
+ break;
+ case AMODE_REG_PRE:
+ // we only support simple base +/- index
+ if (amode.reg >= 0) {
+ mMips->DADDU(R_at, Rn, amode.reg);
+ } else {
+ mMips->DSUBU(R_at, Rn, abs(amode.reg));
+ }
+ mMips->LHU(Rd, R_at, 0);
+ break;
+ }
+}
+
+void ArmToMips64Assembler::LDRSB(int cc, int Rd, int Rn, uint32_t offset)
+{
+ mArmPC[mInum++] = pc();
+ mMips->NOP2();
+ NOT_IMPLEMENTED();
+}
+
+void ArmToMips64Assembler::LDRSH(int cc, int Rd, int Rn, uint32_t offset)
+{
+ mArmPC[mInum++] = pc();
+ mMips->NOP2();
+ NOT_IMPLEMENTED();
+}
+
+void ArmToMips64Assembler::STRH(int cc, int Rd, int Rn, uint32_t offset)
+{
+ mArmPC[mInum++] = pc();
+ // work-around for ARM default address mode of immed8_pre(0)
+ if (offset > AMODE_UNSUPPORTED) offset = 0;
+ switch (offset) {
+ case 0:
+ amode.value = 0;
+ // fall thru to next case ....
+ case AMODE_IMM_8_PRE: // no support yet for writeback
+ mMips->SH(Rd, Rn, amode.value);
+ break;
+ case AMODE_IMM_8_POST:
+ mMips->SH(Rd, Rn, 0);
+ mMips->DADDIU(Rn, Rn, amode.value);
+ break;
+ case AMODE_REG_PRE:
+ // we only support simple base +/- index
+ if (amode.reg >= 0) {
+ mMips->DADDU(R_at, Rn, amode.reg);
+ } else {
+ mMips->DSUBU(R_at, Rn, abs(amode.reg));
+ }
+ mMips->SH(Rd, R_at, 0);
+ break;
+ }
+}
+
+
+
+#if 0
+#pragma mark -
+#pragma mark Block Data Transfer...
+#endif
+
+// block data transfer...
+void ArmToMips64Assembler::LDM(int cc, int dir,
+ int Rn, int W, uint32_t reg_list)
+{ // ED FD EA FA IB IA DB DA
+ // const uint8_t P[8] = { 1, 0, 1, 0, 1, 0, 1, 0 };
+ // const uint8_t U[8] = { 1, 1, 0, 0, 1, 1, 0, 0 };
+ // *mPC++ = (cc<<28) | (4<<25) | (uint32_t(P[dir])<<24) |
+ // (uint32_t(U[dir])<<23) | (1<<20) | (W<<21) | (Rn<<16) | reg_list;
+ mArmPC[mInum++] = pc();
+ mMips->NOP2();
+ NOT_IMPLEMENTED();
+}
+
+void ArmToMips64Assembler::STM(int cc, int dir,
+ int Rn, int W, uint32_t reg_list)
+{ // FA EA FD ED IB IA DB DA
+ // const uint8_t P[8] = { 0, 1, 0, 1, 1, 0, 1, 0 };
+ // const uint8_t U[8] = { 0, 0, 1, 1, 1, 1, 0, 0 };
+ // *mPC++ = (cc<<28) | (4<<25) | (uint32_t(P[dir])<<24) |
+ // (uint32_t(U[dir])<<23) | (0<<20) | (W<<21) | (Rn<<16) | reg_list;
+ mArmPC[mInum++] = pc();
+ mMips->NOP2();
+ NOT_IMPLEMENTED();
+}
+
+
+
+#if 0
+#pragma mark -
+#pragma mark Special...
+#endif
+
+// special...
+void ArmToMips64Assembler::SWP(int cc, int Rn, int Rd, int Rm) {
+ // *mPC++ = (cc<<28) | (2<<23) | (Rn<<16) | (Rd << 12) | 0x90 | Rm;
+ mArmPC[mInum++] = pc();
+ mMips->NOP2();
+ NOT_IMPLEMENTED();
+}
+
+void ArmToMips64Assembler::SWPB(int cc, int Rn, int Rd, int Rm) {
+ // *mPC++ = (cc<<28) | (2<<23) | (1<<22) | (Rn<<16) | (Rd << 12) | 0x90 | Rm;
+ mArmPC[mInum++] = pc();
+ mMips->NOP2();
+ NOT_IMPLEMENTED();
+}
+
+void ArmToMips64Assembler::SWI(int cc, uint32_t comment) {
+ // *mPC++ = (cc<<28) | (0xF<<24) | comment;
+ mArmPC[mInum++] = pc();
+ mMips->NOP2();
+ NOT_IMPLEMENTED();
+}
+
+
+#if 0
+#pragma mark -
+#pragma mark DSP instructions...
+#endif
+
+// DSP instructions...
+void ArmToMips64Assembler::PLD(int Rn, uint32_t offset) {
+ LOG_ALWAYS_FATAL_IF(!((offset&(1<<24)) && !(offset&(1<<21))),
+ "PLD only P=1, W=0");
+ // *mPC++ = 0xF550F000 | (Rn<<16) | offset;
+ mArmPC[mInum++] = pc();
+ mMips->NOP2();
+ NOT_IMPLEMENTED();
+}
+
+void ArmToMips64Assembler::CLZ(int cc, int Rd, int Rm)
+{
+ mArmPC[mInum++] = pc();
+ mMips->CLZ(Rd, Rm);
+}
+
+void ArmToMips64Assembler::QADD(int cc, int Rd, int Rm, int Rn)
+{
+ // *mPC++ = (cc<<28) | 0x1000050 | (Rn<<16) | (Rd<<12) | Rm;
+ mArmPC[mInum++] = pc();
+ mMips->NOP2();
+ NOT_IMPLEMENTED();
+}
+
+void ArmToMips64Assembler::QDADD(int cc, int Rd, int Rm, int Rn)
+{
+ // *mPC++ = (cc<<28) | 0x1400050 | (Rn<<16) | (Rd<<12) | Rm;
+ mArmPC[mInum++] = pc();
+ mMips->NOP2();
+ NOT_IMPLEMENTED();
+}
+
+void ArmToMips64Assembler::QSUB(int cc, int Rd, int Rm, int Rn)
+{
+ // *mPC++ = (cc<<28) | 0x1200050 | (Rn<<16) | (Rd<<12) | Rm;
+ mArmPC[mInum++] = pc();
+ mMips->NOP2();
+ NOT_IMPLEMENTED();
+}
+
+void ArmToMips64Assembler::QDSUB(int cc, int Rd, int Rm, int Rn)
+{
+ // *mPC++ = (cc<<28) | 0x1600050 | (Rn<<16) | (Rd<<12) | Rm;
+ mArmPC[mInum++] = pc();
+ mMips->NOP2();
+ NOT_IMPLEMENTED();
+}
+
+// 16 x 16 signed multiply (like SMLAxx without the accumulate)
+void ArmToMips64Assembler::SMUL(int cc, int xy,
+ int Rd, int Rm, int Rs)
+{
+ mArmPC[mInum++] = pc();
+
+ // the 16 bits may be in the top or bottom half of 32-bit source reg,
+ // as defined by the codes BB, BT, TB, TT (compressed param xy)
+ // where x corresponds to Rm and y to Rs
+
+ // select half-reg for Rm
+ if (xy & xyTB) {
+ // use top 16-bits
+ mMips->SRA(R_at, Rm, 16);
+ } else {
+ // use bottom 16, but sign-extend to 32
+ mMips->SEH(R_at, Rm);
+ }
+ // select half-reg for Rs
+ if (xy & xyBT) {
+ // use top 16-bits
+ mMips->SRA(R_at2, Rs, 16);
+ } else {
+ // use bottom 16, but sign-extend to 32
+ mMips->SEH(R_at2, Rs);
+ }
+ mMips->MUL(Rd, R_at, R_at2);
+}
+
+// signed 32b x 16b multiple, save top 32-bits of 48-bit result
+void ArmToMips64Assembler::SMULW(int cc, int y,
+ int Rd, int Rm, int Rs)
+{
+ mArmPC[mInum++] = pc();
+
+ // the selector yT or yB refers to reg Rs
+ if (y & yT) {
+ // zero the bottom 16-bits, with 2 shifts, it can affect result
+ mMips->SRL(R_at, Rs, 16);
+ mMips->SLL(R_at, R_at, 16);
+
+ } else {
+ // move low 16-bit half, to high half
+ mMips->SLL(R_at, Rs, 16);
+ }
+ mMips->MUH(Rd, Rm, R_at);
+}
+
+// 16 x 16 signed multiply, accumulate: Rd = Rm{16} * Rs{16} + Rn
+void ArmToMips64Assembler::SMLA(int cc, int xy,
+ int Rd, int Rm, int Rs, int Rn)
+{
+ mArmPC[mInum++] = pc();
+
+ // the 16 bits may be in the top or bottom half of 32-bit source reg,
+ // as defined by the codes BB, BT, TB, TT (compressed param xy)
+ // where x corresponds to Rm and y to Rs
+
+ // select half-reg for Rm
+ if (xy & xyTB) {
+ // use top 16-bits
+ mMips->SRA(R_at, Rm, 16);
+ } else {
+ // use bottom 16, but sign-extend to 32
+ mMips->SEH(R_at, Rm);
+ }
+ // select half-reg for Rs
+ if (xy & xyBT) {
+ // use top 16-bits
+ mMips->SRA(R_at2, Rs, 16);
+ } else {
+ // use bottom 16, but sign-extend to 32
+ mMips->SEH(R_at2, Rs);
+ }
+
+ mMips->MUL(R_at, R_at, R_at2);
+ mMips->ADDU(Rd, R_at, Rn);
+}
+
+void ArmToMips64Assembler::SMLAL(int cc, int xy,
+ int RdHi, int RdLo, int Rs, int Rm)
+{
+ // *mPC++ = (cc<<28) | 0x1400080 | (RdHi<<16) | (RdLo<<12) | (Rs<<8) | (xy<<4) | Rm;
+ mArmPC[mInum++] = pc();
+ mMips->NOP2();
+ NOT_IMPLEMENTED();
+}
+
+void ArmToMips64Assembler::SMLAW(int cc, int y,
+ int Rd, int Rm, int Rs, int Rn)
+{
+ // *mPC++ = (cc<<28) | 0x1200080 | (Rd<<16) | (Rn<<12) | (Rs<<8) | (y<<4) | Rm;
+ mArmPC[mInum++] = pc();
+ mMips->NOP2();
+ NOT_IMPLEMENTED();
+}
+
+// used by ARMv6 version of GGLAssembler::filter32
+void ArmToMips64Assembler::UXTB16(int cc, int Rd, int Rm, int rotate)
+{
+ mArmPC[mInum++] = pc();
+
+ //Rd[31:16] := ZeroExtend((Rm ROR (8 * sh))[23:16]),
+ //Rd[15:0] := ZeroExtend((Rm ROR (8 * sh))[7:0]). sh 0-3.
+
+ mMips->ROTR(R_at2, Rm, rotate * 8);
+ mMips->LUI(R_at, 0xFF);
+ mMips->ORI(R_at, R_at, 0xFF);
+ mMips->AND(Rd, R_at2, R_at);
+}
+
+void ArmToMips64Assembler::UBFX(int cc, int Rd, int Rn, int lsb, int width)
+{
+ /* Placeholder for UBFX */
+ mArmPC[mInum++] = pc();
+
+ mMips->NOP2();
+ NOT_IMPLEMENTED();
+}
+
+// ----------------------------------------------------------------------------
+// Address Processing...
+// ----------------------------------------------------------------------------
+
+void ArmToMips64Assembler::ADDR_ADD(int cc,
+ int s, int Rd, int Rn, uint32_t Op2)
+{
+// if(cc != AL){ NOT_IMPLEMENTED(); return;} //Not required
+// if(s != 0) { NOT_IMPLEMENTED(); return;} //Not required
+ dataProcessing(opADD64, cc, s, Rd, Rn, Op2);
+}
+
+void ArmToMips64Assembler::ADDR_SUB(int cc,
+ int s, int Rd, int Rn, uint32_t Op2)
+{
+// if(cc != AL){ NOT_IMPLEMENTED(); return;} //Not required
+// if(s != 0) { NOT_IMPLEMENTED(); return;} //Not required
+ dataProcessing(opSUB64, cc, s, Rd, Rn, Op2);
+}
+
+void ArmToMips64Assembler::ADDR_LDR(int cc, int Rd, int Rn, uint32_t offset) {
+ mArmPC[mInum++] = pc();
+ // work-around for ARM default address mode of immed12_pre(0)
+ if (offset > AMODE_UNSUPPORTED) offset = 0;
+ switch (offset) {
+ case 0:
+ amode.value = 0;
+ amode.writeback = 0;
+ // fall thru to next case ....
+ case AMODE_IMM_12_PRE:
+ if (Rn == ARMAssemblerInterface::SP) {
+ Rn = R_sp; // convert LDR via Arm SP to LW via Mips SP
+ }
+ mMips->LD(Rd, Rn, amode.value);
+ if (amode.writeback) { // OPTIONAL writeback on pre-index mode
+ mMips->DADDIU(Rn, Rn, amode.value);
+ }
+ break;
+ case AMODE_IMM_12_POST:
+ if (Rn == ARMAssemblerInterface::SP) {
+ Rn = R_sp; // convert STR thru Arm SP to STR thru Mips SP
+ }
+ mMips->LD(Rd, Rn, 0);
+ mMips->DADDIU(Rn, Rn, amode.value);
+ break;
+ case AMODE_REG_SCALE_PRE:
+ // we only support simple base + index, no advanced modes for this one yet
+ mMips->DADDU(R_at, Rn, amode.reg);
+ mMips->LD(Rd, R_at, 0);
+ break;
+ }
+}
+
+void ArmToMips64Assembler::ADDR_STR(int cc, int Rd, int Rn, uint32_t offset) {
+ mArmPC[mInum++] = pc();
+ // work-around for ARM default address mode of immed12_pre(0)
+ if (offset > AMODE_UNSUPPORTED) offset = 0;
+ switch (offset) {
+ case 0:
+ amode.value = 0;
+ amode.writeback = 0;
+ // fall thru to next case ....
+ case AMODE_IMM_12_PRE:
+ if (Rn == ARMAssemblerInterface::SP) {
+ Rn = R_sp; // convert STR thru Arm SP to SW thru Mips SP
+ }
+ if (amode.writeback) { // OPTIONAL writeback on pre-index mode
+ // If we will writeback, then update the index reg, then store.
+ // This correctly handles stack-push case.
+ mMips->DADDIU(Rn, Rn, amode.value);
+ mMips->SD(Rd, Rn, 0);
+ } else {
+ // No writeback so store offset by value
+ mMips->SD(Rd, Rn, amode.value);
+ }
+ break;
+ case AMODE_IMM_12_POST:
+ mMips->SD(Rd, Rn, 0);
+ mMips->DADDIU(Rn, Rn, amode.value); // post index always writes back
+ break;
+ case AMODE_REG_SCALE_PRE:
+ // we only support simple base + index, no advanced modes for this one yet
+ mMips->DADDU(R_at, Rn, amode.reg);
+ mMips->SD(Rd, R_at, 0);
+ break;
+ }
+}
+
+#if 0
+#pragma mark -
+#pragma mark MIPS Assembler...
+#endif
+
+
+//**************************************************************************
+//**************************************************************************
+//**************************************************************************
+
+
+/* MIPS64 assembler
+** this is a subset of mips64r6, targeted specifically at ARM instruction
+** replacement in the pixelflinger/codeflinger code.
+**
+** This class is extended from MIPSAssembler class and overrides only
+** MIPS64r6 specific stuff.
+*/
+
+MIPS64Assembler::MIPS64Assembler(const sp<Assembly>& assembly, ArmToMips64Assembler *parent)
+ : mParent(parent),
+ MIPSAssembler::MIPSAssembler(assembly, NULL)
+{
+}
+
+MIPS64Assembler::MIPS64Assembler(void* assembly, ArmToMips64Assembler *parent)
+ : mParent(parent),
+ MIPSAssembler::MIPSAssembler(assembly)
+{
+}
+
+MIPS64Assembler::~MIPS64Assembler()
+{
+}
+
+void MIPS64Assembler::reset()
+{
+ if (mAssembly != NULL) {
+ mBase = mPC = (uint32_t *)mAssembly->base();
+ } else {
+ mPC = mBase = base();
+ }
+ mBranchTargets.clear();
+ mLabels.clear();
+ mLabelsInverseMapping.clear();
+ mComments.clear();
+}
+
+
+void MIPS64Assembler::disassemble(const char* name)
+{
+ char di_buf[140];
+
+ bool arm_disasm_fmt = (mParent->mArmDisassemblyBuffer == NULL) ? false : true;
+
+ typedef char dstr[40];
+ dstr *lines = (dstr *)mParent->mArmDisassemblyBuffer;
+
+ if (mParent->mArmDisassemblyBuffer != NULL) {
+ for (int i=0; i<mParent->mArmInstrCount; ++i) {
+ string_detab(lines[i]);
+ }
+ }
+
+ // iArm is an index to Arm instructions 1...n for this assembly sequence
+ // mArmPC[iArm] holds the value of the Mips-PC for the first MIPS
+ // instruction corresponding to that Arm instruction number
+
+ int iArm = 0;
+ size_t count = pc()-base();
+ uint32_t* mipsPC = base();
+
+ while (count--) {
+ ssize_t label = mLabelsInverseMapping.indexOfKey(mipsPC);
+ if (label >= 0) {
+ ALOGW("%s:\n", mLabelsInverseMapping.valueAt(label));
+ }
+ ssize_t comment = mComments.indexOfKey(mipsPC);
+ if (comment >= 0) {
+ ALOGW("; %s\n", mComments.valueAt(comment));
+ }
+ ::mips_disassem(mipsPC, di_buf, arm_disasm_fmt);
+ string_detab(di_buf);
+ string_pad(di_buf, 30);
+ ALOGW("%08lx: %08x %s", uintptr_t(mipsPC), uint32_t(*mipsPC), di_buf);
+ mipsPC++;
+ }
+}
+
+void MIPS64Assembler::fix_branches()
+{
+ // fixup all the branches
+ size_t count = mBranchTargets.size();
+ while (count--) {
+ const branch_target_t& bt = mBranchTargets[count];
+ uint32_t* target_pc = mLabels.valueFor(bt.label);
+ LOG_ALWAYS_FATAL_IF(!target_pc,
+ "error resolving branch targets, target_pc is null");
+ int32_t offset = int32_t(target_pc - (bt.pc+1));
+ *bt.pc |= offset & 0x00FFFF;
+ }
+}
+
+void MIPS64Assembler::DADDU(int Rd, int Rs, int Rt)
+{
+ *mPC++ = (spec_op<<OP_SHF) | (daddu_fn<<FUNC_SHF)
+ | (Rs<<RS_SHF) | (Rt<<RT_SHF) | (Rd<<RD_SHF);
+}
+
+void MIPS64Assembler::DADDIU(int Rt, int Rs, int16_t imm)
+{
+ *mPC++ = (daddiu_op<<OP_SHF) | (Rt<<RT_SHF) | (Rs<<RS_SHF) | (imm & MSK_16);
+}
+
+void MIPS64Assembler::DSUBU(int Rd, int Rs, int Rt)
+{
+ *mPC++ = (spec_op<<OP_SHF) | (dsubu_fn<<FUNC_SHF) |
+ (Rs<<RS_SHF) | (Rt<<RT_SHF) | (Rd<<RD_SHF) ;
+}
+
+void MIPS64Assembler::DSUBIU(int Rt, int Rs, int16_t imm) // really addiu(d, s, -j)
+{
+ *mPC++ = (daddiu_op<<OP_SHF) | (Rt<<RT_SHF) | (Rs<<RS_SHF) | ((-imm) & MSK_16);
+}
+
+void MIPS64Assembler::MUL(int Rd, int Rs, int Rt)
+{
+ *mPC++ = (spec_op<<OP_SHF) | (mul_fn<<RE_SHF) | (sop30_fn<<FUNC_SHF) |
+ (Rs<<RS_SHF) | (Rt<<RT_SHF) | (Rd<<RD_SHF) ;
+}
+
+void MIPS64Assembler::MUH(int Rd, int Rs, int Rt)
+{
+ *mPC++ = (spec_op<<OP_SHF) | (muh_fn<<RE_SHF) | (sop30_fn<<FUNC_SHF) |
+ (Rs<<RS_SHF) | (Rt<<RT_SHF) | (Rd<<RD_SHF) ;
+}
+
+void MIPS64Assembler::CLO(int Rd, int Rs)
+{
+ *mPC++ = (spec_op<<OP_SHF) | (17<<FUNC_SHF) |
+ (Rd<<RD_SHF) | (Rs<<RS_SHF) | (1<<RE_SHF);
+}
+
+void MIPS64Assembler::CLZ(int Rd, int Rs)
+{
+ *mPC++ = (spec_op<<OP_SHF) | (16<<FUNC_SHF) |
+ (Rd<<RD_SHF) | (Rs<<RS_SHF) | (1<<RE_SHF);
+}
+
+void MIPS64Assembler::LD(int Rt, int Rbase, int16_t offset)
+{
+ *mPC++ = (ld_op<<OP_SHF) | (Rbase<<RS_SHF) | (Rt<<RT_SHF) | (offset & MSK_16);
+}
+
+void MIPS64Assembler::SD(int Rt, int Rbase, int16_t offset)
+{
+ *mPC++ = (sd_op<<OP_SHF) | (Rbase<<RS_SHF) | (Rt<<RT_SHF) | (offset & MSK_16);
+}
+
+void MIPS64Assembler::LUI(int Rt, int16_t offset)
+{
+ *mPC++ = (aui_op<<OP_SHF) | (Rt<<RT_SHF) | (offset & MSK_16);
+}
+
+
+void MIPS64Assembler::JR(int Rs)
+{
+ *mPC++ = (spec_op<<OP_SHF) | (Rs<<RS_SHF) | (jalr_fn << FUNC_SHF);
+ MIPS64Assembler::NOP();
+}
+
+}; // namespace android:
diff --git a/libpixelflinger/codeflinger/MIPS64Assembler.h b/libpixelflinger/codeflinger/MIPS64Assembler.h
new file mode 100644
index 0000000..b43e5da
--- /dev/null
+++ b/libpixelflinger/codeflinger/MIPS64Assembler.h
@@ -0,0 +1,404 @@
+/* libs/pixelflinger/codeflinger/MIPS64Assembler.h
+**
+** Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef ANDROID_MIPS64ASSEMBLER_H
+#define ANDROID_MIPS64ASSEMBLER_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include "utils/KeyedVector.h"
+#include "utils/Vector.h"
+#include "tinyutils/smartpointer.h"
+
+#include "ARMAssemblerInterface.h"
+#include "MIPSAssembler.h"
+#include "CodeCache.h"
+
+namespace android {
+
+class MIPS64Assembler; // forward reference
+
+// this class mimics ARMAssembler interface
+// intent is to translate each ARM instruction to 1 or more MIPS instr
+// implementation calls MIPS64Assembler class to generate mips code
+class ArmToMips64Assembler : public ARMAssemblerInterface
+{
+public:
+ ArmToMips64Assembler(const sp<Assembly>& assembly,
+ char *abuf = 0, int linesz = 0, int instr_count = 0);
+ ArmToMips64Assembler(void* assembly);
+ virtual ~ArmToMips64Assembler();
+
+ uint32_t* base() const;
+ uint32_t* pc() const;
+ void disassemble(const char* name);
+
+ virtual void reset();
+
+ virtual int generate(const char* name);
+ virtual int getCodegenArch();
+
+ virtual void prolog();
+ virtual void epilog(uint32_t touched);
+ virtual void comment(const char* string);
+ // for testing purposes
+ void fix_branches();
+ void set_condition(int mode, int R1, int R2);
+
+
+ // -----------------------------------------------------------------------
+ // shifters and addressing modes
+ // -----------------------------------------------------------------------
+
+ // shifters...
+ virtual bool isValidImmediate(uint32_t immed);
+ virtual int buildImmediate(uint32_t i, uint32_t& rot, uint32_t& imm);
+
+ virtual uint32_t imm(uint32_t immediate);
+ virtual uint32_t reg_imm(int Rm, int type, uint32_t shift);
+ virtual uint32_t reg_rrx(int Rm);
+ virtual uint32_t reg_reg(int Rm, int type, int Rs);
+
+ // addressing modes...
+ // LDR(B)/STR(B)/PLD
+ // (immediate and Rm can be negative, which indicates U=0)
+ virtual uint32_t immed12_pre(int32_t immed12, int W=0);
+ virtual uint32_t immed12_post(int32_t immed12);
+ virtual uint32_t reg_scale_pre(int Rm, int type=0, uint32_t shift=0, int W=0);
+ virtual uint32_t reg_scale_post(int Rm, int type=0, uint32_t shift=0);
+
+ // LDRH/LDRSB/LDRSH/STRH
+ // (immediate and Rm can be negative, which indicates U=0)
+ virtual uint32_t immed8_pre(int32_t immed8, int W=0);
+ virtual uint32_t immed8_post(int32_t immed8);
+ virtual uint32_t reg_pre(int Rm, int W=0);
+ virtual uint32_t reg_post(int Rm);
+
+
+
+
+ virtual void dataProcessing(int opcode, int cc, int s,
+ int Rd, int Rn,
+ uint32_t Op2);
+ virtual void MLA(int cc, int s,
+ int Rd, int Rm, int Rs, int Rn);
+ virtual void MUL(int cc, int s,
+ int Rd, int Rm, int Rs);
+ virtual void UMULL(int cc, int s,
+ int RdLo, int RdHi, int Rm, int Rs);
+ virtual void UMUAL(int cc, int s,
+ int RdLo, int RdHi, int Rm, int Rs);
+ virtual void SMULL(int cc, int s,
+ int RdLo, int RdHi, int Rm, int Rs);
+ virtual void SMUAL(int cc, int s,
+ int RdLo, int RdHi, int Rm, int Rs);
+
+ virtual void B(int cc, uint32_t* pc);
+ virtual void BL(int cc, uint32_t* pc);
+ virtual void BX(int cc, int Rn);
+ virtual void label(const char* theLabel);
+ virtual void B(int cc, const char* label);
+ virtual void BL(int cc, const char* label);
+
+ virtual uint32_t* pcForLabel(const char* label);
+
+ virtual void LDR (int cc, int Rd,
+ int Rn, uint32_t offset = 0);
+ virtual void LDRB(int cc, int Rd,
+ int Rn, uint32_t offset = 0);
+ virtual void STR (int cc, int Rd,
+ int Rn, uint32_t offset = 0);
+ virtual void STRB(int cc, int Rd,
+ int Rn, uint32_t offset = 0);
+ virtual void LDRH (int cc, int Rd,
+ int Rn, uint32_t offset = 0);
+ virtual void LDRSB(int cc, int Rd,
+ int Rn, uint32_t offset = 0);
+ virtual void LDRSH(int cc, int Rd,
+ int Rn, uint32_t offset = 0);
+ virtual void STRH (int cc, int Rd,
+ int Rn, uint32_t offset = 0);
+
+ virtual void LDM(int cc, int dir,
+ int Rn, int W, uint32_t reg_list);
+ virtual void STM(int cc, int dir,
+ int Rn, int W, uint32_t reg_list);
+
+ virtual void SWP(int cc, int Rn, int Rd, int Rm);
+ virtual void SWPB(int cc, int Rn, int Rd, int Rm);
+ virtual void SWI(int cc, uint32_t comment);
+
+ virtual void PLD(int Rn, uint32_t offset);
+ virtual void CLZ(int cc, int Rd, int Rm);
+ virtual void QADD(int cc, int Rd, int Rm, int Rn);
+ virtual void QDADD(int cc, int Rd, int Rm, int Rn);
+ virtual void QSUB(int cc, int Rd, int Rm, int Rn);
+ virtual void QDSUB(int cc, int Rd, int Rm, int Rn);
+ virtual void SMUL(int cc, int xy,
+ int Rd, int Rm, int Rs);
+ virtual void SMULW(int cc, int y,
+ int Rd, int Rm, int Rs);
+ virtual void SMLA(int cc, int xy,
+ int Rd, int Rm, int Rs, int Rn);
+ virtual void SMLAL(int cc, int xy,
+ int RdHi, int RdLo, int Rs, int Rm);
+ virtual void SMLAW(int cc, int y,
+ int Rd, int Rm, int Rs, int Rn);
+
+ // byte/half word extract...
+ virtual void UXTB16(int cc, int Rd, int Rm, int rotate);
+
+ // bit manipulation...
+ virtual void UBFX(int cc, int Rd, int Rn, int lsb, int width);
+
+ // Address loading/storing/manipulation
+ virtual void ADDR_LDR(int cc, int Rd, int Rn, uint32_t offset = __immed12_pre(0));
+ virtual void ADDR_STR(int cc, int Rd, int Rn, uint32_t offset = __immed12_pre(0));
+ virtual void ADDR_ADD(int cc, int s, int Rd, int Rn, uint32_t Op2);
+ virtual void ADDR_SUB(int cc, int s, int Rd, int Rn, uint32_t Op2);
+
+ // this is some crap to share is MIPS64Assembler class for debug
+ char * mArmDisassemblyBuffer;
+ int mArmLineLength;
+ int mArmInstrCount;
+
+ int mInum; // current arm instuction number (0..n)
+ uint32_t** mArmPC; // array: PC for 1st mips instr of
+ // each translated ARM instr
+
+
+private:
+ ArmToMips64Assembler(const ArmToMips64Assembler& rhs);
+ ArmToMips64Assembler& operator = (const ArmToMips64Assembler& rhs);
+
+ void init_conditional_labels(void);
+
+ void protectConditionalOperands(int Rd);
+
+ // reg__tmp set to MIPS AT, reg 1
+ int dataProcAdrModes(int op, int& source, bool sign = false, int reg_tmp = 1);
+
+ sp<Assembly> mAssembly;
+ MIPS64Assembler* mMips;
+
+
+ enum misc_constants_t {
+ ARM_MAX_INSTUCTIONS = 512 // based on ASSEMBLY_SCRATCH_SIZE
+ };
+
+ enum {
+ SRC_REG = 0,
+ SRC_IMM,
+ SRC_ERROR = -1
+ };
+
+ enum addr_modes {
+ // start above the range of legal mips reg #'s (0-31)
+ AMODE_REG = 0x20,
+ AMODE_IMM, AMODE_REG_IMM, // for data processing
+ AMODE_IMM_12_PRE, AMODE_IMM_12_POST, // for load/store
+ AMODE_REG_SCALE_PRE, AMODE_IMM_8_PRE,
+ AMODE_IMM_8_POST, AMODE_REG_PRE,
+ AMODE_UNSUPPORTED
+ };
+
+ struct addr_mode_t { // address modes for current ARM instruction
+ int reg;
+ int stype;
+ uint32_t value;
+ bool writeback; // writeback the adr reg after modification
+ } amode;
+
+ enum cond_types {
+ CMP_COND = 1,
+ SBIT_COND
+ };
+
+ struct cond_mode_t { // conditional-execution info for current ARM instruction
+ cond_types type;
+ int r1;
+ int r2;
+ int labelnum;
+ char label[100][10];
+ } cond;
+};
+
+
+
+
+// ----------------------------------------------------------------------------
+// ----------------------------------------------------------------------------
+// ----------------------------------------------------------------------------
+
+// This is the basic MIPS64 assembler, which just creates the opcodes in memory.
+// All the more complicated work is done in ArmToMips64Assember above.
+// Inherits MIPSAssembler class, and overrides only MIPS64r6 specific stuff
+
+class MIPS64Assembler : public MIPSAssembler
+{
+public:
+ MIPS64Assembler(const sp<Assembly>& assembly, ArmToMips64Assembler *parent);
+ MIPS64Assembler(void* assembly, ArmToMips64Assembler *parent);
+ virtual ~MIPS64Assembler();
+
+ virtual void reset();
+ virtual void disassemble(const char* name);
+
+ void fix_branches();
+
+ // ------------------------------------------------------------------------
+ // MIPS64AssemblerInterface...
+ // ------------------------------------------------------------------------
+
+#if 0
+#pragma mark -
+#pragma mark Arithmetic...
+#endif
+
+ void DADDU(int Rd, int Rs, int Rt);
+ void DADDIU(int Rt, int Rs, int16_t imm);
+ void DSUBU(int Rd, int Rs, int Rt);
+ void DSUBIU(int Rt, int Rs, int16_t imm);
+ virtual void MUL(int Rd, int Rs, int Rt);
+ void MUH(int Rd, int Rs, int Rt);
+
+#if 0
+#pragma mark -
+#pragma mark Logical...
+#endif
+
+ virtual void CLO(int Rd, int Rs);
+ virtual void CLZ(int Rd, int Rs);
+
+#if 0
+#pragma mark -
+#pragma mark Load/store...
+#endif
+
+ void LD(int Rt, int Rbase, int16_t offset);
+ void SD(int Rt, int Rbase, int16_t offset);
+ virtual void LUI(int Rt, int16_t offset);
+
+#if 0
+#pragma mark -
+#pragma mark Branch...
+#endif
+
+ void JR(int Rs);
+
+
+protected:
+ ArmToMips64Assembler *mParent;
+
+ // opcode field of all instructions
+ enum opcode_field {
+ spec_op, regimm_op, j_op, jal_op, // 0x00 - 0x03
+ beq_op, bne_op, pop06_op, pop07_op, // 0x04 - 0x07
+ pop10_op, addiu_op, slti_op, sltiu_op, // 0x08 - 0x0b
+ andi_op, ori_op, xori_op, aui_op, // 0x0c - 0x0f
+ cop0_op, cop1_op, cop2_op, rsrv_opc_0, // 0x10 - 0x13
+ rsrv_opc_1, rsrv_opc_2, pop26_op, pop27_op, // 0x14 - 0x17
+ pop30_op, daddiu_op, rsrv_opc_3, rsrv_opc_4, // 0x18 - 0x1b
+ rsrv_opc_5, daui_op, msa_op, spec3_op, // 0x1c - 0x1f
+ lb_op, lh_op, rsrv_opc_6, lw_op, // 0x20 - 0x23
+ lbu_op, lhu_op, rsrv_opc_7, lwu_op, // 0x24 - 0x27
+ sb_op, sh_op, rsrv_opc_8, sw_op, // 0x28 - 0x2b
+ rsrv_opc_9, rsrv_opc_10, rsrv_opc_11, rsrv_opc_12, // 0x2c - 0x2f
+ rsrv_opc_13, lwc1_op, bc_op, rsrv_opc_14, // 0x2c - 0x2f
+ rsrv_opc_15, ldc1_op, pop66_op, ld_op, // 0x30 - 0x33
+ rsrv_opc_16, swc1_op, balc_op, pcrel_op, // 0x34 - 0x37
+ rsrv_opc_17, sdc1_op, pop76_op, sd_op // 0x38 - 0x3b
+ };
+
+
+ // func field for special opcode
+ enum func_spec_op {
+ sll_fn, rsrv_spec_0, srl_fn, sra_fn,
+ sllv_fn, lsa_fn, srlv_fn, srav_fn,
+ rsrv_spec_1, jalr_fn, rsrv_spec_2, rsrv_spec_3,
+ syscall_fn, break_fn, sdbbp_fn, sync_fn,
+ clz_fn, clo_fn, dclz_fn, dclo_fn,
+ dsllv_fn, dlsa_fn, dsrlv_fn, dsrav_fn,
+ sop30_fn, sop31_fn, sop32_fn, sop33_fn,
+ sop34_fn, sop35_fn, sop36_fn, sop37_fn,
+ add_fn, addu_fn, sub_fn, subu_fn,
+ and_fn, or_fn, xor_fn, nor_fn,
+ rsrv_spec_4, rsrv_spec_5, slt_fn, sltu_fn,
+ dadd_fn, daddu_fn, dsub_fn, dsubu_fn,
+ tge_fn, tgeu_fn, tlt_fn, tltu_fn,
+ teq_fn, seleqz_fn, tne_fn, selnez_fn,
+ dsll_fn, rsrv_spec_6, dsrl_fn, dsra_fn,
+ dsll32_fn, rsrv_spec_7, dsrl32_fn, dsra32_fn
+ };
+
+ // func field for spec3 opcode
+ enum func_spec3_op {
+ ext_fn, dextm_fn, dextu_fn, dext_fn,
+ ins_fn, dinsm_fn, dinsu_fn, dins_fn,
+ cachee_fn = 0x1b, sbe_fn, she_fn, sce_fn, swe_fn,
+ bshfl_fn, prefe_fn = 0x23, dbshfl_fn, cache_fn, sc_fn, scd_fn,
+ lbue_fn, lhue_fn, lbe_fn = 0x2c, lhe_fn, lle_fn, lwe_fn,
+ pref_fn = 0x35, ll_fn, lld_fn, rdhwr_fn = 0x3b
+ };
+
+ // sa field for spec3 opcodes, with BSHFL function
+ enum func_spec3_bshfl {
+ bitswap_fn,
+ wsbh_fn = 0x02,
+ dshd_fn = 0x05,
+ seb_fn = 0x10,
+ seh_fn = 0x18
+ };
+
+ // rt field of regimm opcodes.
+ enum regimm_fn {
+ bltz_fn, bgez_fn,
+ dahi_fn = 0x6,
+ nal_fn = 0x10, bal_fn, bltzall_fn, bgezall_fn,
+ sigrie_fn = 0x17,
+ dati_fn = 0x1e, synci_fn
+ };
+
+ enum muldiv_fn {
+ mul_fn = 0x02, muh_fn
+ };
+
+ enum mips_inst_shifts {
+ OP_SHF = 26,
+ JTARGET_SHF = 0,
+ RS_SHF = 21,
+ RT_SHF = 16,
+ RD_SHF = 11,
+ RE_SHF = 6,
+ SA_SHF = RE_SHF, // synonym
+ IMM_SHF = 0,
+ FUNC_SHF = 0,
+
+ // mask values
+ MSK_16 = 0xffff,
+
+
+ CACHEOP_SHF = 18,
+ CACHESEL_SHF = 16,
+ };
+};
+
+
+}; // namespace android
+
+#endif //ANDROID_MIPS64ASSEMBLER_H
diff --git a/libpixelflinger/codeflinger/MIPSAssembler.cpp b/libpixelflinger/codeflinger/MIPSAssembler.cpp
index a88d2fe..5497fae 100644
--- a/libpixelflinger/codeflinger/MIPSAssembler.cpp
+++ b/libpixelflinger/codeflinger/MIPSAssembler.cpp
@@ -1256,6 +1256,12 @@
mDuration = ggl_system_time();
}
+MIPSAssembler::MIPSAssembler(void* assembly)
+ : mParent(NULL), mAssembly(NULL)
+{
+ mBase = mPC = (uint32_t *)assembly;
+}
+
MIPSAssembler::~MIPSAssembler()
{
}
@@ -1358,7 +1364,7 @@
::mips_disassem(mipsPC, di_buf, arm_disasm_fmt);
string_detab(di_buf);
string_pad(di_buf, 30);
- ALOGW("%08x: %08x %s", uint32_t(mipsPC), uint32_t(*mipsPC), di_buf);
+ ALOGW("%08x: %08x %s", uintptr_t(mipsPC), uint32_t(*mipsPC), di_buf);
mipsPC++;
}
}
@@ -1407,7 +1413,7 @@
#if defined(WITH_LIB_HARDWARE)
if (__builtin_expect(mQemuTracing, 0)) {
- int err = qemu_add_mapping(int(base()), name);
+ int err = qemu_add_mapping(uintptr_t(base()), name);
mQemuTracing = (err >= 0);
}
#endif
diff --git a/libpixelflinger/codeflinger/MIPSAssembler.h b/libpixelflinger/codeflinger/MIPSAssembler.h
index 430ab06..b53fefb 100644
--- a/libpixelflinger/codeflinger/MIPSAssembler.h
+++ b/libpixelflinger/codeflinger/MIPSAssembler.h
@@ -21,9 +21,9 @@
#include <stdint.h>
#include <sys/types.h>
-#include "tinyutils/KeyedVector.h"
-#include "tinyutils/Vector.h"
#include "tinyutils/smartpointer.h"
+#include "utils/KeyedVector.h"
+#include "utils/Vector.h"
#include "ARMAssemblerInterface.h"
#include "CodeCache.h"
@@ -242,22 +242,23 @@
{
public:
MIPSAssembler(const sp<Assembly>& assembly, ArmToMipsAssembler *parent);
+ MIPSAssembler(void* assembly);
virtual ~MIPSAssembler();
- uint32_t* base() const;
- uint32_t* pc() const;
- void reset();
+ virtual uint32_t* base() const;
+ virtual uint32_t* pc() const;
+ virtual void reset();
- void disassemble(const char* name);
+ virtual void disassemble(const char* name);
- void prolog();
- void epilog(uint32_t touched);
- int generate(const char* name);
- void comment(const char* string);
- void label(const char* string);
+ virtual void prolog();
+ virtual void epilog(uint32_t touched);
+ virtual int generate(const char* name);
+ virtual void comment(const char* string);
+ virtual void label(const char* string);
// valid only after generate() has been called
- uint32_t* pcForLabel(const char* label);
+ virtual uint32_t* pcForLabel(const char* label);
// ------------------------------------------------------------------------
@@ -399,9 +400,9 @@
-private:
- void string_detab(char *s);
- void string_pad(char *s, int padded_len);
+protected:
+ virtual void string_detab(char *s);
+ virtual void string_pad(char *s, int padded_len);
ArmToMipsAssembler *mParent;
sp<Assembly> mAssembly;
@@ -537,7 +538,11 @@
enum mips_regnames {
R_zero = 0,
R_at, R_v0, R_v1, R_a0, R_a1, R_a2, R_a3,
+#if __mips_isa_rev < 6
R_t0, R_t1, R_t2, R_t3, R_t4, R_t5, R_t6, R_t7,
+#else
+ R_a4, R_a5, R_a6, R_a7, R_t0, R_t1, R_t2, R_t3,
+#endif
R_s0, R_s1, R_s2, R_s3, R_s4, R_s5, R_s6, R_s7,
R_t8, R_t9, R_k0, R_k1, R_gp, R_sp, R_s8, R_ra,
R_lr = R_s8,
diff --git a/libpixelflinger/codeflinger/mips64_disassem.c b/libpixelflinger/codeflinger/mips64_disassem.c
new file mode 100644
index 0000000..44b7fe7
--- /dev/null
+++ b/libpixelflinger/codeflinger/mips64_disassem.c
@@ -0,0 +1,582 @@
+/* $NetBSD: db_disasm.c,v 1.19 2007/02/28 04:21:53 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Ralph Campbell.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * from: @(#)kadb.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <sys/cdefs.h>
+
+#include <sys/types.h>
+#include "mips_opcode.h"
+
+#include <cutils/log.h>
+
+static char *sprintf_buffer;
+static int sprintf_buf_len;
+
+
+typedef uint64_t db_addr_t;
+static void db_printf(const char* fmt, ...);
+
+static const char * const op_name[64] = {
+/* 0 */ "spec", "bcond", "j", "jal", "beq", "bne", "blez", "bgtz",
+/* 8 */ "pop10", "addiu", "slti", "sltiu", "andi", "ori", "xori", "aui",
+/*16 */ "cop0", "cop1", "cop2", "?", "?", "?", "pop26", "pop27",
+/*24 */ "pop30", "daddiu", "?", "?", "?", "daui", "msa", "op37",
+/*32 */ "lb", "lh", "?", "lw", "lbu", "lhu", "?", "lwu",
+/*40 */ "sb", "sh", "?", "sw", "?", "?", "?", "?",
+/*48 */ "?", "lwc1", "bc", "?", "?", "ldc1", "pop66", "ld",
+/*56 */ "?", "swc1", "balc", "pcrel", "?", "sdc1", "pop76", "sd"
+};
+
+static const char * const spec_name[64] = {
+/* 0 */ "sll", "?", "srl", "sra", "sllv", "?", "srlv", "srav",
+/* 8 */ "?", "jalr", "?", "?", "syscall", "break", "sdbpp", "sync",
+/*16 */ "clz", "clo", "dclz", "dclo", "dsllv", "dlsa", "dsrlv", "dsrav",
+/*24 */ "sop30", "sop31", "sop32", "sop33", "sop34", "sop35", "sop36", "sop37",
+/*32 */ "add", "addu", "sub", "subu", "and", "or", "xor", "nor",
+/*40 */ "?", "?", "slt", "sltu", "dadd", "daddu", "dsub", "dsubu",
+/*48 */ "tge", "tgeu", "tlt", "tltu", "teq", "seleqz", "tne", "selnez",
+/*56 */ "dsll", "?", "dsrl", "dsra", "dsll32", "?", "dsrl32", "dsra32"
+};
+
+static const char * const bcond_name[32] = {
+/* 0 */ "bltz", "bgez", "?", "?", "?", "?", "dahi", "?",
+/* 8 */ "?", "?", "?", "?", "?", "?", "?", "?",
+/*16 */ "nal", "bal", "?", "?", "?", "?", "?", "sigrie",
+/*24 */ "?", "?", "?", "?", "?", "?", "dati", "synci",
+};
+
+static const char * const cop1_name[64] = {
+/* 0 */ "fadd", "fsub", "fmpy", "fdiv", "fsqrt","fabs", "fmov", "fneg",
+/* 8 */ "fop08","fop09","fop0a","fop0b","fop0c","fop0d","fop0e","fop0f",
+/*16 */ "fop10","fop11","fop12","fop13","fop14","fop15","fop16","fop17",
+/*24 */ "fop18","fop19","fop1a","fop1b","fop1c","fop1d","fop1e","fop1f",
+/*32 */ "fcvts","fcvtd","fcvte","fop23","fcvtw","fop25","fop26","fop27",
+/*40 */ "fop28","fop29","fop2a","fop2b","fop2c","fop2d","fop2e","fop2f",
+/*48 */ "fcmp.f","fcmp.un","fcmp.eq","fcmp.ueq","fcmp.olt","fcmp.ult",
+ "fcmp.ole","fcmp.ule",
+/*56 */ "fcmp.sf","fcmp.ngle","fcmp.seq","fcmp.ngl","fcmp.lt","fcmp.nge",
+ "fcmp.le","fcmp.ngt"
+};
+
+static const char * const fmt_name[16] = {
+ "s", "d", "e", "fmt3",
+ "w", "fmt5", "fmt6", "fmt7",
+ "fmt8", "fmt9", "fmta", "fmtb",
+ "fmtc", "fmtd", "fmte", "fmtf"
+};
+
+static char * const mips_reg_name[32] = {
+ "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3",
+ "a4", "a5", "a6", "a7", "t0", "t1", "t2", "t3",
+ "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
+ "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra"
+};
+
+static char * alt_arm_reg_name[32] = { // hacked names for comparison with ARM code
+ "zero", "at", "r0", "r1", "r2", "r3", "r4", "r5",
+ "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r13",
+ "r14", "r15", "at2", "cmp", "s4", "s5", "s6", "s7",
+ "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra"
+};
+
+static char ** reg_name = &mips_reg_name[0];
+
+static const char * const c0_opname[64] = {
+ "c0op00","tlbr", "tlbwi", "c0op03","c0op04","c0op05","tlbwr", "c0op07",
+ "tlbp", "c0op11","c0op12","c0op13","c0op14","c0op15","c0op16","c0op17",
+ "rfe", "c0op21","c0op22","c0op23","c0op24","c0op25","c0op26","c0op27",
+ "eret", "c0op31","c0op32","c0op33","c0op34","c0op35","c0op36","c0op37",
+ "c0op40","c0op41","c0op42","c0op43","c0op44","c0op45","c0op46","c0op47",
+ "c0op50","c0op51","c0op52","c0op53","c0op54","c0op55","c0op56","c0op57",
+ "c0op60","c0op61","c0op62","c0op63","c0op64","c0op65","c0op66","c0op67",
+ "c0op70","c0op71","c0op72","c0op73","c0op74","c0op75","c0op77","c0op77",
+};
+
+static const char * const c0_reg[32] = {
+ "index", "random", "tlblo0", "tlblo1",
+ "context", "pagemask", "wired", "cp0r7",
+ "badvaddr", "count", "tlbhi", "compare",
+ "status", "cause", "epc", "prid",
+ "config", "lladdr", "watchlo", "watchhi",
+ "xcontext", "cp0r21", "cp0r22", "debug",
+ "depc", "perfcnt", "ecc", "cacheerr",
+ "taglo", "taghi", "errepc", "desave"
+};
+
+static void print_addr(db_addr_t);
+db_addr_t mips_disassem(db_addr_t loc, char *di_buffer, int alt_dis_format);
+
+
+/*
+ * Disassemble instruction 'insn' nominally at 'loc'.
+ * 'loc' may in fact contain a breakpoint instruction.
+ */
+static db_addr_t
+db_disasm_insn(int insn, db_addr_t loc, bool altfmt)
+{
+ bool bdslot = false;
+ InstFmt i;
+
+ i.word = insn;
+
+ switch (i.JType.op) {
+ case OP_SPECIAL:
+ if (i.word == 0) {
+ db_printf("nop");
+ break;
+ }
+ if (i.word == 0x0080) {
+ db_printf("NIY");
+ break;
+ }
+ if (i.word == 0x00c0) {
+ db_printf("NOT IMPL");
+ break;
+ }
+ /* Special cases --------------------------------------------------
+ * "addu" is a "move" only in 32-bit mode. What's the correct
+ * answer - never decode addu/daddu as "move"?
+ */
+ if ( (i.RType.func == OP_ADDU && i.RType.rt == 0) ||
+ (i.RType.func == OP_OR && i.RType.rt == 0) ) {
+ db_printf("move\t%s,%s",
+ reg_name[i.RType.rd],
+ reg_name[i.RType.rs]);
+ break;
+ }
+
+ if (i.RType.func == OP_SRL && (i.RType.rs & 1) == 1) {
+ db_printf("rotr\t%s,%s,%d", reg_name[i.RType.rd],
+ reg_name[i.RType.rt], i.RType.shamt);
+ break;
+ }
+ if (i.RType.func == OP_SRLV && (i.RType.shamt & 1) == 1) {
+ db_printf("rotrv\t%s,%s,%s", reg_name[i.RType.rd],
+ reg_name[i.RType.rt], reg_name[i.RType.rs]);
+ break;
+ }
+
+ if (i.RType.func == OP_SOP30) {
+ if (i.RType.shamt == OP_MUL) {
+ db_printf("mul");
+ } else if (i.RType.shamt == OP_MUH) {
+ db_printf("muh");
+ }
+ db_printf("\t%s,%s,%s", reg_name[i.RType.rd],
+ reg_name[i.RType.rs], reg_name[i.RType.rt]);
+ break;
+ }
+ if (i.RType.func == OP_SOP31) {
+ if (i.RType.shamt == OP_MUL) {
+ db_printf("mulu");
+ } else if (i.RType.shamt == OP_MUH) {
+ db_printf("muhu");
+ }
+ db_printf("\t%s,%s,%s", reg_name[i.RType.rd],
+ reg_name[i.RType.rs], reg_name[i.RType.rt]);
+ break;
+ }
+
+ if (i.RType.func == OP_JALR && i.RType.rd == 0) {
+ db_printf("jr\t%s", reg_name[i.RType.rs]);
+ bdslot = true;
+ break;
+ }
+
+ db_printf("%s", spec_name[i.RType.func]);
+ switch (i.RType.func) {
+ case OP_SLL:
+ case OP_SRL:
+ case OP_SRA:
+ case OP_DSLL:
+
+ case OP_DSRL:
+ case OP_DSRA:
+ case OP_DSLL32:
+ case OP_DSRL32:
+ case OP_DSRA32:
+ db_printf("\t%s,%s,%d",
+ reg_name[i.RType.rd],
+ reg_name[i.RType.rt],
+ i.RType.shamt);
+ break;
+
+ case OP_SLLV:
+ case OP_SRLV:
+ case OP_SRAV:
+ case OP_DSLLV:
+ case OP_DSRLV:
+ case OP_DSRAV:
+ db_printf("\t%s,%s,%s",
+ reg_name[i.RType.rd],
+ reg_name[i.RType.rt],
+ reg_name[i.RType.rs]);
+ break;
+
+ case OP_CLZ:
+ case OP_CLO:
+ case OP_DCLZ:
+ case OP_DCLO:
+ db_printf("\t%s,%s",
+ reg_name[i.RType.rd],
+ reg_name[i.RType.rs]);
+ break;
+
+ case OP_JALR:
+ db_printf("\t");
+ if (i.RType.rd != 31) {
+ db_printf("%s,", reg_name[i.RType.rd]);
+ }
+ db_printf("%s", reg_name[i.RType.rs]);
+ bdslot = true;
+ break;
+
+ case OP_SYSCALL:
+ case OP_SYNC:
+ break;
+
+ case OP_BREAK:
+ db_printf("\t%d", (i.RType.rs << 5) | i.RType.rt);
+ break;
+
+ default:
+ db_printf("\t%s,%s,%s",
+ reg_name[i.RType.rd],
+ reg_name[i.RType.rs],
+ reg_name[i.RType.rt]);
+ }
+ break;
+
+ case OP_SPECIAL3:
+ if (i.RType.func == OP_EXT)
+ db_printf("ext\t%s,%s,%d,%d",
+ reg_name[i.RType.rt],
+ reg_name[i.RType.rs],
+ i.RType.shamt,
+ i.RType.rd+1);
+ else if (i.RType.func == OP_DEXT)
+ db_printf("dext\t%s,%s,%d,%d",
+ reg_name[i.RType.rt],
+ reg_name[i.RType.rs],
+ i.RType.shamt,
+ i.RType.rd+1);
+ else if (i.RType.func == OP_DEXTM)
+ db_printf("dextm\t%s,%s,%d,%d",
+ reg_name[i.RType.rt],
+ reg_name[i.RType.rs],
+ i.RType.shamt,
+ i.RType.rd+33);
+ else if (i.RType.func == OP_DEXTU)
+ db_printf("dextu\t%s,%s,%d,%d",
+ reg_name[i.RType.rt],
+ reg_name[i.RType.rs],
+ i.RType.shamt+32,
+ i.RType.rd+1);
+ else if (i.RType.func == OP_INS)
+ db_printf("ins\t%s,%s,%d,%d",
+ reg_name[i.RType.rt],
+ reg_name[i.RType.rs],
+ i.RType.shamt,
+ i.RType.rd-i.RType.shamt+1);
+ else if (i.RType.func == OP_DINS)
+ db_printf("dins\t%s,%s,%d,%d",
+ reg_name[i.RType.rt],
+ reg_name[i.RType.rs],
+ i.RType.shamt,
+ i.RType.rd-i.RType.shamt+1);
+ else if (i.RType.func == OP_DINSM)
+ db_printf("dinsm\t%s,%s,%d,%d",
+ reg_name[i.RType.rt],
+ reg_name[i.RType.rs],
+ i.RType.shamt,
+ i.RType.rd-i.RType.shamt+33);
+ else if (i.RType.func == OP_DINSU)
+ db_printf("dinsu\t%s,%s,%d,%d",
+ reg_name[i.RType.rt],
+ reg_name[i.RType.rs],
+ i.RType.shamt+32,
+ i.RType.rd-i.RType.shamt+1);
+ else if (i.RType.func == OP_BSHFL && i.RType.shamt == OP_WSBH)
+ db_printf("wsbh\t%s,%s",
+ reg_name[i.RType.rd],
+ reg_name[i.RType.rt]);
+ else if (i.RType.func == OP_BSHFL && i.RType.shamt == OP_SEB)
+ db_printf("seb\t%s,%s",
+ reg_name[i.RType.rd],
+ reg_name[i.RType.rt]);
+ else if (i.RType.func == OP_BSHFL && i.RType.shamt == OP_SEH)
+ db_printf("seh\t%s,%s",
+ reg_name[i.RType.rd],
+ reg_name[i.RType.rt]);
+ else if (i.RType.func == OP_RDHWR)
+ db_printf("rdhwr\t%s,%s",
+ reg_name[i.RType.rd],
+ reg_name[i.RType.rt]);
+ else
+ db_printf("Unknown");
+ break;
+
+ case OP_BCOND:
+ db_printf("%s\t%s,", bcond_name[i.IType.rt],
+ reg_name[i.IType.rs]);
+ goto pr_displ;
+
+ case OP_BLEZ:
+ case OP_BGTZ:
+ db_printf("%s\t%s,", op_name[i.IType.op],
+ reg_name[i.IType.rs]);
+ goto pr_displ;
+
+ case OP_BEQ:
+ if (i.IType.rs == 0 && i.IType.rt == 0) {
+ db_printf("b\t");
+ goto pr_displ;
+ }
+ /* FALLTHROUGH */
+ case OP_BNE:
+ db_printf("%s\t%s,%s,", op_name[i.IType.op],
+ reg_name[i.IType.rs],
+ reg_name[i.IType.rt]);
+ pr_displ:
+ print_addr(loc + 4 + ((short)i.IType.imm << 2));
+ bdslot = true;
+ break;
+
+ case OP_COP0:
+ switch (i.RType.rs) {
+ case OP_BCx:
+ case OP_BCy:
+
+ db_printf("bc0%c\t",
+ "ft"[i.RType.rt & COPz_BC_TF_MASK]);
+ goto pr_displ;
+
+ case OP_MT:
+ db_printf("mtc0\t%s,%s",
+ reg_name[i.RType.rt],
+ c0_reg[i.RType.rd]);
+ break;
+
+ case OP_DMT:
+ db_printf("dmtc0\t%s,%s",
+ reg_name[i.RType.rt],
+ c0_reg[i.RType.rd]);
+ break;
+
+ case OP_MF:
+ db_printf("mfc0\t%s,%s",
+ reg_name[i.RType.rt],
+ c0_reg[i.RType.rd]);
+ break;
+
+ case OP_DMF:
+ db_printf("dmfc0\t%s,%s",
+ reg_name[i.RType.rt],
+ c0_reg[i.RType.rd]);
+ break;
+
+ default:
+ db_printf("%s", c0_opname[i.FRType.func]);
+ }
+ break;
+
+ case OP_COP1:
+ switch (i.RType.rs) {
+ case OP_BCx:
+ case OP_BCy:
+ db_printf("bc1%c\t",
+ "ft"[i.RType.rt & COPz_BC_TF_MASK]);
+ goto pr_displ;
+
+ case OP_MT:
+ db_printf("mtc1\t%s,f%d",
+ reg_name[i.RType.rt],
+ i.RType.rd);
+ break;
+
+ case OP_MF:
+ db_printf("mfc1\t%s,f%d",
+ reg_name[i.RType.rt],
+ i.RType.rd);
+ break;
+
+ case OP_CT:
+ db_printf("ctc1\t%s,f%d",
+ reg_name[i.RType.rt],
+ i.RType.rd);
+ break;
+
+ case OP_CF:
+ db_printf("cfc1\t%s,f%d",
+ reg_name[i.RType.rt],
+ i.RType.rd);
+ break;
+
+ default:
+ db_printf("%s.%s\tf%d,f%d,f%d",
+ cop1_name[i.FRType.func],
+ fmt_name[i.FRType.fmt],
+ i.FRType.fd, i.FRType.fs, i.FRType.ft);
+ }
+ break;
+
+ case OP_J:
+ case OP_JAL:
+ db_printf("%s\t", op_name[i.JType.op]);
+ print_addr((loc & 0xFFFFFFFFF0000000) | (i.JType.target << 2));
+ bdslot = true;
+ break;
+
+ case OP_LWC1:
+ case OP_SWC1:
+ db_printf("%s\tf%d,", op_name[i.IType.op],
+ i.IType.rt);
+ goto loadstore;
+
+ case OP_LB:
+ case OP_LH:
+ case OP_LW:
+ case OP_LD:
+ case OP_LBU:
+ case OP_LHU:
+ case OP_LWU:
+ case OP_SB:
+ case OP_SH:
+ case OP_SW:
+ case OP_SD:
+ db_printf("%s\t%s,", op_name[i.IType.op],
+ reg_name[i.IType.rt]);
+ loadstore:
+ db_printf("%d(%s)", (short)i.IType.imm,
+ reg_name[i.IType.rs]);
+ break;
+
+ case OP_ORI:
+ case OP_XORI:
+ if (i.IType.rs == 0) {
+ db_printf("li\t%s,0x%x",
+ reg_name[i.IType.rt],
+ i.IType.imm);
+ break;
+ }
+ /* FALLTHROUGH */
+ case OP_ANDI:
+ db_printf("%s\t%s,%s,0x%x", op_name[i.IType.op],
+ reg_name[i.IType.rt],
+ reg_name[i.IType.rs],
+ i.IType.imm);
+ break;
+
+ case OP_AUI:
+ if (i.IType.rs == 0) {
+ db_printf("lui\t%s,0x%x", reg_name[i.IType.rt],
+ i.IType.imm);
+ } else {
+ db_printf("%s\t%s,%s,%d", op_name[i.IType.op],
+ reg_name[i.IType.rt], reg_name[i.IType.rs],
+ (short)i.IType.imm);
+ }
+ break;
+
+ case OP_ADDIU:
+ case OP_DADDIU:
+ if (i.IType.rs == 0) {
+ db_printf("li\t%s,%d",
+ reg_name[i.IType.rt],
+ (short)i.IType.imm);
+ break;
+ }
+ /* FALLTHROUGH */
+ default:
+ db_printf("%s\t%s,%s,%d", op_name[i.IType.op],
+ reg_name[i.IType.rt],
+ reg_name[i.IType.rs],
+ (short)i.IType.imm);
+ }
+ // db_printf("\n");
+ // if (bdslot) {
+ // db_printf(" bd: ");
+ // mips_disassem(loc+4);
+ // return (loc + 8);
+ // }
+ return (loc + 4);
+}
+
+static void
+print_addr(db_addr_t loc)
+{
+ db_printf("0x%08lx", loc);
+}
+
+static void db_printf(const char* fmt, ...)
+{
+ int cnt;
+ va_list argp;
+ va_start(argp, fmt);
+ if (sprintf_buffer) {
+ cnt = vsnprintf(sprintf_buffer, sprintf_buf_len, fmt, argp);
+ sprintf_buffer += cnt;
+ sprintf_buf_len -= cnt;
+ } else {
+ vprintf(fmt, argp);
+ }
+}
+
+/*
+ * Disassemble instruction at 'loc'.
+ * Return address of start of next instruction.
+ * Since this function is used by 'examine' and by 'step'
+ * "next instruction" does NOT mean the next instruction to
+ * be executed but the 'linear' next instruction.
+ */
+db_addr_t
+mips_disassem(db_addr_t loc, char *di_buffer, int alt_dis_format)
+{
+ u_int32_t instr;
+
+ if (alt_dis_format) { // use ARM register names for disassembly
+ reg_name = &alt_arm_reg_name[0];
+ }
+
+ sprintf_buffer = di_buffer; // quick 'n' dirty printf() vs sprintf()
+ sprintf_buf_len = 39; // should be passed in
+
+ instr = *(u_int32_t *)loc;
+ return (db_disasm_insn(instr, loc, false));
+}
diff --git a/libpixelflinger/codeflinger/mips64_disassem.h b/libpixelflinger/codeflinger/mips64_disassem.h
new file mode 100644
index 0000000..c94f04f
--- /dev/null
+++ b/libpixelflinger/codeflinger/mips64_disassem.h
@@ -0,0 +1,56 @@
+/* $NetBSD: db_disasm.c,v 1.19 2007/02/28 04:21:53 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Ralph Campbell.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * from: @(#)kadb.c 8.1 (Berkeley) 6/10/93
+ */
+
+
+
+#ifndef ANDROID_MIPS_DISASSEM_H
+#define ANDROID_MIPS_DISASSEM_H
+
+#include <sys/types.h>
+
+#if __cplusplus
+extern "C" {
+#endif
+
+/* Prototypes for callable functions */
+
+void mips_disassem(uint32_t *location, char *di_buffer, int alt_fmt);
+
+#if __cplusplus
+}
+#endif
+
+#endif /* !ANDROID_MIPS_DISASSEM_H */
diff --git a/libpixelflinger/codeflinger/mips_disassem.c b/libpixelflinger/codeflinger/mips_disassem.c
index 4ab9bd3..3007b15 100644
--- a/libpixelflinger/codeflinger/mips_disassem.c
+++ b/libpixelflinger/codeflinger/mips_disassem.c
@@ -323,14 +323,14 @@
db_printf("ext\t%s,%s,%d,%d",
reg_name[i.RType.rt],
reg_name[i.RType.rs],
- i.RType.rd+1,
- i.RType.shamt);
+ i.RType.shamt,
+ i.RType.rd+1);
else if (i.RType.func == OP_INS)
db_printf("ins\t%s,%s,%d,%d",
reg_name[i.RType.rt],
reg_name[i.RType.rs],
- i.RType.rd+1,
- i.RType.shamt);
+ i.RType.shamt,
+ i.RType.rd-i.RType.shamt+1);
else if (i.RType.func == OP_BSHFL && i.RType.shamt == OP_WSBH)
db_printf("wsbh\t%s,%s",
reg_name[i.RType.rd],
diff --git a/libpixelflinger/codeflinger/mips_opcode.h b/libpixelflinger/codeflinger/mips_opcode.h
index 7ed5ef5..45bb19e 100644
--- a/libpixelflinger/codeflinger/mips_opcode.h
+++ b/libpixelflinger/codeflinger/mips_opcode.h
@@ -125,69 +125,118 @@
#define OP_BLEZ 006
#define OP_BGTZ 007
+#if __mips_isa_rev < 6
#define OP_ADDI 010
+#else
+#define OP_POP10 010
+#endif
+
#define OP_ADDIU 011
#define OP_SLTI 012
#define OP_SLTIU 013
#define OP_ANDI 014
#define OP_ORI 015
#define OP_XORI 016
+
+#if __mips_isa_rev < 6
#define OP_LUI 017
+#else
+#define OP_AUI 017
+#endif
#define OP_COP0 020
#define OP_COP1 021
#define OP_COP2 022
+
+#if __mips_isa_rev < 6
#define OP_COP3 023
-#define OP_BEQL 024 /* MIPS-II, for r4000 port */
-#define OP_BNEL 025 /* MIPS-II, for r4000 port */
-#define OP_BLEZL 026 /* MIPS-II, for r4000 port */
-#define OP_BGTZL 027 /* MIPS-II, for r4000 port */
+#define OP_BEQL 024
+#define OP_BNEL 025
+#define OP_BLEZL 026
+#define OP_BGTZL 027
+#define OP_DADDI 030
+#else
+#define OP_POP26 026
+#define OP_POP27 027
+#define OP_POP30 030
+#endif
-#define OP_DADDI 030 /* MIPS-II, for r4000 port */
-#define OP_DADDIU 031 /* MIPS-II, for r4000 port */
-#define OP_LDL 032 /* MIPS-II, for r4000 port */
-#define OP_LDR 033 /* MIPS-II, for r4000 port */
+#define OP_DADDIU 031
-#define OP_SPECIAL2 034 /* QED opcodes */
-#define OP_SPECIAL3 037 /* mips32r2 opcodes */
+#if __mips_isa_rev < 6
+#define OP_LDL 032
+#define OP_LDR 033
+#define OP_SPECIAL2 034
+#else
+#define OP_DAUI 035
+#endif
+
+#define OP_SPECIAL3 037
#define OP_LB 040
#define OP_LH 041
+
+#if __mips_isa_rev < 6
#define OP_LWL 042
+#endif
+
#define OP_LW 043
#define OP_LBU 044
#define OP_LHU 045
#define OP_LWR 046
#define OP_LHU 045
+
+#if __mips_isa_rev < 6
#define OP_LWR 046
-#define OP_LWU 047 /* MIPS-II, for r4000 port */
+#endif
+
+#define OP_LWU 047
#define OP_SB 050
#define OP_SH 051
-#define OP_SWL 052
-#define OP_SW 053
-#define OP_SDL 054 /* MIPS-II, for r4000 port */
-#define OP_SDR 055 /* MIPS-II, for r4000 port */
-#define OP_SWR 056
-#define OP_CACHE 057 /* MIPS-II, for r4000 port */
+#if __mips_isa_rev < 6
+#define OP_SWL 052
+#endif
+
+#define OP_SW 053
+
+#if __mips_isa_rev < 6
+#define OP_SDL 054
+#define OP_SDR 055
+#define OP_SWR 056
+#define OP_CACHE 057
#define OP_LL 060
-#define OP_LWC0 OP_LL /* backwards source compatibility */
+#define OP_LWC0 OP_LL
#define OP_LWC1 061
#define OP_LWC2 062
#define OP_LWC3 063
-#define OP_LLD 064 /* MIPS-II, for r4000 port */
-#define OP_LDC1 065
-#define OP_LD 067 /* MIPS-II, for r4000 port */
+#define OP_LLD 064
+#else
+#define OP_LWC1 061
+#define OP_BC 062
+#endif
+#define OP_LDC1 065
+#define OP_LD 067
+
+#if __mips_isa_rev < 6
#define OP_SC 070
-#define OP_SWC0 OP_SC /* backwards source compatibility */
+#define OP_SWC0 OP_SC
+#endif
+
#define OP_SWC1 071
+
+#if __mips_isa_rev < 6
#define OP_SWC2 072
#define OP_SWC3 073
-#define OP_SCD 074 /* MIPS-II, for r4000 port */
+#define OP_SCD 074
+#else
+#define OP_BALC 072
+#endif
+
#define OP_SDC1 075
-#define OP_SD 077 /* MIPS-II, for r4000 port */
+#define OP_SD 077
/*
* Values for the 'func' field when 'op' == OP_SPECIAL.
@@ -199,28 +248,50 @@
#define OP_SRLV 006
#define OP_SRAV 007
+#if __mips_isa_rev < 6
#define OP_JR 010
+#endif
+
#define OP_JALR 011
#define OP_SYSCALL 014
#define OP_BREAK 015
-#define OP_SYNC 017 /* MIPS-II, for r4000 port */
+#define OP_SYNC 017
+#if __mips_isa_rev < 6
#define OP_MFHI 020
#define OP_MTHI 021
#define OP_MFLO 022
#define OP_MTLO 023
-#define OP_DSLLV 024 /* MIPS-II, for r4000 port */
-#define OP_DSRLV 026 /* MIPS-II, for r4000 port */
-#define OP_DSRAV 027 /* MIPS-II, for r4000 port */
+#else
+#define OP_CLZ 020
+#define OP_CLO 021
+#define OP_DCLZ 022
+#define OP_DCLO 023
+#endif
+#define OP_DSLLV 024
+#define OP_DSRLV 026
+#define OP_DSRAV 027
+
+#if __mips_isa_rev < 6
#define OP_MULT 030
#define OP_MULTU 031
#define OP_DIV 032
#define OP_DIVU 033
-#define OP_DMULT 034 /* MIPS-II, for r4000 port */
-#define OP_DMULTU 035 /* MIPS-II, for r4000 port */
-#define OP_DDIV 036 /* MIPS-II, for r4000 port */
-#define OP_DDIVU 037 /* MIPS-II, for r4000 port */
+#define OP_DMULT 034
+#define OP_DMULTU 035
+#define OP_DDIV 036
+#define OP_DDIVU 037
+#else
+#define OP_SOP30 030
+#define OP_SOP31 031
+#define OP_SOP32 032
+#define OP_SOP33 033
+#define OP_SOP34 034
+#define OP_SOP35 035
+#define OP_SOP36 036
+#define OP_SOP37 037
+#endif
#define OP_ADD 040
#define OP_ADDU 041
@@ -233,73 +304,96 @@
#define OP_SLT 052
#define OP_SLTU 053
-#define OP_DADD 054 /* MIPS-II, for r4000 port */
-#define OP_DADDU 055 /* MIPS-II, for r4000 port */
-#define OP_DSUB 056 /* MIPS-II, for r4000 port */
-#define OP_DSUBU 057 /* MIPS-II, for r4000 port */
+#define OP_DADD 054
+#define OP_DADDU 055
+#define OP_DSUB 056
+#define OP_DSUBU 057
-#define OP_TGE 060 /* MIPS-II, for r4000 port */
-#define OP_TGEU 061 /* MIPS-II, for r4000 port */
-#define OP_TLT 062 /* MIPS-II, for r4000 port */
-#define OP_TLTU 063 /* MIPS-II, for r4000 port */
-#define OP_TEQ 064 /* MIPS-II, for r4000 port */
-#define OP_TNE 066 /* MIPS-II, for r4000 port */
+#define OP_TGE 060
+#define OP_TGEU 061
+#define OP_TLT 062
+#define OP_TLTU 063
+#define OP_TEQ 064
+#define OP_TNE 066
-#define OP_DSLL 070 /* MIPS-II, for r4000 port */
-#define OP_DSRL 072 /* MIPS-II, for r4000 port */
-#define OP_DSRA 073 /* MIPS-II, for r4000 port */
-#define OP_DSLL32 074 /* MIPS-II, for r4000 port */
-#define OP_DSRL32 076 /* MIPS-II, for r4000 port */
-#define OP_DSRA32 077 /* MIPS-II, for r4000 port */
+#define OP_DSLL 070
+#define OP_DSRL 072
+#define OP_DSRA 073
+#define OP_DSLL32 074
+#define OP_DSRL32 076
+#define OP_DSRA32 077
+#if __mips_isa_rev < 6
/*
* Values for the 'func' field when 'op' == OP_SPECIAL2.
+ * OP_SPECIAL2 opcodes are removed in mips32r6
*/
#define OP_MAD 000 /* QED */
#define OP_MADU 001 /* QED */
#define OP_MUL 002 /* QED */
+#endif
/*
* Values for the 'func' field when 'op' == OP_SPECIAL3.
*/
#define OP_EXT 000
+#define OP_DEXTM 001
+#define OP_DEXTU 002
+#define OP_DEXT 003
#define OP_INS 004
+#define OP_DINSM 005
+#define OP_DINSU 006
+#define OP_DINS 007
#define OP_BSHFL 040
+#define OP_RDHWR 073
/*
* Values for the 'shamt' field when OP_SPECIAL3 && func OP_BSHFL.
*/
+
#define OP_WSBH 002
#define OP_SEB 020
#define OP_SEH 030
+#if __mips_isa_rev == 6
+/*
+ * Values for the 'shamt' field when OP_SOP30.
+ */
+#define OP_MUL 002
+#define OP_MUH 003
+#endif
+
/*
* Values for the 'func' field when 'op' == OP_BCOND.
*/
#define OP_BLTZ 000
#define OP_BGEZ 001
-#define OP_BLTZL 002 /* MIPS-II, for r4000 port */
-#define OP_BGEZL 003 /* MIPS-II, for r4000 port */
-#define OP_TGEI 010 /* MIPS-II, for r4000 port */
-#define OP_TGEIU 011 /* MIPS-II, for r4000 port */
-#define OP_TLTI 012 /* MIPS-II, for r4000 port */
-#define OP_TLTIU 013 /* MIPS-II, for r4000 port */
-#define OP_TEQI 014 /* MIPS-II, for r4000 port */
-#define OP_TNEI 016 /* MIPS-II, for r4000 port */
-
-#define OP_BLTZAL 020 /* MIPS-II, for r4000 port */
+#if __mips_isa_rev < 6
+#define OP_BLTZL 002
+#define OP_BGEZL 003
+#define OP_TGEI 010
+#define OP_TGEIU 011
+#define OP_TLTI 012
+#define OP_TLTIU 013
+#define OP_TEQI 014
+#define OP_TNEI 016
+#define OP_BLTZAL 020
#define OP_BGEZAL 021
#define OP_BLTZALL 022
#define OP_BGEZALL 023
+#else
+#define OP_NAL 020
+#define OP_BAL 021
+#endif
/*
* Values for the 'rs' field when 'op' == OP_COPz.
*/
#define OP_MF 000
-#define OP_DMF 001 /* MIPS-II, for r4000 port */
+#define OP_DMF 001
#define OP_MT 004
-#define OP_DMT 005 /* MIPS-II, for r4000 port */
+#define OP_DMT 005
#define OP_BCx 010
#define OP_BCy 014
#define OP_CF 002
@@ -311,6 +405,6 @@
#define COPz_BC_TF_MASK 0x01
#define COPz_BC_TRUE 0x01
#define COPz_BC_FALSE 0x00
-#define COPz_BCL_TF_MASK 0x02 /* MIPS-II, for r4000 port */
-#define COPz_BCL_TRUE 0x02 /* MIPS-II, for r4000 port */
-#define COPz_BCL_FALSE 0x00 /* MIPS-II, for r4000 port */
+#define COPz_BCL_TF_MASK 0x02
+#define COPz_BCL_TRUE 0x02
+#define COPz_BCL_FALSE 0x00
diff --git a/libpixelflinger/codeflinger/tinyutils/Errors.h b/libpixelflinger/codeflinger/tinyutils/Errors.h
deleted file mode 100644
index 47ae9d7..0000000
--- a/libpixelflinger/codeflinger/tinyutils/Errors.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * 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_PIXELFLINGER_ERRORS_H
-#define ANDROID_PIXELFLINGER_ERRORS_H
-
-#include <sys/types.h>
-#include <errno.h>
-
-namespace android {
-namespace tinyutils {
-
-// use this type to return error codes
-typedef int32_t status_t;
-
-/*
- * Error codes.
- * All error codes are negative values.
- */
-
-enum {
- NO_ERROR = 0, // No errors.
- NO_MEMORY = -ENOMEM,
- BAD_VALUE = -EINVAL,
- BAD_INDEX = -EOVERFLOW,
- NAME_NOT_FOUND = -ENOENT,
-};
-
-
-} // namespace tinyutils
-} // namespace android
-
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_PIXELFLINGER_ERRORS_H
diff --git a/libpixelflinger/codeflinger/tinyutils/KeyedVector.h b/libpixelflinger/codeflinger/tinyutils/KeyedVector.h
deleted file mode 100644
index 9d8668b..0000000
--- a/libpixelflinger/codeflinger/tinyutils/KeyedVector.h
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * Copyright 2005 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_PIXELFLINGER_KEYED_VECTOR_H
-#define ANDROID_PIXELFLINGER_KEYED_VECTOR_H
-
-#include <assert.h>
-#include <stdint.h>
-#include <sys/types.h>
-
-#include "Errors.h"
-#include "SortedVector.h"
-#include "TypeHelpers.h"
-
-// ---------------------------------------------------------------------------
-
-namespace android {
-namespace tinyutils {
-
-template <typename KEY, typename VALUE>
-class KeyedVector
-{
-public:
- typedef KEY key_type;
- typedef VALUE value_type;
-
- inline KeyedVector();
-
- /*
- * empty the vector
- */
-
- inline void clear() { mVector.clear(); }
-
- /*!
- * vector stats
- */
-
- //! returns number of items in the vector
- inline size_t size() const { return mVector.size(); }
- //! returns wether or not the vector is empty
- inline bool isEmpty() const { return mVector.isEmpty(); }
- //! returns how many items can be stored without reallocating the backing store
- inline size_t capacity() const { return mVector.capacity(); }
- //! setst the capacity. capacity can never be reduced less than size()
- inline ssize_t setCapacity(size_t size) { return mVector.setCapacity(size); }
-
- /*!
- * accessors
- */
- const VALUE& valueFor(const KEY& key) const;
- const VALUE& valueAt(size_t index) const;
- const KEY& keyAt(size_t index) const;
- ssize_t indexOfKey(const KEY& key) const;
-
- /*!
- * modifing the array
- */
-
- VALUE& editValueFor(const KEY& key);
- VALUE& editValueAt(size_t index);
-
- /*!
- * add/insert/replace items
- */
-
- ssize_t add(const KEY& key, const VALUE& item);
- ssize_t replaceValueFor(const KEY& key, const VALUE& item);
- ssize_t replaceValueAt(size_t index, const VALUE& item);
-
- /*!
- * remove items
- */
-
- ssize_t removeItem(const KEY& key);
- ssize_t removeItemsAt(size_t index, size_t count = 1);
-
-private:
- SortedVector< key_value_pair_t<KEY, VALUE> > mVector;
-};
-
-// ---------------------------------------------------------------------------
-
-/**
- * Variation of KeyedVector that holds a default value to return when
- * valueFor() is called with a key that doesn't exist.
- */
-template <typename KEY, typename VALUE>
-class DefaultKeyedVector : public KeyedVector<KEY, VALUE>
-{
-public:
- inline DefaultKeyedVector(const VALUE& defValue = VALUE());
- const VALUE& valueFor(const KEY& key) const;
-
-private:
- VALUE mDefault;
-};
-
-// ---------------------------------------------------------------------------
-
-template<typename KEY, typename VALUE> inline
-KeyedVector<KEY,VALUE>::KeyedVector()
-{
-}
-
-template<typename KEY, typename VALUE> inline
-ssize_t KeyedVector<KEY,VALUE>::indexOfKey(const KEY& key) const {
- return mVector.indexOf( key_value_pair_t<KEY,VALUE>(key) );
-}
-
-template<typename KEY, typename VALUE> inline
-const VALUE& KeyedVector<KEY,VALUE>::valueFor(const KEY& key) const {
- ssize_t i = indexOfKey(key);
- assert(i>=0);
- return mVector.itemAt(i).value;
-}
-
-template<typename KEY, typename VALUE> inline
-const VALUE& KeyedVector<KEY,VALUE>::valueAt(size_t index) const {
- return mVector.itemAt(index).value;
-}
-
-template<typename KEY, typename VALUE> inline
-const KEY& KeyedVector<KEY,VALUE>::keyAt(size_t index) const {
- return mVector.itemAt(index).key;
-}
-
-template<typename KEY, typename VALUE> inline
-VALUE& KeyedVector<KEY,VALUE>::editValueFor(const KEY& key) {
- ssize_t i = indexOfKey(key);
- assert(i>=0);
- return mVector.editItemAt(i).value;
-}
-
-template<typename KEY, typename VALUE> inline
-VALUE& KeyedVector<KEY,VALUE>::editValueAt(size_t index) {
- return mVector.editItemAt(index).value;
-}
-
-template<typename KEY, typename VALUE> inline
-ssize_t KeyedVector<KEY,VALUE>::add(const KEY& key, const VALUE& value) {
- return mVector.add( key_value_pair_t<KEY,VALUE>(key, value) );
-}
-
-template<typename KEY, typename VALUE> inline
-ssize_t KeyedVector<KEY,VALUE>::replaceValueFor(const KEY& key, const VALUE& value) {
- key_value_pair_t<KEY,VALUE> pair(key, value);
- mVector.remove(pair);
- return mVector.add(pair);
-}
-
-template<typename KEY, typename VALUE> inline
-ssize_t KeyedVector<KEY,VALUE>::replaceValueAt(size_t index, const VALUE& item) {
- if (index<size()) {
- mVector.editValueAt(index).value = item;
- return index;
- }
- return BAD_INDEX;
-}
-
-template<typename KEY, typename VALUE> inline
-ssize_t KeyedVector<KEY,VALUE>::removeItem(const KEY& key) {
- return mVector.remove(key_value_pair_t<KEY,VALUE>(key));
-}
-
-template<typename KEY, typename VALUE> inline
-ssize_t KeyedVector<KEY, VALUE>::removeItemsAt(size_t index, size_t count) {
- return mVector.removeItemsAt(index, count);
-}
-
-// ---------------------------------------------------------------------------
-
-template<typename KEY, typename VALUE> inline
-DefaultKeyedVector<KEY,VALUE>::DefaultKeyedVector(const VALUE& defValue)
- : mDefault(defValue)
-{
-}
-
-template<typename KEY, typename VALUE> inline
-const VALUE& DefaultKeyedVector<KEY,VALUE>::valueFor(const KEY& key) const {
- ssize_t i = indexOfKey(key);
- return i >= 0 ? KeyedVector<KEY,VALUE>::valueAt(i) : mDefault;
-}
-
-} // namespace tinyutils
-} // namespace android
-
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_PIXELFLINGER_KEYED_VECTOR_H
diff --git a/libpixelflinger/codeflinger/tinyutils/SharedBuffer.cpp b/libpixelflinger/codeflinger/tinyutils/SharedBuffer.cpp
deleted file mode 100644
index ef453fa..0000000
--- a/libpixelflinger/codeflinger/tinyutils/SharedBuffer.cpp
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright 2005 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 <stdlib.h>
-#include <string.h>
-
-#include <cutils/atomic.h>
-
-#include "SharedBuffer.h"
-
-// ---------------------------------------------------------------------------
-
-namespace android {
-namespace tinyutils {
-
-SharedBuffer* SharedBuffer::alloc(size_t size)
-{
- SharedBuffer* sb = static_cast<SharedBuffer *>(malloc(sizeof(SharedBuffer) + size));
- if (sb) {
- sb->mRefs = 1;
- sb->mSize = size;
- }
- return sb;
-}
-
-
-ssize_t SharedBuffer::dealloc(const SharedBuffer* released)
-{
- if (released->mRefs != 0) return -1; // XXX: invalid operation
- free(const_cast<SharedBuffer*>(released));
- return 0;
-}
-
-SharedBuffer* SharedBuffer::edit() const
-{
- if (onlyOwner()) {
- return const_cast<SharedBuffer*>(this);
- }
- SharedBuffer* sb = alloc(mSize);
- if (sb) {
- memcpy(sb->data(), data(), size());
- release();
- }
- return sb;
-}
-
-SharedBuffer* SharedBuffer::editResize(size_t newSize) const
-{
- if (onlyOwner()) {
- SharedBuffer* buf = const_cast<SharedBuffer*>(this);
- if (buf->mSize == newSize) return buf;
- buf = (SharedBuffer*)realloc(buf, sizeof(SharedBuffer) + newSize);
- if (buf != NULL) {
- buf->mSize = newSize;
- return buf;
- }
- }
- SharedBuffer* sb = alloc(newSize);
- if (sb) {
- const size_t mySize = mSize;
- memcpy(sb->data(), data(), newSize < mySize ? newSize : mySize);
- release();
- }
- return sb;
-}
-
-SharedBuffer* SharedBuffer::attemptEdit() const
-{
- if (onlyOwner()) {
- return const_cast<SharedBuffer*>(this);
- }
- return 0;
-}
-
-SharedBuffer* SharedBuffer::reset(size_t new_size) const
-{
- // cheap-o-reset.
- SharedBuffer* sb = alloc(new_size);
- if (sb) {
- release();
- }
- return sb;
-}
-
-void SharedBuffer::acquire() const {
- android_atomic_inc(&mRefs);
-}
-
-int32_t SharedBuffer::release(uint32_t flags) const
-{
- int32_t prev = 1;
- if (onlyOwner() || ((prev = android_atomic_dec(&mRefs)) == 1)) {
- mRefs = 0;
- if ((flags & eKeepStorage) == 0) {
- free(const_cast<SharedBuffer*>(this));
- }
- }
- return prev;
-}
-
-} // namespace tinyutils
-} // namespace android
diff --git a/libpixelflinger/codeflinger/tinyutils/SharedBuffer.h b/libpixelflinger/codeflinger/tinyutils/SharedBuffer.h
deleted file mode 100644
index d69b417..0000000
--- a/libpixelflinger/codeflinger/tinyutils/SharedBuffer.h
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright 2005 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_PIXELFLINGER_SHARED_BUFFER_H
-#define ANDROID_PIXELFLINGER_SHARED_BUFFER_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-// ---------------------------------------------------------------------------
-
-namespace android {
-namespace tinyutils {
-
-class SharedBuffer
-{
-public:
-
- /* flags to use with release() */
- enum {
- eKeepStorage = 0x00000001
- };
-
- /*! allocate a buffer of size 'size' and acquire() it.
- * call release() to free it.
- */
- static SharedBuffer* alloc(size_t size);
-
- /*! free the memory associated with the SharedBuffer.
- * Fails if there are any users associated with this SharedBuffer.
- * In other words, the buffer must have been release by all its
- * users.
- */
- static ssize_t dealloc(const SharedBuffer* released);
-
- //! get the SharedBuffer from the data pointer
- static inline const SharedBuffer* sharedBuffer(const void* data);
-
- //! access the data for read
- inline const void* data() const;
-
- //! access the data for read/write
- inline void* data();
-
- //! get size of the buffer
- inline size_t size() const;
-
- //! get back a SharedBuffer object from its data
- static inline SharedBuffer* bufferFromData(void* data);
-
- //! get back a SharedBuffer object from its data
- static inline const SharedBuffer* bufferFromData(const void* data);
-
- //! get the size of a SharedBuffer object from its data
- static inline size_t sizeFromData(const void* data);
-
- //! edit the buffer (get a writtable, or non-const, version of it)
- SharedBuffer* edit() const;
-
- //! edit the buffer, resizing if needed
- SharedBuffer* editResize(size_t size) const;
-
- //! like edit() but fails if a copy is required
- SharedBuffer* attemptEdit() const;
-
- //! resize and edit the buffer, loose it's content.
- SharedBuffer* reset(size_t size) const;
-
- //! acquire/release a reference on this buffer
- void acquire() const;
-
- /*! release a reference on this buffer, with the option of not
- * freeing the memory associated with it if it was the last reference
- * returns the previous reference count
- */
- int32_t release(uint32_t flags = 0) const;
-
- //! returns wether or not we're the only owner
- inline bool onlyOwner() const;
-
-
-private:
- inline SharedBuffer() { }
- inline ~SharedBuffer() { }
- inline SharedBuffer(const SharedBuffer&);
-
- // 16 bytes. must be sized to preserve correct alingment.
- mutable int32_t mRefs;
- size_t mSize;
- uint32_t mReserved[2];
-};
-
-// ---------------------------------------------------------------------------
-
-const SharedBuffer* SharedBuffer::sharedBuffer(const void* data) {
- return data ? reinterpret_cast<const SharedBuffer *>(data)-1 : 0;
-}
-
-const void* SharedBuffer::data() const {
- return this + 1;
-}
-
-void* SharedBuffer::data() {
- return this + 1;
-}
-
-size_t SharedBuffer::size() const {
- return mSize;
-}
-
-SharedBuffer* SharedBuffer::bufferFromData(void* data)
-{
- return ((SharedBuffer*)data)-1;
-}
-
-const SharedBuffer* SharedBuffer::bufferFromData(const void* data)
-{
- return ((const SharedBuffer*)data)-1;
-}
-
-size_t SharedBuffer::sizeFromData(const void* data)
-{
- return (((const SharedBuffer*)data)-1)->mSize;
-}
-
-bool SharedBuffer::onlyOwner() const {
- return (mRefs == 1);
-}
-
-} // namespace tinyutils
-} // namespace android
-
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_PIXELFLINGER_SHARED_BUFFER_H
diff --git a/libpixelflinger/codeflinger/tinyutils/SortedVector.h b/libpixelflinger/codeflinger/tinyutils/SortedVector.h
deleted file mode 100644
index a2b7005..0000000
--- a/libpixelflinger/codeflinger/tinyutils/SortedVector.h
+++ /dev/null
@@ -1,284 +0,0 @@
-/*
- * Copyright 2005 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_PIXELFLINGER_SORTED_VECTOR_H
-#define ANDROID_PIXELFLINGER_SORTED_VECTOR_H
-
-#include <assert.h>
-#include <stdint.h>
-#include <sys/types.h>
-
-#include "Vector.h"
-#include "VectorImpl.h"
-#include "TypeHelpers.h"
-
-// ---------------------------------------------------------------------------
-
-namespace android {
-namespace tinyutils {
-
-template <class TYPE>
-class SortedVector : private SortedVectorImpl
-{
-public:
- typedef TYPE value_type;
-
- /*!
- * Constructors and destructors
- */
-
- SortedVector();
- SortedVector(const SortedVector<TYPE>& rhs);
- virtual ~SortedVector();
-
- /*! copy operator */
- const SortedVector<TYPE>& operator = (const SortedVector<TYPE>& rhs) const;
- SortedVector<TYPE>& operator = (const SortedVector<TYPE>& rhs);
-
- /*
- * empty the vector
- */
-
- inline void clear() { VectorImpl::clear(); }
-
- /*!
- * vector stats
- */
-
- //! returns number of items in the vector
- inline size_t size() const { return VectorImpl::size(); }
- //! returns wether or not the vector is empty
- inline bool isEmpty() const { return VectorImpl::isEmpty(); }
- //! returns how many items can be stored without reallocating the backing store
- inline size_t capacity() const { return VectorImpl::capacity(); }
- //! setst the capacity. capacity can never be reduced less than size()
- inline ssize_t setCapacity(size_t size) { return VectorImpl::setCapacity(size); }
-
- /*!
- * C-style array access
- */
-
- //! read-only C-style access
- inline const TYPE* array() const;
-
- //! read-write C-style access. BE VERY CAREFUL when modifying the array
- //! you ust keep it sorted! You usually don't use this function.
- TYPE* editArray();
-
- //! finds the index of an item
- ssize_t indexOf(const TYPE& item) const;
-
- //! finds where this item should be inserted
- size_t orderOf(const TYPE& item) const;
-
-
- /*!
- * accessors
- */
-
- //! read-only access to an item at a given index
- inline const TYPE& operator [] (size_t index) const;
- //! alternate name for operator []
- inline const TYPE& itemAt(size_t index) const;
- //! stack-usage of the vector. returns the top of the stack (last element)
- const TYPE& top() const;
- //! same as operator [], but allows to access the vector backward (from the end) with a negative index
- const TYPE& mirrorItemAt(ssize_t index) const;
-
- /*!
- * modifing the array
- */
-
- //! add an item in the right place (and replace the one that is there)
- ssize_t add(const TYPE& item);
-
- //! editItemAt() MUST NOT change the order of this item
- TYPE& editItemAt(size_t index) {
- return *( static_cast<TYPE *>(VectorImpl::editItemLocation(index)) );
- }
-
- //! merges a vector into this one
- ssize_t merge(const Vector<TYPE>& vector);
- ssize_t merge(const SortedVector<TYPE>& vector);
-
- //! removes an item
- ssize_t remove(const TYPE&);
-
- //! remove several items
- inline ssize_t removeItemsAt(size_t index, size_t count = 1);
- //! remove one item
- inline ssize_t removeAt(size_t index) { return removeItemsAt(index); }
-
-protected:
- virtual void do_construct(void* storage, size_t num) const;
- virtual void do_destroy(void* storage, size_t num) const;
- virtual void do_copy(void* dest, const void* from, size_t num) const;
- virtual void do_splat(void* dest, const void* item, size_t num) const;
- virtual void do_move_forward(void* dest, const void* from, size_t num) const;
- virtual void do_move_backward(void* dest, const void* from, size_t num) const;
- virtual int do_compare(const void* lhs, const void* rhs) const;
-};
-
-
-// ---------------------------------------------------------------------------
-// No user serviceable parts from here...
-// ---------------------------------------------------------------------------
-
-template<class TYPE> inline
-SortedVector<TYPE>::SortedVector()
- : SortedVectorImpl(sizeof(TYPE),
- ((traits<TYPE>::has_trivial_ctor ? HAS_TRIVIAL_CTOR : 0)
- |(traits<TYPE>::has_trivial_dtor ? HAS_TRIVIAL_DTOR : 0)
- |(traits<TYPE>::has_trivial_copy ? HAS_TRIVIAL_COPY : 0)
- |(traits<TYPE>::has_trivial_assign ? HAS_TRIVIAL_ASSIGN : 0))
- )
-{
-}
-
-template<class TYPE> inline
-SortedVector<TYPE>::SortedVector(const SortedVector<TYPE>& rhs)
- : SortedVectorImpl(rhs) {
-}
-
-template<class TYPE> inline
-SortedVector<TYPE>::~SortedVector() {
- finish_vector();
-}
-
-template<class TYPE> inline
-SortedVector<TYPE>& SortedVector<TYPE>::operator = (const SortedVector<TYPE>& rhs) {
- SortedVectorImpl::operator = (rhs);
- return *this;
-}
-
-template<class TYPE> inline
-const SortedVector<TYPE>& SortedVector<TYPE>::operator = (const SortedVector<TYPE>& rhs) const {
- SortedVectorImpl::operator = (rhs);
- return *this;
-}
-
-template<class TYPE> inline
-const TYPE* SortedVector<TYPE>::array() const {
- return static_cast<const TYPE *>(arrayImpl());
-}
-
-template<class TYPE> inline
-TYPE* SortedVector<TYPE>::editArray() {
- return static_cast<TYPE *>(editArrayImpl());
-}
-
-
-template<class TYPE> inline
-const TYPE& SortedVector<TYPE>::operator[](size_t index) const {
- assert( index<size() );
- return *(array() + index);
-}
-
-template<class TYPE> inline
-const TYPE& SortedVector<TYPE>::itemAt(size_t index) const {
- return operator[](index);
-}
-
-template<class TYPE> inline
-const TYPE& SortedVector<TYPE>::mirrorItemAt(ssize_t index) const {
- assert( (index>0 ? index : -index)<size() );
- return *(array() + ((index<0) ? (size()-index) : index));
-}
-
-template<class TYPE> inline
-const TYPE& SortedVector<TYPE>::top() const {
- return *(array() + size() - 1);
-}
-
-template<class TYPE> inline
-ssize_t SortedVector<TYPE>::add(const TYPE& item) {
- return SortedVectorImpl::add(&item);
-}
-
-template<class TYPE> inline
-ssize_t SortedVector<TYPE>::indexOf(const TYPE& item) const {
- return SortedVectorImpl::indexOf(&item);
-}
-
-template<class TYPE> inline
-size_t SortedVector<TYPE>::orderOf(const TYPE& item) const {
- return SortedVectorImpl::orderOf(&item);
-}
-
-template<class TYPE> inline
-ssize_t SortedVector<TYPE>::merge(const Vector<TYPE>& vector) {
- return SortedVectorImpl::merge(reinterpret_cast<const VectorImpl&>(vector));
-}
-
-template<class TYPE> inline
-ssize_t SortedVector<TYPE>::merge(const SortedVector<TYPE>& vector) {
- return SortedVectorImpl::merge(reinterpret_cast<const SortedVectorImpl&>(vector));
-}
-
-template<class TYPE> inline
-ssize_t SortedVector<TYPE>::remove(const TYPE& item) {
- return SortedVectorImpl::remove(&item);
-}
-
-template<class TYPE> inline
-ssize_t SortedVector<TYPE>::removeItemsAt(size_t index, size_t count) {
- return VectorImpl::removeItemsAt(index, count);
-}
-
-// ---------------------------------------------------------------------------
-
-template<class TYPE>
-void SortedVector<TYPE>::do_construct(void* storage, size_t num) const {
- construct_type( reinterpret_cast<TYPE*>(storage), num );
-}
-
-template<class TYPE>
-void SortedVector<TYPE>::do_destroy(void* storage, size_t num) const {
- destroy_type( reinterpret_cast<TYPE*>(storage), num );
-}
-
-template<class TYPE>
-void SortedVector<TYPE>::do_copy(void* dest, const void* from, size_t num) const {
- copy_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
-}
-
-template<class TYPE>
-void SortedVector<TYPE>::do_splat(void* dest, const void* item, size_t num) const {
- splat_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(item), num );
-}
-
-template<class TYPE>
-void SortedVector<TYPE>::do_move_forward(void* dest, const void* from, size_t num) const {
- move_forward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
-}
-
-template<class TYPE>
-void SortedVector<TYPE>::do_move_backward(void* dest, const void* from, size_t num) const {
- move_backward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
-}
-
-template<class TYPE>
-int SortedVector<TYPE>::do_compare(const void* lhs, const void* rhs) const {
- return compare_type( *reinterpret_cast<const TYPE*>(lhs), *reinterpret_cast<const TYPE*>(rhs) );
-}
-
-} // namespace tinyutils
-} // namespace android
-
-
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_PIXELFLINGER_SORTED_VECTOR_H
diff --git a/libpixelflinger/codeflinger/tinyutils/TypeHelpers.h b/libpixelflinger/codeflinger/tinyutils/TypeHelpers.h
deleted file mode 100644
index 7abff07..0000000
--- a/libpixelflinger/codeflinger/tinyutils/TypeHelpers.h
+++ /dev/null
@@ -1,256 +0,0 @@
-/*
- * Copyright 2005 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_PIXELFLINGER_TYPE_HELPERS_H
-#define ANDROID_PIXELFLINGER_TYPE_HELPERS_H
-
-#include <new>
-#include <stdint.h>
-#include <string.h>
-#include <sys/types.h>
-
-// ---------------------------------------------------------------------------
-
-namespace android {
-namespace tinyutils {
-
-/*
- * Types traits
- */
-
-template <typename T> struct trait_trivial_ctor { enum { value = false }; };
-template <typename T> struct trait_trivial_dtor { enum { value = false }; };
-template <typename T> struct trait_trivial_copy { enum { value = false }; };
-template <typename T> struct trait_trivial_assign{ enum { value = false }; };
-
-template <typename T> struct trait_pointer { enum { value = false }; };
-template <typename T> struct trait_pointer<T*> { enum { value = true }; };
-
-#define ANDROID_BASIC_TYPES_TRAITS( T ) \
- template<> struct trait_trivial_ctor< T > { enum { value = true }; }; \
- template<> struct trait_trivial_dtor< T > { enum { value = true }; }; \
- template<> struct trait_trivial_copy< T > { enum { value = true }; }; \
- template<> struct trait_trivial_assign< T >{ enum { value = true }; };
-
-#define ANDROID_TYPE_TRAITS( T, ctor, dtor, copy, assign ) \
- template<> struct trait_trivial_ctor< T > { enum { value = ctor }; }; \
- template<> struct trait_trivial_dtor< T > { enum { value = dtor }; }; \
- template<> struct trait_trivial_copy< T > { enum { value = copy }; }; \
- template<> struct trait_trivial_assign< T >{ enum { value = assign }; };
-
-template <typename TYPE>
-struct traits {
- enum {
- is_pointer = trait_pointer<TYPE>::value,
- has_trivial_ctor = is_pointer || trait_trivial_ctor<TYPE>::value,
- has_trivial_dtor = is_pointer || trait_trivial_dtor<TYPE>::value,
- has_trivial_copy = is_pointer || trait_trivial_copy<TYPE>::value,
- has_trivial_assign = is_pointer || trait_trivial_assign<TYPE>::value
- };
-};
-
-template <typename T, typename U>
-struct aggregate_traits {
- enum {
- is_pointer = false,
- has_trivial_ctor = traits<T>::has_trivial_ctor && traits<U>::has_trivial_ctor,
- has_trivial_dtor = traits<T>::has_trivial_dtor && traits<U>::has_trivial_dtor,
- has_trivial_copy = traits<T>::has_trivial_copy && traits<U>::has_trivial_copy,
- has_trivial_assign = traits<T>::has_trivial_assign && traits<U>::has_trivial_assign
- };
-};
-
-// ---------------------------------------------------------------------------
-
-/*
- * basic types traits
- */
-
-ANDROID_BASIC_TYPES_TRAITS( void );
-ANDROID_BASIC_TYPES_TRAITS( bool );
-ANDROID_BASIC_TYPES_TRAITS( char );
-ANDROID_BASIC_TYPES_TRAITS( unsigned char );
-ANDROID_BASIC_TYPES_TRAITS( short );
-ANDROID_BASIC_TYPES_TRAITS( unsigned short );
-ANDROID_BASIC_TYPES_TRAITS( int );
-ANDROID_BASIC_TYPES_TRAITS( unsigned int );
-ANDROID_BASIC_TYPES_TRAITS( long );
-ANDROID_BASIC_TYPES_TRAITS( unsigned long );
-ANDROID_BASIC_TYPES_TRAITS( long long );
-ANDROID_BASIC_TYPES_TRAITS( unsigned long long );
-ANDROID_BASIC_TYPES_TRAITS( float );
-ANDROID_BASIC_TYPES_TRAITS( double );
-
-// ---------------------------------------------------------------------------
-
-
-/*
- * compare and order types
- */
-
-template<typename TYPE> inline
-int strictly_order_type(const TYPE& lhs, const TYPE& rhs) {
- return (lhs < rhs) ? 1 : 0;
-}
-
-template<typename TYPE> inline
-int compare_type(const TYPE& lhs, const TYPE& rhs) {
- return strictly_order_type(rhs, lhs) - strictly_order_type(lhs, rhs);
-}
-
-/*
- * create, destroy, copy and assign types...
- */
-
-template<typename TYPE> inline
-void construct_type(TYPE* p, size_t n) {
- if (!traits<TYPE>::has_trivial_ctor) {
- while (n--) {
- new(p++) TYPE;
- }
- }
-}
-
-template<typename TYPE> inline
-void destroy_type(TYPE* p, size_t n) {
- if (!traits<TYPE>::has_trivial_dtor) {
- while (n--) {
- p->~TYPE();
- p++;
- }
- }
-}
-
-template<typename TYPE> inline
-void copy_type(TYPE* d, const TYPE* s, size_t n) {
- if (!traits<TYPE>::has_trivial_copy) {
- while (n--) {
- new(d) TYPE(*s);
- d++, s++;
- }
- } else {
- memcpy(d,s,n*sizeof(TYPE));
- }
-}
-
-template<typename TYPE> inline
-void assign_type(TYPE* d, const TYPE* s, size_t n) {
- if (!traits<TYPE>::has_trivial_assign) {
- while (n--) {
- *d++ = *s++;
- }
- } else {
- memcpy(d,s,n*sizeof(TYPE));
- }
-}
-
-template<typename TYPE> inline
-void splat_type(TYPE* where, const TYPE* what, size_t n) {
- if (!traits<TYPE>::has_trivial_copy) {
- while (n--) {
- new(where) TYPE(*what);
- where++;
- }
- } else {
- while (n--) {
- *where++ = *what;
- }
- }
-}
-
-template<typename TYPE> inline
-void move_forward_type(TYPE* d, const TYPE* s, size_t n = 1) {
- if (!traits<TYPE>::has_trivial_copy || !traits<TYPE>::has_trivial_dtor) {
- d += n;
- s += n;
- while (n--) {
- --d, --s;
- if (!traits<TYPE>::has_trivial_copy) {
- new(d) TYPE(*s);
- } else {
- *d = *s;
- }
- if (!traits<TYPE>::has_trivial_dtor) {
- s->~TYPE();
- }
- }
- } else {
- memmove(d,s,n*sizeof(TYPE));
- }
-}
-
-template<typename TYPE> inline
-void move_backward_type(TYPE* d, const TYPE* s, size_t n = 1) {
- if (!traits<TYPE>::has_trivial_copy || !traits<TYPE>::has_trivial_dtor) {
- while (n--) {
- if (!traits<TYPE>::has_trivial_copy) {
- new(d) TYPE(*s);
- } else {
- *d = *s;
- }
- if (!traits<TYPE>::has_trivial_dtor) {
- s->~TYPE();
- }
- d++, s++;
- }
- } else {
- memmove(d,s,n*sizeof(TYPE));
- }
-}
-// ---------------------------------------------------------------------------
-
-/*
- * a key/value pair
- */
-
-template <typename KEY, typename VALUE>
-struct key_value_pair_t {
- KEY key;
- VALUE value;
- key_value_pair_t() { }
- key_value_pair_t(const key_value_pair_t& o) : key(o.key), value(o.value) { }
- key_value_pair_t(const KEY& k, const VALUE& v) : key(k), value(v) { }
- key_value_pair_t(const KEY& k) : key(k) { }
- inline bool operator < (const key_value_pair_t& o) const {
- return strictly_order_type(key, o.key);
- }
-};
-
-template<>
-template <typename K, typename V>
-struct trait_trivial_ctor< key_value_pair_t<K, V> >
-{ enum { value = aggregate_traits<K,V>::has_trivial_ctor }; };
-template<>
-template <typename K, typename V>
-struct trait_trivial_dtor< key_value_pair_t<K, V> >
-{ enum { value = aggregate_traits<K,V>::has_trivial_dtor }; };
-template<>
-template <typename K, typename V>
-struct trait_trivial_copy< key_value_pair_t<K, V> >
-{ enum { value = aggregate_traits<K,V>::has_trivial_copy }; };
-template<>
-template <typename K, typename V>
-struct trait_trivial_assign< key_value_pair_t<K, V> >
-{ enum { value = aggregate_traits<K,V>::has_trivial_assign};};
-
-// ---------------------------------------------------------------------------
-
-} // namespace tinyutils
-} // namespace android
-
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_PIXELFLINGER_TYPE_HELPERS_H
diff --git a/libpixelflinger/codeflinger/tinyutils/Vector.h b/libpixelflinger/codeflinger/tinyutils/Vector.h
deleted file mode 100644
index c07a17a..0000000
--- a/libpixelflinger/codeflinger/tinyutils/Vector.h
+++ /dev/null
@@ -1,353 +0,0 @@
-/*
- * Copyright 2005 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_PIXELFLINGER_VECTOR_H
-#define ANDROID_PIXELFLINGER_VECTOR_H
-
-#include <new>
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <cutils/log.h>
-
-#include "Errors.h"
-#include "VectorImpl.h"
-#include "TypeHelpers.h"
-
-// ---------------------------------------------------------------------------
-
-namespace android {
-namespace tinyutils {
-
-/*!
- * The main templated vector class ensuring type safety
- * while making use of VectorImpl.
- * This is the class users want to use.
- */
-
-template <class TYPE>
-class Vector : private VectorImpl
-{
-public:
- typedef TYPE value_type;
-
- /*!
- * Constructors and destructors
- */
-
- Vector();
- Vector(const Vector<TYPE>& rhs);
- virtual ~Vector();
-
- /*! copy operator */
- const Vector<TYPE>& operator = (const Vector<TYPE>& rhs) const;
- Vector<TYPE>& operator = (const Vector<TYPE>& rhs);
-
- /*
- * empty the vector
- */
-
- inline void clear() { VectorImpl::clear(); }
-
- /*!
- * vector stats
- */
-
- //! returns number of items in the vector
- inline size_t size() const { return VectorImpl::size(); }
- //! returns wether or not the vector is empty
- inline bool isEmpty() const { return VectorImpl::isEmpty(); }
- //! returns how many items can be stored without reallocating the backing store
- inline size_t capacity() const { return VectorImpl::capacity(); }
- //! setst the capacity. capacity can never be reduced less than size()
- inline ssize_t setCapacity(size_t size) { return VectorImpl::setCapacity(size); }
-
- /*!
- * C-style array access
- */
-
- //! read-only C-style access
- inline const TYPE* array() const;
- //! read-write C-style access
- TYPE* editArray();
-
- /*!
- * accessors
- */
-
- //! read-only access to an item at a given index
- inline const TYPE& operator [] (size_t index) const;
- //! alternate name for operator []
- inline const TYPE& itemAt(size_t index) const;
- //! stack-usage of the vector. returns the top of the stack (last element)
- const TYPE& top() const;
- //! same as operator [], but allows to access the vector backward (from the end) with a negative index
- const TYPE& mirrorItemAt(ssize_t index) const;
-
- /*!
- * modifing the array
- */
-
- //! copy-on write support, grants write access to an item
- TYPE& editItemAt(size_t index);
- //! grants right acces to the top of the stack (last element)
- TYPE& editTop();
-
- /*!
- * append/insert another vector
- */
-
- //! insert another vector at a given index
- ssize_t insertVectorAt(const Vector<TYPE>& vector, size_t index);
-
- //! append another vector at the end of this one
- ssize_t appendVector(const Vector<TYPE>& vector);
-
-
- /*!
- * add/insert/replace items
- */
-
- //! insert one or several items initialized with their default constructor
- inline ssize_t insertAt(size_t index, size_t numItems = 1);
- //! insert on onr several items initialized from a prototype item
- ssize_t insertAt(const TYPE& prototype_item, size_t index, size_t numItems = 1);
- //! pop the top of the stack (removes the last element). No-op if the stack's empty
- inline void pop();
- //! pushes an item initialized with its default constructor
- inline void push();
- //! pushes an item on the top of the stack
- void push(const TYPE& item);
- //! same as push() but returns the index the item was added at (or an error)
- inline ssize_t add();
- //! same as push() but returns the index the item was added at (or an error)
- ssize_t add(const TYPE& item);
- //! replace an item with a new one initialized with its default constructor
- inline ssize_t replaceAt(size_t index);
- //! replace an item with a new one
- ssize_t replaceAt(const TYPE& item, size_t index);
-
- /*!
- * remove items
- */
-
- //! remove several items
- inline ssize_t removeItemsAt(size_t index, size_t count = 1);
- //! remove one item
- inline ssize_t removeAt(size_t index) { return removeItemsAt(index); }
-
- /*!
- * sort (stable) the array
- */
-
- typedef int (*compar_t)(const TYPE* lhs, const TYPE* rhs);
- typedef int (*compar_r_t)(const TYPE* lhs, const TYPE* rhs, void* state);
-
- inline status_t sort(compar_t cmp);
- inline status_t sort(compar_r_t cmp, void* state);
-
-protected:
- virtual void do_construct(void* storage, size_t num) const;
- virtual void do_destroy(void* storage, size_t num) const;
- virtual void do_copy(void* dest, const void* from, size_t num) const;
- virtual void do_splat(void* dest, const void* item, size_t num) const;
- virtual void do_move_forward(void* dest, const void* from, size_t num) const;
- virtual void do_move_backward(void* dest, const void* from, size_t num) const;
-};
-
-
-// ---------------------------------------------------------------------------
-// No user serviceable parts from here...
-// ---------------------------------------------------------------------------
-
-template<class TYPE> inline
-Vector<TYPE>::Vector()
- : VectorImpl(sizeof(TYPE),
- ((traits<TYPE>::has_trivial_ctor ? HAS_TRIVIAL_CTOR : 0)
- |(traits<TYPE>::has_trivial_dtor ? HAS_TRIVIAL_DTOR : 0)
- |(traits<TYPE>::has_trivial_copy ? HAS_TRIVIAL_COPY : 0)
- |(traits<TYPE>::has_trivial_assign ? HAS_TRIVIAL_ASSIGN : 0))
- )
-{
-}
-
-template<class TYPE> inline
-Vector<TYPE>::Vector(const Vector<TYPE>& rhs)
- : VectorImpl(rhs) {
-}
-
-template<class TYPE> inline
-Vector<TYPE>::~Vector() {
- finish_vector();
-}
-
-template<class TYPE> inline
-Vector<TYPE>& Vector<TYPE>::operator = (const Vector<TYPE>& rhs) {
- VectorImpl::operator = (rhs);
- return *this;
-}
-
-template<class TYPE> inline
-const Vector<TYPE>& Vector<TYPE>::operator = (const Vector<TYPE>& rhs) const {
- VectorImpl::operator = (rhs);
- return *this;
-}
-
-template<class TYPE> inline
-const TYPE* Vector<TYPE>::array() const {
- return static_cast<const TYPE *>(arrayImpl());
-}
-
-template<class TYPE> inline
-TYPE* Vector<TYPE>::editArray() {
- return static_cast<TYPE *>(editArrayImpl());
-}
-
-
-template<class TYPE> inline
-const TYPE& Vector<TYPE>::operator[](size_t index) const {
- LOG_FATAL_IF( index>=size(),
- "itemAt: index %d is past size %d", (int)index, (int)size() );
- return *(array() + index);
-}
-
-template<class TYPE> inline
-const TYPE& Vector<TYPE>::itemAt(size_t index) const {
- return operator[](index);
-}
-
-template<class TYPE> inline
-const TYPE& Vector<TYPE>::mirrorItemAt(ssize_t index) const {
- LOG_FATAL_IF( (index>0 ? index : -index)>=size(),
- "mirrorItemAt: index %d is past size %d",
- (int)index, (int)size() );
- return *(array() + ((index<0) ? (size()-index) : index));
-}
-
-template<class TYPE> inline
-const TYPE& Vector<TYPE>::top() const {
- return *(array() + size() - 1);
-}
-
-template<class TYPE> inline
-TYPE& Vector<TYPE>::editItemAt(size_t index) {
- return *( static_cast<TYPE *>(editItemLocation(index)) );
-}
-
-template<class TYPE> inline
-TYPE& Vector<TYPE>::editTop() {
- return *( static_cast<TYPE *>(editItemLocation(size()-1)) );
-}
-
-template<class TYPE> inline
-ssize_t Vector<TYPE>::insertVectorAt(const Vector<TYPE>& vector, size_t index) {
- return VectorImpl::insertVectorAt(reinterpret_cast<const VectorImpl&>(vector), index);
-}
-
-template<class TYPE> inline
-ssize_t Vector<TYPE>::appendVector(const Vector<TYPE>& vector) {
- return VectorImpl::appendVector(reinterpret_cast<const VectorImpl&>(vector));
-}
-
-template<class TYPE> inline
-ssize_t Vector<TYPE>::insertAt(const TYPE& item, size_t index, size_t numItems) {
- return VectorImpl::insertAt(&item, index, numItems);
-}
-
-template<class TYPE> inline
-void Vector<TYPE>::push(const TYPE& item) {
- return VectorImpl::push(&item);
-}
-
-template<class TYPE> inline
-ssize_t Vector<TYPE>::add(const TYPE& item) {
- return VectorImpl::add(&item);
-}
-
-template<class TYPE> inline
-ssize_t Vector<TYPE>::replaceAt(const TYPE& item, size_t index) {
- return VectorImpl::replaceAt(&item, index);
-}
-
-template<class TYPE> inline
-ssize_t Vector<TYPE>::insertAt(size_t index, size_t numItems) {
- return VectorImpl::insertAt(index, numItems);
-}
-
-template<class TYPE> inline
-void Vector<TYPE>::pop() {
- VectorImpl::pop();
-}
-
-template<class TYPE> inline
-void Vector<TYPE>::push() {
- VectorImpl::push();
-}
-
-template<class TYPE> inline
-ssize_t Vector<TYPE>::add() {
- return VectorImpl::add();
-}
-
-template<class TYPE> inline
-ssize_t Vector<TYPE>::replaceAt(size_t index) {
- return VectorImpl::replaceAt(index);
-}
-
-template<class TYPE> inline
-ssize_t Vector<TYPE>::removeItemsAt(size_t index, size_t count) {
- return VectorImpl::removeItemsAt(index, count);
-}
-
-// ---------------------------------------------------------------------------
-
-template<class TYPE>
-void Vector<TYPE>::do_construct(void* storage, size_t num) const {
- construct_type( reinterpret_cast<TYPE*>(storage), num );
-}
-
-template<class TYPE>
-void Vector<TYPE>::do_destroy(void* storage, size_t num) const {
- destroy_type( reinterpret_cast<TYPE*>(storage), num );
-}
-
-template<class TYPE>
-void Vector<TYPE>::do_copy(void* dest, const void* from, size_t num) const {
- copy_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
-}
-
-template<class TYPE>
-void Vector<TYPE>::do_splat(void* dest, const void* item, size_t num) const {
- splat_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(item), num );
-}
-
-template<class TYPE>
-void Vector<TYPE>::do_move_forward(void* dest, const void* from, size_t num) const {
- move_forward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
-}
-
-template<class TYPE>
-void Vector<TYPE>::do_move_backward(void* dest, const void* from, size_t num) const {
- move_backward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
-}
-
-} // namespace tinyutils
-} // namespace android
-
-
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_PIXELFLINGER_VECTOR_H
diff --git a/libpixelflinger/codeflinger/tinyutils/VectorImpl.cpp b/libpixelflinger/codeflinger/tinyutils/VectorImpl.cpp
deleted file mode 100644
index 689129a..0000000
--- a/libpixelflinger/codeflinger/tinyutils/VectorImpl.cpp
+++ /dev/null
@@ -1,555 +0,0 @@
-/*
- * Copyright 2005 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "Vector"
-
-#include <string.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <errno.h>
-
-#include <cutils/log.h>
-
-#include "Errors.h"
-#include "SharedBuffer.h"
-#include "VectorImpl.h"
-
-/*****************************************************************************/
-
-
-namespace android {
-namespace tinyutils {
-
-// ----------------------------------------------------------------------------
-
-const size_t kMinVectorCapacity = 4;
-
-static inline size_t max(size_t a, size_t b) {
- return a>b ? a : b;
-}
-
-// ----------------------------------------------------------------------------
-
-VectorImpl::VectorImpl(size_t itemSize, uint32_t flags)
- : mStorage(0), mCount(0), mFlags(flags), mItemSize(itemSize)
-{
-}
-
-VectorImpl::VectorImpl(const VectorImpl& rhs)
- : mStorage(rhs.mStorage), mCount(rhs.mCount),
- mFlags(rhs.mFlags), mItemSize(rhs.mItemSize)
-{
- if (mStorage) {
- SharedBuffer::sharedBuffer(mStorage)->acquire();
- }
-}
-
-VectorImpl::~VectorImpl()
-{
- ALOG_ASSERT(!mCount,
- "[%p] "
- "subclasses of VectorImpl must call finish_vector()"
- " in their destructor. Leaking %d bytes.",
- this, (int)(mCount*mItemSize));
- // We can't call _do_destroy() here because the vtable is already gone.
-}
-
-VectorImpl& VectorImpl::operator = (const VectorImpl& rhs)
-{
- ALOG_ASSERT(mItemSize == rhs.mItemSize,
- "Vector<> have different types (this=%p, rhs=%p)", this, &rhs);
- if (this != &rhs) {
- release_storage();
- if (rhs.mCount) {
- mStorage = rhs.mStorage;
- mCount = rhs.mCount;
- SharedBuffer::sharedBuffer(mStorage)->acquire();
- } else {
- mStorage = 0;
- mCount = 0;
- }
- }
- return *this;
-}
-
-void* VectorImpl::editArrayImpl()
-{
- if (mStorage) {
- SharedBuffer* sb = SharedBuffer::sharedBuffer(mStorage)->attemptEdit();
- if (sb == 0) {
- sb = SharedBuffer::alloc(capacity() * mItemSize);
- if (sb) {
- _do_copy(sb->data(), mStorage, mCount);
- release_storage();
- mStorage = sb->data();
- }
- }
- }
- return mStorage;
-}
-
-size_t VectorImpl::capacity() const
-{
- if (mStorage) {
- return SharedBuffer::sharedBuffer(mStorage)->size() / mItemSize;
- }
- return 0;
-}
-
-ssize_t VectorImpl::insertVectorAt(const VectorImpl& vector, size_t index)
-{
- if (index > size())
- return BAD_INDEX;
- void* where = _grow(index, vector.size());
- if (where) {
- _do_copy(where, vector.arrayImpl(), vector.size());
- }
- return where ? index : (ssize_t)NO_MEMORY;
-}
-
-ssize_t VectorImpl::appendVector(const VectorImpl& vector)
-{
- return insertVectorAt(vector, size());
-}
-
-ssize_t VectorImpl::insertAt(size_t index, size_t numItems)
-{
- return insertAt(0, index, numItems);
-}
-
-ssize_t VectorImpl::insertAt(const void* item, size_t index, size_t numItems)
-{
- if (index > size())
- return BAD_INDEX;
- void* where = _grow(index, numItems);
- if (where) {
- if (item) {
- _do_splat(where, item, numItems);
- } else {
- _do_construct(where, numItems);
- }
- }
- return where ? index : (ssize_t)NO_MEMORY;
-}
-
-void VectorImpl::pop()
-{
- if (size())
- removeItemsAt(size()-1, 1);
-}
-
-void VectorImpl::push()
-{
- push(0);
-}
-
-void VectorImpl::push(const void* item)
-{
- insertAt(item, size());
-}
-
-ssize_t VectorImpl::add()
-{
- return add(0);
-}
-
-ssize_t VectorImpl::add(const void* item)
-{
- return insertAt(item, size());
-}
-
-ssize_t VectorImpl::replaceAt(size_t index)
-{
- return replaceAt(0, index);
-}
-
-ssize_t VectorImpl::replaceAt(const void* prototype, size_t index)
-{
- ALOG_ASSERT(index<size(),
- "[%p] replace: index=%d, size=%d", this, (int)index, (int)size());
-
- void* item = editItemLocation(index);
- if (item == 0)
- return NO_MEMORY;
- _do_destroy(item, 1);
- if (prototype == 0) {
- _do_construct(item, 1);
- } else {
- _do_copy(item, prototype, 1);
- }
- return ssize_t(index);
-}
-
-ssize_t VectorImpl::removeItemsAt(size_t index, size_t count)
-{
- ALOG_ASSERT((index+count)<=size(),
- "[%p] remove: index=%d, count=%d, size=%d",
- this, (int)index, (int)count, (int)size());
-
- if ((index+count) > size())
- return BAD_VALUE;
- _shrink(index, count);
- return index;
-}
-
-void VectorImpl::finish_vector()
-{
- release_storage();
- mStorage = 0;
- mCount = 0;
-}
-
-void VectorImpl::clear()
-{
- _shrink(0, mCount);
-}
-
-void* VectorImpl::editItemLocation(size_t index)
-{
- ALOG_ASSERT(index<capacity(),
- "[%p] itemLocation: index=%d, capacity=%d, count=%d",
- this, (int)index, (int)capacity(), (int)mCount);
-
- void* buffer = editArrayImpl();
- if (buffer)
- return reinterpret_cast<char*>(buffer) + index*mItemSize;
- return 0;
-}
-
-const void* VectorImpl::itemLocation(size_t index) const
-{
- ALOG_ASSERT(index<capacity(),
- "[%p] editItemLocation: index=%d, capacity=%d, count=%d",
- this, (int)index, (int)capacity(), (int)mCount);
-
- const void* buffer = arrayImpl();
- if (buffer)
- return reinterpret_cast<const char*>(buffer) + index*mItemSize;
- return 0;
-}
-
-ssize_t VectorImpl::setCapacity(size_t new_capacity)
-{
- size_t current_capacity = capacity();
- ssize_t amount = new_capacity - size();
- if (amount <= 0) {
- // we can't reduce the capacity
- return current_capacity;
- }
- SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize);
- if (sb) {
- void* array = sb->data();
- _do_copy(array, mStorage, size());
- release_storage();
- mStorage = const_cast<void*>(array);
- } else {
- return NO_MEMORY;
- }
- return new_capacity;
-}
-
-void VectorImpl::release_storage()
-{
- if (mStorage) {
- const SharedBuffer* sb = SharedBuffer::sharedBuffer(mStorage);
- if (sb->release(SharedBuffer::eKeepStorage) == 1) {
- _do_destroy(mStorage, mCount);
- SharedBuffer::dealloc(sb);
- }
- }
-}
-
-void* VectorImpl::_grow(size_t where, size_t amount)
-{
-// ALOGV("_grow(this=%p, where=%d, amount=%d) count=%d, capacity=%d",
-// this, (int)where, (int)amount, (int)mCount, (int)capacity());
-
- if (where > mCount)
- where = mCount;
-
- const size_t new_size = mCount + amount;
- if (capacity() < new_size) {
- const size_t new_capacity = max(kMinVectorCapacity, ((new_size*3)+1)/2);
-// ALOGV("grow vector %p, new_capacity=%d", this, (int)new_capacity);
- if ((mStorage) &&
- (mCount==where) &&
- (mFlags & HAS_TRIVIAL_COPY) &&
- (mFlags & HAS_TRIVIAL_DTOR))
- {
- const SharedBuffer* cur_sb = SharedBuffer::sharedBuffer(mStorage);
- SharedBuffer* sb = cur_sb->editResize(new_capacity * mItemSize);
- mStorage = sb->data();
- } else {
- SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize);
- if (sb) {
- void* array = sb->data();
- if (where>0) {
- _do_copy(array, mStorage, where);
- }
- if (mCount>where) {
- const void* from = reinterpret_cast<const uint8_t *>(mStorage) + where*mItemSize;
- void* dest = reinterpret_cast<uint8_t *>(array) + (where+amount)*mItemSize;
- _do_copy(dest, from, mCount-where);
- }
- release_storage();
- mStorage = const_cast<void*>(array);
- }
- }
- } else {
- ssize_t s = mCount-where;
- if (s>0) {
- void* array = editArrayImpl();
- void* to = reinterpret_cast<uint8_t *>(array) + (where+amount)*mItemSize;
- const void* from = reinterpret_cast<const uint8_t *>(array) + where*mItemSize;
- _do_move_forward(to, from, s);
- }
- }
- mCount += amount;
- void* free_space = const_cast<void*>(itemLocation(where));
- return free_space;
-}
-
-void VectorImpl::_shrink(size_t where, size_t amount)
-{
- if (!mStorage)
- return;
-
-// ALOGV("_shrink(this=%p, where=%d, amount=%d) count=%d, capacity=%d",
-// this, (int)where, (int)amount, (int)mCount, (int)capacity());
-
- if (where >= mCount)
- where = mCount - amount;
-
- const size_t new_size = mCount - amount;
- if (new_size*3 < capacity()) {
- const size_t new_capacity = max(kMinVectorCapacity, new_size*2);
-// ALOGV("shrink vector %p, new_capacity=%d", this, (int)new_capacity);
- if ((where == mCount-amount) &&
- (mFlags & HAS_TRIVIAL_COPY) &&
- (mFlags & HAS_TRIVIAL_DTOR))
- {
- const SharedBuffer* cur_sb = SharedBuffer::sharedBuffer(mStorage);
- SharedBuffer* sb = cur_sb->editResize(new_capacity * mItemSize);
- mStorage = sb->data();
- } else {
- SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize);
- if (sb) {
- void* array = sb->data();
- if (where>0) {
- _do_copy(array, mStorage, where);
- }
- if (mCount > where+amount) {
- const void* from = reinterpret_cast<const uint8_t *>(mStorage) + (where+amount)*mItemSize;
- void* dest = reinterpret_cast<uint8_t *>(array) + where*mItemSize;
- _do_copy(dest, from, mCount-(where+amount));
- }
- release_storage();
- mStorage = const_cast<void*>(array);
- }
- }
- } else {
- void* array = editArrayImpl();
- void* to = reinterpret_cast<uint8_t *>(array) + where*mItemSize;
- _do_destroy(to, amount);
- ssize_t s = mCount-(where+amount);
- if (s>0) {
- const void* from = reinterpret_cast<uint8_t *>(array) + (where+amount)*mItemSize;
- _do_move_backward(to, from, s);
- }
- }
-
- // adjust the number of items...
- mCount -= amount;
-}
-
-size_t VectorImpl::itemSize() const {
- return mItemSize;
-}
-
-void VectorImpl::_do_construct(void* storage, size_t num) const
-{
- if (!(mFlags & HAS_TRIVIAL_CTOR)) {
- do_construct(storage, num);
- }
-}
-
-void VectorImpl::_do_destroy(void* storage, size_t num) const
-{
- if (!(mFlags & HAS_TRIVIAL_DTOR)) {
- do_destroy(storage, num);
- }
-}
-
-void VectorImpl::_do_copy(void* dest, const void* from, size_t num) const
-{
- if (!(mFlags & HAS_TRIVIAL_COPY)) {
- do_copy(dest, from, num);
- } else {
- memcpy(dest, from, num*itemSize());
- }
-}
-
-void VectorImpl::_do_splat(void* dest, const void* item, size_t num) const {
- do_splat(dest, item, num);
-}
-
-void VectorImpl::_do_move_forward(void* dest, const void* from, size_t num) const {
- do_move_forward(dest, from, num);
-}
-
-void VectorImpl::_do_move_backward(void* dest, const void* from, size_t num) const {
- do_move_backward(dest, from, num);
-}
-
-void VectorImpl::reservedVectorImpl1() { }
-void VectorImpl::reservedVectorImpl2() { }
-void VectorImpl::reservedVectorImpl3() { }
-void VectorImpl::reservedVectorImpl4() { }
-void VectorImpl::reservedVectorImpl5() { }
-void VectorImpl::reservedVectorImpl6() { }
-void VectorImpl::reservedVectorImpl7() { }
-void VectorImpl::reservedVectorImpl8() { }
-
-/*****************************************************************************/
-
-SortedVectorImpl::SortedVectorImpl(size_t itemSize, uint32_t flags)
- : VectorImpl(itemSize, flags)
-{
-}
-
-SortedVectorImpl::SortedVectorImpl(const VectorImpl& rhs)
-: VectorImpl(rhs)
-{
-}
-
-SortedVectorImpl::~SortedVectorImpl()
-{
-}
-
-SortedVectorImpl& SortedVectorImpl::operator = (const SortedVectorImpl& rhs)
-{
- return static_cast<SortedVectorImpl&>( VectorImpl::operator = (static_cast<const VectorImpl&>(rhs)) );
-}
-
-ssize_t SortedVectorImpl::indexOf(const void* item) const
-{
- return _indexOrderOf(item);
-}
-
-size_t SortedVectorImpl::orderOf(const void* item) const
-{
- size_t o;
- _indexOrderOf(item, &o);
- return o;
-}
-
-ssize_t SortedVectorImpl::_indexOrderOf(const void* item, size_t* order) const
-{
- // binary search
- ssize_t err = NAME_NOT_FOUND;
- ssize_t l = 0;
- ssize_t h = size()-1;
- ssize_t mid;
- const void* a = arrayImpl();
- const size_t s = itemSize();
- while (l <= h) {
- mid = l + (h - l)/2;
- const void* const curr = reinterpret_cast<const char *>(a) + (mid*s);
- const int c = do_compare(curr, item);
- if (c == 0) {
- err = l = mid;
- break;
- } else if (c < 0) {
- l = mid + 1;
- } else {
- h = mid - 1;
- }
- }
- if (order) *order = l;
- return err;
-}
-
-ssize_t SortedVectorImpl::add(const void* item)
-{
- size_t order;
- ssize_t index = _indexOrderOf(item, &order);
- if (index < 0) {
- index = VectorImpl::insertAt(item, order, 1);
- } else {
- index = VectorImpl::replaceAt(item, index);
- }
- return index;
-}
-
-ssize_t SortedVectorImpl::merge(const VectorImpl& vector)
-{
- // naive merge...
- if (!vector.isEmpty()) {
- const void* buffer = vector.arrayImpl();
- const size_t is = itemSize();
- size_t s = vector.size();
- for (size_t i=0 ; i<s ; i++) {
- ssize_t err = add( reinterpret_cast<const char*>(buffer) + i*is );
- if (err<0) {
- return err;
- }
- }
- }
- return NO_ERROR;
-}
-
-ssize_t SortedVectorImpl::merge(const SortedVectorImpl& vector)
-{
- // we've merging a sorted vector... nice!
- ssize_t err = NO_ERROR;
- if (!vector.isEmpty()) {
- // first take care of the case where the vectors are sorted together
- if (do_compare(vector.itemLocation(vector.size()-1), arrayImpl()) <= 0) {
- err = VectorImpl::insertVectorAt(static_cast<const VectorImpl&>(vector), 0);
- } else if (do_compare(vector.arrayImpl(), itemLocation(size()-1)) >= 0) {
- err = VectorImpl::appendVector(static_cast<const VectorImpl&>(vector));
- } else {
- // this could be made a little better
- err = merge(static_cast<const VectorImpl&>(vector));
- }
- }
- return err;
-}
-
-ssize_t SortedVectorImpl::remove(const void* item)
-{
- ssize_t i = indexOf(item);
- if (i>=0) {
- VectorImpl::removeItemsAt(i, 1);
- }
- return i;
-}
-
-void SortedVectorImpl::reservedSortedVectorImpl1() { };
-void SortedVectorImpl::reservedSortedVectorImpl2() { };
-void SortedVectorImpl::reservedSortedVectorImpl3() { };
-void SortedVectorImpl::reservedSortedVectorImpl4() { };
-void SortedVectorImpl::reservedSortedVectorImpl5() { };
-void SortedVectorImpl::reservedSortedVectorImpl6() { };
-void SortedVectorImpl::reservedSortedVectorImpl7() { };
-void SortedVectorImpl::reservedSortedVectorImpl8() { };
-
-
-/*****************************************************************************/
-
-} // namespace tinyutils
-} // namespace android
-
diff --git a/libpixelflinger/codeflinger/tinyutils/VectorImpl.h b/libpixelflinger/codeflinger/tinyutils/VectorImpl.h
deleted file mode 100644
index 56089b3..0000000
--- a/libpixelflinger/codeflinger/tinyutils/VectorImpl.h
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- * Copyright 2005 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_PIXELFLINGER_VECTOR_IMPL_H
-#define ANDROID_PIXELFLINGER_VECTOR_IMPL_H
-
-#include <assert.h>
-#include <stdint.h>
-#include <sys/types.h>
-
-// ---------------------------------------------------------------------------
-// No user serviceable parts in here...
-// ---------------------------------------------------------------------------
-
-namespace android {
-namespace tinyutils {
-
-/*!
- * Implementation of the guts of the vector<> class
- * this ensures backward binary compatibility and
- * reduces code size.
- * For performance reasons, we expose mStorage and mCount
- * so these fields are set in stone.
- *
- */
-
-class VectorImpl
-{
-public:
- enum { // flags passed to the ctor
- HAS_TRIVIAL_CTOR = 0x00000001,
- HAS_TRIVIAL_DTOR = 0x00000002,
- HAS_TRIVIAL_COPY = 0x00000004,
- HAS_TRIVIAL_ASSIGN = 0x00000008
- };
-
- VectorImpl(size_t itemSize, uint32_t flags);
- VectorImpl(const VectorImpl& rhs);
- virtual ~VectorImpl();
-
- /*! must be called from subclasses destructor */
- void finish_vector();
-
- VectorImpl& operator = (const VectorImpl& rhs);
-
- /*! C-style array access */
- inline const void* arrayImpl() const { return mStorage; }
- void* editArrayImpl();
-
- /*! vector stats */
- inline size_t size() const { return mCount; }
- inline bool isEmpty() const { return mCount == 0; }
- size_t capacity() const;
- ssize_t setCapacity(size_t size);
-
- /*! append/insert another vector */
- ssize_t insertVectorAt(const VectorImpl& vector, size_t index);
- ssize_t appendVector(const VectorImpl& vector);
-
- /*! add/insert/replace items */
- ssize_t insertAt(size_t where, size_t numItems = 1);
- ssize_t insertAt(const void* item, size_t where, size_t numItems = 1);
- void pop();
- void push();
- void push(const void* item);
- ssize_t add();
- ssize_t add(const void* item);
- ssize_t replaceAt(size_t index);
- ssize_t replaceAt(const void* item, size_t index);
-
- /*! remove items */
- ssize_t removeItemsAt(size_t index, size_t count = 1);
- void clear();
-
- const void* itemLocation(size_t index) const;
- void* editItemLocation(size_t index);
-
-protected:
- size_t itemSize() const;
- void release_storage();
-
- virtual void do_construct(void* storage, size_t num) const = 0;
- virtual void do_destroy(void* storage, size_t num) const = 0;
- virtual void do_copy(void* dest, const void* from, size_t num) const = 0;
- virtual void do_splat(void* dest, const void* item, size_t num) const = 0;
- virtual void do_move_forward(void* dest, const void* from, size_t num) const = 0;
- virtual void do_move_backward(void* dest, const void* from, size_t num) const = 0;
-
- // take care of FBC...
- virtual void reservedVectorImpl1();
- virtual void reservedVectorImpl2();
- virtual void reservedVectorImpl3();
- virtual void reservedVectorImpl4();
- virtual void reservedVectorImpl5();
- virtual void reservedVectorImpl6();
- virtual void reservedVectorImpl7();
- virtual void reservedVectorImpl8();
-
-private:
- void* _grow(size_t where, size_t amount);
- void _shrink(size_t where, size_t amount);
-
- inline void _do_construct(void* storage, size_t num) const;
- inline void _do_destroy(void* storage, size_t num) const;
- inline void _do_copy(void* dest, const void* from, size_t num) const;
- inline void _do_splat(void* dest, const void* item, size_t num) const;
- inline void _do_move_forward(void* dest, const void* from, size_t num) const;
- inline void _do_move_backward(void* dest, const void* from, size_t num) const;
-
- // These 2 fields are exposed in the inlines below,
- // so they're set in stone.
- void * mStorage; // base address of the vector
- size_t mCount; // number of items
-
- const uint32_t mFlags;
- const size_t mItemSize;
-};
-
-
-
-class SortedVectorImpl : public VectorImpl
-{
-public:
- SortedVectorImpl(size_t itemSize, uint32_t flags);
- SortedVectorImpl(const VectorImpl& rhs);
- virtual ~SortedVectorImpl();
-
- SortedVectorImpl& operator = (const SortedVectorImpl& rhs);
-
- //! finds the index of an item
- ssize_t indexOf(const void* item) const;
-
- //! finds where this item should be inserted
- size_t orderOf(const void* item) const;
-
- //! add an item in the right place (or replaces it if there is one)
- ssize_t add(const void* item);
-
- //! merges a vector into this one
- ssize_t merge(const VectorImpl& vector);
- ssize_t merge(const SortedVectorImpl& vector);
-
- //! removes an item
- ssize_t remove(const void* item);
-
-protected:
- virtual int do_compare(const void* lhs, const void* rhs) const = 0;
-
- // take care of FBC...
- virtual void reservedSortedVectorImpl1();
- virtual void reservedSortedVectorImpl2();
- virtual void reservedSortedVectorImpl3();
- virtual void reservedSortedVectorImpl4();
- virtual void reservedSortedVectorImpl5();
- virtual void reservedSortedVectorImpl6();
- virtual void reservedSortedVectorImpl7();
- virtual void reservedSortedVectorImpl8();
-
-private:
- ssize_t _indexOrderOf(const void* item, size_t* order = 0) const;
-
- // these are made private, because they can't be used on a SortedVector
- // (they don't have an implementation either)
- ssize_t add();
- void pop();
- void push();
- void push(const void* item);
- ssize_t insertVectorAt(const VectorImpl& vector, size_t index);
- ssize_t appendVector(const VectorImpl& vector);
- ssize_t insertAt(size_t where, size_t numItems = 1);
- ssize_t insertAt(const void* item, size_t where, size_t numItems = 1);
- ssize_t replaceAt(size_t index);
- ssize_t replaceAt(const void* item, size_t index);
-};
-
-} // namespace tinyutils
-} // namespace android
-
-
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_PIXELFLINGER_VECTOR_IMPL_H
diff --git a/libpixelflinger/include/private/pixelflinger/ggl_context.h b/libpixelflinger/include/private/pixelflinger/ggl_context.h
index a18b2f7..d45dabc 100644
--- a/libpixelflinger/include/private/pixelflinger/ggl_context.h
+++ b/libpixelflinger/include/private/pixelflinger/ggl_context.h
@@ -42,7 +42,7 @@
#else
inline uint32_t GGL_RGBA_TO_HOST(uint32_t v) {
-#if defined(__mips__) && __mips==32 && __mips_isa_rev>=2
+#if defined(__mips__) && __mips_isa_rev>=2
uint32_t r;
__asm__("wsbh %0, %1;"
"rotr %0, %0, 16"
@@ -55,7 +55,7 @@
#endif
}
inline uint32_t GGL_HOST_TO_RGBA(uint32_t v) {
-#if defined(__mips__) && __mips==32 && __mips_isa_rev>=2
+#if defined(__mips__) && __mips_isa_rev>=2
uint32_t r;
__asm__("wsbh %0, %1;"
"rotr %0, %0, 16"
diff --git a/libpixelflinger/include/private/pixelflinger/ggl_fixed.h b/libpixelflinger/include/private/pixelflinger/ggl_fixed.h
index 787f620..17b85dd 100644
--- a/libpixelflinger/include/private/pixelflinger/ggl_fixed.h
+++ b/libpixelflinger/include/private/pixelflinger/ggl_fixed.h
@@ -520,6 +520,252 @@
return res;
}
+#elif defined(__mips__) && __mips_isa_rev == 6
+
+/*inline MIPS implementations*/
+inline GGLfixed gglMulx(GGLfixed a, GGLfixed b, int shift) CONST;
+inline GGLfixed gglMulx(GGLfixed a, GGLfixed b, int shift) {
+ GGLfixed result,tmp,tmp1,tmp2;
+
+ if (__builtin_constant_p(shift)) {
+ if (shift == 0) {
+ asm ("mul %[res], %[a], %[b] \t\n"
+ : [res]"=&r"(result)
+ : [a]"r"(a),[b]"r"(b)
+ );
+ } else if (shift == 32)
+ {
+ asm ("mul %[res], %[a], %[b] \t\n"
+ "li %[tmp],1\t\n"
+ "sll %[tmp],%[tmp],0x1f\t\n"
+ "addu %[tmp1],%[tmp],%[res] \t\n"
+ "muh %[res], %[a], %[b] \t\n"
+ "sltu %[tmp1],%[tmp1],%[tmp]\t\n" /*obit*/
+ "sra %[tmp],%[tmp],0x1f \t\n"
+ "addu %[res],%[res],%[tmp]\t\n"
+ "addu %[res],%[res],%[tmp1]\t\n"
+ : [res]"=&r"(result),[tmp]"=&r"(tmp),[tmp1]"=&r"(tmp1)
+ : [a]"r"(a),[b]"r"(b),[shift]"I"(shift)
+ );
+ } else if ((shift >0) && (shift < 32))
+ {
+ asm ("mul %[res], %[a], %[b] \t\n"
+ "li %[tmp],1 \t\n"
+ "sll %[tmp],%[tmp],%[shiftm1] \t\n"
+ "addu %[tmp1],%[tmp],%[res] \t\n"
+ "sltu %[tmp1],%[tmp1],%[tmp] \t\n" /*obit?*/
+ "addu %[res],%[res],%[tmp] \t\n"
+ "muh %[tmp], %[a], %[b] \t\n"
+ "addu %[tmp],%[tmp],%[tmp1] \t\n"
+ "sll %[tmp],%[tmp],%[lshift] \t\n"
+ "srl %[res],%[res],%[rshift] \t\n"
+ "or %[res],%[res],%[tmp] \t\n"
+ : [res]"=&r"(result),[tmp]"=&r"(tmp),[tmp1]"=&r"(tmp1),[tmp2]"=&r"(tmp2)
+ : [a]"r"(a),[b]"r"(b),[lshift]"I"(32-shift),[rshift]"I"(shift),[shiftm1]"I"(shift-1)
+ );
+ } else {
+ asm ("mul %[res], %[a], %[b] \t\n"
+ "li %[tmp],1 \t\n"
+ "sll %[tmp],%[tmp],%[shiftm1] \t\n"
+ "addu %[tmp1],%[tmp],%[res] \t\n"
+ "sltu %[tmp1],%[tmp1],%[tmp] \t\n" /*obit?*/
+ "sra %[tmp2],%[tmp],0x1f \t\n"
+ "addu %[res],%[res],%[tmp] \t\n"
+ "muh %[tmp], %[a], %[b] \t\n"
+ "addu %[tmp],%[tmp],%[tmp2] \t\n"
+ "addu %[tmp],%[tmp],%[tmp1] \t\n" /*tmp=hi*/
+ "srl %[tmp2],%[res],%[rshift] \t\n"
+ "srav %[res], %[tmp],%[rshift]\t\n"
+ "sll %[tmp],%[tmp],1 \t\n"
+ "sll %[tmp],%[tmp],%[norbits] \t\n"
+ "or %[tmp],%[tmp],%[tmp2] \t\n"
+ "seleqz %[tmp],%[tmp],%[bit5] \t\n"
+ "selnez %[res],%[res],%[bit5] \t\n"
+ "or %[res],%[res],%[tmp] \t\n"
+ : [res]"=&r"(result),[tmp]"=&r"(tmp),[tmp1]"=&r"(tmp1),[tmp2]"=&r"(tmp2)
+ : [a]"r"(a),[b]"r"(b),[norbits]"I"(~(shift)),[rshift]"I"(shift),[shiftm1] "I"(shift-1),[bit5]"I"(shift & 0x20)
+ );
+ }
+ } else {
+ asm ("mul %[res], %[a], %[b] \t\n"
+ "li %[tmp],1 \t\n"
+ "sll %[tmp],%[tmp],%[shiftm1] \t\n"
+ "addu %[tmp1],%[tmp],%[res] \t\n"
+ "sltu %[tmp1],%[tmp1],%[tmp] \t\n" /*obit?*/
+ "sra %[tmp2],%[tmp],0x1f \t\n"
+ "addu %[res],%[res],%[tmp] \t\n"
+ "muh %[tmp], %[a], %[b] \t\n"
+ "addu %[tmp],%[tmp],%[tmp2] \t\n"
+ "addu %[tmp],%[tmp],%[tmp1] \t\n" /*tmp=hi*/
+ "srl %[tmp2],%[res],%[rshift] \t\n"
+ "srav %[res], %[tmp],%[rshift]\t\n"
+ "sll %[tmp],%[tmp],1 \t\n"
+ "sll %[tmp],%[tmp],%[norbits] \t\n"
+ "or %[tmp],%[tmp],%[tmp2] \t\n"
+ "seleqz %[tmp],%[tmp],%[bit5] \t\n"
+ "selnez %[res],%[res],%[bit5] \t\n"
+ "or %[res],%[res],%[tmp] \t\n"
+ : [res]"=&r"(result),[tmp]"=&r"(tmp),[tmp1]"=&r"(tmp1),[tmp2]"=&r"(tmp2)
+ : [a]"r"(a),[b]"r"(b),[norbits]"r"(~(shift)),[rshift] "r"(shift),[shiftm1]"r"(shift-1),[bit5] "r"(shift & 0x20)
+ );
+ }
+ return result;
+}
+
+inline GGLfixed gglMulAddx(GGLfixed a, GGLfixed b, GGLfixed c, int shift) CONST;
+inline GGLfixed gglMulAddx(GGLfixed a, GGLfixed b, GGLfixed c, int shift) {
+ GGLfixed result,t,tmp1,tmp2;
+
+ if (__builtin_constant_p(shift)) {
+ if (shift == 0) {
+ asm ("mul %[lo], %[a], %[b] \t\n"
+ "addu %[lo],%[lo],%[c] \t\n"
+ : [lo]"=&r"(result)
+ : [a]"r"(a),[b]"r"(b),[c]"r"(c)
+ );
+ } else if (shift == 32) {
+ asm ("muh %[lo], %[a], %[b] \t\n"
+ "addu %[lo],%[lo],%[c] \t\n"
+ : [lo]"=&r"(result)
+ : [a]"r"(a),[b]"r"(b),[c]"r"(c)
+ );
+ } else if ((shift>0) && (shift<32)) {
+ asm ("mul %[res], %[a], %[b] \t\n"
+ "muh %[t], %[a], %[b] \t\n"
+ "srl %[res],%[res],%[rshift] \t\n"
+ "sll %[t],%[t],%[lshift] \t\n"
+ "or %[res],%[res],%[t] \t\n"
+ "addu %[res],%[res],%[c] \t\n"
+ : [res]"=&r"(result),[t]"=&r"(t)
+ : [a]"r"(a),[b]"r"(b),[c]"r"(c),[lshift]"I"(32-shift),[rshift]"I"(shift)
+ );
+ } else {
+ asm ("mul %[res], %[a], %[b] \t\n"
+ "muh %[t], %[a], %[b] \t\n"
+ "nor %[tmp1],$zero,%[shift]\t\n"
+ "srl %[res],%[res],%[shift] \t\n"
+ "sll %[tmp2],%[t],1 \t\n"
+ "sllv %[tmp2],%[tmp2],%[tmp1] \t\n"
+ "or %[tmp1],%[tmp2],%[res] \t\n"
+ "srav %[res],%[t],%[shift] \t\n"
+ "andi %[tmp2],%[shift],0x20\t\n"
+ "seleqz %[tmp1],%[tmp1],%[tmp2]\t\n"
+ "selnez %[res],%[res],%[tmp2]\t\n"
+ "or %[res],%[res],%[tmp1]\t\n"
+ "addu %[res],%[res],%[c] \t\n"
+ : [res]"=&r"(result),[t]"=&r"(t),[tmp1]"=&r"(tmp1),[tmp2]"=&r"(tmp2)
+ : [a]"r"(a),[b]"r"(b),[c]"r"(c),[shift]"I"(shift)
+ );
+ }
+ } else {
+ asm ("mul %[res], %[a], %[b] \t\n"
+ "muh %[t], %[a], %[b] \t\n"
+ "nor %[tmp1],$zero,%[shift]\t\n"
+ "srl %[res],%[res],%[shift] \t\n"
+ "sll %[tmp2],%[t],1 \t\n"
+ "sllv %[tmp2],%[tmp2],%[tmp1] \t\n"
+ "or %[tmp1],%[tmp2],%[res] \t\n"
+ "srav %[res],%[t],%[shift] \t\n"
+ "andi %[tmp2],%[shift],0x20\t\n"
+ "seleqz %[tmp1],%[tmp1],%[tmp2]\t\n"
+ "selnez %[res],%[res],%[tmp2]\t\n"
+ "or %[res],%[res],%[tmp1]\t\n"
+ "addu %[res],%[res],%[c] \t\n"
+ : [res]"=&r"(result),[t]"=&r"(t),[tmp1]"=&r"(tmp1),[tmp2]"=&r"(tmp2)
+ : [a]"r"(a),[b]"r"(b),[c]"r"(c),[shift]"r"(shift)
+ );
+ }
+ return result;
+}
+
+inline GGLfixed gglMulSubx(GGLfixed a, GGLfixed b, GGLfixed c, int shift) CONST;
+inline GGLfixed gglMulSubx(GGLfixed a, GGLfixed b, GGLfixed c, int shift) {
+ GGLfixed result,t,tmp1,tmp2;
+
+ if (__builtin_constant_p(shift)) {
+ if (shift == 0) {
+ asm ("mul %[lo], %[a], %[b] \t\n"
+ "subu %[lo],%[lo],%[c] \t\n"
+ : [lo]"=&r"(result)
+ : [a]"r"(a),[b]"r"(b),[c]"r"(c)
+ );
+ } else if (shift == 32) {
+ asm ("muh %[lo], %[a], %[b] \t\n"
+ "subu %[lo],%[lo],%[c] \t\n"
+ : [lo]"=&r"(result)
+ : [a]"r"(a),[b]"r"(b),[c]"r"(c)
+ );
+ } else if ((shift>0) && (shift<32)) {
+ asm ("mul %[res], %[a], %[b] \t\n"
+ "muh %[t], %[a], %[b] \t\n"
+ "srl %[res],%[res],%[rshift] \t\n"
+ "sll %[t],%[t],%[lshift] \t\n"
+ "or %[res],%[res],%[t] \t\n"
+ "subu %[res],%[res],%[c] \t\n"
+ : [res]"=&r"(result),[t]"=&r"(t)
+ : [a]"r"(a),[b]"r"(b),[c]"r"(c),[lshift]"I"(32-shift),[rshift]"I"(shift)
+ );
+ } else {
+ asm ("mul %[res], %[a], %[b] \t\n"
+ "muh %[t], %[a], %[b] \t\n"
+ "nor %[tmp1],$zero,%[shift]\t\n"
+ "srl %[res],%[res],%[shift] \t\n"
+ "sll %[tmp2],%[t],1 \t\n"
+ "sllv %[tmp2],%[tmp2],%[tmp1] \t\n"
+ "or %[tmp1],%[tmp2],%[res] \t\n"
+ "srav %[res],%[t],%[shift] \t\n"
+ "andi %[tmp2],%[shift],0x20\t\n"
+ "seleqz %[tmp1],%[tmp1],%[tmp2]\t\n"
+ "selnez %[res],%[res],%[tmp2]\t\n"
+ "or %[res],%[res],%[tmp1]\t\n"
+ "subu %[res],%[res],%[c] \t\n"
+ : [res]"=&r"(result),[t]"=&r"(t),[tmp1]"=&r"(tmp1),[tmp2]"=&r"(tmp2)
+ : [a]"r"(a),[b]"r"(b),[c]"r"(c),[shift]"I"(shift)
+ );
+ }
+ } else {
+ asm ("mul %[res], %[a], %[b] \t\n"
+ "muh %[t], %[a], %[b] \t\n"
+ "nor %[tmp1],$zero,%[shift]\t\n"
+ "srl %[res],%[res],%[shift] \t\n"
+ "sll %[tmp2],%[t],1 \t\n"
+ "sllv %[tmp2],%[tmp2],%[tmp1] \t\n"
+ "or %[tmp1],%[tmp2],%[res] \t\n"
+ "srav %[res],%[t],%[shift] \t\n"
+ "andi %[tmp2],%[shift],0x20\t\n"
+ "seleqz %[tmp1],%[tmp1],%[tmp2]\t\n"
+ "selnez %[res],%[res],%[tmp2]\t\n"
+ "or %[res],%[res],%[tmp1]\t\n"
+ "subu %[res],%[res],%[c] \t\n"
+ : [res]"=&r"(result),[t]"=&r"(t),[tmp1]"=&r"(tmp1),[tmp2]"=&r"(tmp2)
+ : [a]"r"(a),[b]"r"(b),[c]"r"(c),[shift]"r"(shift)
+ );
+ }
+ return result;
+}
+
+inline int64_t gglMulii(int32_t x, int32_t y) CONST;
+inline int64_t gglMulii(int32_t x, int32_t y) {
+ union {
+ struct {
+#if defined(__MIPSEL__)
+ int32_t lo;
+ int32_t hi;
+#elif defined(__MIPSEB__)
+ int32_t hi;
+ int32_t lo;
+#endif
+ } s;
+ int64_t res;
+ }u;
+ asm("mul %0, %2, %3 \t\n"
+ "muh %1, %2, %3 \t\n"
+ : "=r"(u.s.lo), "=&r"(u.s.hi)
+ : "%r"(x), "r"(y)
+ );
+ return u.res;
+}
+
#else // ----------------------------------------------------------------------
inline GGLfixed gglMulx(GGLfixed a, GGLfixed b, int shift) CONST;
diff --git a/libpixelflinger/scanline.cpp b/libpixelflinger/scanline.cpp
index 3d14531..a718b02 100644
--- a/libpixelflinger/scanline.cpp
+++ b/libpixelflinger/scanline.cpp
@@ -41,6 +41,8 @@
#include "codeflinger/Arm64Assembler.h"
#elif defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6
#include "codeflinger/MIPSAssembler.h"
+#elif defined(__mips__) && defined(__LP64__)
+#include "codeflinger/MIPS64Assembler.h"
#endif
//#include "codeflinger/ARMAssemblerOptimizer.h"
@@ -59,7 +61,7 @@
# define ANDROID_CODEGEN ANDROID_CODEGEN_GENERATED
#endif
-#if defined(__arm__) || (defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6) || defined(__aarch64__)
+#if defined(__arm__) || (defined(__mips__) && ((!defined(__LP64__) && __mips_isa_rev < 6) || defined(__LP64__))) || defined(__aarch64__)
# define ANDROID_ARM_CODEGEN 1
#else
# define ANDROID_ARM_CODEGEN 0
@@ -73,7 +75,7 @@
*/
#define DEBUG_NEEDS 0
-#if defined( __mips__) && !defined(__LP64__) && __mips_isa_rev < 6
+#if defined( __mips__) && ((!defined(__LP64__) && __mips_isa_rev < 6) || defined(__LP64__))
#define ASSEMBLY_SCRATCH_SIZE 4096
#elif defined(__aarch64__)
#define ASSEMBLY_SCRATCH_SIZE 8192
@@ -136,6 +138,9 @@
extern "C" void scanline_col32cb16blend_arm64(uint16_t *dst, uint32_t col, size_t ct);
#elif defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6
extern "C" void scanline_t32cb16blend_mips(uint16_t*, uint32_t*, size_t);
+#elif defined(__mips__) && defined(__LP64__)
+extern "C" void scanline_t32cb16blend_mips64(uint16_t*, uint32_t*, size_t);
+extern "C" void scanline_col32cb16blend_mips64(uint16_t *dst, uint32_t col, size_t ct);
#endif
// ----------------------------------------------------------------------------
@@ -286,7 +291,7 @@
#if ANDROID_ARM_CODEGEN
-#if defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6
+#if defined(__mips__) && ((!defined(__LP64__) && __mips_isa_rev < 6) || defined(__LP64__))
static CodeCache gCodeCache(32 * 1024);
#elif defined(__aarch64__)
static CodeCache gCodeCache(48 * 1024);
@@ -406,8 +411,10 @@
//GGLAssembler assembler(
// new ARMAssemblerOptimizer(new ARMAssembler(a)) );
#endif
-#if defined(__mips__)
+#if defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6
GGLAssembler assembler( new ArmToMipsAssembler(a) );
+#elif defined(__mips__) && defined(__LP64__)
+ GGLAssembler assembler( new ArmToMips64Assembler(a) );
#elif defined(__aarch64__)
GGLAssembler assembler( new ArmToArm64Assembler(a) );
#endif
@@ -2103,6 +2110,8 @@
#endif // defined(__ARM_HAVE_NEON) && BYTE_ORDER == LITTLE_ENDIAN
#elif ((ANDROID_CODEGEN >= ANDROID_CODEGEN_ASM) && defined(__aarch64__))
scanline_col32cb16blend_arm64(dst, GGL_RGBA_TO_HOST(c->packed8888), ct);
+#elif ((ANDROID_CODEGEN >= ANDROID_CODEGEN_ASM) && (defined(__mips__) && defined(__LP64__)))
+ scanline_col32cb16blend_mips64(dst, GGL_RGBA_TO_HOST(c->packed8888), ct);
#else
uint32_t s = GGL_RGBA_TO_HOST(c->packed8888);
int sA = (s>>24);
@@ -2175,7 +2184,8 @@
void scanline_t32cb16blend(context_t* c)
{
-#if ((ANDROID_CODEGEN >= ANDROID_CODEGEN_ASM) && (defined(__arm__) || (defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6) || defined(__aarch64__)))
+#if ((ANDROID_CODEGEN >= ANDROID_CODEGEN_ASM) && (defined(__arm__) || defined(__aarch64__) || \
+ (defined(__mips__) && ((!defined(__LP64__) && __mips_isa_rev < 6) || defined(__LP64__)))))
int32_t x = c->iterators.xl;
size_t ct = c->iterators.xr - x;
int32_t y = c->iterators.y;
@@ -2191,8 +2201,10 @@
scanline_t32cb16blend_arm(dst, src, ct);
#elif defined(__aarch64__)
scanline_t32cb16blend_arm64(dst, src, ct);
-#elif defined(__mips__)
+#elif defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6
scanline_t32cb16blend_mips(dst, src, ct);
+#elif defined(__mips__) && defined(__LP64__)
+ scanline_t32cb16blend_mips64(dst, src, ct);
#endif
#else
dst_iterator16 di(c);
diff --git a/libpixelflinger/tests/arch-mips/Android.mk b/libpixelflinger/tests/arch-mips/Android.mk
new file mode 100644
index 0000000..fe6979e
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips/Android.mk
@@ -0,0 +1,6 @@
+ifeq ($(TARGET_ARCH),mips)
+include $(all-subdir-makefiles)
+endif
+ifeq ($(TARGET_ARCH),mipsel)
+include $(all-subdir-makefiles)
+endif
diff --git a/libpixelflinger/tests/arch-mips/col32cb16blend/Android.mk b/libpixelflinger/tests/arch-mips/col32cb16blend/Android.mk
new file mode 100644
index 0000000..40f197f
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips/col32cb16blend/Android.mk
@@ -0,0 +1,18 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ col32cb16blend_test.c \
+ ../../../arch-mips/col32cb16blend.S
+
+LOCAL_SHARED_LIBRARIES :=
+
+LOCAL_C_INCLUDES :=
+
+LOCAL_MODULE:= test-pixelflinger-mips-col32cb16blend
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_MULTILIB := 32
+
+include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/tests/arch-mips/col32cb16blend/col32cb16blend_test.c b/libpixelflinger/tests/arch-mips/col32cb16blend/col32cb16blend_test.c
new file mode 100644
index 0000000..dd0e60f
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips/col32cb16blend/col32cb16blend_test.c
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2015 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 <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+
+#define ARGB_8888_MAX 0xFFFFFFFF
+#define ARGB_8888_MIN 0x00000000
+#define RGB_565_MAX 0xFFFF
+#define RGB_565_MIN 0x0000
+
+struct test_t
+{
+ char name[256];
+ uint32_t src_color;
+ uint16_t dst_color;
+ size_t count;
+};
+
+struct test_t tests[] =
+{
+ {"Count 1, Src=Max, Dst=Min", ARGB_8888_MAX, RGB_565_MIN, 1},
+ {"Count 2, Src=Min, Dst=Max", ARGB_8888_MIN, RGB_565_MAX, 2},
+ {"Count 3, Src=Max, Dst=Max", ARGB_8888_MAX, RGB_565_MAX, 3},
+ {"Count 4, Src=Min, Dst=Min", ARGB_8888_MAX, RGB_565_MAX, 4},
+ {"Count 1, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 1},
+ {"Count 2, Src=Rand, Dst=Rand", 0xABCDEF12, 0x2345, 2},
+ {"Count 3, Src=Rand, Dst=Rand", 0x11111111, 0xEDFE, 3},
+ {"Count 4, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 4},
+ {"Count 5, Src=Rand, Dst=Rand", 0xEFEFFEFE, 0xFACC, 5},
+ {"Count 10, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 10}
+};
+
+void scanline_col32cb16blend_mips(uint16_t *dst, uint32_t src, size_t count);
+void scanline_col32cb16blend_c(uint16_t * dst, uint32_t src, size_t count)
+{
+ uint32_t srcAlpha = (src>>24);
+ uint32_t f = 0x100 - (srcAlpha + (srcAlpha>>7));
+
+ while (count--)
+ {
+ uint16_t d = *dst;
+ int dstR = (d>>11)&0x1f;
+ int dstG = (d>>5)&0x3f;
+ int dstB = (d)&0x1f;
+ int srcR = (src >> ( 3))&0x1F;
+ int srcG = (src >> ( 8+2))&0x3F;
+ int srcB = (src >> (16+3))&0x1F;
+ srcR += (f*dstR)>>8;
+ srcG += (f*dstG)>>8;
+ srcB += (f*dstB)>>8;
+ *dst++ = (uint16_t)((srcR<<11)|(srcG<<5)|srcB);
+ }
+}
+
+void scanline_col32cb16blend_test()
+{
+ uint16_t dst_c[16], dst_asm[16];
+ uint32_t i, j;
+
+ for(i = 0; i < sizeof(tests)/sizeof(struct test_t); ++i)
+ {
+ struct test_t test = tests[i];
+
+ printf("Testing - %s:",test.name);
+
+ memset(dst_c, 0, sizeof(dst_c));
+ memset(dst_asm, 0, sizeof(dst_asm));
+
+ for(j = 0; j < test.count; ++j)
+ {
+ dst_c[j] = test.dst_color;
+ dst_asm[j] = test.dst_color;
+ }
+
+
+ scanline_col32cb16blend_c(dst_c, test.src_color, test.count);
+ scanline_col32cb16blend_mips(dst_asm, test.src_color, test.count);
+
+ if(memcmp(dst_c, dst_asm, sizeof(dst_c)) == 0)
+ printf("Passed\n");
+ else
+ printf("Failed\n");
+
+ for(j = 0; j < test.count; ++j)
+ {
+ printf("dst_c[%d] = %x, dst_asm[%d] = %x \n", j, dst_c[j], j, dst_asm[j]);
+ }
+ }
+}
+
+int main()
+{
+ scanline_col32cb16blend_test();
+ return 0;
+}
diff --git a/libpixelflinger/tests/arch-mips/t32cb16blend/Android.mk b/libpixelflinger/tests/arch-mips/t32cb16blend/Android.mk
new file mode 100644
index 0000000..d0c0ae4
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips/t32cb16blend/Android.mk
@@ -0,0 +1,18 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ t32cb16blend_test.c \
+ ../../../arch-mips/t32cb16blend.S
+
+LOCAL_SHARED_LIBRARIES :=
+
+LOCAL_C_INCLUDES :=
+
+LOCAL_MODULE:= test-pixelflinger-mips-t32cb16blend
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_MULTILIB := 32
+
+include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/tests/arch-mips/t32cb16blend/t32cb16blend_test.c b/libpixelflinger/tests/arch-mips/t32cb16blend/t32cb16blend_test.c
new file mode 100644
index 0000000..c6d6937
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips/t32cb16blend/t32cb16blend_test.c
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2015 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 <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+#define ARGB_8888_MAX 0xFFFFFFFF
+#define ARGB_8888_MIN 0x00000000
+#define RGB_565_MAX 0xFFFF
+#define RGB_565_MIN 0x0000
+
+struct test_t
+{
+ char name[256];
+ uint32_t src_color;
+ uint16_t dst_color;
+ size_t count;
+};
+
+struct test_t tests[] =
+{
+ {"Count 0", 0, 0, 0},
+ {"Count 1, Src=Max, Dst=Min", ARGB_8888_MAX, RGB_565_MIN, 1},
+ {"Count 2, Src=Min, Dst=Max", ARGB_8888_MIN, RGB_565_MAX, 2},
+ {"Count 3, Src=Max, Dst=Max", ARGB_8888_MAX, RGB_565_MAX, 3},
+ {"Count 4, Src=Min, Dst=Min", ARGB_8888_MAX, RGB_565_MAX, 4},
+ {"Count 1, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 1},
+ {"Count 2, Src=Rand, Dst=Rand", 0xABCDEF12, 0x2345, 2},
+ {"Count 3, Src=Rand, Dst=Rand", 0x11111111, 0xEDFE, 3},
+ {"Count 4, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 4},
+ {"Count 5, Src=Rand, Dst=Rand", 0xEFEFFEFE, 0xFACC, 5},
+ {"Count 10, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 10}
+
+};
+
+void scanline_t32cb16blend_mips(uint16_t*, uint32_t*, size_t);
+void scanline_t32cb16blend_c(uint16_t * dst, uint32_t* src, size_t count)
+{
+ while (count--)
+ {
+ uint16_t d = *dst;
+ uint32_t s = *src++;
+ int dstR = (d>>11)&0x1f;
+ int dstG = (d>>5)&0x3f;
+ int dstB = (d)&0x1f;
+ int srcR = (s >> ( 3))&0x1F;
+ int srcG = (s >> ( 8+2))&0x3F;
+ int srcB = (s >> (16+3))&0x1F;
+ int srcAlpha = (s>>24) & 0xFF;
+
+
+ int f = 0x100 - (srcAlpha + ((srcAlpha>>7) & 0x1));
+ srcR += (f*dstR)>>8;
+ srcG += (f*dstG)>>8;
+ srcB += (f*dstB)>>8;
+ // srcR = srcR > 0x1F? 0x1F: srcR;
+ // srcG = srcG > 0x3F? 0x3F: srcG;
+ // srcB = srcB > 0x1F? 0x1F: srcB;
+ *dst++ = (uint16_t)((srcR<<11)|(srcG<<5)|srcB);
+ }
+}
+
+void scanline_t32cb16blend_test()
+{
+ uint16_t dst_c[16], dst_asm[16];
+ uint32_t src[16];
+ uint32_t i;
+ uint32_t j;
+
+ for(i = 0; i < sizeof(tests)/sizeof(struct test_t); ++i)
+ {
+ struct test_t test = tests[i];
+
+ printf("Testing - %s:",test.name);
+
+ memset(dst_c, 0, sizeof(dst_c));
+ memset(dst_asm, 0, sizeof(dst_asm));
+
+ for(j = 0; j < test.count; ++j)
+ {
+ dst_c[j] = test.dst_color;
+ dst_asm[j] = test.dst_color;
+ src[j] = test.src_color;
+ }
+
+ scanline_t32cb16blend_c(dst_c,src,test.count);
+ scanline_t32cb16blend_mips(dst_asm,src,test.count);
+
+
+ if(memcmp(dst_c, dst_asm, sizeof(dst_c)) == 0)
+ printf("Passed\n");
+ else
+ printf("Failed\n");
+
+ for(j = 0; j < test.count; ++j)
+ {
+ printf("dst_c[%d] = %x, dst_asm[%d] = %x \n", j, dst_c[j], j, dst_asm[j]);
+ }
+ }
+}
+
+int main()
+{
+ scanline_t32cb16blend_test();
+ return 0;
+}
diff --git a/libpixelflinger/tests/arch-mips64/Android.mk b/libpixelflinger/tests/arch-mips64/Android.mk
new file mode 100644
index 0000000..3b1c64e
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips64/Android.mk
@@ -0,0 +1,3 @@
+ifeq ($(TARGET_ARCH),mips64)
+include $(all-subdir-makefiles)
+endif
diff --git a/libpixelflinger/tests/arch-mips64/assembler/Android.mk b/libpixelflinger/tests/arch-mips64/assembler/Android.mk
new file mode 100644
index 0000000..4699961
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips64/assembler/Android.mk
@@ -0,0 +1,21 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ mips64_assembler_test.cpp\
+ asm_mips_test_jacket.S
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ libpixelflinger
+
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/../../..
+
+LOCAL_MODULE:= test-pixelflinger-mips64-assembler-test
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_MULTILIB := 64
+
+include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/tests/arch-mips64/assembler/asm_mips_test_jacket.S b/libpixelflinger/tests/arch-mips64/assembler/asm_mips_test_jacket.S
new file mode 100644
index 0000000..8a7f742
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips64/assembler/asm_mips_test_jacket.S
@@ -0,0 +1,93 @@
+# /*
+# * Copyright (C) 2015 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.
+# */
+
+ .text
+ .align 8
+
+ .global asm_mips_test_jacket
+
+ # // Set the register
+ # // Calls the asm function
+ # // Reads the register values to output register
+
+ # // Parameters
+ # // a0 - Function to jump
+ # // a1 - register values array
+ # // a2 - flag values array
+asm_mips_test_jacket:
+ # // Save registers to stack
+ daddiu $sp, $sp, -96
+ sd $s0, 64($sp)
+ sd $s1, 72($sp)
+ sd $s2, 80($sp)
+ sd $ra, 88($sp)
+
+ move $s0, $a0
+ move $s1, $a1
+ move $s2, $a2
+
+ ld $v0, 16($s1)
+ ld $v1, 24($s1)
+ ld $a0, 32($s1)
+ ld $a1, 40($s1)
+ ld $a2, 48($s1)
+ ld $a3, 56($s1)
+ ld $a4, 64($s1)
+ ld $a5, 72($s1)
+ ld $a6, 80($s1)
+ ld $a7, 88($s1)
+ ld $t0, 96($s1)
+ ld $t1, 104($s1)
+ ld $t2, 112($s1)
+ ld $t3, 120($s1)
+
+ jal $s0
+
+ sd $v0, 16($s1)
+ sd $v1, 24($s1)
+ sd $a0, 32($s1)
+ sd $a1, 40($s1)
+ sd $a2, 48($s1)
+ sd $a3, 56($s1)
+ sd $a4, 64($s1)
+ sd $a5, 72($s1)
+ sd $a6, 80($s1)
+ sd $a7, 88($s1)
+ sd $t0, 96($s1)
+ sd $t1, 104($s1)
+ sd $t2, 112($s1)
+ sd $t3, 120($s1)
+
+ ld $s0, 64($sp)
+ ld $s1, 72($sp)
+ ld $s2, 80($sp)
+ ld $ra, 88($sp)
+
+ daddiu $sp, $sp, 96
+
+ j $ra
diff --git a/libpixelflinger/tests/arch-mips64/assembler/mips64_assembler_test.cpp b/libpixelflinger/tests/arch-mips64/assembler/mips64_assembler_test.cpp
new file mode 100644
index 0000000..b680b60
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips64/assembler/mips64_assembler_test.cpp
@@ -0,0 +1,644 @@
+/*
+ * Copyright (C) 2015 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <sys/mman.h>
+#include <cutils/ashmem.h>
+#include <cutils/atomic.h>
+#include <cutils/log.h>
+
+#define __STDC_FORMAT_MACROS
+#include <inttypes.h>
+
+#include "codeflinger/ARMAssemblerInterface.h"
+#include "codeflinger/MIPS64Assembler.h"
+using namespace android;
+
+#define TESTS_DATAOP_ENABLE 1
+#define TESTS_DATATRANSFER_ENABLE 1
+#define ASSEMBLY_SCRATCH_SIZE 4096
+
+void *instrMem;
+uint32_t instrMemSize = 128 * 1024;
+char dataMem[8192];
+
+typedef void (*asm_function_t)();
+extern "C" void asm_mips_test_jacket(asm_function_t function,
+ int64_t regs[], int32_t flags[]);
+
+#define MAX_32BIT (uint32_t)(((uint64_t)1 << 32) - 1)
+#define MAX_64BIT ((uint64_t)0xFFFFFFFFFFFFFFFF)
+const uint32_t NA = 0;
+const uint32_t NUM_REGS = 32;
+const uint32_t NUM_FLAGS = 16;
+
+enum instr_t
+{
+ INSTR_ADD,
+ INSTR_SUB,
+ INSTR_AND,
+ INSTR_ORR,
+ INSTR_RSB,
+ INSTR_BIC,
+ INSTR_CMP,
+ INSTR_MOV,
+ INSTR_MVN,
+ INSTR_MUL,
+ INSTR_MLA,
+ INSTR_SMULBB,
+ INSTR_SMULBT,
+ INSTR_SMULTB,
+ INSTR_SMULTT,
+ INSTR_SMULWB,
+ INSTR_SMULWT,
+ INSTR_SMLABB,
+ INSTR_UXTB16,
+ INSTR_UBFX,
+ INSTR_ADDR_ADD,
+ INSTR_ADDR_SUB,
+ INSTR_LDR,
+ INSTR_LDRB,
+ INSTR_LDRH,
+ INSTR_ADDR_LDR,
+ INSTR_LDM,
+ INSTR_STR,
+ INSTR_STRB,
+ INSTR_STRH,
+ INSTR_ADDR_STR,
+ INSTR_STM
+};
+
+enum shift_t
+{
+ SHIFT_LSL,
+ SHIFT_LSR,
+ SHIFT_ASR,
+ SHIFT_ROR,
+ SHIFT_NONE
+};
+
+enum offset_t
+{
+ REG_SCALE_OFFSET,
+ REG_OFFSET,
+ IMM8_OFFSET,
+ IMM12_OFFSET,
+ NO_OFFSET
+};
+
+enum cond_t
+{
+ EQ, NE, CS, CC, MI, PL, VS, VC, HI, LS, GE, LT, GT, LE, AL, NV,
+ HS = CS,
+ LO = CC
+};
+
+const char * cc_code[] =
+{
+ "EQ", "NE", "CS", "CC", "MI", "PL", "VS", "VC",
+ "HI", "LS","GE","LT", "GT", "LE", "AL", "NV"
+};
+
+struct condTest_t
+{
+ int mode;
+ int32_t Rcond1;
+ int32_t Rcond2;
+ uint64_t Rcond1Value;
+ uint64_t Rcond2Value;
+};
+
+
+struct dataOpTest_t
+{
+ uint32_t id;
+ instr_t op;
+ condTest_t preCond;
+ cond_t cond;
+ bool setFlags;
+ uint64_t RnValue;
+ uint64_t RsValue;
+ bool immediate;
+ uint32_t immValue;
+ uint64_t RmValue;
+ uint32_t shiftMode;
+ uint32_t shiftAmount;
+ uint64_t RdValue;
+ bool checkRd;
+ uint64_t postRdValue;
+};
+
+struct dataTransferTest_t
+{
+ uint32_t id;
+ instr_t op;
+ uint32_t preFlag;
+ cond_t cond;
+ bool setMem;
+ uint64_t memOffset;
+ uint64_t memValue;
+ uint64_t RnValue;
+ offset_t offsetType;
+ uint64_t RmValue;
+ uint32_t immValue;
+ bool writeBack;
+ bool preIndex;
+ bool postIndex;
+ uint64_t RdValue;
+ uint64_t postRdValue;
+ uint64_t postRnValue;
+ bool checkMem;
+ uint64_t postMemOffset;
+ uint32_t postMemLength;
+ uint64_t postMemValue;
+};
+
+
+dataOpTest_t dataOpTests [] =
+{
+ {0xA000,INSTR_ADD,{0,0,0,0,0},AL,0,1,NA,1,MAX_32BIT,NA,NA,NA,NA,1,0},
+ {0xA001,INSTR_ADD,{0,0,0,0,0},AL,0,1,NA,1,MAX_32BIT-1,NA,NA,NA,NA,1,MAX_64BIT},
+ {0xA002,INSTR_ADD,{0,0,0,0,0},AL,0,1,NA,0,NA,MAX_32BIT,NA,NA,NA,1,0},
+ {0xA003,INSTR_ADD,{0,0,0,0,0},AL,0,1,NA,0,NA,MAX_32BIT-1,NA,NA,NA,1,MAX_64BIT},
+ {0xA004,INSTR_ADD,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,SHIFT_LSL, 0,NA,1,0},
+ {0xA005,INSTR_ADD,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,SHIFT_LSL,31,NA,1,0xFFFFFFFF80000001},
+ {0xA006,INSTR_ADD,{0,0,0,0,0},AL,0,1,NA,0,0,3,SHIFT_LSR,1,NA,1,2},
+ {0xA007,INSTR_ADD,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,SHIFT_LSR,31,NA,1,2},
+ {0xA008,INSTR_ADD,{0,0,0,0,0},AL,0,0,NA,0,0,3,SHIFT_ASR,1,NA,1,1},
+ {0xA009,INSTR_ADD,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_64BIT,SHIFT_ASR,31,NA,1,0},
+ {0xA010,INSTR_AND,{0,0,0,0,0},AL,0,1,NA,1,MAX_32BIT,0,0,0,NA,1,1},
+ {0xA011,INSTR_AND,{0,0,0,0,0},AL,0,1,NA,1,MAX_32BIT-1,0,0,0,NA,1,0},
+ {0xA012,INSTR_AND,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,0,0,NA,1,1},
+ {0xA013,INSTR_AND,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT-1,0,0,NA,1,0},
+ {0xA014,INSTR_AND,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,SHIFT_LSL,0,NA,1,1},
+ {0xA015,INSTR_AND,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,SHIFT_LSL,31,NA,1,0},
+ {0xA016,INSTR_AND,{0,0,0,0,0},AL,0,1,NA,0,0,3,SHIFT_LSR,1,NA,1,1},
+ {0xA017,INSTR_AND,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,SHIFT_LSR,31,NA,1,1},
+ {0xA018,INSTR_AND,{0,0,0,0,0},AL,0,0,NA,0,0,3,SHIFT_ASR,1,NA,1,0},
+ {0xA019,INSTR_AND,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,SHIFT_ASR,31,NA,1,1},
+ {0xA020,INSTR_ORR,{0,0,0,0,0},AL,0,3,NA,1,MAX_32BIT,0,0,0,NA,1,MAX_64BIT},
+ {0xA021,INSTR_ORR,{0,0,0,0,0},AL,0,2,NA,1,MAX_32BIT-1,0,0,0,NA,1,MAX_64BIT-1},
+ {0xA022,INSTR_ORR,{0,0,0,0,0},AL,0,3,NA,0,0,MAX_32BIT,0,0,NA,1,MAX_64BIT},
+ {0xA023,INSTR_ORR,{0,0,0,0,0},AL,0,2,NA,0,0,MAX_32BIT-1,0,0,NA,1,MAX_64BIT-1},
+ {0xA024,INSTR_ORR,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,SHIFT_LSL,0,NA,1,MAX_64BIT},
+ {0xA025,INSTR_ORR,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,SHIFT_LSL,31,NA,1,0xFFFFFFFF80000001},
+ {0xA026,INSTR_ORR,{0,0,0,0,0},AL,0,1,NA,0,0,3,SHIFT_LSR,1,NA,1,1},
+ {0xA027,INSTR_ORR,{0,0,0,0,0},AL,0,0,NA,0,0,MAX_32BIT,SHIFT_LSR,31,NA,1,1},
+ {0xA028,INSTR_ORR,{0,0,0,0,0},AL,0,0,NA,0,0,3,SHIFT_ASR,1,NA,1,1},
+ {0xA029,INSTR_ORR,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_64BIT,SHIFT_ASR,31,NA,1,MAX_64BIT},
+ {0xA030,INSTR_CMP,{0,0,0,0,0},AL,1,0x10000,NA,1,0x10000,0,0,0,NA,0,0},
+ {0xA031,INSTR_MUL,{0,0,0,0,0},AL,0,0,0x10000,0,0,0x10000,0,0,NA,1,0},
+ {0xA032,INSTR_MUL,{0,0,0,0,0},AL,0,0,0x1000,0,0,0x10000,0,0,NA,1,0x10000000},
+ {0xA033,INSTR_MUL,{0,0,0,0,0},AL,0,0,MAX_32BIT,0,0,1,0,0,NA,1,MAX_64BIT},
+ {0xA034,INSTR_MLA,{0,0,0,0,0},AL,0,0x10000,0x10000,0,0,0x10000,0,0,NA,1,0x10000},
+ {0xA035,INSTR_MLA,{0,0,0,0,0},AL,0,0x10000,0x1000,0,0,0x10000,0,0,NA,1,0x10010000},
+ {0xA036,INSTR_SUB,{1,R_v1,R_a6,2,4},MI,0,2,NA,0,NA,1,NA,NA,2,1,1},
+ {0xA037,INSTR_SUB,{2,R_v1,R_a6,2,0},MI,0,2,NA,0,NA,1,NA,NA,2,1,2},
+ {0xA038,INSTR_SUB,{1,R_v1,R_a6,4,2},GE,0,2,NA,1,1,NA,NA,NA,2,1,1},
+ {0xA039,INSTR_SUB,{1,R_a5,R_a6,2,7},GE,0,2,NA,1,1,NA,NA,NA,2,1,2},
+ {0xA040,INSTR_SUB,{1,R_a5,R_a6,1,1},HS,0,2,NA,1,1,NA,NA,NA,2,1,1},
+ {0xA041,INSTR_SUB,{1,R_a5,R_a6,0,1},HS,0,2,NA,1,1,NA,NA,NA,2,1,2},
+ {0xA042,INSTR_SUB,{0,0,0,0,0},AL,0,1,NA,1,1<< 16,0,0,0,NA,1,UINT64_C(1) -(1<<16)},
+ {0xA043,INSTR_SUB,{0,0,0,0,0},AL,0,MAX_32BIT,NA,1,1,0,0,0,NA,1,MAX_64BIT-1},
+ {0xA044,INSTR_SUB,{0,0,0,0,0},AL,0,1,NA,1,1,0,0,0,NA,1,0},
+ {0xA045,INSTR_SUB,{0,0,0,0,0},AL,0,1,NA,0,NA,1<<16,0,0,NA,1,UINT64_C(1) -(1<<16)},
+ {0xA046,INSTR_SUB,{0,0,0,0,0},AL,0,MAX_32BIT,NA,0,NA,1,0,0,NA,1,MAX_64BIT-1},
+ {0xA047,INSTR_SUB,{0,0,0,0,0},AL,0,1,NA,0,NA,1,0,0,NA,1,0},
+ {0xA048,INSTR_SUB,{0,0,0,0,0},AL,0,1,NA,0,NA,1,SHIFT_LSL,16,NA,1,UINT64_C(1) -(1<<16)},
+ {0xA049,INSTR_SUB,{0,0,0,0,0},AL,0,0x80000001,NA,0,NA,MAX_32BIT,SHIFT_LSL,31,NA,1,1},
+ {0xA050,INSTR_SUB,{0,0,0,0,0},AL,0,1,NA,0,NA,3,SHIFT_LSR,1,NA,1,0},
+ {0xA051,INSTR_SUB,{0,0,0,0,0},AL,0,1,NA,0,NA,MAX_32BIT,SHIFT_LSR,31,NA,1,0},
+ {0xA052,INSTR_RSB,{1,R_a5,R_a6,4,1},GE,0,2,NA,1,0,NA,NA,NA,2,1,UINT64_C(-2)},
+ {0xA053,INSTR_RSB,{1,R_a5,R_a6,UINT64_C(-1),1},GE,0,2,NA,1,0,NA,NA,NA,2,1,2},
+ {0xA054,INSTR_RSB,{0,0,0,0,0},AL,0,1,NA,1,1<<16,NA,NA,NA,NA,1,(1<<16)-1},
+ {0xA055,INSTR_RSB,{0,0,0,0,0},AL,0,MAX_32BIT,NA,1,1,NA,NA,NA,NA,1,UINT64_C(1)-MAX_64BIT},
+ {0xA056,INSTR_RSB,{0,0,0,0,0},AL,0,1,NA,1,1,NA,NA,NA,NA,1,0},
+ {0xA057,INSTR_RSB,{0,0,0,0,0},AL,0,1,NA,0,NA,1<<16,0,0,NA,1,(1<<16)-1},
+ {0xA058,INSTR_RSB,{0,0,0,0,0},AL,0,MAX_32BIT,NA,0,NA,1,0,0,NA,1,UINT64_C(1)-MAX_64BIT},
+ {0xA059,INSTR_RSB,{0,0,0,0,0},AL,0,1,NA,0,NA,1,0,0,NA,1,0},
+ {0xA060,INSTR_RSB,{0,0,0,0,0},AL,0,1,NA,0,NA,1,SHIFT_LSL,16,NA,1,(1<<16)-1},
+ {0xA061,INSTR_RSB,{0,0,0,0,0},AL,0,0x80000001,NA,0,NA,MAX_32BIT ,SHIFT_LSL,31,NA,1,UINT64_C(-1)},
+ {0xA062,INSTR_RSB,{0,0,0,0,0},AL,0,1,NA,0,NA,3,SHIFT_LSR,1,NA,1,0},
+ {0xA063,INSTR_RSB,{0,0,0,0,0},AL,0,1,NA,0,NA,MAX_32BIT,SHIFT_LSR,31,NA,1,0},
+ {0xA064,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,1,0x80000001,NA,NA,NA,NA,1,0xFFFFFFFF80000001},
+ {0xA065,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,0,0,0x80000001,0,0,NA,1,0xFFFFFFFF80000001},
+ {0xA066,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSL,1,NA,1,MAX_64BIT-1},
+ {0xA067,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSL,31,NA,1,0xFFFFFFFF80000000},
+ {0xA068,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,0,0,3,SHIFT_LSR,1,NA,1,1},
+ {0xA069,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSR,31,NA,1,1},
+ {0xA070,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,0,0,3,SHIFT_ASR,1,NA,1,1},
+ {0xA071,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,0,0,MAX_64BIT ,SHIFT_ASR,31,NA,1,MAX_64BIT},
+ {0xA072,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,0,0,3,SHIFT_ROR,1,NA,1,0xFFFFFFFF80000001},
+ {0xA073,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,0,0,0x80000001,SHIFT_ROR,31,NA,1,3},
+ {0xA074,INSTR_MOV,{0,0,0,0,0},AL,1,NA,NA,0,0,MAX_64BIT -1,SHIFT_ASR,1,NA,1,MAX_64BIT},
+ {0xA075,INSTR_MOV,{0,0,0,0,0},AL,1,NA,NA,0,0,3,SHIFT_ASR,1,NA,1,1},
+ {0xA076,INSTR_MOV,{2,R_a5,R_a6,6,8},MI,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,2},
+ {0xA077,INSTR_MOV,{2,R_a5,R_a6,UINT64_C(-4),UINT64_C(-8)},MI,0,NA,NA,0,0,0x80000001,0,0,2,1,0xFFFFFFFF80000001},
+ {0xA078,INSTR_MOV,{1,R_a5,R_a6,UINT64_C(-1),UINT64_C(-1)},LT,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,2},
+ {0xA079,INSTR_MOV,{1,R_a5,R_a6,UINT64_C(-1),1},LT,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,0xFFFFFFFF80000001},
+ {0xA080,INSTR_MOV,{1,R_a5,R_a6,UINT64_C(-1),UINT64_C(-5)},GE,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSL,1,2,1,MAX_64BIT-1},
+ {0xA081,INSTR_MOV,{1,R_a5,R_a6,5,5},GE,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSL,31,2,1,0xFFFFFFFF80000000},
+ {0xA082,INSTR_MOV,{1,R_a5,R_a6,UINT64_C(-1),1},GE,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSL,31,2,1,2},
+ {0xA083,INSTR_MOV,{1,R_a5,R_a6,4,1},LE,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSL,1,2,1,2},
+ {0xA084,INSTR_MOV,{1,R_a5,R_a6,UINT64_C(-1),UINT64_C(-1)},LE,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,0xFFFFFFFF80000001},
+ {0xA085,INSTR_MOV,{1,R_a5,R_a6,UINT64_C(-1),1},LE,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSL,31,2,1,0xFFFFFFFF80000000},
+ {0xA086,INSTR_MOV,{1,R_a5,R_a6,1,1},GT,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,2},
+ {0xA087,INSTR_MOV,{1,R_a5,R_a6,UINT64_C(-1),UINT64_C(-3)},GT,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,0xFFFFFFFF80000001},
+ {0xA088,INSTR_MOV,{1,R_a5,R_a6,UINT64_C(-1),0},GT,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,2},
+ {0xA089,INSTR_MOV,{1,R_a5,R_a6,UINT64_C(-1),UINT64_C(-1)},GT,0,NA,NA,0,0,0x80000001,0,0,2,1,2},
+ {0xA090,INSTR_MOV,{1,R_a5,R_a6,6,1},GT,0,NA,NA,0,0,0x80000001,0,0,2,1,0xFFFFFFFF80000001},
+ {0xA091,INSTR_MOV,{1,R_a5,R_a6,UINT64_C(-1),1},GT,0,NA,NA,0,0,0x80000001,0,0,2,1,2},
+ {0xA092,INSTR_MOV,{1,R_a5,R_a6,1,1},GT,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSL,1,2,1,2},
+ {0xA093,INSTR_MOV,{1,R_a5,R_a6,4,1},GT,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSL,1,2,1,MAX_64BIT-1},
+ {0xA094,INSTR_MOV,{1,R_a5,R_a6,UINT64_C(-1),1},GT,0,NA,NA,0,0,MAX_32BIT ,SHIFT_LSL,1,2,1,2},
+ {0xA095,INSTR_MOV,{1,R_a5,R_a6,1,UINT64_C(-1)},HS,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,2},
+ {0xA096,INSTR_MOV,{1,R_a5,R_a6,UINT64_C(-1),1},HS,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,0xFFFFFFFF80000001},
+ {0xA097,INSTR_MVN,{1,R_a5,R_a6,1,4},HS,0,NA,NA,1,MAX_32BIT-1,NA,NA,NA,2,1,2},
+ {0xA098,INSTR_MVN,{1,R_a5,R_a6,UINT64_C(-1),1},HS,0,NA,NA,1,MAX_32BIT-1,NA,NA,NA,2,1,1},
+ {0xA099,INSTR_MVN,{0,0,0,0,0},AL,0,NA,NA,1,0,NA,NA,NA,2,1,MAX_64BIT},
+ {0xA100,INSTR_MVN,{0,0,0,0,0},AL,0,NA,NA,0,NA,MAX_32BIT-1,NA,0,2,1,1},
+ {0xA101,INSTR_MVN,{0,0,0,0,0},AL,0,NA,NA,0,NA,0x80000001,NA,0,2,1,0x7FFFFFFE},
+ {0xA102,INSTR_BIC,{0,0,0,0,0},AL,0,1,NA,1,MAX_32BIT,NA,NA,NA,NA,1,0},
+ {0xA103,INSTR_BIC,{0,0,0,0,0},AL,0,1,NA,1,MAX_32BIT-1,NA,NA,NA,NA,1,1},
+ {0xA104,INSTR_BIC,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,0,0,NA,1,0},
+ {0xA105,INSTR_BIC,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT-1,0,0,NA,1,1},
+ {0xA106,INSTR_BIC,{0,0,0,0,0},AL,0,0xF0,NA,0,0,3,SHIFT_ASR,1,NA,1,0xF0},
+ {0xA107,INSTR_BIC,{0,0,0,0,0},AL,0,0xF0,NA,0,0,MAX_64BIT,SHIFT_ASR,31,NA,1,0},
+ {0xA108,INSTR_SMULBB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCDFFFF,0,NA,0xFFFFFFFFABCD0001,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF},
+ {0xA109,INSTR_SMULBB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCD0001,0,NA,0xFFFFFFFFABCD0FFF,NA,NA,NA,1,0x00000FFF},
+ {0xA110,INSTR_SMULBB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCD0001,0,NA,0xFFFFFFFFABCDFFFF,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF},
+ {0xA111,INSTR_SMULBB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCDFFFF,0,NA,0xFFFFFFFFABCDFFFF,NA,NA,NA,1,1},
+ {0xA112,INSTR_SMULBT,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFFFFFABCD,0,NA,0xFFFFFFFFABCD0001,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF},
+ {0xA113,INSTR_SMULBT,{0,0,0,0,0},AL,0,NA,0x000000000001ABCD,0,NA,0xFFFFFFFFABCD0FFF,NA,NA,NA,1,0x00000FFF},
+ {0xA114,INSTR_SMULBT,{0,0,0,0,0},AL,0,NA,0x000000000001ABCD,0,NA,0xFFFFFFFFABCDFFFF,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF},
+ {0xA115,INSTR_SMULBT,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFFFFFABCD,0,NA,0xFFFFFFFFABCDFFFF,NA,NA,NA,1,1},
+ {0xA116,INSTR_SMULTB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCDFFFF,0,NA,0x000000000001ABCD,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF},
+ {0xA117,INSTR_SMULTB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCD0001,0,NA,0x000000000FFFABCD,NA,NA,NA,1,0x00000FFF},
+ {0xA118,INSTR_SMULTB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCD0001,0,NA,0xFFFFFFFFFFFFABCD,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF},
+ {0xA119,INSTR_SMULTB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCDFFFF,0,NA,0xFFFFFFFFFFFFABCD,NA,NA,NA,1,1},
+ {0xA120,INSTR_SMULTT,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFFFFFABCD,0,NA,0x000000000001ABCD,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF},
+ {0xA121,INSTR_SMULTT,{0,0,0,0,0},AL,0,NA,0x000000000001ABCD,0,NA,0x000000000FFFABCD,NA,NA,NA,1,0x00000FFF},
+ {0xA122,INSTR_SMULTT,{0,0,0,0,0},AL,0,NA,0x000000000001ABCD,0,NA,0xFFFFFFFFFFFFABCD,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF},
+ {0xA123,INSTR_SMULTT,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFFFFFABCD,0,NA,0xFFFFFFFFFFFFABCD,NA,NA,NA,1,1},
+ {0xA124,INSTR_SMULWB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCDFFFF,0,NA,0x000000000001ABCD,NA,NA,NA,1,0xFFFFFFFFFFFFFFFE},
+ {0xA125,INSTR_SMULWB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCD0001,0,NA,0x000000000FFFABCD,NA,NA,NA,1,0x00000FFF},
+ {0xA126,INSTR_SMULWB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCD0001,0,NA,0xFFFFFFFFFFFFABCD,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF},
+ {0xA127,INSTR_SMULWB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCDFFFF,0,NA,0xFFFFFFFFFFFFABCD,NA,NA,NA,1,0},
+ {0xA128,INSTR_SMULWT,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFFFFFABCD,0,NA,0x000000000001ABCD,NA,NA,NA,1,0xFFFFFFFFFFFFFFFE},
+ {0xA129,INSTR_SMULWT,{0,0,0,0,0},AL,0,NA,0x000000000001ABCD,0,NA,0x000000000FFFABCD,NA,NA,NA,1,0x00000FFF},
+ {0xA130,INSTR_SMULWT,{0,0,0,0,0},AL,0,NA,0x000000000001ABCD,0,NA,0xFFFFFFFFFFFFABCD,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF},
+ {0xA131,INSTR_SMULWT,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFFFFFABCD,0,NA,0xFFFFFFFFFFFFABCD,NA,NA,NA,1,0},
+ {0xA132,INSTR_SMLABB,{0,0,0,0,0},AL,0,1,0xFFFFFFFFABCDFFFF,0,NA,0xFFFFFFFFABCD0001,NA,NA,NA,1,0},
+ {0xA133,INSTR_SMLABB,{0,0,0,0,0},AL,0,1,0xFFFFFFFFABCD0001,0,NA,0xFFFFFFFFABCD0FFF,NA,NA,NA,1,0x00001000},
+ {0xA134,INSTR_SMLABB,{0,0,0,0,0},AL,0,0xFFFFFFFFFFFFFFFF,0xFFFFFFFFABCD0001,0,NA,0xABCDFFFF,NA,NA,NA,1,0xFFFFFFFFFFFFFFFE},
+ {0xA135,INSTR_SMLABB,{0,0,0,0,0},AL,0,0xFFFFFFFFFFFFFFFF,0xFFFFFFFFABCDFFFF,0,NA,0xABCDFFFF,NA,NA,NA,1,0},
+ {0xA136,INSTR_UXTB16,{0,0,0,0,0},AL,0,NA,NA,0,NA,0xABCDEF01,SHIFT_ROR,0,NA,1,0x00CD0001},
+ {0xA137,INSTR_UXTB16,{0,0,0,0,0},AL,0,NA,NA,0,NA,0xABCDEF01,SHIFT_ROR,1,NA,1,0x00AB00EF},
+ {0xA138,INSTR_UXTB16,{0,0,0,0,0},AL,0,NA,NA,0,NA,0xABCDEF01,SHIFT_ROR,2,NA,1,0x000100CD},
+ {0xA139,INSTR_UXTB16,{0,0,0,0,0},AL,0,NA,NA,0,NA,0xABCDEF01,SHIFT_ROR,3,NA,1,0x00EF00AB},
+ {0xA140,INSTR_ADDR_ADD,{0,0,0,0,0},AL,0,0xCFFFFFFFF,NA,0,NA,0x1,SHIFT_LSL,1,NA,1,0xD00000001},
+ {0xA141,INSTR_ADDR_ADD,{0,0,0,0,0},AL,0,0x01,NA,0,NA,0x1,SHIFT_LSL,2,NA,1,0x5},
+ {0xA142,INSTR_ADDR_ADD,{0,0,0,0,0},AL,0,0xCFFFFFFFF,NA,0,NA,0x1,NA,0,NA,1,0xD00000000},
+ {0xA143,INSTR_ADDR_SUB,{0,0,0,0,0},AL,0,0xD00000001,NA,0,NA,0x010000,SHIFT_LSR,15,NA,1,0xCFFFFFFFF},
+ {0xA144,INSTR_ADDR_SUB,{0,0,0,0,0},AL,0,0xCFFFFFFFF,NA,0,NA,0x020000,SHIFT_LSR,15,NA,1,0xCFFFFFFFB},
+ {0xA145,INSTR_ADDR_SUB,{0,0,0,0,0},AL,0,3,NA,0,NA,0x010000,SHIFT_LSR,15,NA,1,1},
+};
+
+dataTransferTest_t dataTransferTests [] =
+{
+ {0xB000,INSTR_LDR,AL,AL,1,24,0xABCDEF0123456789,0,REG_SCALE_OFFSET,24,NA,NA,NA,NA,NA,0x23456789,0,0,NA,NA,NA},
+ {0xB001,INSTR_LDR,AL,AL,1,0,0xABCDEF0123456789,0,IMM12_OFFSET,NA,4,1,0,1,NA,0x23456789,4,0,NA,NA,NA},
+ {0xB002,INSTR_LDR,AL,AL,1,0,0xABCDEF0123456789,0,NO_OFFSET,NA,NA,0,0,0,NA,0x23456789,0,0,NA,NA,NA},
+ {0xB003,INSTR_LDRB,AL,AL,1,4064,0xABCDEF0123456789,0,REG_SCALE_OFFSET,4064,NA,NA,NA,NA,NA,0x89,0,0,NA,NA,NA},
+ {0xB004,INSTR_LDRB,AL,AL,1,4064,0xABCDEF0123456789,4065,IMM12_OFFSET,NA,0,0,1,0,NA,0x67,4065,0,NA,NA,NA},
+ {0xB005,INSTR_LDRB,AL,AL,1,4064,0xABCDEF0123456789,4065,IMM12_OFFSET,NA,1,0,1,0,NA,0x45,4065,0,NA,NA,NA},
+ {0xB006,INSTR_LDRB,AL,AL,1,4064,0xABCDEF0123456789,4065,IMM12_OFFSET,NA,2,0,1,0,NA,0x23,4065,0,NA,NA,NA},
+ {0xB007,INSTR_LDRB,AL,AL,1,4064,0xABCDEF0123456789,4065,IMM12_OFFSET,NA,1,1,0,1,NA,0x67,4066,0,NA,NA,NA},
+ {0xB008,INSTR_LDRB,AL,AL,1,4064,0xABCDEF0123456789,0,NO_OFFSET,NA,NA,0,0,0,NA,0x89,0,0,NA,NA,NA},
+ {0xB009,INSTR_LDRH,AL,AL,1,0,0xABCDEF0123456789,0,IMM8_OFFSET,NA,2,1,0,1,NA,0x6789,2,0,NA,NA,NA},
+ {0xB010,INSTR_LDRH,AL,AL,1,4064,0xABCDEF0123456789,0,REG_OFFSET,4064,0,0,1,0,NA,0x6789,0,0,NA,NA,NA},
+ {0xB011,INSTR_LDRH,AL,AL,1,4064,0xABCDEF0123456789,0,REG_OFFSET,4066,0,0,1,0,NA,0x2345,0,0,NA,NA,NA},
+ {0xB012,INSTR_LDRH,AL,AL,1,0,0xABCDEF0123456789,0,NO_OFFSET,NA,0,0,0,0,NA,0x6789,0,0,NA,NA,NA},
+ {0xB013,INSTR_LDRH,AL,AL,1,0,0xABCDEF0123456789,2,NO_OFFSET,NA,0,0,0,0,NA,0x2345,2,0,NA,NA,NA},
+ {0xB014,INSTR_STR,AL,AL,1,2,0xDEADBEEFDEADBEEF,4,IMM12_OFFSET,NA,4,1,0,1,0xABCDEF0123456789,0xABCDEF0123456789,8,1,2,8,0xDEAD23456789BEEF},
+ {0xB015,INSTR_STR,AL,AL,1,2,0xDEADBEEFDEADBEEF,4,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,4,1,2,8,0xDEAD23456789BEEF},
+ {0xB016,INSTR_STRB,AL,AL,1,0,0xDEADBEEFDEADBEEF,1,IMM12_OFFSET,NA,0,0,1,0,0xABCDEF0123456789,0xABCDEF0123456789,1,1,0,8,0xDEADBEEFDEAD89EF},
+ {0xB017,INSTR_STRB,AL,AL,1,0,0xDEADBEEFDEADBEEF,1,IMM12_OFFSET,NA,1,0,1,0,0xABCDEF0123456789,0xABCDEF0123456789,1,1,0,8,0xDEADBEEFDE89BEEF},
+ {0xB018,INSTR_STRB,AL,AL,1,0,0xDEADBEEFDEADBEEF,1,IMM12_OFFSET,NA,2,0,1,0,0xABCDEF0123456789,0xABCDEF0123456789,1,1,0,8,0xDEADBEEF89ADBEEF},
+ {0xB019,INSTR_STRB,AL,AL,1,0,0xDEADBEEFDEADBEEF,1,IMM12_OFFSET,NA,4,1,0,1,0xABCDEF0123456789,0xABCDEF0123456789,5,1,0,8,0xDEADBEEFDEAD89EF},
+ {0xB020,INSTR_STRB,AL,AL,1,0,0xDEADBEEFDEADBEEF,1,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,1,1,0,8,0xDEADBEEFDEAD89EF},
+ {0xB021,INSTR_STRH,AL,AL,1,4066,0xDEADBEEFDEADBEEF,4070,IMM8_OFFSET,NA,2,1,0,1,0xABCDEF0123456789,0xABCDEF0123456789,4072,1,4066,8,0xDEAD6789DEADBEEF},
+ {0xB022,INSTR_STRH,AL,AL,1,4066,0xDEADBEEFDEADBEEF,4070,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,4070,1,4066,8,0xDEAD6789DEADBEEF},
+};
+
+
+void flushcache()
+{
+ const long base = long(instrMem);
+ const long curr = base + long(instrMemSize);
+ __builtin___clear_cache((char*)base, (char*)curr);
+}
+
+void dataOpTest(dataOpTest_t test, ArmToMips64Assembler *a64asm, uint32_t Rd = R_v1,
+ uint32_t Rn = R_t0, uint32_t Rm = R_t1, uint32_t Rs = R_t2)
+{
+ int64_t regs[NUM_REGS] = {0};
+ int32_t flags[NUM_FLAGS] = {0};
+ int64_t savedRegs[NUM_REGS] = {0};
+ uint32_t i;
+ uint32_t op2;
+
+ for(i = 0; i < NUM_REGS; ++i)
+ {
+ regs[i] = i;
+ }
+
+ regs[Rd] = test.RdValue;
+ regs[Rn] = test.RnValue;
+ regs[Rs] = test.RsValue;
+ a64asm->reset();
+ if (test.preCond.mode) {
+ a64asm->set_condition(test.preCond.mode, test.preCond.Rcond1, test.preCond.Rcond2);
+ regs[test.preCond.Rcond1] = test.preCond.Rcond1Value;
+ regs[test.preCond.Rcond2] = test.preCond.Rcond2Value;
+ }
+ a64asm->prolog();
+ if(test.immediate == true)
+ {
+ op2 = a64asm->imm(test.immValue);
+ }
+ else if(test.immediate == false && test.shiftAmount == 0)
+ {
+ op2 = Rm;
+ regs[Rm] = (int64_t)((int32_t)(test.RmValue));
+ }
+ else
+ {
+ op2 = a64asm->reg_imm(Rm, test.shiftMode, test.shiftAmount);
+ regs[Rm] = (int64_t)((int32_t)(test.RmValue));
+ }
+ switch(test.op)
+ {
+ case INSTR_ADD: a64asm->ADD(test.cond, test.setFlags, Rd,Rn,op2); break;
+ case INSTR_SUB: a64asm->SUB(test.cond, test.setFlags, Rd,Rn,op2); break;
+ case INSTR_RSB: a64asm->RSB(test.cond, test.setFlags, Rd,Rn,op2); break;
+ case INSTR_AND: a64asm->AND(test.cond, test.setFlags, Rd,Rn,op2); break;
+ case INSTR_ORR: a64asm->ORR(test.cond, test.setFlags, Rd,Rn,op2); break;
+ case INSTR_BIC: a64asm->BIC(test.cond, test.setFlags, Rd,Rn,op2); break;
+ case INSTR_MUL: a64asm->MUL(test.cond, test.setFlags, Rd,Rm,Rs); break;
+ case INSTR_MLA: a64asm->MLA(test.cond, test.setFlags, Rd,Rm,Rs,Rn); break;
+ case INSTR_CMP: a64asm->CMP(test.cond, Rn,op2); break;
+ case INSTR_MOV: a64asm->MOV(test.cond, test.setFlags,Rd,op2); break;
+ case INSTR_MVN: a64asm->MVN(test.cond, test.setFlags,Rd,op2); break;
+ case INSTR_SMULBB:a64asm->SMULBB(test.cond, Rd,Rm,Rs); break;
+ case INSTR_SMULBT:a64asm->SMULBT(test.cond, Rd,Rm,Rs); break;
+ case INSTR_SMULTB:a64asm->SMULTB(test.cond, Rd,Rm,Rs); break;
+ case INSTR_SMULTT:a64asm->SMULTT(test.cond, Rd,Rm,Rs); break;
+ case INSTR_SMULWB:a64asm->SMULWB(test.cond, Rd,Rm,Rs); break;
+ case INSTR_SMULWT:a64asm->SMULWT(test.cond, Rd,Rm,Rs); break;
+ case INSTR_SMLABB:a64asm->SMLABB(test.cond, Rd,Rm,Rs,Rn); break;
+ case INSTR_UXTB16:a64asm->UXTB16(test.cond, Rd,Rm,test.shiftAmount); break;
+ case INSTR_ADDR_ADD: a64asm->ADDR_ADD(test.cond, test.setFlags, Rd,Rn,op2); break;
+ case INSTR_ADDR_SUB: a64asm->ADDR_SUB(test.cond, test.setFlags, Rd,Rn,op2); break;
+ default: printf("Error"); return;
+ }
+ a64asm->epilog(0);
+ a64asm->fix_branches();
+ flushcache();
+
+ asm_function_t asm_function = (asm_function_t)(instrMem);
+
+ for(i = 0; i < NUM_REGS; ++i)
+ savedRegs[i] = regs[i];
+
+ asm_mips_test_jacket(asm_function, regs, flags);
+
+ /* Check if all regs except Rd is same */
+ for(i = 0; i < NUM_REGS; ++i)
+ {
+ if((i == Rd) || i == 2) continue;
+ if(regs[i] != savedRegs[i])
+ {
+ printf("Test %x failed Reg(%d) tampered Expected(0x%" PRIx64 "),"
+ "Actual(0x%" PRIx64 ") t\n", test.id, i, savedRegs[i],
+ regs[i]);
+ exit(0);
+ return;
+ }
+ }
+
+ if(test.checkRd == 1 && regs[Rd] != test.postRdValue)
+ {
+ printf("Test %x failed, Expected(%" PRIx64 "), Actual(%" PRIx64 ")\n",
+ test.id, test.postRdValue, regs[Rd]);
+ exit(0);
+ }
+ else
+ {
+ printf("Test %x passed\n", test.id);
+ }
+}
+
+
+void dataTransferTest(dataTransferTest_t test, ARMAssemblerInterface *a64asm,
+ uint32_t Rd = R_v1, uint32_t Rn = R_t0,uint32_t Rm = R_t1)
+{
+ int64_t regs[NUM_REGS] = {0};
+ int64_t savedRegs[NUM_REGS] = {0};
+ int32_t flags[NUM_FLAGS] = {0};
+ uint32_t i;
+ for(i = 0; i < NUM_REGS; ++i)
+ {
+ regs[i] = i;
+ }
+
+ uint32_t op2;
+
+ regs[Rd] = test.RdValue;
+ regs[Rn] = (uint64_t)(&dataMem[test.RnValue]);
+ regs[Rm] = test.RmValue;
+ flags[test.preFlag] = 1;
+
+ if(test.setMem == true)
+ {
+ unsigned char *mem = (unsigned char *)&dataMem[test.memOffset];
+ uint64_t value = test.memValue;
+ for(int j = 0; j < 8; ++j)
+ {
+ mem[j] = value & 0x00FF;
+ value >>= 8;
+ }
+ }
+ a64asm->reset();
+ a64asm->prolog();
+ if(test.offsetType == REG_SCALE_OFFSET)
+ {
+ op2 = a64asm->reg_scale_pre(Rm);
+ }
+ else if(test.offsetType == REG_OFFSET)
+ {
+ op2 = a64asm->reg_pre(Rm);
+ }
+ else if(test.offsetType == IMM12_OFFSET && test.preIndex == true)
+ {
+ op2 = a64asm->immed12_pre(test.immValue, test.writeBack);
+ }
+ else if(test.offsetType == IMM12_OFFSET && test.postIndex == true)
+ {
+ op2 = a64asm->immed12_post(test.immValue);
+ }
+ else if(test.offsetType == IMM8_OFFSET && test.preIndex == true)
+ {
+ op2 = a64asm->immed8_pre(test.immValue, test.writeBack);
+ }
+ else if(test.offsetType == IMM8_OFFSET && test.postIndex == true)
+ {
+ op2 = a64asm->immed8_post(test.immValue);
+ }
+ else if(test.offsetType == NO_OFFSET)
+ {
+ op2 = a64asm->__immed12_pre(0);
+ }
+ else
+ {
+ printf("Error - Unknown offset\n"); return;
+ }
+
+ switch(test.op)
+ {
+ case INSTR_LDR: a64asm->LDR(test.cond, Rd,Rn,op2); break;
+ case INSTR_LDRB: a64asm->LDRB(test.cond, Rd,Rn,op2); break;
+ case INSTR_LDRH: a64asm->LDRH(test.cond, Rd,Rn,op2); break;
+ case INSTR_ADDR_LDR: a64asm->ADDR_LDR(test.cond, Rd,Rn,op2); break;
+ case INSTR_STR: a64asm->STR(test.cond, Rd,Rn,op2); break;
+ case INSTR_STRB: a64asm->STRB(test.cond, Rd,Rn,op2); break;
+ case INSTR_STRH: a64asm->STRH(test.cond, Rd,Rn,op2); break;
+ case INSTR_ADDR_STR: a64asm->ADDR_STR(test.cond, Rd,Rn,op2); break;
+ default: printf("Error"); return;
+ }
+ a64asm->epilog(0);
+ flushcache();
+
+ asm_function_t asm_function = (asm_function_t)(instrMem);
+
+ for(i = 0; i < NUM_REGS; ++i)
+ savedRegs[i] = regs[i];
+
+ asm_mips_test_jacket(asm_function, regs, flags);
+
+ /* Check if all regs except Rd/Rn are same */
+ for(i = 0; i < NUM_REGS; ++i)
+ {
+ if(i == Rd || i == Rn || i == R_v0) continue;
+
+ if(regs[i] != savedRegs[i])
+ {
+ printf("Test %x failed Reg(%d) tampered"
+ " Expected(0x%" PRIx64 "), Actual(0x%" PRIx64 ") t\n",
+ test.id, i, savedRegs[i], regs[i]);
+ return;
+ }
+ }
+
+ if((uint64_t)regs[Rd] != test.postRdValue)
+ {
+ printf("Test %x failed, "
+ "Expected in Rd(0x%" PRIx64 "), Actual(0x%" PRIx64 ")\n",
+ test.id, test.postRdValue, regs[Rd]);
+ }
+ else if((uint64_t)regs[Rn] != (uint64_t)(&dataMem[test.postRnValue]))
+ {
+ printf("Test %x failed, "
+ "Expected in Rn(0x%" PRIx64 "), Actual(0x%" PRIx64 ")\n",
+ test.id, test.postRnValue, regs[Rn] - (uint64_t)dataMem);
+ }
+ else if(test.checkMem == true)
+ {
+ unsigned char *addr = (unsigned char *)&dataMem[test.postMemOffset];
+ uint64_t value;
+ value = 0;
+ for(uint32_t j = 0; j < test.postMemLength; ++j)
+ value = (value << 8) | addr[test.postMemLength-j-1];
+ if(value != test.postMemValue)
+ {
+ printf("Test %x failed, "
+ "Expected in Mem(0x%" PRIx64 "), Actual(0x%" PRIx64 ")\n",
+ test.id, test.postMemValue, value);
+ }
+ else
+ {
+ printf("Test %x passed\n", test.id);
+ }
+ }
+ else
+ {
+ printf("Test %x passed\n", test.id);
+ }
+}
+
+int main(void)
+{
+ uint32_t i;
+
+ /* Allocate memory to store instructions generated by ArmToArm64Assembler */
+ {
+ int fd = ashmem_create_region("code cache", instrMemSize);
+ if(fd < 0) {
+ printf("IF < 0\n");
+ printf("Creating code cache, ashmem_create_region "
+ "failed with error '%s'", strerror(errno));
+ }
+ instrMem = mmap(NULL, instrMemSize,
+ PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_PRIVATE, fd, 0);
+ }
+
+ ArmToMips64Assembler a64asm(instrMem);
+
+ if(TESTS_DATAOP_ENABLE)
+ {
+ printf("Running data processing tests\n");
+ for(i = 0; i < sizeof(dataOpTests)/sizeof(dataOpTest_t); ++i) {
+ dataOpTest(dataOpTests[i], &a64asm);
+ }
+ }
+
+ if(TESTS_DATATRANSFER_ENABLE)
+ {
+ printf("Running data transfer tests\n");
+ for(i = 0; i < sizeof(dataTransferTests)/sizeof(dataTransferTest_t); ++i)
+ dataTransferTest(dataTransferTests[i], &a64asm);
+ }
+
+ return 0;
+}
diff --git a/libpixelflinger/tests/arch-mips64/col32cb16blend/Android.mk b/libpixelflinger/tests/arch-mips64/col32cb16blend/Android.mk
new file mode 100644
index 0000000..7d4177e
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips64/col32cb16blend/Android.mk
@@ -0,0 +1,18 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ col32cb16blend_test.c \
+ ../../../arch-mips64/col32cb16blend.S
+
+LOCAL_SHARED_LIBRARIES :=
+
+LOCAL_C_INCLUDES :=
+
+LOCAL_MODULE:= test-pixelflinger-mips64-col32cb16blend
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_MULTILIB := 64
+
+include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/tests/arch-mips64/col32cb16blend/col32cb16blend_test.c b/libpixelflinger/tests/arch-mips64/col32cb16blend/col32cb16blend_test.c
new file mode 100644
index 0000000..066eab6
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips64/col32cb16blend/col32cb16blend_test.c
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2015 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 <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+
+#define ARGB_8888_MAX 0xFFFFFFFF
+#define ARGB_8888_MIN 0x00000000
+#define RGB_565_MAX 0xFFFF
+#define RGB_565_MIN 0x0000
+
+struct test_t
+{
+ char name[256];
+ uint32_t src_color;
+ uint16_t dst_color;
+ size_t count;
+};
+
+struct test_t tests[] =
+{
+ {"Count 1, Src=Max, Dst=Min", ARGB_8888_MAX, RGB_565_MIN, 1},
+ {"Count 2, Src=Min, Dst=Max", ARGB_8888_MIN, RGB_565_MAX, 2},
+ {"Count 3, Src=Max, Dst=Max", ARGB_8888_MAX, RGB_565_MAX, 3},
+ {"Count 4, Src=Min, Dst=Min", ARGB_8888_MAX, RGB_565_MAX, 4},
+ {"Count 1, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 1},
+ {"Count 2, Src=Rand, Dst=Rand", 0xABCDEF12, 0x2345, 2},
+ {"Count 3, Src=Rand, Dst=Rand", 0x11111111, 0xEDFE, 3},
+ {"Count 4, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 4},
+ {"Count 5, Src=Rand, Dst=Rand", 0xEFEFFEFE, 0xFACC, 5},
+ {"Count 10, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 10}
+};
+
+void scanline_col32cb16blend_mips64(uint16_t *dst, uint32_t src, size_t count);
+void scanline_col32cb16blend_c(uint16_t * dst, uint32_t src, size_t count)
+{
+ uint32_t srcAlpha = (src>>24);
+ uint32_t f = 0x100 - (srcAlpha + (srcAlpha>>7));
+
+ while (count--)
+ {
+ uint16_t d = *dst;
+ int dstR = (d>>11)&0x1f;
+ int dstG = (d>>5)&0x3f;
+ int dstB = (d)&0x1f;
+ int srcR = (src >> ( 3))&0x1F;
+ int srcG = (src >> ( 8+2))&0x3F;
+ int srcB = (src >> (16+3))&0x1F;
+ srcR += (f*dstR)>>8;
+ srcG += (f*dstG)>>8;
+ srcB += (f*dstB)>>8;
+ *dst++ = (uint16_t)((srcR<<11)|(srcG<<5)|srcB);
+ }
+}
+
+void scanline_col32cb16blend_test()
+{
+ uint16_t dst_c[16], dst_asm[16];
+ uint32_t i, j;
+
+ for(i = 0; i < sizeof(tests)/sizeof(struct test_t); ++i)
+ {
+ struct test_t test = tests[i];
+
+ printf("Testing - %s:",test.name);
+
+ memset(dst_c, 0, sizeof(dst_c));
+ memset(dst_asm, 0, sizeof(dst_asm));
+
+ for(j = 0; j < test.count; ++j)
+ {
+ dst_c[j] = test.dst_color;
+ dst_asm[j] = test.dst_color;
+ }
+
+
+ scanline_col32cb16blend_c(dst_c, test.src_color, test.count);
+ scanline_col32cb16blend_mips64(dst_asm, test.src_color, test.count);
+
+ if(memcmp(dst_c, dst_asm, sizeof(dst_c)) == 0)
+ printf("Passed\n");
+ else
+ printf("Failed\n");
+
+ for(j = 0; j < test.count; ++j)
+ {
+ printf("dst_c[%d] = %x, dst_asm[%d] = %x \n", j, dst_c[j], j, dst_asm[j]);
+ }
+ }
+}
+
+int main()
+{
+ scanline_col32cb16blend_test();
+ return 0;
+}
diff --git a/libpixelflinger/tests/arch-mips64/disassembler/Android.mk b/libpixelflinger/tests/arch-mips64/disassembler/Android.mk
new file mode 100644
index 0000000..4e72b57
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips64/disassembler/Android.mk
@@ -0,0 +1,16 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ mips64_disassembler_test.cpp \
+ ../../../codeflinger/mips64_disassem.c
+
+LOCAL_SHARED_LIBRARIES :=
+
+LOCAL_MODULE:= test-pixelflinger-mips64-disassembler-test
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_MULTILIB := 64
+
+include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/tests/arch-mips64/disassembler/mips64_disassembler_test.cpp b/libpixelflinger/tests/arch-mips64/disassembler/mips64_disassembler_test.cpp
new file mode 100644
index 0000000..22efa9f
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips64/disassembler/mips64_disassembler_test.cpp
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2015 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 <stdio.h>
+#include <inttypes.h>
+#include <string.h>
+#include "../../../codeflinger/mips64_disassem.h"
+
+//typedef uint64_t db_addr_t;
+//db_addr_t mips_disassem(db_addr_t loc, char *di_buffer, int alt_format);
+
+struct test_table_entry_t
+{
+ uint32_t code;
+ const char *instr;
+};
+
+static test_table_entry_t test_table [] =
+{
+ { 0x00011020, "add\tv0,zero,at" },
+ { 0x00832820, "add\ta1,a0,v1" },
+ { 0x00c74020, "add\ta4,a2,a3" },
+ { 0x012a5820, "add\ta7,a5,a6" },
+ { 0x258dffff, "addiu\tt1,t0,-1" },
+ { 0x25cf0004, "addiu\tt3,t2,4" },
+ { 0x02119021, "addu\ts2,s0,s1" },
+ { 0x0274a821, "addu\ts5,s3,s4" },
+ { 0x02d7c024, "and\tt8,s6,s7" },
+ { 0x333aff00, "andi\tk0,t9,0xff00" },
+ { 0x3f7cffff, "aui\tgp,k1,-1" },
+ { 0x3c1dffff, "lui\tsp,0xffff" },
+ { 0x00e04051, "clo\ta4,a3" },
+ { 0x01205050, "clz\ta6,a5" },
+ { 0x016c682c, "dadd\tt1,a7,t0" },
+ { 0x65cf0008, "daddiu\tt3,t2,8" },
+ { 0x0211902d, "daddu\ts2,s0,s1" },
+ { 0x7e741403, "dext\ts4,s3,16,3" },
+ { 0x7eb6f801, "dextm\ts6,s5,0,64" },
+ { 0x7ef87c02, "dextu\tt8,s7,48,16" },
+ { 0x7f3a8207, "dins\tk0,t9,8,9" },
+ { 0x7f7c0005, "dinsm\tgp,k1,0,33" },
+ { 0x7fbe0806, "dinsu\ts8,sp,32,2" },
+ { 0x03e1102e, "dsub\tv0,ra,at" },
+ { 0x0064282f, "dsubu\ta1,v1,a0" },
+ { 0x7cc77a00, "ext\ta3,a2,8,16" },
+ { 0x7d09fc04, "ins\ta5,a4,16,16" },
+ { 0x00200009, "jr\tat" },
+ { 0x00201009, "jalr\tv0,at" },
+ { 0x0020f809, "jalr\tat" },
+ { 0x8082fff0, "lb\tv0,-16(a0)" },
+ { 0x916c0008, "lbu\tt0,8(a7)" },
+ { 0xdfa3ffe8, "ld\tv1,-24(sp)" },
+ { 0x84850080, "lh\ta1,128(a0)" },
+ { 0x94c7ff80, "lhu\ta3,-128(a2)" },
+ { 0x8d09000c, "lw\ta5,12(a4)" },
+ { 0x9d4bfff4, "lwu\ta7,-12(a6)" },
+ { 0x00620898, "mul\tat,v1,v0" },
+ { 0x006208d8, "muh\tat,v1,v0" },
+ { 0x00620899, "mulu\tat,v1,v0" },
+ { 0x006208d9, "muhu\tat,v1,v0" },
+ { 0x00000000, "nop" },
+ { 0x02329827, "nor\ts3,s1,s2" },
+ { 0x0295b025, "or\ts6,s4,s5" },
+ { 0x36f0ff00, "ori\ts0,s7,0xff00" },
+ { 0x7c03103b, "rdhwr\tv0,v1" },
+ { 0x00242a02, "rotr\ta1,a0,8" },
+ { 0x00c74046, "rotrv\ta4,a3,a2" },
+ { 0xa12afff0, "sb\ta6,-16(a5)" },
+ { 0xfd6c0100, "sd\tt0,256(a7)" },
+ { 0x7c0d7420, "seb\tt2,t1" },
+ { 0x7c0f8620, "seh\ts0,t3" },
+ { 0x02329835, "seleqz\ts3,s1,s2" },
+ { 0x0295b037, "selnez\ts6,s4,s5" },
+ { 0xa6f84000, "sh\tt8,16384(s7)" },
+ { 0x0019d100, "sll\tk0,t9,4" },
+ { 0x037ce804, "sllv\tsp,gp,k1" },
+ { 0x03df082a, "slt\tat,s8,ra" },
+ { 0x28430007, "slti\tv1,v0,7" },
+ { 0x2c850020, "sltiu\ta1,a0,32" },
+ { 0x00c7402b, "sltu\ta4,a2,a3" },
+ { 0x00095103, "sra\ta6,a5,4" },
+ { 0x016c6807, "srav\tt1,t0,a7" },
+ { 0x000e7a02, "srl\tt3,t2,8" },
+ { 0x02119006, "srlv\ts2,s1,s0" },
+ { 0x0274a822, "sub\ts5,s3,s4" },
+ { 0x02d7c023, "subu\tt8,s6,s7" },
+ { 0xaf3afffc, "sw\tk0,-4(t9)" },
+ { 0x7c1be0a0, "wsbh\tgp,k1" },
+ { 0x03bef826, "xor\tra,sp,s8" },
+ { 0x3801ffff, "li\tat,0xffff" },
+ { 0x3843ffff, "xori\tv1,v0,0xffff" },
+};
+
+struct test_branches_table_entry_t
+{
+ uint32_t code;
+ const char *instr;
+ int16_t offset;
+};
+
+static test_branches_table_entry_t test_branches_table [] = {
+ { 0x1000ffff, "b\t", static_cast<int16_t>(0xffff) },
+ { 0x13df0008, "beq\ts8,ra,", 0x8 },
+ { 0x042100ff, "bgez\tat,", 0xff },
+ { 0x1c40ff00, "bgtz\tv0,", static_cast<int16_t>(0xff00) },
+ { 0x18605555, "blez\tv1,", 0x5555 },
+ { 0x0480aaaa, "bltz\ta0,", static_cast<int16_t>(0xaaaa) },
+ { 0x14a68888, "bne\ta1,a2,", static_cast<int16_t>(0x8888) },
+};
+
+struct test_jump_table_entry_t
+{
+ uint32_t code;
+ const char *instr;
+ int32_t offset;
+};
+
+static test_jump_table_entry_t test_jump_table [] = {
+ { 0x0956ae66, "j\t", 0x156ae66 },
+ { 0x0d56ae66, "jal\t", 0x156ae66 },
+};
+
+int main()
+{
+ char instr[256];
+ uint32_t failed = 0;
+
+ for(uint32_t i = 0; i < sizeof(test_table)/sizeof(test_table_entry_t); ++i)
+ {
+ test_table_entry_t *test;
+ test = &test_table[i];
+ mips_disassem(&test->code, instr, 0);
+ if(strcmp(instr, test->instr) != 0)
+ {
+ printf("Test Failed \n"
+ "Code : 0x%0x\n"
+ "Expected : %s\n"
+ "Actual : %s\n", test->code, test->instr, instr);
+ failed++;
+ }
+ }
+ for(uint32_t i = 0; i < sizeof(test_branches_table)/sizeof(test_branches_table_entry_t); ++i)
+ {
+ test_branches_table_entry_t *test;
+ test = &test_branches_table[i];
+ mips_disassem(&test->code, instr, 0);
+ //printf("DBG code address: %lx\n", (uint64_t)(&test->code));
+ uint64_t loc = (uint64_t)test + 4 + (test->offset << 2);
+ //printf("DBG loc: %lx\n", loc);
+ char temp[256], address[16];
+ strcpy(temp, test->instr);
+ sprintf(address, "0x%lx", loc);
+ strcat(temp, address);
+ if(strcmp(instr, temp) != 0)
+ {
+ printf("Test Failed \n"
+ "Code : 0x%0x\n"
+ "Expected : %s\n"
+ "Actual : %s\n", test->code, temp, instr);
+ failed++;
+ }
+ }
+ for(uint32_t i = 0; i < sizeof(test_jump_table)/sizeof(test_jump_table_entry_t); ++i)
+ {
+ test_jump_table_entry_t *test;
+ test = &test_jump_table[i];
+ mips_disassem(&test->code, instr, 0);
+ //printf("DBG code address: %lx\n", (uint64_t)(&test->code));
+ uint64_t loc = ((uint64_t)test & 0xfffffffff0000000) | (test->offset << 2);
+ //printf("DBG loc: %lx\n", loc);
+ char temp[256], address[16];
+ strcpy(temp, test->instr);
+ sprintf(address, "0x%08lx", loc);
+ strcat(temp, address);
+ if(strcmp(instr, temp) != 0)
+ {
+ printf("Test Failed \n"
+ "Code : 0x%0x\n"
+ "Expected : '%s'\n"
+ "Actual : '%s'\n", test->code, temp, instr);
+ failed++;
+ }
+ }
+ if(failed == 0)
+ {
+ printf("All tests PASSED\n");
+ return 0;
+ }
+ else
+ {
+ printf("%d tests FAILED\n", failed);
+ return -1;
+ }
+}
diff --git a/libpixelflinger/tests/codegen/codegen.cpp b/libpixelflinger/tests/codegen/codegen.cpp
index 148b6f4..efa6d87 100644
--- a/libpixelflinger/tests/codegen/codegen.cpp
+++ b/libpixelflinger/tests/codegen/codegen.cpp
@@ -11,16 +11,18 @@
#include "codeflinger/ARMAssembler.h"
#if defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6
#include "codeflinger/MIPSAssembler.h"
+#elif defined(__mips__) && defined(__LP64__) && __mips_isa_rev == 6
+#include "codeflinger/MIPS64Assembler.h"
#endif
#include "codeflinger/Arm64Assembler.h"
-#if defined(__arm__) || (defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6) || defined(__aarch64__)
+#if defined(__arm__) || (defined(__mips__) && ((!defined(__LP64__) && __mips_isa_rev < 6) || (defined(__LP64__) && __mips_isa_rev == 6))) || defined(__aarch64__)
# define ANDROID_ARM_CODEGEN 1
#else
# define ANDROID_ARM_CODEGEN 0
#endif
-#if defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6
+#if defined(__mips__) && ((!defined(__LP64__) && __mips_isa_rev < 6) || (defined(__LP64__) && __mips_isa_rev == 6))
#define ASSEMBLY_SCRATCH_SIZE 4096
#elif defined(__aarch64__)
#define ASSEMBLY_SCRATCH_SIZE 8192
@@ -58,6 +60,10 @@
GGLAssembler assembler( new ArmToMipsAssembler(a) );
#endif
+#if defined(__mips__) && defined(__LP64__) && __mips_isa_rev == 6
+ GGLAssembler assembler( new ArmToMips64Assembler(a) );
+#endif
+
#if defined(__aarch64__)
GGLAssembler assembler( new ArmToArm64Assembler(a) );
#endif
diff --git a/libsparse/append2simg.c b/libsparse/append2simg.c
index 1cf827c..eef8764 100644
--- a/libsparse/append2simg.c
+++ b/libsparse/append2simg.c
@@ -82,7 +82,7 @@
exit(-1);
}
- sparse_output = sparse_file_import_auto(output, true, true);
+ sparse_output = sparse_file_import_auto(output, false, true);
if (!sparse_output) {
fprintf(stderr, "Couldn't import output file\n");
exit(-1);
diff --git a/libsync/tests/sync_test.cpp b/libsync/tests/sync_test.cpp
index 55cd687..2c409dc 100644
--- a/libsync/tests/sync_test.cpp
+++ b/libsync/tests/sync_test.cpp
@@ -50,7 +50,7 @@
bool isValid() const {
if (m_fdInitialized) {
int status = fcntl(m_fd, F_GETFD, 0);
- if (status == 0)
+ if (status >= 0)
return true;
else
return false;
@@ -92,7 +92,7 @@
bool isValid() const {
if (m_fdInitialized) {
int status = fcntl(m_fd, F_GETFD, 0);
- if (status == 0)
+ if (status >= 0)
return true;
else
return false;
diff --git a/libsysutils/src/SocketListener.cpp b/libsysutils/src/SocketListener.cpp
index 3011ed7..168899c 100644
--- a/libsysutils/src/SocketListener.cpp
+++ b/libsysutils/src/SocketListener.cpp
@@ -199,13 +199,14 @@
continue;
}
if (mListen && FD_ISSET(mSock, &read_fds)) {
- struct sockaddr addr;
+ sockaddr_storage ss;
+ sockaddr* addrp = reinterpret_cast<sockaddr*>(&ss);
socklen_t alen;
int c;
do {
- alen = sizeof(addr);
- c = accept(mSock, &addr, &alen);
+ alen = sizeof(ss);
+ c = accept(mSock, addrp, &alen);
SLOGV("%s got %d from accept", mSocketName, c);
} while (c < 0 && errno == EINTR);
if (c < 0) {
diff --git a/libutils/Android.mk b/libutils/Android.mk
index bae16bb..631b5a3 100644
--- a/libutils/Android.mk
+++ b/libutils/Android.mk
@@ -53,6 +53,7 @@
LOCAL_CFLAGS_windows := -DMB_CUR_MAX=1
LOCAL_MULTILIB := both
LOCAL_MODULE_HOST_OS := darwin linux windows
+LOCAL_C_INCLUDES += external/safe-iop/include
include $(BUILD_HOST_STATIC_LIBRARY)
@@ -85,6 +86,7 @@
LOCAL_MODULE := libutils
LOCAL_CLANG := true
LOCAL_SANITIZE := integer
+LOCAL_C_INCLUDES += external/safe-iop/include
include $(BUILD_STATIC_LIBRARY)
# For the device, shared
@@ -98,6 +100,7 @@
libdl \
liblog
LOCAL_CFLAGS := -Werror
+LOCAL_C_INCLUDES += external/safe-iop/include
LOCAL_CLANG := true
LOCAL_SANITIZE := integer
diff --git a/libutils/CallStack.cpp b/libutils/CallStack.cpp
index 0bfb520..699da74 100644
--- a/libutils/CallStack.cpp
+++ b/libutils/CallStack.cpp
@@ -16,11 +16,12 @@
#define LOG_TAG "CallStack"
+#include <memory>
+
#include <utils/CallStack.h>
#include <utils/Printer.h>
#include <utils/Errors.h>
#include <utils/Log.h>
-#include <UniquePtr.h>
#include <backtrace/Backtrace.h>
@@ -40,7 +41,7 @@
void CallStack::update(int32_t ignoreDepth, pid_t tid) {
mFrameLines.clear();
- UniquePtr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, tid));
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, tid));
if (!backtrace->Unwind(ignoreDepth)) {
ALOGW("%s: Failed to unwind callstack.", __FUNCTION__);
}
diff --git a/libutils/Looper.cpp b/libutils/Looper.cpp
index b14884b..952c992 100644
--- a/libutils/Looper.cpp
+++ b/libutils/Looper.cpp
@@ -74,7 +74,7 @@
mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),
mPolling(false), mEpollFd(-1), mEpollRebuildRequired(false),
mNextRequestSeq(0), mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
- mWakeEventFd = eventfd(0, EFD_NONBLOCK);
+ mWakeEventFd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
LOG_ALWAYS_FATAL_IF(mWakeEventFd < 0, "Could not make wake event fd: %s",
strerror(errno));
diff --git a/libutils/VectorImpl.cpp b/libutils/VectorImpl.cpp
index 2ac158b..e8d40ed 100644
--- a/libutils/VectorImpl.cpp
+++ b/libutils/VectorImpl.cpp
@@ -21,6 +21,7 @@
#include <stdio.h>
#include <cutils/log.h>
+#include <safe_iop.h>
#include <utils/Errors.h>
#include <utils/VectorImpl.h>
@@ -86,14 +87,19 @@
void* VectorImpl::editArrayImpl()
{
if (mStorage) {
- SharedBuffer* sb = SharedBuffer::bufferFromData(mStorage)->attemptEdit();
- if (sb == 0) {
- sb = SharedBuffer::alloc(capacity() * mItemSize);
- if (sb) {
- _do_copy(sb->data(), mStorage, mCount);
- release_storage();
- mStorage = sb->data();
- }
+ const SharedBuffer* sb = SharedBuffer::bufferFromData(mStorage);
+ SharedBuffer* editable = sb->attemptEdit();
+ if (editable == 0) {
+ // If we're here, we're not the only owner of the buffer.
+ // We must make a copy of it.
+ editable = SharedBuffer::alloc(sb->size());
+ // Fail instead of returning a pointer to storage that's not
+ // editable. Otherwise we'd be editing the contents of a buffer
+ // for which we're not the only owner, which is undefined behaviour.
+ LOG_ALWAYS_FATAL_IF(editable == NULL);
+ _do_copy(editable->data(), mStorage, mCount);
+ release_storage();
+ mStorage = editable->data();
}
}
return mStorage;
@@ -329,13 +335,15 @@
ssize_t VectorImpl::setCapacity(size_t new_capacity)
{
- size_t current_capacity = capacity();
- ssize_t amount = new_capacity - size();
- if (amount <= 0) {
- // we can't reduce the capacity
- return current_capacity;
- }
- SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize);
+ // The capacity must always be greater than or equal to the size
+ // of this vector.
+ if (new_capacity <= size()) {
+ return capacity();
+ }
+
+ size_t new_allocation_size = 0;
+ LOG_ALWAYS_FATAL_IF(!safe_mul(&new_allocation_size, new_capacity, mItemSize));
+ SharedBuffer* sb = SharedBuffer::alloc(new_allocation_size);
if (sb) {
void* array = sb->data();
_do_copy(array, mStorage, size());
@@ -377,9 +385,28 @@
"[%p] _grow: where=%d, amount=%d, count=%d",
this, (int)where, (int)amount, (int)mCount); // caller already checked
- const size_t new_size = mCount + amount;
+ size_t new_size;
+ LOG_ALWAYS_FATAL_IF(!safe_add(&new_size, mCount, amount), "new_size overflow");
+
if (capacity() < new_size) {
- const size_t new_capacity = max(kMinVectorCapacity, ((new_size*3)+1)/2);
+ // NOTE: This implementation used to resize vectors as per ((3*x + 1) / 2)
+ // (sigh..). Also note, the " + 1" was necessary to handle the special case
+ // where x == 1, where the resized_capacity will be equal to the old
+ // capacity without the +1. The old calculation wouldn't work properly
+ // if x was zero.
+ //
+ // This approximates the old calculation, using (x + (x/2) + 1) instead.
+ size_t new_capacity = 0;
+ LOG_ALWAYS_FATAL_IF(!safe_add(&new_capacity, new_size, (new_size / 2)),
+ "new_capacity overflow");
+ LOG_ALWAYS_FATAL_IF(!safe_add(&new_capacity, new_capacity, static_cast<size_t>(1u)),
+ "new_capacity overflow");
+ new_capacity = max(kMinVectorCapacity, new_capacity);
+
+ size_t new_alloc_size = 0;
+ LOG_ALWAYS_FATAL_IF(!safe_mul(&new_alloc_size, new_capacity, mItemSize),
+ "new_alloc_size overflow");
+
// ALOGV("grow vector %p, new_capacity=%d", this, (int)new_capacity);
if ((mStorage) &&
(mCount==where) &&
@@ -387,14 +414,14 @@
(mFlags & HAS_TRIVIAL_DTOR))
{
const SharedBuffer* cur_sb = SharedBuffer::bufferFromData(mStorage);
- SharedBuffer* sb = cur_sb->editResize(new_capacity * mItemSize);
+ SharedBuffer* sb = cur_sb->editResize(new_alloc_size);
if (sb) {
mStorage = sb->data();
} else {
return NULL;
}
} else {
- SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize);
+ SharedBuffer* sb = SharedBuffer::alloc(new_alloc_size);
if (sb) {
void* array = sb->data();
if (where != 0) {
@@ -436,10 +463,19 @@
"[%p] _shrink: where=%d, amount=%d, count=%d",
this, (int)where, (int)amount, (int)mCount); // caller already checked
- const size_t new_size = mCount - amount;
- if (new_size*3 < capacity()) {
- const size_t new_capacity = max(kMinVectorCapacity, new_size*2);
-// ALOGV("shrink vector %p, new_capacity=%d", this, (int)new_capacity);
+ size_t new_size;
+ LOG_ALWAYS_FATAL_IF(!safe_sub(&new_size, mCount, amount));
+
+ if (new_size < (capacity() / 2)) {
+ // NOTE: (new_size * 2) is safe because capacity didn't overflow and
+ // new_size < (capacity / 2)).
+ const size_t new_capacity = max(kMinVectorCapacity, new_size * 2);
+
+ // NOTE: (new_capacity * mItemSize), (where * mItemSize) and
+ // ((where + amount) * mItemSize) beyond this point are safe because
+ // we are always reducing the capacity of the underlying SharedBuffer.
+ // In other words, (old_capacity * mItemSize) did not overflow, and
+ // where < (where + amount) < new_capacity < old_capacity.
if ((where == new_size) &&
(mFlags & HAS_TRIVIAL_COPY) &&
(mFlags & HAS_TRIVIAL_DTOR))
diff --git a/libutils/tests/Android.mk b/libutils/tests/Android.mk
index cb9e8a2..8f07f1a 100644
--- a/libutils/tests/Android.mk
+++ b/libutils/tests/Android.mk
@@ -38,3 +38,11 @@
libutils \
include $(BUILD_NATIVE_TEST)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libutils_tests_host
+LOCAL_SRC_FILES := Vector_test.cpp
+LOCAL_STATIC_LIBRARIES := libutils liblog
+
+include $(BUILD_HOST_NATIVE_TEST)
diff --git a/libutils/tests/LruCache_test.cpp b/libutils/tests/LruCache_test.cpp
index 580b980..dd95c57 100644
--- a/libutils/tests/LruCache_test.cpp
+++ b/libutils/tests/LruCache_test.cpp
@@ -73,6 +73,13 @@
ssize_t ComplexValue::instanceCount = 0;
+struct KeyWithPointer {
+ int *ptr;
+ bool operator ==(const KeyWithPointer& other) const {
+ return *ptr == *other.ptr;
+ }
+};
+
} // namespace
@@ -84,6 +91,10 @@
return hash_type(value.k);
}
+template<> inline android::hash_t hash_type(const KeyWithPointer& value) {
+ return hash_type(*value.ptr);
+}
+
class EntryRemovedCallback : public OnEntryRemoved<SimpleKey, StringValue> {
public:
EntryRemovedCallback() : callbackCount(0), lastKey(-1), lastValue(NULL) { }
@@ -98,6 +109,14 @@
StringValue lastValue;
};
+class InvalidateKeyCallback : public OnEntryRemoved<KeyWithPointer, StringValue> {
+public:
+ void operator()(KeyWithPointer& k, StringValue&) {
+ delete k.ptr;
+ k.ptr = nullptr;
+ }
+};
+
class LruCacheTest : public testing::Test {
protected:
virtual void SetUp() {
@@ -293,6 +312,25 @@
EXPECT_EQ(3, callback.callbackCount);
}
+TEST_F(LruCacheTest, CallbackRemovesKeyWorksOK) {
+ LruCache<KeyWithPointer, StringValue> cache(1);
+ InvalidateKeyCallback callback;
+ cache.setOnEntryRemovedListener(&callback);
+ KeyWithPointer key1;
+ key1.ptr = new int(1);
+ KeyWithPointer key2;
+ key2.ptr = new int(2);
+
+ cache.put(key1, "one");
+ // As the size of the cache is 1, the put will call the callback.
+ // Make sure everything goes smoothly even if the callback invalidates
+ // the key (b/24785286)
+ cache.put(key2, "two");
+ EXPECT_EQ(1U, cache.size());
+ EXPECT_STREQ("two", cache.get(key2));
+ cache.clear();
+}
+
TEST_F(LruCacheTest, IteratorCheck) {
LruCache<int, int> cache(100);
diff --git a/libutils/tests/Vector_test.cpp b/libutils/tests/Vector_test.cpp
index 0ba7161..d9b32f9 100644
--- a/libutils/tests/Vector_test.cpp
+++ b/libutils/tests/Vector_test.cpp
@@ -16,6 +16,8 @@
#define LOG_TAG "Vector_test"
+#define __STDC_LIMIT_MACROS
+#include <stdint.h>
#include <utils/Vector.h>
#include <cutils/log.h>
#include <gtest/gtest.h>
@@ -71,5 +73,80 @@
EXPECT_EQ(other[3], 5);
}
+// TODO: gtest isn't capable of parsing Abort messages formatted by
+// Android (fails differently on host and target), so we always need to
+// use an empty error message for death tests.
+TEST_F(VectorTest, SetCapacity_Overflow) {
+ Vector<int> vector;
+ EXPECT_DEATH(vector.setCapacity(SIZE_MAX / sizeof(int) + 1), "");
+}
+
+TEST_F(VectorTest, SetCapacity_ShrinkBelowSize) {
+ Vector<int> vector;
+ vector.add(1);
+ vector.add(2);
+ vector.add(3);
+ vector.add(4);
+
+ vector.setCapacity(8);
+ ASSERT_EQ(8, vector.capacity());
+ vector.setCapacity(2);
+ ASSERT_EQ(8, vector.capacity());
+}
+
+// NOTE: All of the tests below are useless because of the "TODO" above.
+// We have no way of knowing *why* the process crashed. Given that we're
+// inserting a NULL array, we'll fail with a SIGSEGV eventually. We need
+// the ability to make assertions on the abort message to make sure we're
+// failing for the right reasons.
+TEST_F(VectorTest, _grow_OverflowSize) {
+ Vector<int> vector;
+ vector.add(1);
+
+ // Checks that the size calculation (not the capacity calculation) doesn't
+ // overflow : the size here will be (1 + SIZE_MAX).
+ //
+ // EXPECT_DEATH(vector.insertArrayAt(NULL, 0, SIZE_MAX), "new_size_overflow");
+ EXPECT_DEATH(vector.insertArrayAt(NULL, 0, SIZE_MAX), "");
+}
+
+TEST_F(VectorTest, _grow_OverflowCapacityDoubling) {
+ Vector<int> vector;
+
+ // This should fail because the calculated capacity will overflow even though
+ // the size of the vector doesn't.
+ //
+ // EXPECT_DEATH(vector.insertArrayAt(NULL, 0, (SIZE_MAX - 1)), "new_capacity_overflow");
+ EXPECT_DEATH(vector.insertArrayAt(NULL, 0, (SIZE_MAX - 1)), "");
+}
+
+TEST_F(VectorTest, _grow_OverflowBufferAlloc) {
+ Vector<int> vector;
+ // This should fail because the capacity * sizeof(int) overflows, even
+ // though the capacity itself doesn't.
+ //
+ // EXPECT_DEATH(vector.insertArrayAt(NULL, 0, (SIZE_MAX / 2)), "new_alloc_size overflow");
+ EXPECT_DEATH(vector.insertArrayAt(NULL, 0, (SIZE_MAX / 2)), "");
+}
+
+TEST_F(VectorTest, editArray_Shared) {
+ Vector<int> vector1;
+ vector1.add(1);
+ vector1.add(2);
+ vector1.add(3);
+ vector1.add(4);
+
+ Vector<int> vector2 = vector1;
+ ASSERT_EQ(vector1.array(), vector2.array());
+ // We must make a copy here, since we're not the exclusive owners
+ // of this array.
+ ASSERT_NE(vector1.editArray(), vector2.editArray());
+
+ // Vector doesn't implement operator ==.
+ ASSERT_EQ(vector1.size(), vector2.size());
+ for (size_t i = 0; i < vector1.size(); ++i) {
+ EXPECT_EQ(vector1[i], vector2[i]);
+ }
+}
} // namespace android
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index 3d18f7c..07ef6cd 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -30,9 +30,9 @@
#include <memory>
#include <vector>
-#include "base/file.h"
-#include "base/macros.h" // TEMP_FAILURE_RETRY may or may not be in unistd
-#include "base/memory.h"
+#include "android-base/file.h"
+#include "android-base/macros.h" // TEMP_FAILURE_RETRY may or may not be in unistd
+#include "android-base/memory.h"
#include "log/log.h"
#include "utils/Compat.h"
#include "utils/FileMap.h"
diff --git a/libziparchive/zip_archive_common.h b/libziparchive/zip_archive_common.h
index 7f20d51..ca42509 100644
--- a/libziparchive/zip_archive_common.h
+++ b/libziparchive/zip_archive_common.h
@@ -17,7 +17,7 @@
#ifndef LIBZIPARCHIVE_ZIPARCHIVECOMMON_H_
#define LIBZIPARCHIVE_ZIPARCHIVECOMMON_H_
-#include "base/macros.h"
+#include "android-base/macros.h"
#include <inttypes.h>
diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc
index 32b1a38..cb0f410 100644
--- a/libziparchive/zip_archive_test.cc
+++ b/libziparchive/zip_archive_test.cc
@@ -23,7 +23,7 @@
#include <unistd.h>
#include <vector>
-#include <base/file.h>
+#include <android-base/file.h>
#include <gtest/gtest.h>
static std::string test_data_dir;
diff --git a/libziparchive/zip_writer.cc b/libziparchive/zip_writer.cc
index 22a7c53..f117cc5 100644
--- a/libziparchive/zip_writer.cc
+++ b/libziparchive/zip_writer.cc
@@ -267,12 +267,12 @@
if (z_stream_->avail_out == 0) {
// The output is full, let's write it to disk.
- size_t dataToWrite = z_stream_->next_out - buffer_.data();
- if (fwrite(buffer_.data(), 1, dataToWrite, file_) != dataToWrite) {
+ size_t write_bytes = z_stream_->next_out - buffer_.data();
+ if (fwrite(buffer_.data(), 1, write_bytes, file_) != write_bytes) {
return HandleError(kIoError);
}
- file->compressed_size += dataToWrite;
- current_offset_ += dataToWrite;
+ file->compressed_size += write_bytes;
+ current_offset_ += write_bytes;
// Reset the output buffer for the next input.
z_stream_->next_out = buffer_.data();
@@ -288,18 +288,32 @@
assert(z_stream_->next_out != nullptr);
assert(z_stream_->avail_out != 0);
- int zerr = deflate(z_stream_.get(), Z_FINISH);
+ // Keep deflating while there isn't enough space in the buffer to
+ // to complete the compress.
+ int zerr;
+ while ((zerr = deflate(z_stream_.get(), Z_FINISH)) == Z_OK) {
+ assert(z_stream_->avail_out == 0);
+ size_t write_bytes = z_stream_->next_out - buffer_.data();
+ if (fwrite(buffer_.data(), 1, write_bytes, file_) != write_bytes) {
+ return HandleError(kIoError);
+ }
+ file->compressed_size += write_bytes;
+ current_offset_ += write_bytes;
+
+ z_stream_->next_out = buffer_.data();
+ z_stream_->avail_out = buffer_.size();
+ }
if (zerr != Z_STREAM_END) {
return HandleError(kZlibError);
}
- size_t dataToWrite = z_stream_->next_out - buffer_.data();
- if (dataToWrite != 0) {
- if (fwrite(buffer_.data(), 1, dataToWrite, file_) != dataToWrite) {
+ size_t write_bytes = z_stream_->next_out - buffer_.data();
+ if (write_bytes != 0) {
+ if (fwrite(buffer_.data(), 1, write_bytes, file_) != write_bytes) {
return HandleError(kIoError);
}
- file->compressed_size += dataToWrite;
- current_offset_ += dataToWrite;
+ file->compressed_size += write_bytes;
+ current_offset_ += write_bytes;
}
z_stream_.reset();
return kNoError;
diff --git a/libziparchive/zip_writer_test.cc b/libziparchive/zip_writer_test.cc
index 046f195..b7d1458 100644
--- a/libziparchive/zip_writer_test.cc
+++ b/libziparchive/zip_writer_test.cc
@@ -17,9 +17,10 @@
#include "ziparchive/zip_archive.h"
#include "ziparchive/zip_writer.h"
-#include <base/test_utils.h>
+#include <android-base/test_utils.h>
#include <gtest/gtest.h>
#include <memory>
+#include <vector>
struct zipwriter : public ::testing::Test {
TemporaryFile* temp_file_;
@@ -168,3 +169,40 @@
CloseArchive(handle);
}
+
+TEST_F(zipwriter, WriteCompressedZipFlushFull) {
+ // This exact data will cause the Finish() to require multiple calls
+ // to deflate() because the ZipWriter buffer isn't big enough to hold
+ // the entire compressed data buffer.
+ constexpr size_t kBufSize = 10000000;
+ std::vector<uint8_t> buffer(kBufSize);
+ size_t prev = 1;
+ for (size_t i = 0; i < kBufSize; i++) {
+ buffer[i] = i + prev;
+ prev = i;
+ }
+
+ ZipWriter writer(file_);
+ ASSERT_EQ(0, writer.StartEntry("file.txt", ZipWriter::kCompress));
+ ASSERT_EQ(0, writer.WriteBytes(buffer.data(), buffer.size()));
+ ASSERT_EQ(0, writer.FinishEntry());
+ ASSERT_EQ(0, writer.Finish());
+
+ ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
+
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
+
+ ZipEntry data;
+ ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
+ EXPECT_EQ(kCompressDeflated, data.method);
+ EXPECT_EQ(kBufSize, data.uncompressed_length);
+
+ std::vector<uint8_t> decompress(kBufSize);
+ memset(decompress.data(), 0, kBufSize);
+ ASSERT_EQ(0, ExtractToMemory(handle, &data, decompress.data(), decompress.size()));
+ EXPECT_EQ(0, memcmp(decompress.data(), buffer.data(), kBufSize))
+ << "Input buffer and output buffer are different.";
+
+ CloseArchive(handle);
+}
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index 7bbc811..aa3db8a 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -230,7 +230,7 @@
}
static void writefilestring(char *path, char *s) {
- int fd = open(path, O_WRONLY);
+ int fd = open(path, O_WRONLY | O_CLOEXEC);
int len = strlen(s);
int ret;
@@ -410,7 +410,8 @@
}
static void ctrl_connect_handler(uint32_t events __unused) {
- struct sockaddr addr;
+ struct sockaddr_storage ss;
+ struct sockaddr *addrp = (struct sockaddr *)&ss;
socklen_t alen;
struct epoll_event epev;
@@ -419,8 +420,8 @@
ctrl_dfd_reopened = 1;
}
- alen = sizeof(addr);
- ctrl_dfd = accept(ctrl_lfd, &addr, &alen);
+ alen = sizeof(ss);
+ ctrl_dfd = accept(ctrl_lfd, addrp, &alen);
if (ctrl_dfd < 0) {
ALOGE("lmkd control socket accept failed; errno=%d", errno);
@@ -486,7 +487,7 @@
memset(mip, 0, sizeof(struct sysmeminfo));
- fd = open(ZONEINFO_PATH, O_RDONLY);
+ fd = open(ZONEINFO_PATH, O_RDONLY | O_CLOEXEC);
if (fd == -1) {
ALOGE("%s open: errno=%d", ZONEINFO_PATH, errno);
return -1;
@@ -517,7 +518,7 @@
ssize_t ret;
snprintf(path, PATH_MAX, "/proc/%d/statm", pid);
- fd = open(path, O_RDONLY);
+ fd = open(path, O_RDONLY | O_CLOEXEC);
if (fd == -1)
return -1;
@@ -540,7 +541,7 @@
ssize_t ret;
snprintf(path, PATH_MAX, "/proc/%d/cmdline", pid);
- fd = open(path, O_RDONLY);
+ fd = open(path, O_RDONLY | O_CLOEXEC);
if (fd == -1)
return NULL;
ret = read_all(fd, line, sizeof(line) - 1);
@@ -685,19 +686,19 @@
struct epoll_event epev;
int ret;
- mpfd = open(MEMCG_SYSFS_PATH "memory.pressure_level", O_RDONLY);
+ mpfd = open(MEMCG_SYSFS_PATH "memory.pressure_level", O_RDONLY | O_CLOEXEC);
if (mpfd < 0) {
ALOGI("No kernel memory.pressure_level support (errno=%d)", errno);
goto err_open_mpfd;
}
- evctlfd = open(MEMCG_SYSFS_PATH "cgroup.event_control", O_WRONLY);
+ evctlfd = open(MEMCG_SYSFS_PATH "cgroup.event_control", O_WRONLY | O_CLOEXEC);
if (evctlfd < 0) {
ALOGI("No kernel memory cgroup event control (errno=%d)", errno);
goto err_open_evctlfd;
}
- evfd = eventfd(0, EFD_NONBLOCK);
+ evfd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
if (evfd < 0) {
ALOGE("eventfd failed for level %s; errno=%d", levelstr, errno);
goto err_eventfd;
diff --git a/lmkd/lmkd.rc b/lmkd/lmkd.rc
index 83c5ff0..3bb84ab 100644
--- a/lmkd/lmkd.rc
+++ b/lmkd/lmkd.rc
@@ -1,4 +1,6 @@
service lmkd /system/bin/lmkd
class core
+ group root readproc
critical
socket lmkd seqpacket 0660 system system
+ writepid /dev/cpuset/system-background/tasks
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index cb9a85b..ddc91ca 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -6,6 +6,7 @@
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
+#include <getopt.h>
#include <math.h>
#include <sched.h>
#include <signal.h>
@@ -24,8 +25,8 @@
#include <memory>
#include <string>
-#include <base/file.h>
-#include <base/strings.h>
+#include <android-base/file.h>
+#include <android-base/strings.h>
#include <cutils/sched_policy.h>
#include <cutils/sockets.h>
#include <log/event_tag_map.h>
@@ -256,13 +257,19 @@
" -s Set default filter to silent.\n"
" Like specifying filterspec '*:S'\n"
" -f <filename> Log to file. Default is stdout\n"
+ " --file=<filename>\n"
" -r <kbytes> Rotate log every kbytes. Requires -f\n"
+ " --rotate_kbytes=<kbytes>\n"
" -n <count> Sets max number of rotated logs to <count>, default 4\n"
- " -v <format> Sets the log print format, where <format> is:\n\n"
+ " --rotate_count=<count>\n"
+ " -v <format> Sets the log print format, where <format> is:\n"
+ " --format=<format>\n"
" brief color epoch long monotonic printable process raw\n"
- " tag thread threadtime time usec UTC year zone\n\n"
+ " tag thread threadtime time uid usec UTC year zone\n\n"
" -D print dividers between each log buffer\n"
+ " --dividers\n"
" -c clear (flush) the entire log and exit\n"
+ " --clear\n"
" -d dump the log and then exit (don't block)\n"
" -t <count> print only the most recent <count> lines (implies -d)\n"
" -t '<time>' print most recent lines since specified time (implies -d)\n"
@@ -271,22 +278,34 @@
" count is pure numerical, time is 'MM-DD hh:mm:ss.mmm...'\n"
" 'YYYY-MM-DD hh:mm:ss.mmm...' or 'sssss.mmm...' format\n"
" -g get the size of the log's ring buffer and exit\n"
- " -L dump logs from prior to last reboot\n"
- " -b <buffer> Request alternate ring buffer, 'main', 'system', 'radio',\n"
- " 'events', 'crash' or 'all'. Multiple -b parameters are\n"
- " allowed and results are interleaved. The default is\n"
- " -b main -b system -b crash.\n"
- " -B output the log in binary.\n"
- " -S output statistics.\n"
+ " --buffer_size\n"
" -G <size> set size of log ring buffer, may suffix with K or M.\n"
+ " --buffer_size=<size>\n"
+ " -L dump logs from prior to last reboot\n"
+ " --last\n"
+ // Leave security (Device Owner only installations) and
+ // kernel (userdebug and eng) buffers undocumented.
+ " -b <buffer> Request alternate ring buffer, 'main', 'system', 'radio',\n"
+ " --buffer=<buffer> 'events', 'crash', 'default' or 'all'. Multiple -b\n"
+ " parameters are allowed and results are interleaved. The\n"
+ " default is -b main -b system -b crash.\n"
+ " -B output the log in binary.\n"
+ " --binary\n"
+ " -S output statistics.\n"
+ " --statistics\n"
" -p print prune white and ~black list. Service is specified as\n"
- " UID, UID/PID or /PID. Weighed for quicker pruning if prefix\n"
+ " --prune UID, UID/PID or /PID. Weighed for quicker pruning if prefix\n"
" with ~, otherwise weighed for longevity if unadorned. All\n"
" other pruning activity is oldest first. Special case ~!\n"
" represents an automatic quicker pruning for the noisiest\n"
" UID as determined by the current statistics.\n"
" -P '<list> ...' set prune white and ~black list, using same format as\n"
- " printed above. Must be quoted.\n");
+ " --prune='<list> ...' printed above. Must be quoted.\n"
+ " --pid=<pid> Only prints logs from the given pid.\n"
+ // Check ANDROID_LOG_WRAP_DEFAULT_TIMEOUT value
+ " --wrap Sleep for 2 hours or when buffer about to wrap whichever\n"
+ " comes first. Improves efficiency of polling by providing\n"
+ " an about-to-wrap wakeup.\n");
fprintf(stderr,"\nfilterspecs are a series of \n"
" <tag>[:priority]\n\n"
@@ -348,15 +367,19 @@
static bool getSizeTArg(char *ptr, size_t *val, size_t min = 0,
size_t max = SIZE_MAX)
{
- char *endp;
- errno = 0;
- size_t ret = (size_t) strtoll(ptr, &endp, 0);
-
- if (endp[0] != '\0' || errno != 0 ) {
+ if (!ptr) {
return false;
}
- if (ret > max || ret < min) {
+ char *endp;
+ errno = 0;
+ size_t ret = (size_t)strtoll(ptr, &endp, 0);
+
+ if (endp[0] || errno) {
+ return false;
+ }
+
+ if ((ret > max) || (ret < min)) {
return false;
}
@@ -398,7 +421,9 @@
return retval;
}
- log_time now(CLOCK_REALTIME);
+ clockid_t clock_type = android_log_clockid();
+ log_time now(clock_type);
+ bool monotonic = clock_type == CLOCK_MONOTONIC;
std::string directory;
char *file = strrchr(outputFileName, '/');
@@ -417,7 +442,11 @@
struct dirent *dp;
while ((dp = readdir(dir.get())) != NULL) {
if ((dp->d_type != DT_REG)
- || strncmp(dp->d_name, file, len)
+ // If we are using realtime, check all files that match the
+ // basename for latest time. If we are using monotonic time
+ // then only check the main file because time cycles on
+ // every reboot.
+ || strncmp(dp->d_name, file, len + monotonic)
|| (dp->d_name[len]
&& ((dp->d_name[len] != '.')
|| !isdigit(dp->d_name[len+1])))) {
@@ -491,6 +520,7 @@
struct logger_list *logger_list;
size_t tail_lines = 0;
log_time tail_time(log_time::EPOCH);
+ size_t pid = 0;
signal(SIGPIPE, exit);
@@ -504,13 +534,66 @@
for (;;) {
int ret;
- ret = getopt(argc, argv, ":cdDLt:T:gG:sQf:r:n:v:b:BSpP:");
+ int option_index = 0;
+ static const char pid_str[] = "pid";
+ static const char wrap_str[] = "wrap";
+ static const struct option long_options[] = {
+ { "binary", no_argument, NULL, 'B' },
+ { "buffer", required_argument, NULL, 'b' },
+ { "buffer_size", optional_argument, NULL, 'g' },
+ { "clear", no_argument, NULL, 'c' },
+ { "dividers", no_argument, NULL, 'D' },
+ { "file", required_argument, NULL, 'f' },
+ { "format", required_argument, NULL, 'v' },
+ { "last", no_argument, NULL, 'L' },
+ { pid_str, required_argument, NULL, 0 },
+ { "prune", optional_argument, NULL, 'p' },
+ { "rotate_count", required_argument, NULL, 'n' },
+ { "rotate_kbytes", required_argument, NULL, 'r' },
+ { "statistics", no_argument, NULL, 'S' },
+ // support, but ignore and do not document, the optional argument
+ { wrap_str, optional_argument, NULL, 0 },
+ { NULL, 0, NULL, 0 }
+ };
+
+ ret = getopt_long(argc, argv, ":cdDLt:T:gG:sQf:r:n:v:b:BSpP:",
+ long_options, &option_index);
if (ret < 0) {
break;
}
- switch(ret) {
+ switch (ret) {
+ case 0:
+ // One of the long options
+ if (long_options[option_index].name == pid_str) {
+ // ToDo: determine runtime PID_MAX?
+ if (!getSizeTArg(optarg, &pid, 1)) {
+ logcat_panic(true, "%s %s out of range\n",
+ long_options[option_index].name, optarg);
+ }
+ break;
+ }
+ if (long_options[option_index].name == wrap_str) {
+ mode |= ANDROID_LOG_WRAP |
+ ANDROID_LOG_RDONLY |
+ ANDROID_LOG_NONBLOCK;
+ // ToDo: implement API that supports setting a wrap timeout
+ size_t dummy = ANDROID_LOG_WRAP_DEFAULT_TIMEOUT;
+ if (optarg && !getSizeTArg(optarg, &dummy, 1)) {
+ logcat_panic(true, "%s %s out of range\n",
+ long_options[option_index].name, optarg);
+ }
+ if (dummy != ANDROID_LOG_WRAP_DEFAULT_TIMEOUT) {
+ fprintf(stderr,
+ "WARNING: %s %u seconds, ignoring %zu\n",
+ long_options[option_index].name,
+ ANDROID_LOG_WRAP_DEFAULT_TIMEOUT, dummy);
+ }
+ break;
+ }
+ break;
+
case 's':
// default to all silent
android_log_addFilterRule(g_logformat, "*:s");
@@ -562,8 +645,11 @@
break;
case 'g':
- getLogSize = 1;
- break;
+ if (!optarg) {
+ getLogSize = 1;
+ break;
+ }
+ // FALLTHRU
case 'G': {
char *cp;
@@ -601,24 +687,31 @@
break;
case 'p':
- getPruneList = 1;
- break;
+ if (!optarg) {
+ getPruneList = 1;
+ break;
+ }
+ // FALLTHRU
case 'P':
setPruneList = optarg;
break;
case 'b': {
- if (strcmp(optarg, "all") == 0) {
- while (devices) {
- dev = devices;
- devices = dev->next;
- delete dev;
- }
+ if (strcmp(optarg, "default") == 0) {
+ for (int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
+ switch (i) {
+ case LOG_ID_SECURITY:
+ case LOG_ID_EVENTS:
+ continue;
+ case LOG_ID_MAIN:
+ case LOG_ID_SYSTEM:
+ case LOG_ID_CRASH:
+ break;
+ default:
+ continue;
+ }
- devices = dev = NULL;
- g_devCount = 0;
- for(int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
const char *name = android_log_id_to_name((log_id_t)i);
log_id_t log_id = android_name_to_log_id(name);
@@ -626,7 +719,58 @@
continue;
}
- bool binary = strcmp(name, "events") == 0;
+ bool found = false;
+ for (dev = devices; dev; dev = dev->next) {
+ if (!strcmp(optarg, dev->device)) {
+ found = true;
+ break;
+ }
+ if (!dev->next) {
+ break;
+ }
+ }
+ if (found) {
+ break;
+ }
+
+ log_device_t* d = new log_device_t(name, false);
+
+ if (dev) {
+ dev->next = d;
+ dev = d;
+ } else {
+ devices = dev = d;
+ }
+ g_devCount++;
+ }
+ break;
+ }
+
+ if (strcmp(optarg, "all") == 0) {
+ for (int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
+ const char *name = android_log_id_to_name((log_id_t)i);
+ log_id_t log_id = android_name_to_log_id(name);
+
+ if (log_id != (log_id_t)i) {
+ continue;
+ }
+
+ bool found = false;
+ for (dev = devices; dev; dev = dev->next) {
+ if (!strcmp(optarg, dev->device)) {
+ found = true;
+ break;
+ }
+ if (!dev->next) {
+ break;
+ }
+ }
+ if (found) {
+ break;
+ }
+
+ bool binary = !strcmp(name, "events") ||
+ !strcmp(name, "security");
log_device_t* d = new log_device_t(name, binary);
if (dev) {
@@ -640,14 +784,21 @@
break;
}
- bool binary = strcmp(optarg, "events") == 0;
+ bool binary = !(strcmp(optarg, "events") &&
+ strcmp(optarg, "security"));
if (devices) {
dev = devices;
while (dev->next) {
+ if (!strcmp(optarg, dev->device)) {
+ dev = NULL;
+ break;
+ }
dev = dev->next;
}
- dev->next = new log_device_t(optarg, binary);
+ if (dev) {
+ dev->next = new log_device_t(optarg, binary);
+ }
} else {
devices = new log_device_t(optarg, binary);
}
@@ -832,9 +983,9 @@
dev = devices;
if (tail_time != log_time::EPOCH) {
- logger_list = android_logger_list_alloc_time(mode, tail_time, 0);
+ logger_list = android_logger_list_alloc_time(mode, tail_time, pid);
} else {
- logger_list = android_logger_list_alloc(mode, tail_lines, 0);
+ logger_list = android_logger_list_alloc(mode, tail_lines, pid);
}
const char *openDeviceFail = NULL;
const char *clearFail = NULL;
@@ -917,7 +1068,7 @@
size_t len = 8192;
char *buf;
- for(int retry = 32;
+ for (int retry = 32;
(retry >= 0) && ((buf = new char [len]));
delete [] buf, buf = NULL, --retry) {
if (getPruneList) {
@@ -1007,7 +1158,7 @@
logcat_panic(false, "logcat read failure");
}
- for(d = devices; d; d = d->next) {
+ for (d = devices; d; d = d->next) {
if (android_name_to_log_id(d->device) == log_msg.id()) {
break;
}
diff --git a/logcat/logcatd.rc b/logcat/logcatd.rc
index 33d39ac..cf0e0d2 100644
--- a/logcat/logcatd.rc
+++ b/logcat/logcatd.rc
@@ -11,3 +11,4 @@
# logd for write to /data/misc/logd, log group for read from log daemon
user logd
group log
+ writepid /dev/cpuset/system-background/tasks
diff --git a/logcat/logpersist b/logcat/logpersist
index 6f666f6..dab466d 100755
--- a/logcat/logpersist
+++ b/logcat/logpersist
@@ -10,7 +10,7 @@
data=/data/misc/logd
property=persist.logd.logpersistd
service=logcatd
-if [ X"${1}" = "-h" -o X"${1}" = X"--help" ]; then
+if [ X"${1}" = X"-h" -o X"${1}" = X"--help" ]; then
echo "${progname%.*}.cat - dump current ${service%d} logs"
echo "${progname%.*}.start - start ${service} service"
echo "${progname%.*}.stop [--clear] - stop ${service} service"
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index 610a6ec..4f517bb 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -75,6 +75,12 @@
}
TEST(logcat, year) {
+
+ if (android_log_clockid() == CLOCK_MONOTONIC) {
+ fprintf(stderr, "Skipping test, logd is monotonic time\n");
+ return;
+ }
+
FILE *fp;
char needle[32];
@@ -108,7 +114,44 @@
ASSERT_EQ(3, count);
}
+// Return a pointer to each null terminated -v long time field.
+char *fgetLongTime(char *buffer, size_t buflen, FILE *fp) {
+ while (fgets(buffer, buflen, fp)) {
+ char *cp = buffer;
+ if (*cp != '[') {
+ continue;
+ }
+ while (*++cp == ' ') {
+ ;
+ }
+ char *ep = cp;
+ while (isdigit(*ep)) {
+ ++ep;
+ }
+ if ((*ep != '-') && (*ep != '.')) {
+ continue;
+ }
+ // Find PID field
+ while (((ep = strchr(ep, ':'))) && (*++ep != ' ')) {
+ ;
+ }
+ if (!ep) {
+ continue;
+ }
+ ep -= 7;
+ *ep = '\0';
+ return cp;
+ }
+ return NULL;
+}
+
TEST(logcat, tz) {
+
+ if (android_log_clockid() == CLOCK_MONOTONIC) {
+ fprintf(stderr, "Skipping test, logd is monotonic time\n");
+ return;
+ }
+
FILE *fp;
ASSERT_TRUE(NULL != (fp = popen(
@@ -119,11 +162,8 @@
int count = 0;
- while (fgets(buffer, sizeof(buffer), fp)) {
- if ((buffer[0] == '[') && (buffer[1] == ' ')
- && isdigit(buffer[2]) && isdigit(buffer[3])
- && (buffer[4] == '-')
- && (strstr(buffer, " -0700 ") || strstr(buffer, " -0800 "))) {
+ while (fgetLongTime(buffer, sizeof(buffer), fp)) {
+ if (strstr(buffer, " -0700") || strstr(buffer, " -0800")) {
++count;
}
}
@@ -144,11 +184,8 @@
int count = 0;
- while (fgets(buffer, sizeof(buffer), fp)) {
- if ((buffer[0] == '[') && (buffer[1] == ' ')
- && isdigit(buffer[2]) && isdigit(buffer[3])
- && (buffer[4] == '-')
- && (strstr(buffer, " -0700 ") || strstr(buffer, " -0800 "))) {
+ while (fgetLongTime(buffer, sizeof(buffer), fp)) {
+ if (strstr(buffer, " -0700") || strstr(buffer, " -0800")) {
++count;
}
}
@@ -169,12 +206,8 @@
int count = 0;
- while (fgets(buffer, sizeof(buffer), fp)) {
- if ((buffer[0] == '[') && (buffer[1] == ' ')
- && isdigit(buffer[2]) && isdigit(buffer[3])
- && (buffer[4] == '-')) {
- ++count;
- }
+ while (fgetLongTime(buffer, sizeof(buffer), fp)) {
+ ++count;
}
pclose(fp);
@@ -193,12 +226,8 @@
int count = 0;
- while (fgets(buffer, sizeof(buffer), fp)) {
- if ((buffer[0] == '[') && (buffer[1] == ' ')
- && isdigit(buffer[2]) && isdigit(buffer[3])
- && (buffer[4] == '-')) {
- ++count;
- }
+ while (fgetLongTime(buffer, sizeof(buffer), fp)) {
+ ++count;
}
pclose(fp);
@@ -217,12 +246,8 @@
int count = 0;
- while (fgets(buffer, sizeof(buffer), fp)) {
- if ((buffer[0] == '[') && (buffer[1] == ' ')
- && isdigit(buffer[2]) && isdigit(buffer[3])
- && (buffer[4] == '-')) {
- ++count;
- }
+ while (fgetLongTime(buffer, sizeof(buffer), fp)) {
+ ++count;
}
pclose(fp);
@@ -241,12 +266,8 @@
int count = 0;
- while (fgets(buffer, sizeof(buffer), fp)) {
- if ((buffer[0] == '[') && (buffer[1] == ' ')
- && isdigit(buffer[2]) && isdigit(buffer[3])
- && (buffer[4] == '-')) {
- ++count;
- }
+ while (fgetLongTime(buffer, sizeof(buffer), fp)) {
+ ++count;
}
pclose(fp);
@@ -263,21 +284,15 @@
char *last_timestamp = NULL;
char *first_timestamp = NULL;
int count = 0;
- const unsigned int time_length = 18;
- const unsigned int time_offset = 2;
- while (fgets(buffer, sizeof(buffer), fp)) {
- if ((buffer[0] == '[') && (buffer[1] == ' ')
- && isdigit(buffer[time_offset]) && isdigit(buffer[time_offset + 1])
- && (buffer[time_offset + 2] == '-')) {
- ++count;
- buffer[time_length + time_offset] = '\0';
- if (!first_timestamp) {
- first_timestamp = strdup(buffer + time_offset);
- }
- free(last_timestamp);
- last_timestamp = strdup(buffer + time_offset);
+ char *cp;
+ while ((cp = fgetLongTime(buffer, sizeof(buffer), fp))) {
+ ++count;
+ if (!first_timestamp) {
+ first_timestamp = strdup(cp);
}
+ free(last_timestamp);
+ last_timestamp = strdup(cp);
}
pclose(fp);
@@ -292,28 +307,24 @@
int second_count = 0;
int last_timestamp_count = -1;
- while (fgets(buffer, sizeof(buffer), fp)) {
- if ((buffer[0] == '[') && (buffer[1] == ' ')
- && isdigit(buffer[time_offset]) && isdigit(buffer[time_offset + 1])
- && (buffer[time_offset + 2] == '-')) {
- ++second_count;
- buffer[time_length + time_offset] = '\0';
- if (first_timestamp) {
- // we can get a transitory *extremely* rare failure if hidden
- // underneath the time is *exactly* XX-XX XX:XX:XX.XXX000000
- EXPECT_STREQ(buffer + time_offset, first_timestamp);
- free(first_timestamp);
- first_timestamp = NULL;
- }
- if (!strcmp(buffer + time_offset, last_timestamp)) {
- last_timestamp_count = second_count;
- }
+ while ((cp = fgetLongTime(buffer, sizeof(buffer), fp))) {
+ ++second_count;
+ if (first_timestamp) {
+ // we can get a transitory *extremely* rare failure if hidden
+ // underneath the time is *exactly* XX-XX XX:XX:XX.XXX000000
+ EXPECT_STREQ(cp, first_timestamp);
+ free(first_timestamp);
+ first_timestamp = NULL;
+ }
+ if (!strcmp(cp, last_timestamp)) {
+ last_timestamp_count = second_count;
}
}
pclose(fp);
free(last_timestamp);
last_timestamp = NULL;
+ free(first_timestamp);
EXPECT_TRUE(first_timestamp == NULL);
EXPECT_LE(count, second_count);
@@ -594,13 +605,16 @@
char c;
if ((2 == sscanf(buffer, "%d log.tx%c", &num, &c)) &&
- (num <= 24)) {
+ (num <= 40)) {
++count;
} else if (strncmp(buffer, total, sizeof(total) - 1)) {
fprintf(stderr, "WARNING: Parse error: %s", buffer);
}
}
pclose(fp);
+ if ((count != 7) && (count != 8)) {
+ fprintf(stderr, "count=%d\n", count);
+ }
EXPECT_TRUE(count == 7 || count == 8);
}
}
diff --git a/logd/Android.mk b/logd/Android.mk
index feca8d5..d19c255 100644
--- a/logd/Android.mk
+++ b/logd/Android.mk
@@ -38,7 +38,9 @@
# event_flag := $(call event_logtags,auditd)
# event_flag += $(call event_logtags,logd)
# so make sure we do not regret hard-coding it as follows:
-event_flag := -DAUDITD_LOG_TAG=1003 -DLOGD_LOG_TAG=1004
+event_flag := -DAUDITD_LOG_TAG=1003 \
+ -DLOGD_LOG_TAG=1004 \
+ -DSNET_EVENT_LOG_TAG=1397638484
LOCAL_CFLAGS := -Werror $(event_flag)
diff --git a/logd/CommandListener.cpp b/logd/CommandListener.cpp
index eafa28f..7394f11 100644
--- a/logd/CommandListener.cpp
+++ b/logd/CommandListener.cpp
@@ -27,13 +27,14 @@
#include <string>
-#include <base/stringprintf.h>
+#include <android-base/stringprintf.h>
#include <cutils/sockets.h>
#include <private/android_filesystem_config.h>
#include <sysutils/SocketClient.h>
#include "CommandListener.h"
#include "LogCommand.h"
+#include "LogUtils.h"
CommandListener::CommandListener(LogBuffer *buf, LogReader * /*reader*/,
LogListener * /*swl*/) :
@@ -209,9 +210,20 @@
}
unsigned int logMask = -1;
+ pid_t pid = 0;
if (argc > 1) {
logMask = 0;
for (int i = 1; i < argc; ++i) {
+ static const char _pid[] = "pid=";
+ if (!strncmp(argv[i], _pid, sizeof(_pid) - 1)) {
+ pid = atol(argv[i] + sizeof(_pid) - 1);
+ if (pid == 0) {
+ cli->sendMsg("PID Error");
+ return 0;
+ }
+ continue;
+ }
+
int id = atoi(argv[i]);
if ((id < LOG_ID_MIN) || (LOG_ID_MAX <= id)) {
cli->sendMsg("Range Error");
@@ -221,7 +233,8 @@
}
}
- cli->sendMsg(package_string(mBuf.formatStatistics(uid, logMask)).c_str());
+ cli->sendMsg(package_string(mBuf.formatStatistics(uid, pid,
+ logMask)).c_str());
return 0;
}
diff --git a/logd/FlushCommand.cpp b/logd/FlushCommand.cpp
index 823a842..cb3d1c2 100644
--- a/logd/FlushCommand.cpp
+++ b/logd/FlushCommand.cpp
@@ -21,19 +21,22 @@
#include "LogCommand.h"
#include "LogReader.h"
#include "LogTimes.h"
+#include "LogUtils.h"
FlushCommand::FlushCommand(LogReader &reader,
bool nonBlock,
unsigned long tail,
unsigned int logMask,
pid_t pid,
- uint64_t start) :
+ uint64_t start,
+ uint64_t timeout) :
mReader(reader),
mNonBlock(nonBlock),
mTail(tail),
mLogMask(logMask),
mPid(pid),
- mStart(start) {
+ mStart(start),
+ mTimeout(timeout) {
}
// runSocketCommand is called once for every open client on the
@@ -54,6 +57,10 @@
while(it != times.end()) {
entry = (*it);
if (entry->mClient == client) {
+ if (entry->mTimeout.tv_sec || entry->mTimeout.tv_nsec) {
+ LogTimeEntry::unlock();
+ return;
+ }
entry->triggerReader_Locked();
if (entry->runningReader_Locked()) {
LogTimeEntry::unlock();
@@ -71,7 +78,8 @@
LogTimeEntry::unlock();
return;
}
- entry = new LogTimeEntry(mReader, client, mNonBlock, mTail, mLogMask, mPid, mStart);
+ entry = new LogTimeEntry(mReader, client, mNonBlock, mTail, mLogMask,
+ mPid, mStart, mTimeout);
times.push_front(entry);
}
diff --git a/logd/FlushCommand.h b/logd/FlushCommand.h
index 61c6858..e0f2212 100644
--- a/logd/FlushCommand.h
+++ b/logd/FlushCommand.h
@@ -32,6 +32,7 @@
unsigned int mLogMask;
pid_t mPid;
uint64_t mStart;
+ uint64_t mTimeout;
public:
FlushCommand(LogReader &mReader,
@@ -39,7 +40,8 @@
unsigned long tail = -1,
unsigned int logMask = -1,
pid_t pid = 0,
- uint64_t start = 1);
+ uint64_t start = 1,
+ uint64_t timeout = 0);
virtual void runSocketCommand(SocketClient *client);
static bool hasReadLogs(SocketClient *client);
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
index 2f7cbfb..143fb04 100644
--- a/logd/LogAudit.cpp
+++ b/logd/LogAudit.cpp
@@ -123,17 +123,19 @@
&& (*cp == ':')) {
memcpy(timeptr + sizeof(audit_str) - 1, "0.0", 3);
memmove(timeptr + sizeof(audit_str) - 1 + 3, cp, strlen(cp) + 1);
- //
- // We are either in 1970ish (MONOTONIC) or 2015+ish (REALTIME) so to
- // differentiate without prejudice, we use 1980 to delineate, earlier
- // is monotonic, later is real.
- //
-# define EPOCH_PLUS_10_YEARS (10 * 1461 / 4 * 24 * 60 * 60)
- if (now.tv_sec < EPOCH_PLUS_10_YEARS) {
- LogKlog::convertMonotonicToReal(now);
+ if (!isMonotonic()) {
+ if (android::isMonotonic(now)) {
+ LogKlog::convertMonotonicToReal(now);
+ }
+ } else {
+ if (!android::isMonotonic(now)) {
+ LogKlog::convertRealToMonotonic(now);
+ }
}
+ } else if (isMonotonic()) {
+ now = log_time(CLOCK_MONOTONIC);
} else {
- now.strptime("", ""); // side effect of setting CLOCK_REALTIME
+ now = log_time(CLOCK_REALTIME);
}
static const char pid_str[] = " pid=";
diff --git a/logd/LogAudit.h b/logd/LogAudit.h
index 2342822..8a82630 100644
--- a/logd/LogAudit.h
+++ b/logd/LogAudit.h
@@ -29,6 +29,7 @@
public:
LogAudit(LogBuffer *buf, LogReader *reader, int fdDmesg);
int log(char *buf, size_t len);
+ bool isMonotonic() { return logbuf->isMonotonic(); }
protected:
virtual bool onDataAvailable(SocketClient *cli);
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index 1de8e64..ae950b5 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -28,10 +28,11 @@
#include <log/logger.h>
#include "LogBuffer.h"
+#include "LogKlog.h"
#include "LogReader.h"
// Default
-#define LOG_BUFFER_SIZE (256 * 1024) // Tuned on a per-platform basis here?
+#define LOG_BUFFER_SIZE (256 * 1024) // Tuned with ro.logd.size per-platform
#define log_buffer_size(id) mMaxSize[id]
#define LOG_BUFFER_MIN_SIZE (64 * 1024UL)
#define LOG_BUFFER_MAX_SIZE (256 * 1024 * 1024UL)
@@ -99,6 +100,12 @@
unsigned long default_size = property_get_size(global_tuneable);
if (!default_size) {
default_size = property_get_size(global_default);
+ if (!default_size) {
+ default_size = property_get_bool("ro.config.low_ram",
+ BOOL_DEFAULT_FALSE)
+ ? LOG_BUFFER_MIN_SIZE // 64K
+ : LOG_BUFFER_SIZE; // 256K
+ }
}
log_id_for_each(i) {
@@ -126,9 +133,64 @@
setSize(i, LOG_BUFFER_MIN_SIZE);
}
}
+ bool lastMonotonic = monotonic;
+ monotonic = android_log_clockid() == CLOCK_MONOTONIC;
+ if (lastMonotonic != monotonic) {
+ //
+ // Fixup all timestamps, may not be 100% accurate, but better than
+ // throwing what we have away when we get 'surprised' by a change.
+ // In-place element fixup so no need to check reader-lock. Entries
+ // should already be in timestamp order, but we could end up with a
+ // few out-of-order entries if new monotonics come in before we
+ // are notified of the reinit change in status. A Typical example would
+ // be:
+ // --------- beginning of system
+ // 10.494082 184 201 D Cryptfs : Just triggered post_fs_data
+ // --------- beginning of kernel
+ // 0.000000 0 0 I : Initializing cgroup subsys
+ // as the act of mounting /data would trigger persist.logd.timestamp to
+ // be corrected. 1/30 corner case YMMV.
+ //
+ pthread_mutex_lock(&mLogElementsLock);
+ LogBufferElementCollection::iterator it = mLogElements.begin();
+ while((it != mLogElements.end())) {
+ LogBufferElement *e = *it;
+ if (monotonic) {
+ if (!android::isMonotonic(e->mRealTime)) {
+ LogKlog::convertRealToMonotonic(e->mRealTime);
+ }
+ } else {
+ if (android::isMonotonic(e->mRealTime)) {
+ LogKlog::convertMonotonicToReal(e->mRealTime);
+ }
+ }
+ ++it;
+ }
+ pthread_mutex_unlock(&mLogElementsLock);
+ }
+
+ // We may have been triggered by a SIGHUP. Release any sleeping reader
+ // threads to dump their current content.
+ //
+ // NB: this is _not_ performed in the context of a SIGHUP, it is
+ // performed during startup, and in context of reinit administrative thread
+ LogTimeEntry::lock();
+
+ LastLogTimes::iterator times = mTimes.begin();
+ while(times != mTimes.end()) {
+ LogTimeEntry *entry = (*times);
+ if (entry->owned_Locked()) {
+ entry->triggerReader_Locked();
+ }
+ times++;
+ }
+
+ LogTimeEntry::unlock();
}
-LogBuffer::LogBuffer(LastLogTimes *times) : mTimes(*times) {
+LogBuffer::LogBuffer(LastLogTimes *times):
+ monotonic(android_log_clockid() == CLOCK_MONOTONIC),
+ mTimes(*times) {
pthread_mutex_init(&mLogElementsLock, NULL);
init();
@@ -143,22 +205,28 @@
LogBufferElement *elem = new LogBufferElement(log_id, realtime,
uid, pid, tid, msg, len);
- int prio = ANDROID_LOG_INFO;
- const char *tag = NULL;
- if (log_id == LOG_ID_EVENTS) {
- tag = android::tagToName(elem->getTag());
- } else {
- prio = *msg;
- tag = msg + 1;
- }
- if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
- // Log traffic received to total
- pthread_mutex_lock(&mLogElementsLock);
- stats.add(elem);
- stats.subtract(elem);
- pthread_mutex_unlock(&mLogElementsLock);
- delete elem;
- return -EACCES;
+ if (log_id != LOG_ID_SECURITY) { // whitelist LOG_ID_SECURITY
+ int prio = ANDROID_LOG_INFO;
+ const char *tag = (const char *)-1;
+ if (log_id == LOG_ID_EVENTS) {
+ // whitelist "snet_event_log"
+ if (elem->getTag() != SNET_EVENT_LOG_TAG) {
+ tag = android::tagToName(elem->getTag());
+ }
+ } else {
+ prio = *msg;
+ tag = msg + 1;
+ }
+ if ((tag != (const char *)-1) &&
+ !__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
+ // Log traffic received to total
+ pthread_mutex_lock(&mLogElementsLock);
+ stats.add(elem);
+ stats.subtract(elem);
+ pthread_mutex_unlock(&mLogElementsLock);
+ delete elem;
+ return -EACCES;
+ }
}
pthread_mutex_lock(&mLogElementsLock);
@@ -184,9 +252,9 @@
LogTimeEntry::lock();
- LastLogTimes::iterator t = mTimes.begin();
- while(t != mTimes.end()) {
- LogTimeEntry *entry = (*t);
+ LastLogTimes::iterator times = mTimes.begin();
+ while(times != mTimes.end()) {
+ LogTimeEntry *entry = (*times);
if (entry->owned_Locked()) {
if (!entry->mNonBlock) {
end_always = true;
@@ -197,7 +265,7 @@
end_set = true;
}
}
- t++;
+ times++;
}
if (end_always
@@ -243,20 +311,35 @@
LogBufferElementCollection::iterator LogBuffer::erase(
LogBufferElementCollection::iterator it, bool coalesce) {
- LogBufferElement *e = *it;
- log_id_t id = e->getLogId();
+ LogBufferElement *element = *it;
+ log_id_t id = element->getLogId();
- LogBufferIteratorMap::iterator f = mLastWorstUid[id].find(e->getUid());
- if ((f != mLastWorstUid[id].end()) && (it == f->second)) {
- mLastWorstUid[id].erase(f);
+ { // start of scope for uid found iterator
+ LogBufferIteratorMap::iterator found =
+ mLastWorstUid[id].find(element->getUid());
+ if ((found != mLastWorstUid[id].end())
+ && (it == found->second)) {
+ mLastWorstUid[id].erase(found);
+ }
}
+
+ if (element->getUid() == AID_SYSTEM) {
+ // start of scope for pid found iterator
+ LogBufferPidIteratorMap::iterator found =
+ mLastWorstPidOfSystem[id].find(element->getPid());
+ if ((found != mLastWorstPidOfSystem[id].end())
+ && (it == found->second)) {
+ mLastWorstPidOfSystem[id].erase(found);
+ }
+ }
+
it = mLogElements.erase(it);
if (coalesce) {
- stats.erase(e);
+ stats.erase(element);
} else {
- stats.subtract(e);
+ stats.subtract(element);
}
- delete e;
+ delete element;
return it;
}
@@ -276,8 +359,13 @@
} __packed;
public:
- LogBufferElementKey(uid_t u, pid_t p, pid_t t):uid(u),pid(p),tid(t),padding(0) { }
- LogBufferElementKey(uint64_t k):value(k) { }
+ LogBufferElementKey(uid_t uid, pid_t pid, pid_t tid):
+ uid(uid),
+ pid(pid),
+ tid(tid),
+ padding(0) {
+ }
+ LogBufferElementKey(uint64_t key):value(key) { }
uint64_t getKey() { return value; }
};
@@ -289,38 +377,42 @@
public:
- bool coalesce(LogBufferElement *e, unsigned short dropped) {
- LogBufferElementKey key(e->getUid(), e->getPid(), e->getTid());
+ bool coalesce(LogBufferElement *element, unsigned short dropped) {
+ LogBufferElementKey key(element->getUid(),
+ element->getPid(),
+ element->getTid());
LogBufferElementMap::iterator it = map.find(key.getKey());
if (it != map.end()) {
- LogBufferElement *l = it->second;
- unsigned short d = l->getDropped();
- if ((dropped + d) > USHRT_MAX) {
+ LogBufferElement *found = it->second;
+ unsigned short moreDropped = found->getDropped();
+ if ((dropped + moreDropped) > USHRT_MAX) {
map.erase(it);
} else {
- l->setDropped(dropped + d);
+ found->setDropped(dropped + moreDropped);
return true;
}
}
return false;
}
- void add(LogBufferElement *e) {
- LogBufferElementKey key(e->getUid(), e->getPid(), e->getTid());
- map[key.getKey()] = e;
+ void add(LogBufferElement *element) {
+ LogBufferElementKey key(element->getUid(),
+ element->getPid(),
+ element->getTid());
+ map[key.getKey()] = element;
}
inline void clear() {
map.clear();
}
- void clear(LogBufferElement *e) {
- uint64_t current = e->getRealTime().nsec()
+ void clear(LogBufferElement *element) {
+ uint64_t current = element->getRealTime().nsec()
- (EXPIRE_RATELIMIT * NS_PER_SEC);
for(LogBufferElementMap::iterator it = map.begin(); it != map.end();) {
- LogBufferElement *l = it->second;
- if ((l->getDropped() >= EXPIRE_THRESHOLD)
- && (current > l->getRealTime().nsec())) {
+ LogBufferElement *mapElement = it->second;
+ if ((mapElement->getDropped() >= EXPIRE_THRESHOLD)
+ && (current > mapElement->getRealTime().nsec())) {
it = map.erase(it);
} else {
++it;
@@ -385,14 +477,17 @@
LogTimeEntry::lock();
// Region locked?
- LastLogTimes::iterator t = mTimes.begin();
- while(t != mTimes.end()) {
- LogTimeEntry *entry = (*t);
+ LastLogTimes::iterator times = mTimes.begin();
+ while(times != mTimes.end()) {
+ LogTimeEntry *entry = (*times);
if (entry->owned_Locked() && entry->isWatching(id)
- && (!oldest || (oldest->mStart > entry->mStart))) {
+ && (!oldest ||
+ (oldest->mStart > entry->mStart) ||
+ ((oldest->mStart == entry->mStart) &&
+ (entry->mTimeout.tv_sec || entry->mTimeout.tv_nsec)))) {
oldest = entry;
}
- t++;
+ times++;
}
LogBufferElementCollection::iterator it;
@@ -400,16 +495,20 @@
if (caller_uid != AID_ROOT) {
// Only here if clearAll condition (pruneRows == ULONG_MAX)
for(it = mLogElements.begin(); it != mLogElements.end();) {
- LogBufferElement *e = *it;
+ LogBufferElement *element = *it;
- if ((e->getLogId() != id) || (e->getUid() != caller_uid)) {
+ if ((element->getLogId() != id) || (element->getUid() != caller_uid)) {
++it;
continue;
}
- if (oldest && (oldest->mStart <= e->getSequence())) {
- oldest->triggerSkip_Locked(id, pruneRows);
+ if (oldest && (oldest->mStart <= element->getSequence())) {
busy = true;
+ if (oldest->mTimeout.tv_sec || oldest->mTimeout.tv_nsec) {
+ oldest->triggerReader_Locked();
+ } else {
+ oldest->triggerSkip_Locked(id, pruneRows);
+ }
break;
}
@@ -420,23 +519,28 @@
return busy;
}
- // prune by worst offender by uid
- bool hasBlacklist = mPrune.naughty();
+ // prune by worst offenders; by blacklist, UID, and by PID of system UID
+ bool hasBlacklist = (id != LOG_ID_SECURITY) && mPrune.naughty();
while (!clearAll && (pruneRows > 0)) {
// recalculate the worst offender on every batched pass
uid_t worst = (uid_t) -1;
size_t worst_sizes = 0;
size_t second_worst_sizes = 0;
+ pid_t worstPid = 0; // POSIX guarantees PID != 0
if (worstUidEnabledForLogid(id) && mPrune.worstUidEnabled()) {
- std::unique_ptr<const UidEntry *[]> sorted = stats.sort(2, id);
+ { // begin scope for UID sorted list
+ std::unique_ptr<const UidEntry *[]> sorted = stats.sort(
+ AID_ROOT, (pid_t)0, 2, id);
- if (sorted.get()) {
- if (sorted[0] && sorted[1]) {
+ if (sorted.get() && sorted[0] && sorted[1]) {
worst_sizes = sorted[0]->getSizes();
// Calculate threshold as 12.5% of available storage
size_t threshold = log_buffer_size(id) / 8;
- if (worst_sizes > threshold) {
+ if ((worst_sizes > threshold)
+ // Allow time horizon to extend roughly tenfold, assume
+ // average entry length is 100 characters.
+ && (worst_sizes > (10 * sorted[0]->getDropped()))) {
worst = sorted[0]->getKey();
second_worst_sizes = sorted[1]->getSizes();
if (second_worst_sizes < threshold) {
@@ -445,6 +549,18 @@
}
}
}
+
+ if ((worst == AID_SYSTEM) && mPrune.worstPidOfSystemEnabled()) {
+ // begin scope of PID sorted list
+ std::unique_ptr<const PidEntry *[]> sorted = stats.sort(
+ worst, (pid_t)0, 2, id, worst);
+ if (sorted.get() && sorted[0] && sorted[1]) {
+ worstPid = sorted[0]->getKey();
+ second_worst_sizes = worst_sizes
+ - sorted[0]->getSizes()
+ + sorted[1]->getSizes();
+ }
+ }
}
// skip if we have neither worst nor naughty filters
@@ -461,11 +577,23 @@
// - check age-out of preserved logs
bool gc = pruneRows <= 1;
if (!gc && (worst != (uid_t) -1)) {
- LogBufferIteratorMap::iterator f = mLastWorstUid[id].find(worst);
- if ((f != mLastWorstUid[id].end())
- && (f->second != mLogElements.end())) {
- leading = false;
- it = f->second;
+ { // begin scope for uid worst found iterator
+ LogBufferIteratorMap::iterator found = mLastWorstUid[id].find(worst);
+ if ((found != mLastWorstUid[id].end())
+ && (found->second != mLogElements.end())) {
+ leading = false;
+ it = found->second;
+ }
+ }
+ if (worstPid) {
+ // begin scope for pid worst found iterator
+ LogBufferPidIteratorMap::iterator found
+ = mLastWorstPidOfSystem[id].find(worstPid);
+ if ((found != mLastWorstPidOfSystem[id].end())
+ && (found->second != mLogElements.end())) {
+ leading = false;
+ it = found->second;
+ }
}
}
static const timespec too_old = {
@@ -476,19 +604,22 @@
--lastt;
LogBufferElementLast last;
while (it != mLogElements.end()) {
- LogBufferElement *e = *it;
+ LogBufferElement *element = *it;
- if (oldest && (oldest->mStart <= e->getSequence())) {
+ if (oldest && (oldest->mStart <= element->getSequence())) {
busy = true;
+ if (oldest->mTimeout.tv_sec || oldest->mTimeout.tv_nsec) {
+ oldest->triggerReader_Locked();
+ }
break;
}
- if (e->getLogId() != id) {
+ if (element->getLogId() != id) {
++it;
continue;
}
- unsigned short dropped = e->getDropped();
+ unsigned short dropped = element->getDropped();
// remove any leading drops
if (leading && dropped) {
@@ -496,13 +627,13 @@
continue;
}
- if (dropped && last.coalesce(e, dropped)) {
+ if (dropped && last.coalesce(element, dropped)) {
it = erase(it, true);
continue;
}
- if (hasBlacklist && mPrune.naughty(e)) {
- last.clear(e);
+ if (hasBlacklist && mPrune.naughty(element)) {
+ last.clear(element);
it = erase(it);
if (dropped) {
continue;
@@ -513,35 +644,42 @@
break;
}
- if (e->getUid() == worst) {
+ if (element->getUid() == worst) {
kick = true;
if (worst_sizes < second_worst_sizes) {
break;
}
- worst_sizes -= e->getMsgLen();
+ worst_sizes -= element->getMsgLen();
}
continue;
}
- if ((e->getRealTime() < ((*lastt)->getRealTime() - too_old))
- || (e->getRealTime() > (*lastt)->getRealTime())) {
+ if ((element->getRealTime() < ((*lastt)->getRealTime() - too_old))
+ || (element->getRealTime() > (*lastt)->getRealTime())) {
break;
}
if (dropped) {
- last.add(e);
- if ((!gc && (e->getUid() == worst))
- || (mLastWorstUid[id].find(e->getUid())
+ last.add(element);
+ if (worstPid
+ && ((!gc && (element->getPid() == worstPid))
+ || (mLastWorstPidOfSystem[id].find(element->getPid())
+ == mLastWorstPidOfSystem[id].end()))) {
+ mLastWorstPidOfSystem[id][element->getUid()] = it;
+ }
+ if ((!gc && !worstPid && (element->getUid() == worst))
+ || (mLastWorstUid[id].find(element->getUid())
== mLastWorstUid[id].end())) {
- mLastWorstUid[id][e->getUid()] = it;
+ mLastWorstUid[id][element->getUid()] = it;
}
++it;
continue;
}
- if (e->getUid() != worst) {
+ if ((element->getUid() != worst)
+ || (worstPid && (element->getPid() != worstPid))) {
leading = false;
- last.clear(e);
+ last.clear(element);
++it;
continue;
}
@@ -553,19 +691,24 @@
kick = true;
- unsigned short len = e->getMsgLen();
+ unsigned short len = element->getMsgLen();
// do not create any leading drops
if (leading) {
it = erase(it);
} else {
- stats.drop(e);
- e->setDropped(1);
- if (last.coalesce(e, 1)) {
+ stats.drop(element);
+ element->setDropped(1);
+ if (last.coalesce(element, 1)) {
it = erase(it, true);
} else {
- last.add(e);
- if (!gc || (mLastWorstUid[id].find(worst)
+ last.add(element);
+ if (worstPid && (!gc
+ || (mLastWorstPidOfSystem[id].find(worstPid)
+ == mLastWorstPidOfSystem[id].end()))) {
+ mLastWorstPidOfSystem[id][worstPid] = it;
+ }
+ if ((!gc && !worstPid) || (mLastWorstUid[id].find(worst)
== mLastWorstUid[id].end())) {
mLastWorstUid[id][worst] = it;
}
@@ -585,19 +728,18 @@
}
bool whitelist = false;
- bool hasWhitelist = mPrune.nice() && !clearAll;
+ bool hasWhitelist = (id != LOG_ID_SECURITY) && mPrune.nice() && !clearAll;
it = mLogElements.begin();
while((pruneRows > 0) && (it != mLogElements.end())) {
- LogBufferElement *e = *it;
+ LogBufferElement *element = *it;
- if (e->getLogId() != id) {
+ if (element->getLogId() != id) {
it++;
continue;
}
- if (oldest && (oldest->mStart <= e->getSequence())) {
+ if (oldest && (oldest->mStart <= element->getSequence())) {
busy = true;
-
if (whitelist) {
break;
}
@@ -605,13 +747,16 @@
if (stats.sizes(id) > (2 * log_buffer_size(id))) {
// kick a misbehaving log reader client off the island
oldest->release_Locked();
+ } else if (oldest->mTimeout.tv_sec || oldest->mTimeout.tv_nsec) {
+ oldest->triggerReader_Locked();
} else {
oldest->triggerSkip_Locked(id, pruneRows);
}
break;
}
- if (hasWhitelist && !e->getDropped() && mPrune.nice(e)) { // WhiteListed
+ if (hasWhitelist && !element->getDropped() && mPrune.nice(element)) {
+ // WhiteListed
whitelist = true;
it++;
continue;
@@ -625,18 +770,20 @@
if (whitelist && (pruneRows > 0)) {
it = mLogElements.begin();
while((it != mLogElements.end()) && (pruneRows > 0)) {
- LogBufferElement *e = *it;
+ LogBufferElement *element = *it;
- if (e->getLogId() != id) {
+ if (element->getLogId() != id) {
++it;
continue;
}
- if (oldest && (oldest->mStart <= e->getSequence())) {
+ if (oldest && (oldest->mStart <= element->getSequence())) {
busy = true;
if (stats.sizes(id) > (2 * log_buffer_size(id))) {
// kick a misbehaving log reader client off the island
oldest->release_Locked();
+ } else if (oldest->mTimeout.tv_sec || oldest->mTimeout.tv_nsec) {
+ oldest->triggerReader_Locked();
} else {
oldest->triggerSkip_Locked(id, pruneRows);
}
@@ -773,7 +920,7 @@
pthread_mutex_unlock(&mLogElementsLock);
// range locking in LastLogTimes looks after us
- max = element->flushTo(reader, this);
+ max = element->flushTo(reader, this, privileged);
if (max == element->FLUSH_ERROR) {
return max;
@@ -786,10 +933,11 @@
return max;
}
-std::string LogBuffer::formatStatistics(uid_t uid, unsigned int logMask) {
+std::string LogBuffer::formatStatistics(uid_t uid, pid_t pid,
+ unsigned int logMask) {
pthread_mutex_lock(&mLogElementsLock);
- std::string ret = stats.format(uid, logMask);
+ std::string ret = stats.format(uid, pid, logMask);
pthread_mutex_unlock(&mLogElementsLock);
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index 7ed92e9..2667e78 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -32,6 +32,47 @@
#include "LogStatistics.h"
#include "LogWhiteBlackList.h"
+//
+// We are either in 1970ish (MONOTONIC) or 2016+ish (REALTIME) so to
+// differentiate without prejudice, we use 1972 to delineate, earlier
+// is likely monotonic, later is real. Otherwise we start using a
+// dividing line between monotonic and realtime if more than a minute
+// difference between them.
+//
+namespace android {
+
+static bool isMonotonic(const log_time &mono) {
+ static const uint32_t EPOCH_PLUS_2_YEARS = 2 * 24 * 60 * 60 * 1461 / 4;
+ static const uint32_t EPOCH_PLUS_MINUTE = 60;
+
+ if (mono.tv_sec >= EPOCH_PLUS_2_YEARS) {
+ return false;
+ }
+
+ log_time now(CLOCK_REALTIME);
+
+ /* Timezone and ntp time setup? */
+ if (now.tv_sec >= EPOCH_PLUS_2_YEARS) {
+ return true;
+ }
+
+ /* no way to differentiate realtime from monotonic time */
+ if (now.tv_sec < EPOCH_PLUS_MINUTE) {
+ return false;
+ }
+
+ log_time cpu(CLOCK_MONOTONIC);
+ /* too close to call to differentiate monotonic times from realtime */
+ if ((cpu.tv_sec + EPOCH_PLUS_MINUTE) >= now.tv_sec) {
+ return false;
+ }
+
+ /* dividing line half way between monotonic and realtime */
+ return mono.tv_sec < ((cpu.tv_sec + now.tv_sec) / 2);
+}
+
+}
+
typedef std::list<LogBufferElement *> LogBufferElementCollection;
class LogBuffer {
@@ -46,14 +87,22 @@
LogBufferElementCollection::iterator>
LogBufferIteratorMap;
LogBufferIteratorMap mLastWorstUid[LOG_ID_MAX];
+ // watermark of any worst/chatty pid of system processing
+ typedef std::unordered_map<pid_t,
+ LogBufferElementCollection::iterator>
+ LogBufferPidIteratorMap;
+ LogBufferPidIteratorMap mLastWorstPidOfSystem[LOG_ID_MAX];
unsigned long mMaxSize[LOG_ID_MAX];
+ bool monotonic;
+
public:
LastLogTimes &mTimes;
LogBuffer(LastLogTimes *times);
void init();
+ bool isMonotonic() { return monotonic; }
int log(log_id_t log_id, log_time realtime,
uid_t uid, pid_t pid, pid_t tid,
@@ -68,7 +117,7 @@
int setSize(log_id_t id, unsigned long size);
unsigned long getSizeUsed(log_id_t id);
// *strp uses malloc, use free to release.
- std::string formatStatistics(uid_t uid, unsigned int logMask);
+ std::string formatStatistics(uid_t uid, pid_t pid, unsigned int logMask);
void enableStatistics() {
stats.enableStatistics();
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index c4c302b..fde9ad7 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -51,7 +51,8 @@
}
uint32_t LogBufferElement::getTag() const {
- if ((mLogId != LOG_ID_EVENTS) || !mMsg || (mMsgLen < sizeof(uint32_t))) {
+ if (((mLogId != LOG_ID_EVENTS) && (mLogId != LOG_ID_SECURITY)) ||
+ !mMsg || (mMsgLen < sizeof(uint32_t))) {
return 0;
}
return le32toh(reinterpret_cast<android_event_header_t *>(mMsg)->tag);
@@ -158,7 +159,9 @@
mDropped, (mDropped > 1) ? "s" : "");
size_t hdrLen;
- if (mLogId == LOG_ID_EVENTS) {
+ // LOG_ID_SECURITY not strictly needed since spam filter not activated,
+ // but required for accuracy.
+ if ((mLogId == LOG_ID_EVENTS) || (mLogId == LOG_ID_SECURITY)) {
hdrLen = sizeof(android_log_event_string_t);
} else {
hdrLen = 1 + sizeof(tag);
@@ -172,7 +175,7 @@
}
size_t retval = hdrLen + len;
- if (mLogId == LOG_ID_EVENTS) {
+ if ((mLogId == LOG_ID_EVENTS) || (mLogId == LOG_ID_SECURITY)) {
android_log_event_string_t *event =
reinterpret_cast<android_log_event_string_t *>(buffer);
@@ -194,21 +197,25 @@
return retval;
}
-uint64_t LogBufferElement::flushTo(SocketClient *reader, LogBuffer *parent) {
- struct logger_entry_v3 entry;
+uint64_t LogBufferElement::flushTo(SocketClient *reader, LogBuffer *parent,
+ bool privileged) {
+ struct logger_entry_v4 entry;
- memset(&entry, 0, sizeof(struct logger_entry_v3));
+ memset(&entry, 0, sizeof(struct logger_entry_v4));
- entry.hdr_size = sizeof(struct logger_entry_v3);
+ entry.hdr_size = privileged ?
+ sizeof(struct logger_entry_v4) :
+ sizeof(struct logger_entry_v3);
entry.lid = mLogId;
entry.pid = mPid;
entry.tid = mTid;
+ entry.uid = mUid;
entry.sec = mRealTime.tv_sec;
entry.nsec = mRealTime.tv_nsec;
struct iovec iovec[2];
iovec[0].iov_base = &entry;
- iovec[0].iov_len = sizeof(struct logger_entry_v3);
+ iovec[0].iov_len = entry.hdr_size;
char *buffer = NULL;
diff --git a/logd/LogBufferElement.h b/logd/LogBufferElement.h
index 30e43c6..e7f88b9 100644
--- a/logd/LogBufferElement.h
+++ b/logd/LogBufferElement.h
@@ -34,6 +34,9 @@
#define EXPIRE_RATELIMIT 10 // maximum rate in seconds to report expiration
class LogBufferElement {
+
+ friend LogBuffer;
+
const log_id_t mLogId;
const uid_t mUid;
const pid_t mPid;
@@ -44,7 +47,7 @@
unsigned short mDropped; // mMsg == NULL
};
const uint64_t mSequence;
- const log_time mRealTime;
+ log_time mRealTime;
static atomic_int_fast64_t sequence;
// assumption: mMsg == NULL
@@ -77,7 +80,7 @@
uint32_t getTag(void) const;
static const uint64_t FLUSH_ERROR;
- uint64_t flushTo(SocketClient *writer, LogBuffer *parent);
+ uint64_t flushTo(SocketClient *writer, LogBuffer *parent, bool privileged);
};
#endif
diff --git a/logd/LogCommand.cpp b/logd/LogCommand.cpp
index 6d0e92e..3b17576 100644
--- a/logd/LogCommand.cpp
+++ b/logd/LogCommand.cpp
@@ -22,6 +22,7 @@
#include <private/android_filesystem_config.h>
#include "LogCommand.h"
+#include "LogUtils.h"
LogCommand::LogCommand(const char *cmd) : FrameworkCommand(cmd) {
}
@@ -56,20 +57,18 @@
return false;
}
-bool clientHasLogCredentials(SocketClient * cli) {
- uid_t uid = cli->getUid();
- if (uid == AID_ROOT) {
+bool clientHasLogCredentials(uid_t uid, gid_t gid, pid_t pid) {
+ if ((uid == AID_ROOT) || (uid == AID_SYSTEM) || (uid == AID_LOG)) {
return true;
}
- gid_t gid = cli->getGid();
if ((gid == AID_ROOT) || (gid == AID_SYSTEM) || (gid == AID_LOG)) {
return true;
}
// FYI We will typically be here for 'adb logcat'
char filename[256];
- snprintf(filename, sizeof(filename), "/proc/%u/status", cli->getPid());
+ snprintf(filename, sizeof(filename), "/proc/%u/status", pid);
bool ret;
bool foundLog = false;
@@ -145,3 +144,7 @@
return ret;
}
+
+bool clientHasLogCredentials(SocketClient *cli) {
+ return clientHasLogCredentials(cli->getUid(), cli->getGid(), cli->getPid());
+}
diff --git a/logd/LogCommand.h b/logd/LogCommand.h
index e3b96a2..c944478 100644
--- a/logd/LogCommand.h
+++ b/logd/LogCommand.h
@@ -26,6 +26,4 @@
virtual ~LogCommand() {}
};
-bool clientHasLogCredentials(SocketClient * cli);
-
#endif
diff --git a/logd/LogKlog.cpp b/logd/LogKlog.cpp
index d28161e..9690489 100644
--- a/logd/LogKlog.cpp
+++ b/logd/LogKlog.cpp
@@ -206,7 +206,10 @@
// NOTREACHED
}
-log_time LogKlog::correction = log_time(CLOCK_REALTIME) - log_time(CLOCK_MONOTONIC);
+log_time LogKlog::correction =
+ (log_time(CLOCK_REALTIME) < log_time(CLOCK_MONOTONIC))
+ ? log_time::EPOCH
+ : (log_time(CLOCK_REALTIME) - log_time(CLOCK_MONOTONIC));
LogKlog::LogKlog(LogBuffer *buf, LogReader *reader, int fdWrite, int fdRead, bool auditd) :
SocketListener(fdRead, false),
@@ -272,7 +275,7 @@
size_t len) {
log_time real;
const char *ep = real.strptime(real_string, "%Y-%m-%d %H:%M:%S.%09q UTC");
- if (!ep || (ep > &real_string[len])) {
+ if (!ep || (ep > &real_string[len]) || (real > log_time(CLOCK_REALTIME))) {
return;
}
// kernel report UTC, log_time::strptime is localtime from calendar.
@@ -283,8 +286,16 @@
memset(&tm, 0, sizeof(tm));
tm.tm_isdst = -1;
localtime_r(&now, &tm);
- real.tv_sec += tm.tm_gmtoff;
- correction = real - monotonic;
+ if ((tm.tm_gmtoff < 0) && ((-tm.tm_gmtoff) > (long)real.tv_sec)) {
+ real = log_time::EPOCH;
+ } else {
+ real.tv_sec += tm.tm_gmtoff;
+ }
+ if (monotonic > real) {
+ correction = log_time::EPOCH;
+ } else {
+ correction = real - monotonic;
+ }
}
static const char suspendStr[] = "PM: suspend entry ";
@@ -319,17 +330,21 @@
if (cp && (cp >= &(*buf)[len])) {
cp = NULL;
}
- len -= cp - *buf;
if (cp) {
static const char healthd[] = "healthd";
static const char battery[] = ": battery ";
+ len -= cp - *buf;
if (len && isspace(*cp)) {
++cp;
--len;
}
*buf = cp;
+ if (isMonotonic()) {
+ return;
+ }
+
const char *b;
if (((b = strnstr(cp, len, suspendStr)))
&& ((size_t)((b += sizeof(suspendStr) - 1) - cp) < len)) {
@@ -343,16 +358,11 @@
&& ((size_t)((b += sizeof(healthd) - 1) - cp) < len)
&& ((b = strnstr(b, len -= b - cp, battery)))
&& ((size_t)((b += sizeof(battery) - 1) - cp) < len)) {
- len -= b - cp;
- // NB: healthd is roughly 150us late, worth the price to deal with
- // ntp-induced or hardware clock drift.
- // look for " 2???-??-?? ??:??:??.????????? ???"
- for (; len && *b && (*b != '\n'); ++b, --len) {
- if ((b[0] == ' ') && (b[1] == '2') && (b[5] == '-')) {
- calculateCorrection(now, b + 1, len - 1);
- break;
- }
- }
+ // NB: healthd is roughly 150us late, so we use it instead to
+ // trigger a check for ntp-induced or hardware clock drift.
+ log_time real(CLOCK_REALTIME);
+ log_time mono(CLOCK_MONOTONIC);
+ correction = (real < mono) ? log_time::EPOCH : (real - mono);
} else if (((b = strnstr(cp, len, suspendedStr)))
&& ((size_t)((b += sizeof(suspendStr) - 1) - cp) < len)) {
len -= b - cp;
@@ -367,7 +377,11 @@
real.tv_nsec += (*endp - '0') * multiplier;
}
if (reverse) {
- correction -= real;
+ if (real > correction) {
+ correction = log_time::EPOCH;
+ } else {
+ correction -= real;
+ }
} else {
correction += real;
}
@@ -376,7 +390,11 @@
convertMonotonicToReal(now);
} else {
- now = log_time(CLOCK_REALTIME);
+ if (isMonotonic()) {
+ now = log_time(CLOCK_MONOTONIC);
+ } else {
+ now = log_time(CLOCK_REALTIME);
+ }
}
}
@@ -574,7 +592,7 @@
// Some may view the following as an ugly heuristic, the desire is to
// beautify the kernel logs into an Android Logging format; the goal is
// admirable but costly.
- while ((isspace(*p) || !*p) && (p < &buf[len])) {
+ while ((p < &buf[len]) && (isspace(*p) || !*p)) {
++p;
}
if (p >= &buf[len]) { // timestamp, no content
@@ -588,7 +606,7 @@
const char *bt, *et, *cp;
bt = p;
- if (!fast<strncmp>(p, "[INFO]", 6)) {
+ if ((taglen >= 6) && !fast<strncmp>(p, "[INFO]", 6)) {
// <PRI>[<TIME>] "[INFO]"<tag> ":" message
bt = p + 6;
taglen -= 6;
@@ -612,7 +630,9 @@
p = cp + 1;
} else if (taglen) {
size = et - bt;
- if ((*bt == *cp) && fast<strncmp>(bt + 1, cp + 1, size - 1)) {
+ if ((taglen > size) && // enough space for match plus trailing :
+ (*bt == *cp) && // ubber fast<strncmp> pair
+ fast<strncmp>(bt + 1, cp + 1, size - 1)) {
// <PRI>[<TIME>] <tag>_host '<tag>.<num>' : message
if (!fast<strncmp>(bt + size - 5, "_host", 5)
&& !fast<strncmp>(bt + 1, cp + 1, size - 6)) {
@@ -686,7 +706,7 @@
p = cp + 1;
}
}
- }
+ } /* else no tag */
size = etag - tag;
if ((size <= 1)
// register names like x9
@@ -713,8 +733,12 @@
taglen = mp - tag;
}
}
+ // Deal with sloppy and simplistic harmless p = cp + 1 etc above.
+ if (len < (size_t)(p - buf)) {
+ p = &buf[len];
+ }
// skip leading space
- while ((isspace(*p) || !*p) && (p < &buf[len])) {
+ while ((p < &buf[len]) && (isspace(*p) || !*p)) {
++p;
}
// truncate trailing space or nuls
@@ -727,16 +751,26 @@
p = " ";
b = 1;
}
+ // paranoid sanity check, can not happen ...
if (b > LOGGER_ENTRY_MAX_PAYLOAD) {
b = LOGGER_ENTRY_MAX_PAYLOAD;
}
+ if (taglen > LOGGER_ENTRY_MAX_PAYLOAD) {
+ taglen = LOGGER_ENTRY_MAX_PAYLOAD;
+ }
+ // calculate buffer copy requirements
size_t n = 1 + taglen + 1 + b + 1;
- int rc = n;
- if ((taglen > n) || (b > n)) { // Can not happen ...
- rc = -EINVAL;
- return rc;
+ // paranoid sanity check, first two just can not happen ...
+ if ((taglen > n) || (b > n) || (n > USHRT_MAX)) {
+ return -EINVAL;
}
+ // Careful.
+ // We are using the stack to house the log buffer for speed reasons.
+ // If we malloc'd this buffer, we could get away without n's USHRT_MAX
+ // test above, but we would then required a max(n, USHRT_MAX) as
+ // truncating length argument to logbuf->log() below. Gain is protection
+ // of stack sanity and speedup, loss is truncated long-line content.
char newstr[n];
char *np = newstr;
@@ -754,9 +788,34 @@
memcpy(np, p, b);
np[b] = '\0';
+ if (!isMonotonic()) {
+ // Watch out for singular race conditions with timezone causing near
+ // integer quarter-hour jumps in the time and compensate accordingly.
+ // Entries will be temporal within near_seconds * 2. b/21868540
+ static uint32_t vote_time[3];
+ vote_time[2] = vote_time[1];
+ vote_time[1] = vote_time[0];
+ vote_time[0] = now.tv_sec;
+
+ if (vote_time[1] && vote_time[2]) {
+ static const unsigned near_seconds = 10;
+ static const unsigned timezones_seconds = 900;
+ int diff0 = (vote_time[0] - vote_time[1]) / near_seconds;
+ unsigned abs0 = (diff0 < 0) ? -diff0 : diff0;
+ int diff1 = (vote_time[1] - vote_time[2]) / near_seconds;
+ unsigned abs1 = (diff1 < 0) ? -diff1 : diff1;
+ if ((abs1 <= 1) && // last two were in agreement on timezone
+ ((abs0 + 1) % (timezones_seconds / near_seconds)) <= 2) {
+ abs0 = (abs0 + 1) / (timezones_seconds / near_seconds) *
+ timezones_seconds;
+ now.tv_sec -= (diff0 < 0) ? -abs0 : abs0;
+ }
+ }
+ }
+
// Log message
- rc = logbuf->log(LOG_ID_KERNEL, now, uid, pid, tid, newstr,
- (n <= USHRT_MAX) ? (unsigned short) n : USHRT_MAX);
+ int rc = logbuf->log(LOG_ID_KERNEL, now, uid, pid, tid, newstr,
+ (unsigned short) n);
// notify readers
if (!rc) {
diff --git a/logd/LogKlog.h b/logd/LogKlog.h
index 469affd..3c8cc87 100644
--- a/logd/LogKlog.h
+++ b/logd/LogKlog.h
@@ -43,7 +43,9 @@
int log(const char *buf, size_t len);
void synchronize(const char *buf, size_t len);
+ bool isMonotonic() { return logbuf->isMonotonic(); }
static void convertMonotonicToReal(log_time &real) { real += correction; }
+ static void convertRealToMonotonic(log_time &real) { real -= correction; }
protected:
void sniffTime(log_time &now, const char **buf, size_t len, bool reverse);
diff --git a/logd/LogListener.cpp b/logd/LogListener.cpp
index b29f5ab..5348a2d 100644
--- a/logd/LogListener.cpp
+++ b/logd/LogListener.cpp
@@ -27,6 +27,7 @@
#include <private/android_logger.h>
#include "LogListener.h"
+#include "LogUtils.h"
LogListener::LogListener(LogBuffer *buf, LogReader *reader) :
SocketListener(getLogSocket(), false),
@@ -44,7 +45,6 @@
char buffer[sizeof_log_id_t + sizeof(uint16_t) + sizeof(log_time)
+ LOGGER_ENTRY_MAX_PAYLOAD];
struct iovec iov = { buffer, sizeof(buffer) };
- memset(buffer, 0, sizeof(buffer));
char control[CMSG_SPACE(sizeof(struct ucred))];
struct msghdr hdr = {
@@ -59,6 +59,9 @@
int socket = cli->getSocket();
+ // To clear the entire buffer is secure/safe, but this contributes to 1.68%
+ // overhead under logging load. We are safe because we check counts.
+ // memset(buffer, 0, sizeof(buffer));
ssize_t n = recvmsg(socket, &hdr, 0);
if (n <= (ssize_t)(sizeof(android_log_header_t))) {
return false;
@@ -92,6 +95,12 @@
return false;
}
+ if ((header->id == LOG_ID_SECURITY) &&
+ (!__android_log_security() ||
+ !clientHasLogCredentials(cred->uid, cred->gid, cred->pid))) {
+ return false;
+ }
+
char *msg = ((char *)buffer) + sizeof(android_log_header_t);
n -= sizeof(android_log_header_t);
diff --git a/logd/LogReader.cpp b/logd/LogReader.cpp
index 3833843..c2d65b6 100644
--- a/logd/LogReader.cpp
+++ b/logd/LogReader.cpp
@@ -67,6 +67,14 @@
start.strptime(cp + sizeof(_start) - 1, "%s.%q");
}
+ uint64_t timeout = 0;
+ static const char _timeout[] = " timeout=";
+ cp = strstr(buffer, _timeout);
+ if (cp) {
+ timeout = atol(cp + sizeof(_timeout) - 1) * NS_PER_SEC +
+ log_time(CLOCK_REALTIME).nsec();
+ }
+
unsigned int logMask = -1;
static const char _logIds[] = " lids=";
cp = strstr(buffer, _logIds);
@@ -114,15 +122,17 @@
log_time &start;
uint64_t &sequence;
uint64_t last;
+ bool isMonotonic;
public:
- LogFindStart(unsigned logMask, pid_t pid, log_time &start, uint64_t &sequence) :
+ LogFindStart(unsigned logMask, pid_t pid, log_time &start, uint64_t &sequence, bool isMonotonic) :
mPid(pid),
mLogMask(logMask),
startTimeSet(false),
start(start),
sequence(sequence),
- last(sequence) {
+ last(sequence),
+ isMonotonic(isMonotonic) {
}
static int callback(const LogBufferElement *element, void *obj) {
@@ -133,20 +143,24 @@
me->sequence = element->getSequence();
me->startTimeSet = true;
return -1;
- } else {
+ } else if (!me->isMonotonic ||
+ android::isMonotonic(element->getRealTime())) {
if (me->start < element->getRealTime()) {
me->sequence = me->last;
me->startTimeSet = true;
return -1;
}
me->last = element->getSequence();
+ } else {
+ me->last = element->getSequence();
}
}
return false;
}
bool found() { return startTimeSet; }
- } logFindStart(logMask, pid, start, sequence);
+ } logFindStart(logMask, pid, start, sequence,
+ logbuf().isMonotonic() && android::isMonotonic(start));
logbuf().flushTo(cli, sequence, FlushCommand::hasReadLogs(cli),
logFindStart.callback, &logFindStart);
@@ -160,7 +174,7 @@
}
}
- FlushCommand command(*this, nonBlock, tail, logMask, pid, sequence);
+ FlushCommand command(*this, nonBlock, tail, logMask, pid, sequence, timeout);
command.runSocketCommand(cli);
return true;
}
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp
index 416edd8..2b02bc1 100644
--- a/logd/LogStatistics.cpp
+++ b/logd/LogStatistics.cpp
@@ -75,6 +75,9 @@
}
uidTable[log_id].add(element->getUid(), element);
+ if (element->getUid() == AID_SYSTEM) {
+ pidSystemTable[log_id].add(element->getPid(), element);
+ }
if (!enable) {
return;
@@ -85,7 +88,11 @@
uint32_t tag = element->getTag();
if (tag) {
- tagTable.add(tag, element);
+ if (log_id == LOG_ID_SECURITY) {
+ securityTagTable.add(tag, element);
+ } else {
+ tagTable.add(tag, element);
+ }
}
}
@@ -103,6 +110,9 @@
}
uidTable[log_id].subtract(element->getUid(), element);
+ if (element->getUid() == AID_SYSTEM) {
+ pidSystemTable[log_id].subtract(element->getPid(), element);
+ }
if (!enable) {
return;
@@ -113,7 +123,11 @@
uint32_t tag = element->getTag();
if (tag) {
- tagTable.subtract(tag, element);
+ if (log_id == LOG_ID_SECURITY) {
+ securityTagTable.subtract(tag, element);
+ } else {
+ tagTable.subtract(tag, element);
+ }
}
}
@@ -126,6 +140,9 @@
++mDroppedElements[log_id];
uidTable[log_id].drop(element->getUid(), element);
+ if (element->getUid() == AID_SYSTEM) {
+ pidSystemTable[log_id].drop(element->getPid(), element);
+ }
if (!enable) {
return;
@@ -197,7 +214,7 @@
}
std::string UidEntry::format(const LogStatistics &stat, log_id_t id) const {
- uid_t uid = getKey();
+ uid_t uid = getUid();
std::string name = android::base::StringPrintf("%u", uid);
const char *nameTmp = stat.uidToName(uid);
if (nameTmp) {
@@ -265,7 +282,43 @@
}
}
- return formatLine(name, size, pruned);
+ std::string output = formatLine(name, size, pruned);
+
+ if (uid != AID_SYSTEM) {
+ return output;
+ }
+
+ static const size_t maximum_sorted_entries = 32;
+ std::unique_ptr<const PidEntry *[]> sorted
+ = stat.pidSystemTable[id].sort(uid, (pid_t)0, maximum_sorted_entries);
+
+ if (!sorted.get()) {
+ return output;
+ }
+ std::string byPid;
+ size_t index;
+ bool hasDropped = false;
+ for (index = 0; index < maximum_sorted_entries; ++index) {
+ const PidEntry *entry = sorted[index];
+ if (!entry) {
+ break;
+ }
+ if (entry->getSizes() <= (getSizes() / 100)) {
+ break;
+ }
+ if (entry->getDropped()) {
+ hasDropped = true;
+ }
+ byPid += entry->format(stat, id);
+ }
+ if (index > 1) { // print this only if interesting
+ std::string ditto("\" ");
+ output += formatLine(std::string(" PID/UID COMMAND LINE"),
+ ditto, hasDropped ? ditto : std::string(""));
+ output += byPid;
+ }
+
+ return output;
}
std::string PidEntry::formatHeader(const std::string &name, log_id_t /* id */) const {
@@ -279,8 +332,8 @@
std::string PidEntry::format(const LogStatistics &stat, log_id_t /* id */) const {
uid_t uid = getUid();
- std::string name = android::base::StringPrintf("%5u/%u",
- getKey(), uid);
+ pid_t pid = getPid();
+ std::string name = android::base::StringPrintf("%5u/%u", pid, uid);
const char *nameTmp = getName();
if (nameTmp) {
name += android::base::StringPrintf(
@@ -317,7 +370,7 @@
std::string TidEntry::format(const LogStatistics &stat, log_id_t /* id */) const {
uid_t uid = getUid();
std::string name = android::base::StringPrintf("%5u/%u",
- getKey(), uid);
+ getTid(), uid);
const char *nameTmp = getName();
if (nameTmp) {
name += android::base::StringPrintf(
@@ -380,7 +433,8 @@
return formatLine(name, size, pruned);
}
-std::string LogStatistics::format(uid_t uid, unsigned int logMask) const {
+std::string LogStatistics::format(uid_t uid, pid_t pid,
+ unsigned int logMask) const {
static const unsigned short spaces_total = 19;
// Report on total logging, current and for all time
@@ -453,19 +507,38 @@
name = (uid == AID_ROOT)
? "Chattiest UIDs in %s log buffer:"
: "Logging for your UID in %s log buffer:";
- output += uidTable[id].format(*this, uid, name, id);
+ output += uidTable[id].format(*this, uid, pid, name, id);
}
if (enable) {
- name = (uid == AID_ROOT) ? "Chattiest PIDs:" : "Logging for this PID:";
- output += pidTable.format(*this, uid, name);
- name = "Chattiest TIDs:";
- output += tidTable.format(*this, uid, name);
+ name = ((uid == AID_ROOT) && !pid)
+ ? "Chattiest PIDs:"
+ : "Logging for this PID:";
+ output += pidTable.format(*this, uid, pid, name);
+ name = "Chattiest TIDs";
+ if (pid) {
+ name += android::base::StringPrintf(" for PID %d", pid);
+ }
+ name += ":";
+ output += tidTable.format(*this, uid, pid, name);
}
if (enable && (logMask & (1 << LOG_ID_EVENTS))) {
- name = "Chattiest events log buffer TAGs:";
- output += tagTable.format(*this, uid, name, LOG_ID_EVENTS);
+ name = "Chattiest events log buffer TAGs";
+ if (pid) {
+ name += android::base::StringPrintf(" for PID %d", pid);
+ }
+ name += ":";
+ output += tagTable.format(*this, uid, pid, name, LOG_ID_EVENTS);
+ }
+
+ if (enable && (logMask & (1 << LOG_ID_SECURITY))) {
+ name = "Chattiest security log buffer TAGs";
+ if (pid) {
+ name += android::base::StringPrintf(" for PID %d", pid);
+ }
+ name += ":";
+ output += securityTagTable.format(*this, uid, pid, name, LOG_ID_SECURITY);
}
return output;
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index 41f8b95..6f7d264 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -25,7 +25,7 @@
#include <string> // std::string
#include <unordered_map>
-#include <base/stringprintf.h>
+#include <android-base/stringprintf.h>
#include <log/log.h>
#include <private/android_filesystem_config.h>
@@ -47,7 +47,8 @@
typedef typename std::unordered_map<TKey, TEntry>::iterator iterator;
typedef typename std::unordered_map<TKey, TEntry>::const_iterator const_iterator;
- std::unique_ptr<const TEntry *[]> sort(size_t len) const {
+ std::unique_ptr<const TEntry *[]> sort(uid_t uid, pid_t pid,
+ size_t len) const {
if (!len) {
std::unique_ptr<const TEntry *[]> sorted(NULL);
return sorted;
@@ -58,6 +59,14 @@
for(const_iterator it = map.begin(); it != map.end(); ++it) {
const TEntry &entry = it->second;
+
+ if ((uid != AID_ROOT) && (uid != entry.getUid())) {
+ continue;
+ }
+ if (pid && entry.getPid() && (pid != entry.getPid())) {
+ continue;
+ }
+
size_t sizes = entry.getSizes();
ssize_t index = len - 1;
while ((!retval[index] || (sizes > retval[index]->getSizes()))
@@ -118,12 +127,13 @@
std::string format(
const LogStatistics &stat,
uid_t uid,
+ pid_t pid,
const std::string &name = std::string(""),
log_id_t id = LOG_ID_MAX) const {
static const size_t maximum_sorted_entries = 32;
std::string output;
- std::unique_ptr<const TEntry *[]> sorted = sort(maximum_sorted_entries);
-
+ std::unique_ptr<const TEntry *[]> sorted = sort(uid, pid,
+ maximum_sorted_entries);
if (!sorted.get()) {
return output;
}
@@ -136,9 +146,6 @@
if (entry->getSizes() <= (sorted[0]->getSizes() / 100)) {
break;
}
- if ((uid != AID_ROOT) && (uid != entry->getUid())) {
- continue;
- }
if (!headerPrinted) {
output += "\n\n";
output += entry->formatHeader(name, id);
@@ -217,14 +224,24 @@
struct UidEntry : public EntryBaseDropped {
const uid_t uid;
+ pid_t pid;
UidEntry(LogBufferElement *element):
EntryBaseDropped(element),
- uid(element->getUid()) {
+ uid(element->getUid()),
+ pid(element->getPid()) {
}
inline const uid_t&getKey() const { return uid; }
- inline const uid_t&getUid() const { return uid; }
+ inline const uid_t&getUid() const { return getKey(); }
+ inline const pid_t&getPid() const { return pid; }
+
+ inline void add(LogBufferElement *element) {
+ if (pid != element->getPid()) {
+ pid = -1;
+ }
+ EntryBase::add(element);
+ }
std::string formatHeader(const std::string &name, log_id_t id) const;
std::string format(const LogStatistics &stat, log_id_t id) const;
@@ -260,6 +277,7 @@
~PidEntry() { free(name); }
const pid_t&getKey() const { return pid; }
+ const pid_t&getPid() const { return getKey(); }
const uid_t&getUid() const { return uid; }
const char*getName() const { return name; }
@@ -291,30 +309,36 @@
struct TidEntry : public EntryBaseDropped {
const pid_t tid;
+ pid_t pid;
uid_t uid;
char *name;
- TidEntry(pid_t tid):
+ TidEntry(pid_t tid, pid_t pid):
EntryBaseDropped(),
tid(tid),
+ pid(pid),
uid(android::pidToUid(tid)),
name(android::tidToName(tid)) {
}
TidEntry(LogBufferElement *element):
EntryBaseDropped(element),
tid(element->getTid()),
+ pid(element->getPid()),
uid(element->getUid()),
name(android::tidToName(tid)) {
}
TidEntry(const TidEntry &element):
EntryBaseDropped(element),
tid(element.tid),
+ pid(element.pid),
uid(element.uid),
name(element.name ? strdup(element.name) : NULL) {
}
~TidEntry() { free(name); }
const pid_t&getKey() const { return tid; }
+ const pid_t&getTid() const { return getKey(); }
+ const pid_t&getPid() const { return pid; }
const uid_t&getUid() const { return uid; }
const char*getName() const { return name; }
@@ -330,8 +354,10 @@
inline void add(LogBufferElement *element) {
uid_t incomingUid = element->getUid();
- if (getUid() != incomingUid) {
+ pid_t incomingPid = element->getPid();
+ if ((getUid() != incomingUid) || (getPid() != incomingPid)) {
uid = incomingUid;
+ pid = incomingPid;
free(name);
name = android::tidToName(element->getTid());
} else {
@@ -346,23 +372,28 @@
struct TagEntry : public EntryBase {
const uint32_t tag;
+ pid_t pid;
uid_t uid;
TagEntry(LogBufferElement *element):
EntryBase(element),
tag(element->getTag()),
+ pid(element->getPid()),
uid(element->getUid()) {
}
const uint32_t&getKey() const { return tag; }
+ const pid_t&getPid() const { return pid; }
const uid_t&getUid() const { return uid; }
const char*getName() const { return android::tagToName(tag); }
inline void add(LogBufferElement *element) {
- uid_t incomingUid = element->getUid();
- if (uid != incomingUid) {
+ if (uid != element->getUid()) {
uid = -1;
}
+ if (pid != element->getPid()) {
+ pid = -1;
+ }
EntryBase::add(element);
}
@@ -385,6 +416,10 @@
typedef LogHashtable<uid_t, UidEntry> uidTable_t;
uidTable_t uidTable[LOG_ID_MAX];
+ // pid of system to size list
+ typedef LogHashtable<pid_t, PidEntry> pidSystemTable_t;
+ pidSystemTable_t pidSystemTable[LOG_ID_MAX];
+
// pid to uid list
typedef LogHashtable<pid_t, PidEntry> pidTable_t;
pidTable_t pidTable;
@@ -397,6 +432,9 @@
typedef LogHashtable<uint32_t, TagEntry> tagTable_t;
tagTable_t tagTable;
+ // security tag list
+ tagTable_t securityTagTable;
+
public:
LogStatistics();
@@ -413,8 +451,13 @@
--mDroppedElements[log_id];
}
- std::unique_ptr<const UidEntry *[]> sort(size_t len, log_id id) {
- return uidTable[id].sort(len);
+ std::unique_ptr<const UidEntry *[]> sort(uid_t uid, pid_t pid,
+ size_t len, log_id id) {
+ return uidTable[id].sort(uid, pid, len);
+ }
+ std::unique_ptr<const PidEntry *[]> sort(uid_t uid, pid_t pid,
+ size_t len, log_id id, uid_t) {
+ return pidSystemTable[id].sort(uid, pid, len);
}
// fast track current value by id only
@@ -426,7 +469,7 @@
size_t sizesTotal(log_id_t id) const { return mSizesTotal[id]; }
size_t elementsTotal(log_id_t id) const { return mElementsTotal[id]; }
- std::string format(uid_t uid, unsigned int logMask) const;
+ std::string format(uid_t uid, pid_t pid, unsigned int logMask) const;
// helper (must be locked directly or implicitly by mLogElementsLock)
const char *pidToName(pid_t pid) const;
diff --git a/logd/LogTimes.cpp b/logd/LogTimes.cpp
index 229be3c..b4c97a9 100644
--- a/logd/LogTimes.cpp
+++ b/logd/LogTimes.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <errno.h>
#include <sys/prctl.h>
#include "FlushCommand.h"
@@ -26,7 +27,7 @@
LogTimeEntry::LogTimeEntry(LogReader &reader, SocketClient *client,
bool nonBlock, unsigned long tail,
unsigned int logMask, pid_t pid,
- uint64_t start) :
+ uint64_t start, uint64_t timeout) :
mRefCount(1),
mRelease(false),
mError(false),
@@ -42,6 +43,8 @@
mStart(start),
mNonBlock(nonBlock),
mEnd(LogBufferElement::getCurrentSequence()) {
+ mTimeout.tv_sec = timeout / NS_PER_SEC;
+ mTimeout.tv_nsec = timeout % NS_PER_SEC;
pthread_cond_init(&threadTriggeredCondition, NULL);
cleanSkip_Locked();
}
@@ -131,6 +134,19 @@
uint64_t start = me->mStart;
while (me->threadRunning && !me->isError_Locked()) {
+
+ if (me->mTimeout.tv_sec || me->mTimeout.tv_nsec) {
+ if (pthread_cond_timedwait(&me->threadTriggeredCondition,
+ ×Lock,
+ &me->mTimeout) == ETIMEDOUT) {
+ me->mTimeout.tv_sec = 0;
+ me->mTimeout.tv_nsec = 0;
+ }
+ if (!me->threadRunning || me->isError_Locked()) {
+ break;
+ }
+ }
+
unlock();
if (me->mTail) {
@@ -154,7 +170,9 @@
me->cleanSkip_Locked();
- pthread_cond_wait(&me->threadTriggeredCondition, ×Lock);
+ if (!me->mTimeout.tv_sec && !me->mTimeout.tv_nsec) {
+ pthread_cond_wait(&me->threadTriggeredCondition, ×Lock);
+ }
}
unlock();
diff --git a/logd/LogTimes.h b/logd/LogTimes.h
index 39bcdd4..1117088 100644
--- a/logd/LogTimes.h
+++ b/logd/LogTimes.h
@@ -50,10 +50,11 @@
public:
LogTimeEntry(LogReader &reader, SocketClient *client, bool nonBlock,
unsigned long tail, unsigned int logMask, pid_t pid,
- uint64_t start);
+ uint64_t start, uint64_t timeout);
SocketClient *mClient;
uint64_t mStart;
+ struct timespec mTimeout;
const bool mNonBlock;
const uint64_t mEnd; // only relevant if mNonBlock
diff --git a/logd/LogUtils.h b/logd/LogUtils.h
index 533eb1c..fd4800e 100644
--- a/logd/LogUtils.h
+++ b/logd/LogUtils.h
@@ -20,6 +20,7 @@
#include <sys/types.h>
#include <log/log.h>
+#include <sysutils/SocketClient.h>
// Hijack this header as a common include file used by most all sources
// to report some utilities defined here and there.
@@ -38,8 +39,22 @@
}
+// Furnished in LogCommand.cpp
+bool clientHasLogCredentials(uid_t uid, gid_t gid, pid_t pid);
+bool clientHasLogCredentials(SocketClient *cli);
+
+// Furnished in main.cpp
+#define BOOL_DEFAULT_FLAG_TRUE_FALSE 0x1
+#define BOOL_DEFAULT_FALSE 0x0 // false if property not present
+#define BOOL_DEFAULT_TRUE 0x1 // true if property not present
+#define BOOL_DEFAULT_FLAG_PERSIST 0x2 // <key>, persist.<key>, ro.<key>
+#define BOOL_DEFAULT_FLAG_ENG 0x4 // off for user
+#define BOOL_DEFAULT_FLAG_SVELTE 0x8 // off for low_ram
+
+bool property_get_bool(const char *key, int def);
+
static inline bool worstUidEnabledForLogid(log_id_t id) {
- return (id != LOG_ID_CRASH) && (id != LOG_ID_KERNEL) && (id != LOG_ID_EVENTS);
+ return (id == LOG_ID_MAIN) || (id == LOG_ID_SYSTEM) || (id == LOG_ID_RADIO);
}
template <int (*cmp)(const char *l, const char *r, const size_t s)>
diff --git a/logd/LogWhiteBlackList.cpp b/logd/LogWhiteBlackList.cpp
index c71beb5..ae933b5 100644
--- a/logd/LogWhiteBlackList.cpp
+++ b/logd/LogWhiteBlackList.cpp
@@ -16,7 +16,7 @@
#include <ctype.h>
-#include <base/stringprintf.h>
+#include <android-base/stringprintf.h>
#include <cutils/properties.h>
#include "LogWhiteBlackList.h"
@@ -66,6 +66,7 @@
int PruneList::init(const char *str) {
mWorstUidEnabled = true;
+ mWorstPidOfSystemEnabled = true;
PruneCollection::iterator it;
for (it = mNice.begin(); it != mNice.end();) {
it = mNice.erase(it);
@@ -103,13 +104,14 @@
// default here means take internal default.
if (filter == _default) {
// See README.property for description of filter format
- filter = "~!";
+ filter = "~! ~1000/!";
}
if (filter == _disable) {
filter = "";
}
mWorstUidEnabled = false;
+ mWorstPidOfSystemEnabled = false;
for(str = filter.c_str(); *str; ++str) {
if (isspace(*str)) {
@@ -131,6 +133,19 @@
}
continue;
}
+ // special case, translated to worst PID of System at priority
+ static const char worstPid[] = "1000/!";
+ if (!strncmp(str, worstPid, sizeof(worstPid) - 1)) {
+ mWorstPidOfSystemEnabled = true;
+ str += sizeof(worstPid) - 1;
+ if (!*str) {
+ break;
+ }
+ if (!isspace(*str)) {
+ return 1;
+ }
+ continue;
+ }
if (!*str) {
return 1;
}
@@ -209,6 +224,9 @@
if (mWorstUidEnabled) {
string = "~!";
fmt = nice_format;
+ if (mWorstPidOfSystemEnabled) {
+ string += " ~1000/!";
+ }
}
PruneCollection::iterator it;
diff --git a/logd/LogWhiteBlackList.h b/logd/LogWhiteBlackList.h
index 6f17402..8b8e02f 100644
--- a/logd/LogWhiteBlackList.h
+++ b/logd/LogWhiteBlackList.h
@@ -53,6 +53,7 @@
PruneCollection mNaughty;
PruneCollection mNice;
bool mWorstUidEnabled;
+ bool mWorstPidOfSystemEnabled;
public:
PruneList();
@@ -65,6 +66,7 @@
bool nice(LogBufferElement *element);
bool nice(void) { return !mNice.empty(); }
bool worstUidEnabled() const { return mWorstUidEnabled; }
+ bool worstPidOfSystemEnabled() const { return mWorstPidOfSystemEnabled; }
std::string format();
};
diff --git a/logd/README.property b/logd/README.property
index 05ef528..22f86b9 100644
--- a/logd/README.property
+++ b/logd/README.property
@@ -1,35 +1,63 @@
The properties that logd responds to are:
name type default description
-logd.auditd bool true Enable selinux audit daemon
-logd.auditd.dmesg bool true selinux audit messages duplicated and
+ro.logd.auditd bool true Enable selinux audit daemon
+ro.logd.auditd.dmesg bool true selinux audit messages duplicated and
sent on to dmesg log
-logd.klogd bool depends Enable klogd daemon
-logd.statistics bool depends Enable logcat -S statistics.
-ro.config.low_ram bool false if true, logd.statistics & logd.klogd
- default false
-ro.build.type string if user, logd.statistics & logd.klogd
- default false
-persist.logd.logpersistd string Enable logpersist daemon, "logcatd"
+persist.logd.security bool false Enable security buffer.
+ro.device_owner bool false Override persist.logd.security to false
+ro.logd.kernel bool+ svelte+ Enable klogd daemon
+ro.logd.statistics bool+ svelte+ Enable logcat -S statistics.
+ro.build.type string if user, logd.statistics &
+ ro.logd.kernel default false.
+persist.logd.logpersistd string Enable logpersist daemon, "logcatd"
turns on logcat -f in logd context
-persist.logd.size number 256K Global default size of the buffer for
+persist.logd.size number ro Global default size of the buffer for
all log ids at initial startup, at
runtime use: logcat -b all -G <value>
-persist.logd.size.main number 256K Size of the buffer for the main log
-persist.logd.size.system number 256K Size of the buffer for the system log
-persist.logd.size.radio number 256K Size of the buffer for the radio log
-persist.logd.size.event number 256K Size of the buffer for the event log
-persist.logd.size.crash number 256K Size of the buffer for the crash log
-persist.logd.filter string Pruning filter to optimize content,
- default is ro.logd.filter or
- "~!" which means to prune the oldest
- entries of chattiest UID. At runtime
- use: logcat -P "<string>"
+ro.logd.size number svelte default for persist.logd.size. Larger
+ platform default sizes than 256KB are
+ known to not scale well under log spam
+ pressure. Address the spam first,
+ resist increasing the log buffer.
+persist.logd.size.<buffer> number ro Size of the buffer for <buffer> log
+ro.logd.size.<buffer> number svelte default for persist.logd.size.<buffer>
+ro.config.low_ram bool false if true, logd.statistics, logd.kernel
+ default false, logd.size 64K instead
+ of 256K.
+persist.logd.filter string Pruning filter to optimize content.
+ At runtime use: logcat -P "<string>"
+ro.logd.filter string "~! ~1000/!" default for persist.logd.filter.
+ This default means to prune the
+ oldest entries of chattiest UID, and
+ the chattiest PID of system
+ (1000, or AID_SYSTEM).
+persist.logd.timestamp string ro The recording timestamp source.
+ "m[onotonic]" is the only supported
+ key character, otherwise realtime.
+ro.logd.timestamp string realtime default for persist.logd.timestamp
+log.tag string persist The global logging level, VERBOSE,
+ DEBUG, INFO, WARN, ERROR, ASSERT or
+ SILENT. Only the first character is
+ the key character.
+persist.log.tag string build default for log.tag
+log.tag.<tag> string persist The <tag> specific logging level.
+persist.log.tag.<tag> string build default for log.tag.<tag>
NB:
-- Number support multipliers (K or M) for convenience. Range is limited
+- bool+ - "true", "false" and comma separated list of "eng" (forced false if
+ ro.build.type is "user") or "svelte" (forced false if ro.config.low_ram is
+ true).
+- svelte - see ro.config.low_ram for details.
+- svelte+ - see ro.config.low_ram and ro.build.type for details.
+- ro - <base property> temporary override, ro.<base property> platform default.
+- persist - <base property> override, persist.<base property> platform default.
+- build - VERBOSE for native, DEBUG for jvm isLoggable, or developer option.
+- number - support multipliers (K or M) for convenience. Range is limited
to between 64K and 256M for log buffer sizes. Individual log buffer ids
such as main, system, ... override global default.
- Pruning filter is of form of a space-separated list of [~][UID][/PID]
references, where '~' prefix means to blacklist otherwise whitelist. For
- blacklisting, UID may be a '!' to instead reference the chattiest client.
+ blacklisting, UID or PID may be a '!' to instead reference the chattiest
+ client, with the restriction that the PID must be in the UID group 1000
+ (system or AID_SYSTEM).
diff --git a/logd/logd.rc b/logd/logd.rc
index da6a0bc..10f3553 100644
--- a/logd/logd.rc
+++ b/logd/logd.rc
@@ -3,8 +3,10 @@
socket logd stream 0666 logd logd
socket logdr seqpacket 0666 logd logd
socket logdw dgram 0222 logd logd
- group root system
+ group root system readproc
+ writepid /dev/cpuset/system-background/tasks
service logd-reinit /system/bin/logd --reinit
oneshot
disabled
+ writepid /dev/cpuset/system-background/tasks
diff --git a/logd/main.cpp b/logd/main.cpp
index cf8cb8f..ba56e57 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -106,7 +106,9 @@
return -1;
}
- if (setgroups(0, NULL) == -1) {
+ gid_t groups[] = { AID_READPROC };
+
+ if (setgroups(sizeof(groups) / sizeof(groups[0]), groups) == -1) {
return -1;
}
@@ -141,18 +143,72 @@
}
// Property helper
-static bool property_get_bool(const char *key, bool def) {
- char property[PROPERTY_VALUE_MAX];
- property_get(key, property, "");
-
- if (!strcasecmp(property, "true")) {
- return true;
- }
- if (!strcasecmp(property, "false")) {
+static bool check_flag(const char *prop, const char *flag) {
+ const char *cp = strcasestr(prop, flag);
+ if (!cp) {
return false;
}
+ // We only will document comma (,)
+ static const char sep[] = ",:;|+ \t\f";
+ if ((cp != prop) && !strchr(sep, cp[-1])) {
+ return false;
+ }
+ cp += strlen(flag);
+ return !*cp || !!strchr(sep, *cp);
+}
- return def;
+bool property_get_bool(const char *key, int flag) {
+ char def[PROPERTY_VALUE_MAX];
+ char property[PROPERTY_VALUE_MAX];
+ def[0] = '\0';
+ if (flag & BOOL_DEFAULT_FLAG_PERSIST) {
+ char newkey[PROPERTY_KEY_MAX];
+ snprintf(newkey, sizeof(newkey), "ro.%s", key);
+ property_get(newkey, property, "");
+ // persist properties set by /data require innoculation with
+ // logd-reinit. They may be set in init.rc early and function, but
+ // otherwise are defunct unless reset. Do not rely on persist
+ // properties for startup-only keys unless you are willing to restart
+ // logd daemon (not advised).
+ snprintf(newkey, sizeof(newkey), "persist.%s", key);
+ property_get(newkey, def, property);
+ }
+
+ property_get(key, property, def);
+
+ if (check_flag(property, "true")) {
+ return true;
+ }
+ if (check_flag(property, "false")) {
+ return false;
+ }
+ if (check_flag(property, "eng")) {
+ flag |= BOOL_DEFAULT_FLAG_ENG;
+ }
+ // this is really a "not" flag
+ if (check_flag(property, "svelte")) {
+ flag |= BOOL_DEFAULT_FLAG_SVELTE;
+ }
+
+ // Sanity Check
+ if (flag & (BOOL_DEFAULT_FLAG_SVELTE | BOOL_DEFAULT_FLAG_ENG)) {
+ flag &= ~BOOL_DEFAULT_FLAG_TRUE_FALSE;
+ flag |= BOOL_DEFAULT_TRUE;
+ }
+
+ if ((flag & BOOL_DEFAULT_FLAG_SVELTE)
+ && property_get_bool("ro.config.low_ram",
+ BOOL_DEFAULT_FALSE)) {
+ return false;
+ }
+ if (flag & BOOL_DEFAULT_FLAG_ENG) {
+ property_get("ro.build.type", property, "");
+ if (!strcmp(property, "user")) {
+ return false;
+ }
+ }
+
+ return (flag & BOOL_DEFAULT_FLAG_TRUE_FALSE) != BOOL_DEFAULT_FALSE;
}
// Remove the static, and use this variable
@@ -264,17 +320,6 @@
return android_lookupEventTag(map, tag);
}
-static bool property_get_bool_svelte(const char *key) {
- bool not_user;
- {
- char property[PROPERTY_VALUE_MAX];
- property_get("ro.build.type", property, "");
- not_user = !!strcmp(property, "user");
- }
- return property_get_bool(key, not_user
- && !property_get_bool("ro.config.low_ram", false));
-}
-
static void readDmesg(LogAudit *al, LogKlog *kl) {
if (!al && !kl) {
return;
@@ -298,7 +343,7 @@
}
buf[--len] = '\0';
- if (kl) {
+ if (kl && kl->isMonotonic()) {
kl->synchronize(buf.get(), len);
}
@@ -323,7 +368,11 @@
// transitory per-client threads are created for each reader.
int main(int argc, char *argv[]) {
int fdPmesg = -1;
- bool klogd = property_get_bool_svelte("logd.klogd");
+ bool klogd = property_get_bool("logd.kernel",
+ BOOL_DEFAULT_TRUE |
+ BOOL_DEFAULT_FLAG_PERSIST |
+ BOOL_DEFAULT_FLAG_ENG |
+ BOOL_DEFAULT_FLAG_SVELTE);
if (klogd) {
fdPmesg = open("/proc/kmsg", O_RDONLY | O_NDELAY);
}
@@ -347,7 +396,7 @@
memset(&p, 0, sizeof(p));
p.fd = sock;
p.events = POLLIN;
- ret = TEMP_FAILURE_RETRY(poll(&p, 1, 100));
+ ret = TEMP_FAILURE_RETRY(poll(&p, 1, 1000));
if (ret < 0) {
return -errno;
}
@@ -403,7 +452,11 @@
signal(SIGHUP, reinit_signal_handler);
- if (property_get_bool_svelte("logd.statistics")) {
+ if (property_get_bool("logd.statistics",
+ BOOL_DEFAULT_TRUE |
+ BOOL_DEFAULT_FLAG_PERSIST |
+ BOOL_DEFAULT_FLAG_ENG |
+ BOOL_DEFAULT_FLAG_SVELTE)) {
logBuf->enableStatistics();
}
@@ -437,12 +490,17 @@
// initiated log messages. New log entries are added to LogBuffer
// and LogReader is notified to send updates to connected clients.
- bool auditd = property_get_bool("logd.auditd", true);
-
+ bool auditd = property_get_bool("logd.auditd",
+ BOOL_DEFAULT_TRUE |
+ BOOL_DEFAULT_FLAG_PERSIST);
LogAudit *al = NULL;
if (auditd) {
- bool dmesg = property_get_bool("logd.auditd.dmesg", true);
- al = new LogAudit(logBuf, reader, dmesg ? fdDmesg : -1);
+ al = new LogAudit(logBuf, reader,
+ property_get_bool("logd.auditd.dmesg",
+ BOOL_DEFAULT_TRUE |
+ BOOL_DEFAULT_FLAG_PERSIST)
+ ? fdDmesg
+ : -1);
}
LogKlog *kl = NULL;
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
index 44fa95c..7d0dd44 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/tests/logd_test.cpp
@@ -26,8 +26,6 @@
#include "log/log.h"
#include "log/logger.h"
-#define __unused __attribute__((__unused__))
-
/*
* returns statistics
*/
@@ -112,18 +110,38 @@
++cp;
}
benchmark = cp;
+#ifdef DEBUG
+ char *end = strstr(benchmark, "\n");
+ if (end == NULL) {
+ end = benchmark + strlen(benchmark);
+ }
+ fprintf(stderr, "parse for spam counter in \"%.*s\"\n",
+ (int)(end - benchmark), benchmark);
+#endif
+ // content
while (isdigit(*cp)) {
++cp;
}
while (isspace(*cp)) {
++cp;
}
+ // optional +/- field?
+ if ((*cp == '-') || (*cp == '+')) {
+ while (isdigit(*++cp) ||
+ (*cp == '.') || (*cp == '%') || (*cp == 'X')) {
+ ;
+ }
+ while (isspace(*cp)) {
+ ++cp;
+ }
+ }
+ // number of entries pruned
unsigned long value = 0;
while (isdigit(*cp)) {
value = value * 10ULL + *cp - '0';
++cp;
}
- if (value > 100000UL) {
+ if (value > 10UL) {
break;
}
benchmark = NULL;
@@ -176,7 +194,7 @@
delete [] buf;
}
-static void caught_signal(int signum __unused) { }
+static void caught_signal(int /* signum */) { }
static void dump_log_msg(const char *prefix,
log_msg *msg, unsigned int version, int lid) {
@@ -223,6 +241,12 @@
case 3:
fprintf(stderr, "lid=system ");
break;
+ case 4:
+ fprintf(stderr, "lid=crash ");
+ break;
+ case 5:
+ fprintf(stderr, "lid=kernel ");
+ break;
default:
if (lid >= 0) {
fprintf(stderr, "lid=%d ", lid);
@@ -419,7 +443,7 @@
EXPECT_GE(250000UL, ns[log_overhead]); // 126886 user
- EXPECT_GE(10000UL, ns[log_latency]); // 5669 user space
+ EXPECT_GE(10000000UL, ns[log_latency]); // 1453559 user space (background cgroup)
EXPECT_GE(20000000UL, ns[log_delay]); // 10500289 user
@@ -499,3 +523,68 @@
// 50% threshold for SPAM filter (<20% typical, lots of engineering margin)
ASSERT_GT(totalSize, nowSpamSize * 2);
}
+
+TEST(logd, timeout) {
+ log_msg msg_wrap, msg_timeout;
+ bool content_wrap = false, content_timeout = false, written = false;
+ unsigned int alarm_wrap = 0, alarm_timeout = 0;
+ // A few tries to get it right just in case wrap kicks in due to
+ // content providers being active during the test.
+ int i = 3;
+
+ while (--i) {
+ int fd = socket_local_client("logdr",
+ ANDROID_SOCKET_NAMESPACE_RESERVED,
+ SOCK_SEQPACKET);
+ ASSERT_LT(0, fd);
+
+ struct sigaction ignore, old_sigaction;
+ memset(&ignore, 0, sizeof(ignore));
+ ignore.sa_handler = caught_signal;
+ sigemptyset(&ignore.sa_mask);
+ sigaction(SIGALRM, &ignore, &old_sigaction);
+ unsigned int old_alarm = alarm(3);
+
+ static const char ask[] = "dumpAndClose lids=0,1,2,3,4,5 timeout=6";
+ written = write(fd, ask, sizeof(ask)) == sizeof(ask);
+ if (!written) {
+ alarm(old_alarm);
+ sigaction(SIGALRM, &old_sigaction, NULL);
+ close(fd);
+ continue;
+ }
+
+ content_wrap = recv(fd, msg_wrap.buf, sizeof(msg_wrap), 0) > 0;
+
+ alarm_wrap = alarm(5);
+
+ content_timeout = recv(fd, msg_timeout.buf, sizeof(msg_timeout), 0) > 0;
+
+ alarm_timeout = alarm((old_alarm <= 0)
+ ? old_alarm
+ : (old_alarm > (1 + 3 - alarm_wrap))
+ ? old_alarm - 3 + alarm_wrap
+ : 2);
+ sigaction(SIGALRM, &old_sigaction, NULL);
+
+ close(fd);
+
+ if (!content_wrap && !alarm_wrap && content_timeout && !alarm_timeout) {
+ break;
+ }
+ }
+
+ if (content_wrap) {
+ dump_log_msg("wrap", &msg_wrap, 3, -1);
+ }
+
+ if (content_timeout) {
+ dump_log_msg("timeout", &msg_timeout, 3, -1);
+ }
+
+ EXPECT_TRUE(written);
+ EXPECT_FALSE(content_wrap);
+ EXPECT_EQ(0U, alarm_wrap);
+ EXPECT_TRUE(content_timeout);
+ EXPECT_NE(0U, alarm_timeout);
+}
diff --git a/metricsd/.clang-format b/metricsd/.clang-format
new file mode 100644
index 0000000..65d8277
--- /dev/null
+++ b/metricsd/.clang-format
@@ -0,0 +1,9 @@
+BasedOnStyle: Google
+AllowShortFunctionsOnASingleLine: Inline
+AllowShortIfStatementsOnASingleLine: false
+AllowShortLoopsOnASingleLine: false
+BinPackParameters: false
+CommentPragmas: NOLINT:.*
+DerivePointerAlignment: false
+PointerAlignment: Left
+TabWidth: 2
diff --git a/metricsd/Android.mk b/metricsd/Android.mk
index b08c153..89e4e93 100644
--- a/metricsd/Android.mk
+++ b/metricsd/Android.mk
@@ -18,43 +18,46 @@
libmetrics_sources := \
c_metrics_library.cc \
metrics_library.cc \
- serialization/metric_sample.cc \
- serialization/serialization_utils.cc \
timer.cc
metrics_client_sources := \
metrics_client.cc
-metrics_daemon_common := \
+metrics_collector_common := \
collectors/averaged_statistics_collector.cc \
+ collectors/cpu_usage_collector.cc \
collectors/disk_usage_collector.cc \
- metrics_daemon.cc \
+ metrics_collector.cc \
+ persistent_integer.cc
+
+metricsd_common := \
persistent_integer.cc \
- serialization/metric_sample.cc \
- serialization/serialization_utils.cc \
+ uploader/bn_metricsd_impl.cc \
+ uploader/crash_counters.cc \
uploader/metrics_hashes.cc \
uploader/metrics_log_base.cc \
uploader/metrics_log.cc \
uploader/sender_http.cc \
uploader/system_profile_cache.cc \
- uploader/upload_service.cc \
+ uploader/upload_service.cc
-metrics_tests_sources := \
+metrics_collector_tests_sources := \
collectors/averaged_statistics_collector_test.cc \
- metrics_daemon_test.cc \
+ collectors/cpu_usage_collector_test.cc \
+ metrics_collector_test.cc \
metrics_library_test.cc \
persistent_integer_test.cc \
- serialization/serialization_utils_unittest.cc \
- timer_test.cc \
+ timer_test.cc
+
+metricsd_tests_sources := \
uploader/metrics_hashes_unittest.cc \
uploader/metrics_log_base_unittest.cc \
uploader/mock/sender_mock.cc \
- uploader/upload_service_test.cc \
+ uploader/upload_service_test.cc
metrics_CFLAGS := -Wall \
-Wno-char-subscripts \
-Wno-missing-field-initializers \
- -Wno-unused-function \
-Wno-unused-parameter \
-Werror \
-fvisibility=default
@@ -64,17 +67,49 @@
-fvisibility=default
metrics_includes := external/gtest/include \
$(LOCAL_PATH)/include
-libmetrics_shared_libraries := libchrome libbrillo
-metrics_daemon_shared_libraries := $(libmetrics_shared_libraries) \
- libbrillo-http \
+libmetrics_shared_libraries := libchrome libbinder libbrillo libutils
+metrics_collector_shared_libraries := $(libmetrics_shared_libraries) \
+ libbrillo-binder \
libbrillo-dbus \
+ libbrillo-http \
libchrome-dbus \
libdbus \
libmetrics \
- libprotobuf-cpp-lite \
librootdev \
+ libweaved
+
+metrics_collector_static_libraries := libmetricscollectorservice
+
+metricsd_shared_libraries := \
+ libbinder \
+ libbrillo \
+ libbrillo-http \
+ libchrome \
+ libprotobuf-cpp-lite \
libupdate_engine_client \
- libweaved \
+ libutils
+
+# Static proxy library for the metricsd binder interface.
+# ========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := metricsd_binder_proxy
+LOCAL_SHARED_LIBRARIES := libbinder libutils
+LOCAL_SRC_FILES := aidl/android/brillo/metrics/IMetricsd.aidl
+include $(BUILD_STATIC_LIBRARY)
+
+# Static library for the metrics_collector binder interface.
+# ==========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := libmetricscollectorservice
+LOCAL_CLANG := true
+LOCAL_SHARED_LIBRARIES := libbinder libbrillo-binder libchrome libutils
+LOCAL_CPP_EXTENSION := $(metrics_cpp_extension)
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_SRC_FILES := \
+ aidl/android/brillo/metrics/IMetricsCollectorService.aidl \
+ metrics_collector_service_client.cc
+include $(BUILD_STATIC_LIBRARY)
# Shared library for metrics.
# ========================================================
@@ -85,10 +120,10 @@
LOCAL_CLANG := true
LOCAL_CPP_EXTENSION := $(metrics_cpp_extension)
LOCAL_CPPFLAGS := $(metrics_CPPFLAGS)
-LOCAL_RTTI_FLAG := -frtti
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
LOCAL_SHARED_LIBRARIES := $(libmetrics_shared_libraries)
LOCAL_SRC_FILES := $(libmetrics_sources)
+LOCAL_STATIC_LIBRARIES := metricsd_binder_proxy
include $(BUILD_SHARED_LIBRARY)
# CLI client for metrics.
@@ -103,12 +138,13 @@
LOCAL_SHARED_LIBRARIES := $(libmetrics_shared_libraries) \
libmetrics
LOCAL_SRC_FILES := $(metrics_client_sources)
+LOCAL_STATIC_LIBRARIES := metricsd_binder_proxy
include $(BUILD_EXECUTABLE)
-# Protobuf library for metrics_daemon.
+# Protobuf library for metricsd.
# ========================================================
include $(CLEAR_VARS)
-LOCAL_MODULE := metrics_daemon_protos
+LOCAL_MODULE := metricsd_protos
LOCAL_MODULE_CLASS := STATIC_LIBRARIES
generated_sources_dir := $(call local-generated-sources-dir)
LOCAL_EXPORT_C_INCLUDE_DIRS += \
@@ -116,40 +152,69 @@
LOCAL_SRC_FILES := $(call all-proto-files-under,uploader/proto)
include $(BUILD_STATIC_LIBRARY)
-# metrics daemon.
+# metrics_collector daemon.
# ========================================================
include $(CLEAR_VARS)
-LOCAL_MODULE := metrics_daemon
+LOCAL_MODULE := metrics_collector
LOCAL_C_INCLUDES := $(metrics_includes)
LOCAL_CFLAGS := $(metrics_CFLAGS)
+LOCAL_CLANG := true
LOCAL_CPP_EXTENSION := $(metrics_cpp_extension)
LOCAL_CPPFLAGS := $(metrics_CPPFLAGS)
-LOCAL_INIT_RC := metrics_daemon.rc
-LOCAL_REQUIRED_MODULES := \
- metrics.json \
- metrics.schema.json \
-
-LOCAL_RTTI_FLAG := -frtti
-LOCAL_SHARED_LIBRARIES := $(metrics_daemon_shared_libraries)
-LOCAL_CLANG := true
-LOCAL_SRC_FILES := $(metrics_daemon_common) \
- metrics_daemon_main.cc
-LOCAL_STATIC_LIBRARIES := metrics_daemon_protos
+LOCAL_INIT_RC := metrics_collector.rc
+LOCAL_REQUIRED_MODULES := metrics.json
+LOCAL_SHARED_LIBRARIES := $(metrics_collector_shared_libraries)
+LOCAL_SRC_FILES := $(metrics_collector_common) \
+ metrics_collector_main.cc \
+ metrics_collector_service_impl.cc
+LOCAL_STATIC_LIBRARIES := metricsd_binder_proxy \
+ $(metrics_collector_static_libraries)
include $(BUILD_EXECUTABLE)
-# Unit tests for metrics.
+# metricsd daemon.
# ========================================================
include $(CLEAR_VARS)
-LOCAL_MODULE := metrics_tests
-LOCAL_CLANG := true
+LOCAL_MODULE := metricsd
+LOCAL_C_INCLUDES := $(metrics_includes)
LOCAL_CFLAGS := $(metrics_CFLAGS)
+LOCAL_CLANG := true
+LOCAL_CPP_EXTENSION := $(metrics_cpp_extension)
+LOCAL_CPPFLAGS := $(metrics_CPPFLAGS)
+LOCAL_INIT_RC := metricsd.rc
+LOCAL_REQUIRED_MODULES := \
+ metrics_collector
+LOCAL_SHARED_LIBRARIES := $(metricsd_shared_libraries)
+LOCAL_STATIC_LIBRARIES := metricsd_protos metricsd_binder_proxy
+LOCAL_SRC_FILES := $(metricsd_common) \
+ metricsd_main.cc
+include $(BUILD_EXECUTABLE)
+
+# Unit tests for metricsd.
+# ========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := metricsd_tests
+LOCAL_CFLAGS := $(metrics_CFLAGS)
+LOCAL_CLANG := true
LOCAL_CPP_EXTENSION := $(metrics_cpp_extension)
LOCAL_CPPFLAGS := $(metrics_CPPFLAGS) -Wno-sign-compare
-LOCAL_RTTI_FLAG := -frtti
-LOCAL_SHARED_LIBRARIES := $(metrics_daemon_shared_libraries)
-LOCAL_SRC_FILES := $(metrics_tests_sources) $(metrics_daemon_common)
-LOCAL_STATIC_LIBRARIES := libBionicGtestMain libgmock metrics_daemon_protos
+LOCAL_SHARED_LIBRARIES := $(metricsd_shared_libraries)
+LOCAL_SRC_FILES := $(metricsd_tests_sources) $(metricsd_common)
+LOCAL_STATIC_LIBRARIES := libBionicGtestMain libgmock metricsd_protos metricsd_binder_proxy
+include $(BUILD_NATIVE_TEST)
+# Unit tests for metrics_collector.
+# ========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := metrics_collector_tests
+LOCAL_CFLAGS := $(metrics_CFLAGS)
+LOCAL_CLANG := true
+LOCAL_CPP_EXTENSION := $(metrics_cpp_extension)
+LOCAL_CPPFLAGS := $(metrics_CPPFLAGS) -Wno-sign-compare
+LOCAL_SHARED_LIBRARIES := $(metrics_collector_shared_libraries)
+LOCAL_SRC_FILES := $(metrics_collector_tests_sources) \
+ $(metrics_collector_common)
+LOCAL_STATIC_LIBRARIES := libBionicGtestMain libgmock metricsd_binder_proxy \
+ $(metrics_collector_static_libraries)
include $(BUILD_NATIVE_TEST)
# Weave schema files
@@ -157,13 +222,6 @@
include $(CLEAR_VARS)
LOCAL_MODULE := metrics.json
LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/weaved/commands
-LOCAL_SRC_FILES := etc/weaved/commands/$(LOCAL_MODULE)
-include $(BUILD_PREBUILT)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := metrics.schema.json
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/weaved/states
-LOCAL_SRC_FILES := etc/weaved/states/$(LOCAL_MODULE)
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/weaved/traits
+LOCAL_SRC_FILES := etc/weaved/traits/$(LOCAL_MODULE)
include $(BUILD_PREBUILT)
diff --git a/metricsd/aidl/android/brillo/metrics/IMetricsCollectorService.aidl b/metricsd/aidl/android/brillo/metrics/IMetricsCollectorService.aidl
new file mode 100644
index 0000000..49f484f
--- /dev/null
+++ b/metricsd/aidl/android/brillo/metrics/IMetricsCollectorService.aidl
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.brillo.metrics;
+
+interface IMetricsCollectorService {
+ oneway void notifyUserCrash();
+}
diff --git a/metricsd/aidl/android/brillo/metrics/IMetricsd.aidl b/metricsd/aidl/android/brillo/metrics/IMetricsd.aidl
new file mode 100644
index 0000000..aa3cb34
--- /dev/null
+++ b/metricsd/aidl/android/brillo/metrics/IMetricsd.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.brillo.metrics;
+
+interface IMetricsd {
+ oneway void recordHistogram(String name, int sample, int min, int max,
+ int nbuckets);
+ oneway void recordLinearHistogram(String name, int sample, int max);
+ oneway void recordSparseHistogram(String name, int sample);
+ oneway void recordCrash(String type);
+ String getHistogramsDump();
+}
diff --git a/metricsd/c_metrics_library.cc b/metricsd/c_metrics_library.cc
index 0503876..47a543e 100644
--- a/metricsd/c_metrics_library.cc
+++ b/metricsd/c_metrics_library.cc
@@ -66,14 +66,6 @@
return lib->SendSparseToUMA(std::string(name), sample);
}
-extern "C" int CMetricsLibrarySendUserActionToUMA(CMetricsLibrary handle,
- const char* action) {
- MetricsLibrary* lib = reinterpret_cast<MetricsLibrary*>(handle);
- if (lib == NULL)
- return 0;
- return lib->SendUserActionToUMA(std::string(action));
-}
-
extern "C" int CMetricsLibrarySendCrashToUMA(CMetricsLibrary handle,
const char* crash_kind) {
MetricsLibrary* lib = reinterpret_cast<MetricsLibrary*>(handle);
diff --git a/metricsd/collectors/averaged_statistics_collector.cc b/metricsd/collectors/averaged_statistics_collector.cc
index 0931e7b..bac2870 100644
--- a/metricsd/collectors/averaged_statistics_collector.cc
+++ b/metricsd/collectors/averaged_statistics_collector.cc
@@ -21,7 +21,7 @@
#include <base/strings/string_number_conversions.h>
#include <base/strings/string_split.h>
-#include "metrics_daemon.h"
+#include "metrics_collector.h"
namespace {
@@ -90,7 +90,7 @@
}
void AveragedStatisticsCollector::ReadInitialValues() {
- stats_start_time_ = MetricsDaemon::GetActiveTime();
+ stats_start_time_ = MetricsCollector::GetActiveTime();
DiskStatsReadStats(&read_sectors_, &write_sectors_);
VmStatsReadStats(&vmstats_);
}
@@ -168,7 +168,7 @@
void AveragedStatisticsCollector::Collect() {
uint64_t read_sectors_now, write_sectors_now;
struct VmstatRecord vmstats_now;
- double time_now = MetricsDaemon::GetActiveTime();
+ double time_now = MetricsCollector::GetActiveTime();
double delta_time = time_now - stats_start_time_;
bool diskstats_success = DiskStatsReadStats(&read_sectors_now,
&write_sectors_now);
diff --git a/metricsd/collectors/averaged_statistics_collector_test.cc b/metricsd/collectors/averaged_statistics_collector_test.cc
index 9c97f00..68f9f2f 100644
--- a/metricsd/collectors/averaged_statistics_collector_test.cc
+++ b/metricsd/collectors/averaged_statistics_collector_test.cc
@@ -16,11 +16,12 @@
#include "averaged_statistics_collector.h"
+#include <memory>
+
#include <inttypes.h>
#include <base/files/file_util.h>
#include <base/files/scoped_temp_dir.h>
-#include <base/memory/scoped_ptr.h>
#include <base/strings/stringprintf.h>
#include <gtest/gtest.h>
@@ -62,7 +63,7 @@
}
// Collector used for tests.
- scoped_ptr<AveragedStatisticsCollector> collector_;
+ std::unique_ptr<AveragedStatisticsCollector> collector_;
// Temporary directory used for tests.
base::ScopedTempDir temp_dir_;
diff --git a/metricsd/collectors/cpu_usage_collector.cc b/metricsd/collectors/cpu_usage_collector.cc
new file mode 100644
index 0000000..05934b4
--- /dev/null
+++ b/metricsd/collectors/cpu_usage_collector.cc
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "collectors/cpu_usage_collector.h"
+
+#include <base/bind.h>
+#include <base/files/file_path.h>
+#include <base/files/file_util.h>
+#include <base/message_loop/message_loop.h>
+#include <base/strings/string_number_conversions.h>
+#include <base/strings/string_split.h>
+#include <base/strings/string_util.h>
+#include <base/sys_info.h>
+
+#include "metrics/metrics_library.h"
+
+namespace {
+
+const char kCpuUsagePercent[] = "Platform.CpuUsage.Percent";
+const char kMetricsProcStatFileName[] = "/proc/stat";
+const int kMetricsProcStatFirstLineItemsCount = 11;
+
+// Collect every minute.
+const int kCollectionIntervalSecs = 60;
+
+} // namespace
+
+using base::TimeDelta;
+
+CpuUsageCollector::CpuUsageCollector(MetricsLibraryInterface* metrics_library) {
+ CHECK(metrics_library);
+ metrics_lib_ = metrics_library;
+ collect_interval_ = TimeDelta::FromSeconds(kCollectionIntervalSecs);
+}
+
+void CpuUsageCollector::Init() {
+ num_cpu_ = base::SysInfo::NumberOfProcessors();
+
+ // Get ticks per second (HZ) on this system.
+ // Sysconf cannot fail, so no sanity checks are needed.
+ ticks_per_second_ = sysconf(_SC_CLK_TCK);
+ CHECK_GT(ticks_per_second_, uint64_t(0))
+ << "Number of ticks per seconds should be positive.";
+
+ latest_cpu_use_ = GetCumulativeCpuUse();
+}
+
+void CpuUsageCollector::CollectCallback() {
+ Collect();
+ Schedule();
+}
+
+void CpuUsageCollector::Schedule() {
+ base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
+ base::Bind(&CpuUsageCollector::CollectCallback, base::Unretained(this)),
+ collect_interval_);
+}
+
+void CpuUsageCollector::Collect() {
+ TimeDelta cpu_use = GetCumulativeCpuUse();
+ TimeDelta diff_per_cpu = (cpu_use - latest_cpu_use_) / num_cpu_;
+ latest_cpu_use_ = cpu_use;
+
+ // Report the cpu usage as a percentage of the total cpu usage possible.
+ int percent_use = diff_per_cpu.InMilliseconds() * 100 /
+ (kCollectionIntervalSecs * 1000);
+
+ metrics_lib_->SendEnumToUMA(kCpuUsagePercent, percent_use, 101);
+}
+
+TimeDelta CpuUsageCollector::GetCumulativeCpuUse() {
+ base::FilePath proc_stat_path(kMetricsProcStatFileName);
+ std::string proc_stat_string;
+ if (!base::ReadFileToString(proc_stat_path, &proc_stat_string)) {
+ LOG(WARNING) << "cannot open " << kMetricsProcStatFileName;
+ return TimeDelta();
+ }
+
+ uint64_t user_ticks, user_nice_ticks, system_ticks;
+ if (!ParseProcStat(proc_stat_string, &user_ticks, &user_nice_ticks,
+ &system_ticks)) {
+ return TimeDelta();
+ }
+
+ uint64_t total = user_ticks + user_nice_ticks + system_ticks;
+ return TimeDelta::FromMicroseconds(
+ total * 1000 * 1000 / ticks_per_second_);
+}
+
+bool CpuUsageCollector::ParseProcStat(const std::string& stat_content,
+ uint64_t *user_ticks,
+ uint64_t *user_nice_ticks,
+ uint64_t *system_ticks) {
+ std::vector<std::string> proc_stat_lines;
+ base::SplitString(stat_content, '\n', &proc_stat_lines);
+ if (proc_stat_lines.empty()) {
+ LOG(WARNING) << "No lines found in " << kMetricsProcStatFileName;
+ return false;
+ }
+ std::vector<std::string> proc_stat_totals;
+ base::SplitStringAlongWhitespace(proc_stat_lines[0], &proc_stat_totals);
+
+ if (proc_stat_totals.size() != kMetricsProcStatFirstLineItemsCount ||
+ proc_stat_totals[0] != "cpu" ||
+ !base::StringToUint64(proc_stat_totals[1], user_ticks) ||
+ !base::StringToUint64(proc_stat_totals[2], user_nice_ticks) ||
+ !base::StringToUint64(proc_stat_totals[3], system_ticks)) {
+ LOG(WARNING) << "cannot parse first line: " << proc_stat_lines[0];
+ return false;
+ }
+ return true;
+}
diff --git a/metricsd/collectors/cpu_usage_collector.h b/metricsd/collectors/cpu_usage_collector.h
new file mode 100644
index 0000000..f81dfcb
--- /dev/null
+++ b/metricsd/collectors/cpu_usage_collector.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef METRICSD_COLLECTORS_CPU_USAGE_COLLECTOR_H_
+#define METRICSD_COLLECTORS_CPU_USAGE_COLLECTOR_H_
+
+#include <base/time/time.h>
+
+#include "metrics/metrics_library.h"
+
+class CpuUsageCollector {
+ public:
+ CpuUsageCollector(MetricsLibraryInterface* metrics_library);
+
+ // Initialize this collector's state.
+ void Init();
+
+ // Schedule a collection interval.
+ void Schedule();
+
+ // Callback called at the end of the collection interval.
+ void CollectCallback();
+
+ // Measure the cpu use and report it.
+ void Collect();
+
+ // Gets the current cumulated Cpu usage.
+ base::TimeDelta GetCumulativeCpuUse();
+
+ private:
+ FRIEND_TEST(CpuUsageTest, ParseProcStat);
+ bool ParseProcStat(const std::string& stat_content,
+ uint64_t *user_ticks,
+ uint64_t *user_nice_ticks,
+ uint64_t *system_ticks);
+
+ int num_cpu_;
+ uint32_t ticks_per_second_;
+
+ base::TimeDelta collect_interval_;
+ base::TimeDelta latest_cpu_use_;
+
+ MetricsLibraryInterface* metrics_lib_;
+};
+
+#endif // METRICSD_COLLECTORS_CPU_USAGE_COLLECTOR_H_
diff --git a/metricsd/collectors/cpu_usage_collector_test.cc b/metricsd/collectors/cpu_usage_collector_test.cc
new file mode 100644
index 0000000..ee5c92b
--- /dev/null
+++ b/metricsd/collectors/cpu_usage_collector_test.cc
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "collectors/cpu_usage_collector.h"
+#include "metrics/metrics_library_mock.h"
+
+
+TEST(CpuUsageTest, ParseProcStat) {
+ MetricsLibraryMock metrics_lib_mock;
+ CpuUsageCollector collector(&metrics_lib_mock);
+ std::vector<std::string> invalid_contents = {
+ "",
+ // First line does not start with cpu.
+ "spu 17191 11 36579 151118 289 0 2 0 0 0\n"
+ "cpu0 1564 2 866 48650 68 0 2 0 0 0\n"
+ "cpu1 14299 0 35116 1844 81 0 0 0 0 0\n",
+ // One of the field is not a number.
+ "cpu a17191 11 36579 151118 289 0 2 0 0 0",
+ // To many numbers in the first line.
+ "cpu 17191 11 36579 151118 289 0 2 0 0 0 102"
+ };
+
+ uint64_t user, nice, system;
+ for (int i = 0; i < invalid_contents.size(); i++) {
+ ASSERT_FALSE(collector.ParseProcStat(invalid_contents[i], &user, &nice,
+ &system));
+ }
+
+ ASSERT_TRUE(collector.ParseProcStat(
+ std::string("cpu 17191 11 36579 151118 289 0 2 0 0 0"),
+ &user, &nice, &system));
+ ASSERT_EQ(17191, user);
+ ASSERT_EQ(11, nice);
+ ASSERT_EQ(36579, system);
+}
diff --git a/metricsd/constants.h b/metricsd/constants.h
index 3a7569b..4815888 100644
--- a/metricsd/constants.h
+++ b/metricsd/constants.h
@@ -18,8 +18,10 @@
#define METRICS_CONSTANTS_H_
namespace metrics {
-static const char kMetricsDirectory[] = "/data/misc/metrics/";
-static const char kMetricsEventsFileName[] = "uma-events";
+static const char kSharedMetricsDirectory[] = "/data/misc/metrics/";
+static const char kMetricsdDirectory[] = "/data/misc/metricsd/";
+static const char kMetricsCollectorDirectory[] =
+ "/data/misc/metrics_collector/";
static const char kMetricsGUIDFileName[] = "Sysinfo.GUID";
static const char kMetricsServer[] = "https://clients4.google.com/uma/v2";
static const char kConsentFileName[] = "enabled";
diff --git a/metricsd/etc/weaved/commands/metrics.json b/metricsd/etc/weaved/commands/metrics.json
deleted file mode 100644
index b7f32d5..0000000
--- a/metricsd/etc/weaved/commands/metrics.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "_metrics": {
- "_enableAnalyticsReporting": {
- "minimalRole": "manager"
- },
- "_disableAnalyticsReporting": {
- "minimalRole": "manager"
- }
- }
-}
diff --git a/metricsd/etc/weaved/states/metrics.schema.json b/metricsd/etc/weaved/states/metrics.schema.json
deleted file mode 100644
index 130ac46..0000000
--- a/metricsd/etc/weaved/states/metrics.schema.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "_metrics": {
- "_AnalyticsReportingState": {
- "enum": ["enabled", "disabled"],
- "default": "disabled"
- }
- }
-}
diff --git a/metricsd/etc/weaved/traits/metrics.json b/metricsd/etc/weaved/traits/metrics.json
new file mode 100644
index 0000000..7583270
--- /dev/null
+++ b/metricsd/etc/weaved/traits/metrics.json
@@ -0,0 +1,20 @@
+{
+ "_metrics": {
+ "commands": {
+ "enableAnalyticsReporting": {
+ "minimalRole": "manager",
+ "parameters": {}
+ },
+ "disableAnalyticsReporting": {
+ "minimalRole": "manager",
+ "parameters": {}
+ }
+ },
+ "state": {
+ "analyticsReportingState": {
+ "type": "string",
+ "enum": [ "enabled", "disabled" ]
+ }
+ }
+ }
+}
diff --git a/metricsd/include/metrics/c_metrics_library.h b/metricsd/include/metrics/c_metrics_library.h
index 4e7e666..1e597c2 100644
--- a/metricsd/include/metrics/c_metrics_library.h
+++ b/metricsd/include/metrics/c_metrics_library.h
@@ -44,10 +44,6 @@
int CMetricsLibrarySendSparseToUMA(CMetricsLibrary handle,
const char* name, int sample);
-// C wrapper for MetricsLibrary::SendUserActionToUMA.
-int CMetricsLibrarySendUserActionToUMA(CMetricsLibrary handle,
- const char* action);
-
// C wrapper for MetricsLibrary::SendCrashToUMA.
int CMetricsLibrarySendCrashToUMA(CMetricsLibrary handle,
const char* crash_kind);
diff --git a/metricsd/include/metrics/metrics_collector_service_client.h b/metricsd/include/metrics/metrics_collector_service_client.h
new file mode 100644
index 0000000..c800eae
--- /dev/null
+++ b/metricsd/include/metrics/metrics_collector_service_client.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Client interface to IMetricsCollectorService.
+
+#ifndef METRICS_METRICS_COLLECTOR_SERVICE_CLIENT_H_
+#define METRICS_METRICS_COLLECTOR_SERVICE_CLIENT_H_
+
+#include "android/brillo/metrics/IMetricsCollectorService.h"
+
+class MetricsCollectorServiceClient {
+ public:
+ MetricsCollectorServiceClient() = default;
+ ~MetricsCollectorServiceClient() = default;
+
+ // Initialize. Returns true if OK, or false if IMetricsCollectorService
+ // is not registered.
+ bool Init();
+
+ // Called by crash_reporter to report a userspace crash event. Returns
+ // true if successfully called the IMetricsCollectorService method of the
+ // same name, or false if the service was not registered at Init() time.
+ bool notifyUserCrash();
+
+ private:
+ // IMetricsCollectorService binder proxy
+ android::sp<android::brillo::metrics::IMetricsCollectorService>
+ metrics_collector_service_;
+};
+
+#endif // METRICS_METRICS_COLLECTOR_SERVICE_CLIENT_H_
diff --git a/metricsd/include/metrics/metrics_library.h b/metricsd/include/metrics/metrics_library.h
index b766194..a1bb926 100644
--- a/metricsd/include/metrics/metrics_library.h
+++ b/metricsd/include/metrics/metrics_library.h
@@ -24,9 +24,17 @@
#include <base/compiler_specific.h>
#include <base/files/file_path.h>
#include <base/macros.h>
-#include <base/memory/scoped_ptr.h>
+#include <binder/IServiceManager.h>
#include <gtest/gtest_prod.h> // for FRIEND_TEST
+namespace android {
+namespace brillo {
+namespace metrics {
+class IMetricsd;
+} // namespace metrics
+} // namespace brillo
+} // namespace android
+
class MetricsLibraryInterface {
public:
virtual void Init() = 0;
@@ -34,8 +42,8 @@
virtual bool SendToUMA(const std::string& name, int sample,
int min, int max, int nbuckets) = 0;
virtual bool SendEnumToUMA(const std::string& name, int sample, int max) = 0;
+ virtual bool SendBoolToUMA(const std::string& name, bool sample) = 0;
virtual bool SendSparseToUMA(const std::string& name, int sample) = 0;
- virtual bool SendUserActionToUMA(const std::string& action) = 0;
virtual ~MetricsLibraryInterface() {}
};
@@ -96,24 +104,15 @@
// normal, while 100 is high).
bool SendEnumToUMA(const std::string& name, int sample, int max) override;
+ // Specialization of SendEnumToUMA for boolean values.
+ bool SendBoolToUMA(const std::string& name, bool sample) override;
+
// Sends sparse histogram sample to Chrome for transport to UMA. Returns
// true on success.
//
// |sample| is the 32-bit integer value to be recorded.
bool SendSparseToUMA(const std::string& name, int sample) override;
- // Sends a user action to Chrome for transport to UMA and returns true on
- // success. This method results in the equivalent of an asynchronous
- // non-blocking RPC to UserMetrics::RecordAction. The new metric must be
- // added to chrome/tools/extract_actions.py in the Chromium repository, which
- // should then be run to generate a hash for the new action.
- //
- // Until http://crosbug.com/11125 is fixed, the metric must also be added to
- // chrome/browser/chromeos/external_metrics.cc.
- //
- // |action| is the user-generated event (e.g., "MuteKeyPressed").
- bool SendUserActionToUMA(const std::string& action) override;
-
// Sends a signal to UMA that a crash of the given |crash_kind|
// has occurred. Used by UMA to generate stability statistics.
bool SendCrashToUMA(const char *crash_kind);
@@ -126,6 +125,11 @@
// number in the histograms dashboard).
bool SendCrosEventToUMA(const std::string& event);
+ // Debugging only.
+ // Dumps the histograms aggregated since metricsd started into |dump|.
+ // Returns true iff the dump succeeds.
+ bool GetHistogramsDump(std::string* dump);
+
private:
friend class CMetricsLibraryTest;
friend class MetricsLibraryTest;
@@ -148,6 +152,10 @@
char* buffer, int buffer_size,
bool* result);
+ // Connects to IMetricsd if the proxy does not exist or is not alive.
+ // Don't block if we fail to get the proxy for any reason.
+ bool CheckService();
+
// Time at which we last checked if metrics were enabled.
time_t cached_enabled_time_;
@@ -157,7 +165,8 @@
// True iff we should cache the enabled/disabled status.
bool use_caching_;
- base::FilePath uma_events_file_;
+ android::sp<android::IServiceManager> manager_;
+ android::sp<android::brillo::metrics::IMetricsd> metricsd_proxy_;
base::FilePath consent_file_;
DISALLOW_COPY_AND_ASSIGN(MetricsLibrary);
diff --git a/metricsd/include/metrics/metrics_library_mock.h b/metricsd/include/metrics/metrics_library_mock.h
index 3de87a9..3b0b24d 100644
--- a/metricsd/include/metrics/metrics_library_mock.h
+++ b/metricsd/include/metrics/metrics_library_mock.h
@@ -32,8 +32,8 @@
int min, int max, int nbuckets));
MOCK_METHOD3(SendEnumToUMA, bool(const std::string& name, int sample,
int max));
+ MOCK_METHOD2(SendBoolToUMA, bool(const std::string& name, bool sample));
MOCK_METHOD2(SendSparseToUMA, bool(const std::string& name, int sample));
- MOCK_METHOD1(SendUserActionToUMA, bool(const std::string& action));
bool AreMetricsEnabled() override {return metrics_enabled_;};
};
diff --git a/metricsd/include/metrics/timer.h b/metricsd/include/metrics/timer.h
index b36ffff..c1b8ede 100644
--- a/metricsd/include/metrics/timer.h
+++ b/metricsd/include/metrics/timer.h
@@ -19,10 +19,10 @@
#ifndef METRICS_TIMER_H_
#define METRICS_TIMER_H_
+#include <memory>
#include <string>
#include <base/macros.h>
-#include <base/memory/scoped_ptr.h>
#include <base/time/time.h>
#include <gtest/gtest_prod.h> // for FRIEND_TEST
@@ -121,7 +121,7 @@
TimerState timer_state_;
// Wrapper for the calls to the system clock.
- scoped_ptr<ClockWrapper> clock_wrapper_;
+ std::unique_ptr<ClockWrapper> clock_wrapper_;
DISALLOW_COPY_AND_ASSIGN(Timer);
};
diff --git a/metricsd/metrics_client.cc b/metricsd/metrics_client.cc
index 78174ef..c66b975 100644
--- a/metricsd/metrics_client.cc
+++ b/metricsd/metrics_client.cc
@@ -17,19 +17,14 @@
#include <cstdio>
#include <cstdlib>
-#include <base/memory/scoped_vector.h>
-
#include "constants.h"
#include "metrics/metrics_library.h"
-#include "serialization/metric_sample.h"
-#include "serialization/serialization_utils.h"
enum Mode {
- kModeDumpLogs,
+ kModeDumpHistograms,
kModeSendSample,
kModeSendEnumSample,
kModeSendSparseSample,
- kModeSendUserAction,
kModeSendCrosEvent,
kModeHasConsent,
kModeIsGuestMode,
@@ -41,19 +36,17 @@
" metrics_client -e name sample max\n"
" metrics_client -s name sample\n"
" metrics_client -v event\n"
- " metrics_client -u action\n"
" metrics_client [-cdg]\n"
"\n"
" default: send metric with integer values \n"
" |min| > 0, |min| <= sample < |max|\n"
" -c: return exit status 0 if user consents to stats, 1 otherwise,\n"
" in guest mode always return 1\n"
- " -d: dump cached logs to the console\n"
+ " -d: dump the histograms recorded by metricsd to stdout\n"
" -e: send linear/enumeration histogram data\n"
" -g: return exit status 0 if machine in guest mode, 1 otherwise\n"
" -s: send a sparse histogram sample\n"
" -t: convert sample from double seconds to int milliseconds\n"
- " -u: send a user action to Chrome\n"
" -v: send a Platform.CrOSEvent enum histogram sample\n");
exit(1);
}
@@ -78,6 +71,20 @@
return value;
}
+static int DumpHistograms() {
+ MetricsLibrary metrics_lib;
+ metrics_lib.Init();
+
+ std::string dump;
+ if (!metrics_lib.GetHistogramsDump(&dump)) {
+ printf("Failed to dump the histograms.");
+ return 1;
+ }
+
+ printf("%s\n", dump.c_str());
+ return 0;
+}
+
static int SendStats(char* argv[],
int name_index,
enum Mode mode,
@@ -106,14 +113,6 @@
return 0;
}
-static int SendUserAction(char* argv[], int action_index) {
- const char* action = argv[action_index];
- MetricsLibrary metrics_lib;
- metrics_lib.Init();
- metrics_lib.SendUserActionToUMA(action);
- return 0;
-}
-
static int SendCrosEvent(char* argv[], int action_index) {
const char* event = argv[action_index];
bool result;
@@ -139,58 +138,19 @@
return metrics_lib.IsGuestMode() ? 0 : 1;
}
-static int DumpLogs() {
- base::FilePath events_file = base::FilePath(
- metrics::kMetricsDirectory).Append(metrics::kMetricsEventsFileName);
- printf("Metrics from %s\n\n", events_file.value().data());
-
- ScopedVector<metrics::MetricSample> metrics;
- metrics::SerializationUtils::ReadMetricsFromFile(events_file.value(),
- &metrics);
-
- for (ScopedVector<metrics::MetricSample>::const_iterator i = metrics.begin();
- i != metrics.end(); ++i) {
- const metrics::MetricSample* sample = *i;
- printf("name: %s\t", sample->name().c_str());
- printf("type: ");
-
- switch (sample->type()) {
- case metrics::MetricSample::CRASH:
- printf("CRASH");
- break;
- case metrics::MetricSample::HISTOGRAM:
- printf("HISTOGRAM");
- break;
- case metrics::MetricSample::LINEAR_HISTOGRAM:
- printf("LINEAR_HISTOGRAM");
- break;
- case metrics::MetricSample::SPARSE_HISTOGRAM:
- printf("SPARSE_HISTOGRAM");
- break;
- case metrics::MetricSample::USER_ACTION:
- printf("USER_ACTION");
- break;
- }
-
- printf("\n");
- }
-
- return 0;
-}
-
int main(int argc, char** argv) {
enum Mode mode = kModeSendSample;
bool secs_to_msecs = false;
// Parse arguments
int flag;
- while ((flag = getopt(argc, argv, "abcdegstuv")) != -1) {
+ while ((flag = getopt(argc, argv, "abcdegstv")) != -1) {
switch (flag) {
case 'c':
mode = kModeHasConsent;
break;
case 'd':
- mode = kModeDumpLogs;
+ mode = kModeDumpHistograms;
break;
case 'e':
mode = kModeSendEnumSample;
@@ -204,9 +164,6 @@
case 't':
secs_to_msecs = true;
break;
- case 'u':
- mode = kModeSendUserAction;
- break;
case 'v':
mode = kModeSendCrosEvent;
break;
@@ -224,8 +181,6 @@
expected_args = 3;
else if (mode == kModeSendSparseSample)
expected_args = 2;
- else if (mode == kModeSendUserAction)
- expected_args = 1;
else if (mode == kModeSendCrosEvent)
expected_args = 1;
@@ -234,6 +189,8 @@
}
switch (mode) {
+ case kModeDumpHistograms:
+ return DumpHistograms();
case kModeSendSample:
case kModeSendEnumSample:
case kModeSendSparseSample:
@@ -244,16 +201,12 @@
arg_index,
mode,
secs_to_msecs);
- case kModeSendUserAction:
- return SendUserAction(argv, arg_index);
case kModeSendCrosEvent:
return SendCrosEvent(argv, arg_index);
case kModeHasConsent:
return HasConsent();
case kModeIsGuestMode:
return IsGuestMode();
- case kModeDumpLogs:
- return DumpLogs();
default:
ShowUsage();
return 0;
diff --git a/metricsd/metrics_collector.cc b/metricsd/metrics_collector.cc
new file mode 100644
index 0000000..97690dd
--- /dev/null
+++ b/metricsd/metrics_collector.cc
@@ -0,0 +1,755 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "metrics_collector.h"
+
+#include <sysexits.h>
+#include <time.h>
+
+#include <memory>
+
+#include <base/bind.h>
+#include <base/files/file_path.h>
+#include <base/files/file_util.h>
+#include <base/hash.h>
+#include <base/logging.h>
+#include <base/strings/string_number_conversions.h>
+#include <base/strings/string_split.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+#include <brillo/binder_watcher.h>
+#include <brillo/osrelease_reader.h>
+#include <dbus/dbus.h>
+#include <dbus/message.h>
+
+#include "constants.h"
+#include "metrics_collector_service_impl.h"
+
+using base::FilePath;
+using base::StringPrintf;
+using base::Time;
+using base::TimeDelta;
+using base::TimeTicks;
+using chromeos_metrics::PersistentInteger;
+using std::map;
+using std::string;
+using std::vector;
+
+namespace {
+
+const int kSecondsPerMinute = 60;
+const int kMinutesPerHour = 60;
+const int kHoursPerDay = 24;
+const int kMinutesPerDay = kHoursPerDay * kMinutesPerHour;
+const int kSecondsPerDay = kSecondsPerMinute * kMinutesPerDay;
+const int kDaysPerWeek = 7;
+const int kSecondsPerWeek = kSecondsPerDay * kDaysPerWeek;
+
+// Interval between calls to UpdateStats().
+const uint32_t kUpdateStatsIntervalMs = 300000;
+
+const char kKernelCrashDetectedFile[] = "/var/run/kernel-crash-detected";
+const char kUncleanShutdownDetectedFile[] =
+ "/var/run/unclean-shutdown-detected";
+
+const int kMetricMeminfoInterval = 30; // seconds
+
+const char kMeminfoFileName[] = "/proc/meminfo";
+const char kVmStatFileName[] = "/proc/vmstat";
+
+const char kWeaveComponent[] = "metrics";
+
+} // namespace
+
+// Zram sysfs entries.
+
+const char MetricsCollector::kComprDataSizeName[] = "compr_data_size";
+const char MetricsCollector::kOrigDataSizeName[] = "orig_data_size";
+const char MetricsCollector::kZeroPagesName[] = "zero_pages";
+
+// Memory use stats collection intervals. We collect some memory use interval
+// at these intervals after boot, and we stop collecting after the last one,
+// with the assumption that in most cases the memory use won't change much
+// after that.
+static const int kMemuseIntervals[] = {
+ 1 * kSecondsPerMinute, // 1 minute mark
+ 4 * kSecondsPerMinute, // 5 minute mark
+ 25 * kSecondsPerMinute, // 0.5 hour mark
+ 120 * kSecondsPerMinute, // 2.5 hour mark
+ 600 * kSecondsPerMinute, // 12.5 hour mark
+};
+
+MetricsCollector::MetricsCollector()
+ : memuse_final_time_(0),
+ memuse_interval_index_(0) {}
+
+MetricsCollector::~MetricsCollector() {
+}
+
+// static
+double MetricsCollector::GetActiveTime() {
+ struct timespec ts;
+ int r = clock_gettime(CLOCK_MONOTONIC, &ts);
+ if (r < 0) {
+ PLOG(WARNING) << "clock_gettime(CLOCK_MONOTONIC) failed";
+ return 0;
+ } else {
+ return ts.tv_sec + static_cast<double>(ts.tv_nsec) / (1000 * 1000 * 1000);
+ }
+}
+
+int MetricsCollector::Run() {
+ if (CheckSystemCrash(kKernelCrashDetectedFile)) {
+ ProcessKernelCrash();
+ }
+
+ if (CheckSystemCrash(kUncleanShutdownDetectedFile)) {
+ ProcessUncleanShutdown();
+ }
+
+ // On OS version change, clear version stats (which are reported daily).
+ int32_t version = GetOsVersionHash();
+ if (version_cycle_->Get() != version) {
+ version_cycle_->Set(version);
+ kernel_crashes_version_count_->Set(0);
+ version_cumulative_active_use_->Set(0);
+ version_cumulative_cpu_use_->Set(0);
+ }
+
+ // Start metricscollectorservice
+ android::sp<BnMetricsCollectorServiceImpl> metrics_collector_service =
+ new BnMetricsCollectorServiceImpl(this);
+ android::status_t status = android::defaultServiceManager()->addService(
+ metrics_collector_service->getInterfaceDescriptor(),
+ metrics_collector_service);
+ CHECK(status == android::OK)
+ << "failed to register service metricscollectorservice";
+
+ // Watch Binder events in the main loop
+ brillo::BinderWatcher binder_watcher;
+ CHECK(binder_watcher.Init()) << "Binder FD watcher init failed";
+ return brillo::DBusDaemon::Run();
+}
+
+uint32_t MetricsCollector::GetOsVersionHash() {
+ brillo::OsReleaseReader reader;
+ reader.Load();
+ string version;
+ if (!reader.GetString(metrics::kProductVersion, &version)) {
+ LOG(ERROR) << "failed to read the product version.";
+ version = metrics::kDefaultVersion;
+ }
+
+ uint32_t version_hash = base::Hash(version);
+ if (testing_) {
+ version_hash = 42; // return any plausible value for the hash
+ }
+ return version_hash;
+}
+
+void MetricsCollector::Init(bool testing, MetricsLibraryInterface* metrics_lib,
+ const string& diskstats_path,
+ const base::FilePath& private_metrics_directory,
+ const base::FilePath& shared_metrics_directory) {
+ CHECK(metrics_lib);
+ testing_ = testing;
+ shared_metrics_directory_ = shared_metrics_directory;
+ metrics_lib_ = metrics_lib;
+
+ daily_active_use_.reset(new PersistentInteger("Platform.UseTime.PerDay",
+ private_metrics_directory));
+ version_cumulative_active_use_.reset(new PersistentInteger(
+ "Platform.CumulativeUseTime", private_metrics_directory));
+ version_cumulative_cpu_use_.reset(new PersistentInteger(
+ "Platform.CumulativeCpuTime", private_metrics_directory));
+
+ kernel_crash_interval_.reset(new PersistentInteger(
+ "Platform.KernelCrashInterval", private_metrics_directory));
+ unclean_shutdown_interval_.reset(new PersistentInteger(
+ "Platform.UncleanShutdownInterval", private_metrics_directory));
+ user_crash_interval_.reset(new PersistentInteger("Platform.UserCrashInterval",
+ private_metrics_directory));
+
+ any_crashes_daily_count_.reset(new PersistentInteger(
+ "Platform.AnyCrashes.PerDay", private_metrics_directory));
+ any_crashes_weekly_count_.reset(new PersistentInteger(
+ "Platform.AnyCrashes.PerWeek", private_metrics_directory));
+ user_crashes_daily_count_.reset(new PersistentInteger(
+ "Platform.UserCrashes.PerDay", private_metrics_directory));
+ user_crashes_weekly_count_.reset(new PersistentInteger(
+ "Platform.UserCrashes.PerWeek", private_metrics_directory));
+ kernel_crashes_daily_count_.reset(new PersistentInteger(
+ "Platform.KernelCrashes.PerDay", private_metrics_directory));
+ kernel_crashes_weekly_count_.reset(new PersistentInteger(
+ "Platform.KernelCrashes.PerWeek", private_metrics_directory));
+ kernel_crashes_version_count_.reset(new PersistentInteger(
+ "Platform.KernelCrashesSinceUpdate", private_metrics_directory));
+ unclean_shutdowns_daily_count_.reset(new PersistentInteger(
+ "Platform.UncleanShutdown.PerDay", private_metrics_directory));
+ unclean_shutdowns_weekly_count_.reset(new PersistentInteger(
+ "Platform.UncleanShutdowns.PerWeek", private_metrics_directory));
+
+ daily_cycle_.reset(
+ new PersistentInteger("daily.cycle", private_metrics_directory));
+ weekly_cycle_.reset(
+ new PersistentInteger("weekly.cycle", private_metrics_directory));
+ version_cycle_.reset(
+ new PersistentInteger("version.cycle", private_metrics_directory));
+
+ disk_usage_collector_.reset(new DiskUsageCollector(metrics_lib_));
+ averaged_stats_collector_.reset(
+ new AveragedStatisticsCollector(metrics_lib_, diskstats_path,
+ kVmStatFileName));
+ cpu_usage_collector_.reset(new CpuUsageCollector(metrics_lib_));
+}
+
+int MetricsCollector::OnInit() {
+ int return_code = brillo::DBusDaemon::OnInit();
+ if (return_code != EX_OK)
+ return return_code;
+
+ StatsReporterInit();
+
+ // Start collecting meminfo stats.
+ ScheduleMeminfoCallback(kMetricMeminfoInterval);
+ memuse_final_time_ = GetActiveTime() + kMemuseIntervals[0];
+ ScheduleMemuseCallback(kMemuseIntervals[0]);
+
+ if (testing_)
+ return EX_OK;
+
+ bus_->AssertOnDBusThread();
+ CHECK(bus_->SetUpAsyncOperations());
+
+ device_ = weaved::Device::CreateInstance(
+ bus_,
+ base::Bind(&MetricsCollector::UpdateWeaveState, base::Unretained(this)));
+ device_->AddComponent(kWeaveComponent, {"_metrics"});
+ device_->AddCommandHandler(
+ kWeaveComponent,
+ "_metrics.enableAnalyticsReporting",
+ base::Bind(&MetricsCollector::OnEnableMetrics, base::Unretained(this)));
+ device_->AddCommandHandler(
+ kWeaveComponent,
+ "_metrics.disableAnalyticsReporting",
+ base::Bind(&MetricsCollector::OnDisableMetrics, base::Unretained(this)));
+
+ latest_cpu_use_microseconds_ = cpu_usage_collector_->GetCumulativeCpuUse();
+ base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
+ base::Bind(&MetricsCollector::HandleUpdateStatsTimeout,
+ base::Unretained(this)),
+ base::TimeDelta::FromMilliseconds(kUpdateStatsIntervalMs));
+
+ return EX_OK;
+}
+
+void MetricsCollector::OnShutdown(int* return_code) {
+ brillo::DBusDaemon::OnShutdown(return_code);
+}
+
+void MetricsCollector::OnEnableMetrics(
+ const std::weak_ptr<weaved::Command>& cmd) {
+ auto command = cmd.lock();
+ if (!command)
+ return;
+
+ if (base::WriteFile(
+ shared_metrics_directory_.Append(metrics::kConsentFileName), "", 0) !=
+ 0) {
+ PLOG(ERROR) << "Could not create the consent file.";
+ command->Abort("metrics_error", "Could not create the consent file",
+ nullptr);
+ return;
+ }
+
+ UpdateWeaveState();
+ command->Complete({}, nullptr);
+}
+
+void MetricsCollector::OnDisableMetrics(
+ const std::weak_ptr<weaved::Command>& cmd) {
+ auto command = cmd.lock();
+ if (!command)
+ return;
+
+ if (!base::DeleteFile(
+ shared_metrics_directory_.Append(metrics::kConsentFileName), false)) {
+ PLOG(ERROR) << "Could not delete the consent file.";
+ command->Abort("metrics_error", "Could not delete the consent file",
+ nullptr);
+ return;
+ }
+
+ UpdateWeaveState();
+ command->Complete({}, nullptr);
+}
+
+void MetricsCollector::UpdateWeaveState() {
+ if (!device_)
+ return;
+
+ std::string enabled =
+ metrics_lib_->AreMetricsEnabled() ? "enabled" : "disabled";
+
+ if (!device_->SetStateProperty(kWeaveComponent,
+ "_metrics.analyticsReportingState",
+ enabled,
+ nullptr)) {
+ LOG(ERROR) << "failed to update weave's state";
+ }
+}
+
+void MetricsCollector::ProcessUserCrash() {
+ // Counts the active time up to now.
+ UpdateStats(TimeTicks::Now(), Time::Now());
+
+ // Reports the active use time since the last crash and resets it.
+ SendAndResetCrashIntervalSample(user_crash_interval_);
+
+ any_crashes_daily_count_->Add(1);
+ any_crashes_weekly_count_->Add(1);
+ user_crashes_daily_count_->Add(1);
+ user_crashes_weekly_count_->Add(1);
+}
+
+void MetricsCollector::ProcessKernelCrash() {
+ // Counts the active time up to now.
+ UpdateStats(TimeTicks::Now(), Time::Now());
+
+ // Reports the active use time since the last crash and resets it.
+ SendAndResetCrashIntervalSample(kernel_crash_interval_);
+
+ any_crashes_daily_count_->Add(1);
+ any_crashes_weekly_count_->Add(1);
+ kernel_crashes_daily_count_->Add(1);
+ kernel_crashes_weekly_count_->Add(1);
+
+ kernel_crashes_version_count_->Add(1);
+}
+
+void MetricsCollector::ProcessUncleanShutdown() {
+ // Counts the active time up to now.
+ UpdateStats(TimeTicks::Now(), Time::Now());
+
+ // Reports the active use time since the last crash and resets it.
+ SendAndResetCrashIntervalSample(unclean_shutdown_interval_);
+
+ unclean_shutdowns_daily_count_->Add(1);
+ unclean_shutdowns_weekly_count_->Add(1);
+ any_crashes_daily_count_->Add(1);
+ any_crashes_weekly_count_->Add(1);
+}
+
+bool MetricsCollector::CheckSystemCrash(const string& crash_file) {
+ FilePath crash_detected(crash_file);
+ if (!base::PathExists(crash_detected))
+ return false;
+
+ // Deletes the crash-detected file so that the daemon doesn't report
+ // another kernel crash in case it's restarted.
+ base::DeleteFile(crash_detected, false); // not recursive
+ return true;
+}
+
+void MetricsCollector::StatsReporterInit() {
+ disk_usage_collector_->Schedule();
+
+ cpu_usage_collector_->Init();
+ cpu_usage_collector_->Schedule();
+
+ // Don't start a collection cycle during the first run to avoid delaying the
+ // boot.
+ averaged_stats_collector_->ScheduleWait();
+}
+
+void MetricsCollector::ScheduleMeminfoCallback(int wait) {
+ if (testing_) {
+ return;
+ }
+ base::TimeDelta waitDelta = base::TimeDelta::FromSeconds(wait);
+ base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
+ base::Bind(&MetricsCollector::MeminfoCallback, base::Unretained(this),
+ waitDelta),
+ waitDelta);
+}
+
+void MetricsCollector::MeminfoCallback(base::TimeDelta wait) {
+ string meminfo_raw;
+ const FilePath meminfo_path(kMeminfoFileName);
+ if (!base::ReadFileToString(meminfo_path, &meminfo_raw)) {
+ LOG(WARNING) << "cannot read " << meminfo_path.value().c_str();
+ return;
+ }
+ // Make both calls even if the first one fails.
+ if (ProcessMeminfo(meminfo_raw)) {
+ base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
+ base::Bind(&MetricsCollector::MeminfoCallback, base::Unretained(this),
+ wait),
+ wait);
+ }
+}
+
+// static
+bool MetricsCollector::ReadFileToUint64(const base::FilePath& path,
+ uint64_t* value) {
+ std::string content;
+ if (!base::ReadFileToString(path, &content)) {
+ PLOG(WARNING) << "cannot read " << path.MaybeAsASCII();
+ return false;
+ }
+ // Remove final newline.
+ base::TrimWhitespaceASCII(content, base::TRIM_TRAILING, &content);
+ if (!base::StringToUint64(content, value)) {
+ LOG(WARNING) << "invalid integer: " << content;
+ return false;
+ }
+ return true;
+}
+
+bool MetricsCollector::ReportZram(const base::FilePath& zram_dir) {
+ // Data sizes are in bytes. |zero_pages| is in number of pages.
+ uint64_t compr_data_size, orig_data_size, zero_pages;
+ const size_t page_size = 4096;
+
+ if (!ReadFileToUint64(zram_dir.Append(kComprDataSizeName),
+ &compr_data_size) ||
+ !ReadFileToUint64(zram_dir.Append(kOrigDataSizeName), &orig_data_size) ||
+ !ReadFileToUint64(zram_dir.Append(kZeroPagesName), &zero_pages)) {
+ return false;
+ }
+
+ // |orig_data_size| does not include zero-filled pages.
+ orig_data_size += zero_pages * page_size;
+
+ const int compr_data_size_mb = compr_data_size >> 20;
+ const int savings_mb = (orig_data_size - compr_data_size) >> 20;
+ const int zero_ratio_percent = zero_pages * page_size * 100 / orig_data_size;
+
+ // Report compressed size in megabytes. 100 MB or less has little impact.
+ SendSample("Platform.ZramCompressedSize", compr_data_size_mb, 100, 4000, 50);
+ SendSample("Platform.ZramSavings", savings_mb, 100, 4000, 50);
+ // The compression ratio is multiplied by 100 for better resolution. The
+ // ratios of interest are between 1 and 6 (100% and 600% as reported). We
+ // don't want samples when very little memory is being compressed.
+ if (compr_data_size_mb >= 1) {
+ SendSample("Platform.ZramCompressionRatioPercent",
+ orig_data_size * 100 / compr_data_size, 100, 600, 50);
+ }
+ // The values of interest for zero_pages are between 1MB and 1GB. The units
+ // are number of pages.
+ SendSample("Platform.ZramZeroPages", zero_pages, 256, 256 * 1024, 50);
+ SendSample("Platform.ZramZeroRatioPercent", zero_ratio_percent, 1, 50, 50);
+
+ return true;
+}
+
+bool MetricsCollector::ProcessMeminfo(const string& meminfo_raw) {
+ static const MeminfoRecord fields_array[] = {
+ { "MemTotal", "MemTotal" }, // SPECIAL CASE: total system memory
+ { "MemFree", "MemFree" },
+ { "Buffers", "Buffers" },
+ { "Cached", "Cached" },
+ // { "SwapCached", "SwapCached" },
+ { "Active", "Active" },
+ { "Inactive", "Inactive" },
+ { "ActiveAnon", "Active(anon)" },
+ { "InactiveAnon", "Inactive(anon)" },
+ { "ActiveFile" , "Active(file)" },
+ { "InactiveFile", "Inactive(file)" },
+ { "Unevictable", "Unevictable", kMeminfoOp_HistLog },
+ // { "Mlocked", "Mlocked" },
+ { "SwapTotal", "SwapTotal", kMeminfoOp_SwapTotal },
+ { "SwapFree", "SwapFree", kMeminfoOp_SwapFree },
+ // { "Dirty", "Dirty" },
+ // { "Writeback", "Writeback" },
+ { "AnonPages", "AnonPages" },
+ { "Mapped", "Mapped" },
+ { "Shmem", "Shmem", kMeminfoOp_HistLog },
+ { "Slab", "Slab", kMeminfoOp_HistLog },
+ // { "SReclaimable", "SReclaimable" },
+ // { "SUnreclaim", "SUnreclaim" },
+ };
+ vector<MeminfoRecord> fields(fields_array,
+ fields_array + arraysize(fields_array));
+ if (!FillMeminfo(meminfo_raw, &fields)) {
+ return false;
+ }
+ int total_memory = fields[0].value;
+ if (total_memory == 0) {
+ // this "cannot happen"
+ LOG(WARNING) << "borked meminfo parser";
+ return false;
+ }
+ int swap_total = 0;
+ int swap_free = 0;
+ // Send all fields retrieved, except total memory.
+ for (unsigned int i = 1; i < fields.size(); i++) {
+ string metrics_name = base::StringPrintf("Platform.Meminfo%s",
+ fields[i].name);
+ int percent;
+ switch (fields[i].op) {
+ case kMeminfoOp_HistPercent:
+ // report value as percent of total memory
+ percent = fields[i].value * 100 / total_memory;
+ SendLinearSample(metrics_name, percent, 100, 101);
+ break;
+ case kMeminfoOp_HistLog:
+ // report value in kbytes, log scale, 4Gb max
+ SendSample(metrics_name, fields[i].value, 1, 4 * 1000 * 1000, 100);
+ break;
+ case kMeminfoOp_SwapTotal:
+ swap_total = fields[i].value;
+ case kMeminfoOp_SwapFree:
+ swap_free = fields[i].value;
+ break;
+ }
+ }
+ if (swap_total > 0) {
+ int swap_used = swap_total - swap_free;
+ int swap_used_percent = swap_used * 100 / swap_total;
+ SendSample("Platform.MeminfoSwapUsed", swap_used, 1, 8 * 1000 * 1000, 100);
+ SendLinearSample("Platform.MeminfoSwapUsed.Percent", swap_used_percent,
+ 100, 101);
+ }
+ return true;
+}
+
+bool MetricsCollector::FillMeminfo(const string& meminfo_raw,
+ vector<MeminfoRecord>* fields) {
+ vector<string> lines;
+ unsigned int nlines = Tokenize(meminfo_raw, "\n", &lines);
+
+ // Scan meminfo output and collect field values. Each field name has to
+ // match a meminfo entry (case insensitive) after removing non-alpha
+ // characters from the entry.
+ unsigned int ifield = 0;
+ for (unsigned int iline = 0;
+ iline < nlines && ifield < fields->size();
+ iline++) {
+ vector<string> tokens;
+ Tokenize(lines[iline], ": ", &tokens);
+ if (strcmp((*fields)[ifield].match, tokens[0].c_str()) == 0) {
+ // Name matches. Parse value and save.
+ if (!base::StringToInt(tokens[1], &(*fields)[ifield].value)) {
+ LOG(WARNING) << "Cound not convert " << tokens[1] << " to int";
+ return false;
+ }
+ ifield++;
+ }
+ }
+ if (ifield < fields->size()) {
+ // End of input reached while scanning.
+ LOG(WARNING) << "cannot find field " << (*fields)[ifield].match
+ << " and following";
+ return false;
+ }
+ return true;
+}
+
+void MetricsCollector::ScheduleMemuseCallback(double interval) {
+ if (testing_) {
+ return;
+ }
+ base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
+ base::Bind(&MetricsCollector::MemuseCallback, base::Unretained(this)),
+ base::TimeDelta::FromSeconds(interval));
+}
+
+void MetricsCollector::MemuseCallback() {
+ // Since we only care about active time (i.e. uptime minus sleep time) but
+ // the callbacks are driven by real time (uptime), we check if we should
+ // reschedule this callback due to intervening sleep periods.
+ double now = GetActiveTime();
+ // Avoid intervals of less than one second.
+ double remaining_time = ceil(memuse_final_time_ - now);
+ if (remaining_time > 0) {
+ ScheduleMemuseCallback(remaining_time);
+ } else {
+ // Report stats and advance the measurement interval unless there are
+ // errors or we've completed the last interval.
+ if (MemuseCallbackWork() &&
+ memuse_interval_index_ < arraysize(kMemuseIntervals)) {
+ double interval = kMemuseIntervals[memuse_interval_index_++];
+ memuse_final_time_ = now + interval;
+ ScheduleMemuseCallback(interval);
+ }
+ }
+}
+
+bool MetricsCollector::MemuseCallbackWork() {
+ string meminfo_raw;
+ const FilePath meminfo_path(kMeminfoFileName);
+ if (!base::ReadFileToString(meminfo_path, &meminfo_raw)) {
+ LOG(WARNING) << "cannot read " << meminfo_path.value().c_str();
+ return false;
+ }
+ return ProcessMemuse(meminfo_raw);
+}
+
+bool MetricsCollector::ProcessMemuse(const string& meminfo_raw) {
+ static const MeminfoRecord fields_array[] = {
+ { "MemTotal", "MemTotal" }, // SPECIAL CASE: total system memory
+ { "ActiveAnon", "Active(anon)" },
+ { "InactiveAnon", "Inactive(anon)" },
+ };
+ vector<MeminfoRecord> fields(fields_array,
+ fields_array + arraysize(fields_array));
+ if (!FillMeminfo(meminfo_raw, &fields)) {
+ return false;
+ }
+ int total = fields[0].value;
+ int active_anon = fields[1].value;
+ int inactive_anon = fields[2].value;
+ if (total == 0) {
+ // this "cannot happen"
+ LOG(WARNING) << "borked meminfo parser";
+ return false;
+ }
+ string metrics_name = base::StringPrintf("Platform.MemuseAnon%d",
+ memuse_interval_index_);
+ SendLinearSample(metrics_name, (active_anon + inactive_anon) * 100 / total,
+ 100, 101);
+ return true;
+}
+
+void MetricsCollector::SendSample(const string& name, int sample,
+ int min, int max, int nbuckets) {
+ metrics_lib_->SendToUMA(name, sample, min, max, nbuckets);
+}
+
+void MetricsCollector::SendKernelCrashesCumulativeCountStats() {
+ // Report the number of crashes for this OS version, but don't clear the
+ // counter. It is cleared elsewhere on version change.
+ int64_t crashes_count = kernel_crashes_version_count_->Get();
+ SendSample(kernel_crashes_version_count_->Name(),
+ crashes_count,
+ 1, // value of first bucket
+ 500, // value of last bucket
+ 100); // number of buckets
+
+
+ int64_t cpu_use_ms = version_cumulative_cpu_use_->Get();
+ SendSample(version_cumulative_cpu_use_->Name(),
+ cpu_use_ms / 1000, // stat is in seconds
+ 1, // device may be used very little...
+ 8 * 1000 * 1000, // ... or a lot (a little over 90 days)
+ 100);
+
+ // On the first run after an autoupdate, cpu_use_ms and active_use_seconds
+ // can be zero. Avoid division by zero.
+ if (cpu_use_ms > 0) {
+ // Send the crash frequency since update in number of crashes per CPU year.
+ SendSample("Logging.KernelCrashesPerCpuYear",
+ crashes_count * kSecondsPerDay * 365 * 1000 / cpu_use_ms,
+ 1,
+ 1000 * 1000, // about one crash every 30s of CPU time
+ 100);
+ }
+
+ int64_t active_use_seconds = version_cumulative_active_use_->Get();
+ if (active_use_seconds > 0) {
+ SendSample(version_cumulative_active_use_->Name(),
+ active_use_seconds,
+ 1, // device may be used very little...
+ 8 * 1000 * 1000, // ... or a lot (about 90 days)
+ 100);
+ // Same as above, but per year of active time.
+ SendSample("Logging.KernelCrashesPerActiveYear",
+ crashes_count * kSecondsPerDay * 365 / active_use_seconds,
+ 1,
+ 1000 * 1000, // about one crash every 30s of active time
+ 100);
+ }
+}
+
+void MetricsCollector::SendAndResetDailyUseSample(
+ const unique_ptr<PersistentInteger>& use) {
+ SendSample(use->Name(),
+ use->GetAndClear(),
+ 1, // value of first bucket
+ kSecondsPerDay, // value of last bucket
+ 50); // number of buckets
+}
+
+void MetricsCollector::SendAndResetCrashIntervalSample(
+ const unique_ptr<PersistentInteger>& interval) {
+ SendSample(interval->Name(),
+ interval->GetAndClear(),
+ 1, // value of first bucket
+ 4 * kSecondsPerWeek, // value of last bucket
+ 50); // number of buckets
+}
+
+void MetricsCollector::SendAndResetCrashFrequencySample(
+ const unique_ptr<PersistentInteger>& frequency) {
+ SendSample(frequency->Name(),
+ frequency->GetAndClear(),
+ 1, // value of first bucket
+ 100, // value of last bucket
+ 50); // number of buckets
+}
+
+void MetricsCollector::SendLinearSample(const string& name, int sample,
+ int max, int nbuckets) {
+ // TODO(semenzato): add a proper linear histogram to the Chrome external
+ // metrics API.
+ LOG_IF(FATAL, nbuckets != max + 1) << "unsupported histogram scale";
+ metrics_lib_->SendEnumToUMA(name, sample, max);
+}
+
+void MetricsCollector::UpdateStats(TimeTicks now_ticks,
+ Time now_wall_time) {
+ const int elapsed_seconds = (now_ticks - last_update_stats_time_).InSeconds();
+ daily_active_use_->Add(elapsed_seconds);
+ version_cumulative_active_use_->Add(elapsed_seconds);
+ user_crash_interval_->Add(elapsed_seconds);
+ kernel_crash_interval_->Add(elapsed_seconds);
+ TimeDelta cpu_use = cpu_usage_collector_->GetCumulativeCpuUse();
+ version_cumulative_cpu_use_->Add(
+ (cpu_use - latest_cpu_use_microseconds_).InMilliseconds());
+ latest_cpu_use_microseconds_ = cpu_use;
+ last_update_stats_time_ = now_ticks;
+
+ const TimeDelta since_epoch = now_wall_time - Time::UnixEpoch();
+ const int day = since_epoch.InDays();
+ const int week = day / 7;
+
+ if (daily_cycle_->Get() != day) {
+ daily_cycle_->Set(day);
+ SendAndResetDailyUseSample(daily_active_use_);
+ SendAndResetCrashFrequencySample(any_crashes_daily_count_);
+ SendAndResetCrashFrequencySample(user_crashes_daily_count_);
+ SendAndResetCrashFrequencySample(kernel_crashes_daily_count_);
+ SendAndResetCrashFrequencySample(unclean_shutdowns_daily_count_);
+ SendKernelCrashesCumulativeCountStats();
+ }
+
+ if (weekly_cycle_->Get() != week) {
+ weekly_cycle_->Set(week);
+ SendAndResetCrashFrequencySample(any_crashes_weekly_count_);
+ SendAndResetCrashFrequencySample(user_crashes_weekly_count_);
+ SendAndResetCrashFrequencySample(kernel_crashes_weekly_count_);
+ SendAndResetCrashFrequencySample(unclean_shutdowns_weekly_count_);
+ }
+}
+
+void MetricsCollector::HandleUpdateStatsTimeout() {
+ UpdateStats(TimeTicks::Now(), Time::Now());
+ base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
+ base::Bind(&MetricsCollector::HandleUpdateStatsTimeout,
+ base::Unretained(this)),
+ base::TimeDelta::FromMilliseconds(kUpdateStatsIntervalMs));
+}
diff --git a/metricsd/metrics_daemon.h b/metricsd/metrics_collector.h
similarity index 63%
rename from metricsd/metrics_daemon.h
rename to metricsd/metrics_collector.h
index 3d691c5..45ef63d 100644
--- a/metricsd/metrics_daemon.h
+++ b/metricsd/metrics_collector.h
@@ -14,17 +14,17 @@
* limitations under the License.
*/
-#ifndef METRICS_METRICS_DAEMON_H_
-#define METRICS_METRICS_DAEMON_H_
+#ifndef METRICS_METRICS_COLLECTOR_H_
+#define METRICS_METRICS_COLLECTOR_H_
#include <stdint.h>
#include <map>
+#include <memory>
#include <string>
#include <vector>
#include <base/files/file_path.h>
-#include <base/memory/scoped_ptr.h>
#include <base/time/time.h>
#include <brillo/daemons/dbus_daemon.h>
#include <libweaved/command.h>
@@ -32,29 +32,25 @@
#include <gtest/gtest_prod.h> // for FRIEND_TEST
#include "collectors/averaged_statistics_collector.h"
+#include "collectors/cpu_usage_collector.h"
#include "collectors/disk_usage_collector.h"
#include "metrics/metrics_library.h"
#include "persistent_integer.h"
-#include "uploader/upload_service.h"
using chromeos_metrics::PersistentInteger;
+using std::unique_ptr;
-class MetricsDaemon : public brillo::DBusDaemon {
+class MetricsCollector : public brillo::DBusDaemon {
public:
- MetricsDaemon();
- ~MetricsDaemon();
+ MetricsCollector();
+ ~MetricsCollector();
// Initializes metrics class variables.
void Init(bool testing,
- bool uploader_active,
- bool dbus_enabled,
MetricsLibraryInterface* metrics_lib,
const std::string& diskstats_path,
- const std::string& cpuinfo_max_freq_path,
- const std::string& scaling_max_freq_path,
- const base::TimeDelta& upload_interval,
- const std::string& server,
- const base::FilePath& metrics_directory);
+ const base::FilePath& private_metrics_directory,
+ const base::FilePath& shared_metrics_directory);
// Initializes DBus and MessageLoop variables before running the MessageLoop.
int OnInit() override;
@@ -65,12 +61,13 @@
// Does all the work.
int Run() override;
- // Triggers an upload event and exit. (Used to test UploadService)
- void RunUploaderTest();
-
// Returns the active time since boot (uptime minus sleep time) in seconds.
static double GetActiveTime();
+ // Updates the active use time and logs time between user-space
+ // process crashes. Called via MetricsCollectorServiceTrampoline.
+ void ProcessUserCrash();
+
protected:
// Used also by the unit tests.
static const char kComprDataSizeName[];
@@ -78,26 +75,24 @@
static const char kZeroPagesName[];
private:
- friend class MetricsDaemonTest;
- FRIEND_TEST(MetricsDaemonTest, CheckSystemCrash);
- FRIEND_TEST(MetricsDaemonTest, ComputeEpochNoCurrent);
- FRIEND_TEST(MetricsDaemonTest, ComputeEpochNoLast);
- FRIEND_TEST(MetricsDaemonTest, GetHistogramPath);
- FRIEND_TEST(MetricsDaemonTest, IsNewEpoch);
- FRIEND_TEST(MetricsDaemonTest, MessageFilter);
- FRIEND_TEST(MetricsDaemonTest, ProcessKernelCrash);
- FRIEND_TEST(MetricsDaemonTest, ProcessMeminfo);
- FRIEND_TEST(MetricsDaemonTest, ProcessMeminfo2);
- FRIEND_TEST(MetricsDaemonTest, ProcessUncleanShutdown);
- FRIEND_TEST(MetricsDaemonTest, ProcessUserCrash);
- FRIEND_TEST(MetricsDaemonTest, ReportCrashesDailyFrequency);
- FRIEND_TEST(MetricsDaemonTest, ReadFreqToInt);
- FRIEND_TEST(MetricsDaemonTest, ReportKernelCrashInterval);
- FRIEND_TEST(MetricsDaemonTest, ReportUncleanShutdownInterval);
- FRIEND_TEST(MetricsDaemonTest, ReportUserCrashInterval);
- FRIEND_TEST(MetricsDaemonTest, SendSample);
- FRIEND_TEST(MetricsDaemonTest, SendCpuThrottleMetrics);
- FRIEND_TEST(MetricsDaemonTest, SendZramMetrics);
+ friend class MetricsCollectorTest;
+ FRIEND_TEST(MetricsCollectorTest, CheckSystemCrash);
+ FRIEND_TEST(MetricsCollectorTest, ComputeEpochNoCurrent);
+ FRIEND_TEST(MetricsCollectorTest, ComputeEpochNoLast);
+ FRIEND_TEST(MetricsCollectorTest, GetHistogramPath);
+ FRIEND_TEST(MetricsCollectorTest, IsNewEpoch);
+ FRIEND_TEST(MetricsCollectorTest, MessageFilter);
+ FRIEND_TEST(MetricsCollectorTest, ProcessKernelCrash);
+ FRIEND_TEST(MetricsCollectorTest, ProcessMeminfo);
+ FRIEND_TEST(MetricsCollectorTest, ProcessMeminfo2);
+ FRIEND_TEST(MetricsCollectorTest, ProcessUncleanShutdown);
+ FRIEND_TEST(MetricsCollectorTest, ProcessUserCrash);
+ FRIEND_TEST(MetricsCollectorTest, ReportCrashesDailyFrequency);
+ FRIEND_TEST(MetricsCollectorTest, ReportKernelCrashInterval);
+ FRIEND_TEST(MetricsCollectorTest, ReportUncleanShutdownInterval);
+ FRIEND_TEST(MetricsCollectorTest, ReportUserCrashInterval);
+ FRIEND_TEST(MetricsCollectorTest, SendSample);
+ FRIEND_TEST(MetricsCollectorTest, SendZramMetrics);
// Type of scale to use for meminfo histograms. For most of them we use
// percent of total RAM, but for some we use absolute numbers, usually in
@@ -118,11 +113,6 @@
int value; // value from /proc/meminfo
};
- // D-Bus filter callback.
- static DBusHandlerResult MessageFilter(DBusConnection* connection,
- DBusMessage* message,
- void* user_data);
-
// Enables metrics reporting.
void OnEnableMetrics(const std::weak_ptr<weaved::Command>& cmd);
@@ -132,10 +122,6 @@
// Updates the weave device state.
void UpdateWeaveState();
- // Updates the active use time and logs time between user-space
- // process crashes.
- void ProcessUserCrash();
-
// Updates the active use time and logs time between kernel crashes.
void ProcessKernelCrash();
@@ -164,24 +150,19 @@
// total number of kernel crashes since the last version update.
void SendKernelCrashesCumulativeCountStats();
- // Returns the total (system-wide) CPU usage between the time of the most
- // recent call to this function and now.
- base::TimeDelta GetIncrementalCpuUse();
-
// Sends a sample representing the number of seconds of active use
// for a 24-hour period and reset |use|.
- void SendAndResetDailyUseSample(
- const scoped_ptr<PersistentInteger>& use);
+ void SendAndResetDailyUseSample(const unique_ptr<PersistentInteger>& use);
// Sends a sample representing a time interval between two crashes of the
// same type and reset |interval|.
void SendAndResetCrashIntervalSample(
- const scoped_ptr<PersistentInteger>& interval);
+ const unique_ptr<PersistentInteger>& interval);
// Sends a sample representing a frequency of crashes of some type and reset
// |frequency|.
void SendAndResetCrashFrequencySample(
- const scoped_ptr<PersistentInteger>& frequency);
+ const unique_ptr<PersistentInteger>& frequency);
// Initializes vm and disk stats reporting.
void StatsReporterInit();
@@ -218,12 +199,6 @@
// Parses meminfo data and sends it to UMA.
bool ProcessMemuse(const std::string& meminfo_raw);
- // Sends stats for thermal CPU throttling.
- void SendCpuThrottleMetrics();
-
- // Reads an integer CPU frequency value from sysfs.
- bool ReadFreqToInt(const std::string& sysfs_file_name, int* value);
-
// Reads the current OS version from /etc/lsb-release and hashes it
// to a unsigned 32-bit int.
uint32_t GetOsVersionHash();
@@ -246,15 +221,8 @@
// Test mode.
bool testing_;
- // Whether the uploader is enabled or disabled.
- bool uploader_active_;
-
- // Whether or not dbus should be used.
- // If disabled, we will not collect the frequency of crashes.
- bool dbus_enabled_;
-
- // Root of the configuration files to use.
- base::FilePath metrics_directory_;
+ // Publicly readable metrics directory.
+ base::FilePath shared_metrics_directory_;
// The metrics library handle.
MetricsLibraryInterface* metrics_lib_;
@@ -268,51 +236,43 @@
// Selects the wait time for the next memory use callback.
unsigned int memuse_interval_index_;
- // The system "HZ", or frequency of ticks. Some system data uses ticks as a
- // unit, and this is used to convert to standard time units.
- uint32_t ticks_per_second_;
// Used internally by GetIncrementalCpuUse() to return the CPU utilization
// between calls.
- uint64_t latest_cpu_use_ticks_;
+ base::TimeDelta latest_cpu_use_microseconds_;
// Persistent values and accumulators for crash statistics.
- scoped_ptr<PersistentInteger> daily_cycle_;
- scoped_ptr<PersistentInteger> weekly_cycle_;
- scoped_ptr<PersistentInteger> version_cycle_;
+ unique_ptr<PersistentInteger> daily_cycle_;
+ unique_ptr<PersistentInteger> weekly_cycle_;
+ unique_ptr<PersistentInteger> version_cycle_;
// Active use accumulated in a day.
- scoped_ptr<PersistentInteger> daily_active_use_;
+ unique_ptr<PersistentInteger> daily_active_use_;
// Active use accumulated since the latest version update.
- scoped_ptr<PersistentInteger> version_cumulative_active_use_;
+ unique_ptr<PersistentInteger> version_cumulative_active_use_;
// The CPU time accumulator. This contains the CPU time, in milliseconds,
// used by the system since the most recent OS version update.
- scoped_ptr<PersistentInteger> version_cumulative_cpu_use_;
+ unique_ptr<PersistentInteger> version_cumulative_cpu_use_;
- scoped_ptr<PersistentInteger> user_crash_interval_;
- scoped_ptr<PersistentInteger> kernel_crash_interval_;
- scoped_ptr<PersistentInteger> unclean_shutdown_interval_;
+ unique_ptr<PersistentInteger> user_crash_interval_;
+ unique_ptr<PersistentInteger> kernel_crash_interval_;
+ unique_ptr<PersistentInteger> unclean_shutdown_interval_;
- scoped_ptr<PersistentInteger> any_crashes_daily_count_;
- scoped_ptr<PersistentInteger> any_crashes_weekly_count_;
- scoped_ptr<PersistentInteger> user_crashes_daily_count_;
- scoped_ptr<PersistentInteger> user_crashes_weekly_count_;
- scoped_ptr<PersistentInteger> kernel_crashes_daily_count_;
- scoped_ptr<PersistentInteger> kernel_crashes_weekly_count_;
- scoped_ptr<PersistentInteger> kernel_crashes_version_count_;
- scoped_ptr<PersistentInteger> unclean_shutdowns_daily_count_;
- scoped_ptr<PersistentInteger> unclean_shutdowns_weekly_count_;
- scoped_ptr<DiskUsageCollector> disk_usage_collector_;
- scoped_ptr<AveragedStatisticsCollector> averaged_stats_collector_;
+ unique_ptr<PersistentInteger> any_crashes_daily_count_;
+ unique_ptr<PersistentInteger> any_crashes_weekly_count_;
+ unique_ptr<PersistentInteger> user_crashes_daily_count_;
+ unique_ptr<PersistentInteger> user_crashes_weekly_count_;
+ unique_ptr<PersistentInteger> kernel_crashes_daily_count_;
+ unique_ptr<PersistentInteger> kernel_crashes_weekly_count_;
+ unique_ptr<PersistentInteger> kernel_crashes_version_count_;
+ unique_ptr<PersistentInteger> unclean_shutdowns_daily_count_;
+ unique_ptr<PersistentInteger> unclean_shutdowns_weekly_count_;
- std::string scaling_max_freq_path_;
- std::string cpuinfo_max_freq_path_;
+ unique_ptr<CpuUsageCollector> cpu_usage_collector_;
+ unique_ptr<DiskUsageCollector> disk_usage_collector_;
+ unique_ptr<AveragedStatisticsCollector> averaged_stats_collector_;
- base::TimeDelta upload_interval_;
- std::string server_;
-
- scoped_ptr<UploadService> upload_service_;
std::unique_ptr<weaved::Device> device_;
};
-#endif // METRICS_METRICS_DAEMON_H_
+#endif // METRICS_METRICS_COLLECTOR_H_
diff --git a/metricsd/metrics_collector.rc b/metricsd/metrics_collector.rc
new file mode 100644
index 0000000..2e7e0ae
--- /dev/null
+++ b/metricsd/metrics_collector.rc
@@ -0,0 +1,4 @@
+service metricscollector /system/bin/metrics_collector --foreground --logtosyslog
+ class late_start
+ user system
+ group system dbus
diff --git a/metricsd/metrics_collector_main.cc b/metricsd/metrics_collector_main.cc
new file mode 100644
index 0000000..d7aaaf5
--- /dev/null
+++ b/metricsd/metrics_collector_main.cc
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <base/at_exit.h>
+#include <base/command_line.h>
+#include <base/logging.h>
+#include <base/strings/string_util.h>
+#include <brillo/flag_helper.h>
+#include <brillo/syslog_logging.h>
+#include <rootdev.h>
+
+#include "constants.h"
+#include "metrics_collector.h"
+
+
+// Returns the path to the disk stats in the sysfs. Returns the null string if
+// it cannot find the disk stats file.
+static
+const std::string MetricsMainDiskStatsPath() {
+ char dev_path_cstr[PATH_MAX];
+ std::string dev_prefix = "/dev/block/";
+ std::string dev_path;
+
+ int ret = rootdev(dev_path_cstr, sizeof(dev_path_cstr), true, true);
+ if (ret != 0) {
+ LOG(WARNING) << "error " << ret << " determining root device";
+ return "";
+ }
+ dev_path = dev_path_cstr;
+ // Check that rootdev begins with "/dev/block/".
+ if (!base::StartsWithASCII(dev_path, dev_prefix, false)) {
+ LOG(WARNING) << "unexpected root device " << dev_path;
+ return "";
+ }
+ return "/sys/class/block/" + dev_path.substr(dev_prefix.length()) + "/stat";
+}
+
+int main(int argc, char** argv) {
+ DEFINE_bool(foreground, false, "Don't daemonize");
+
+ DEFINE_string(private_directory, metrics::kMetricsCollectorDirectory,
+ "Path to the private directory used by metrics_collector "
+ "(testing only)");
+ DEFINE_string(shared_directory, metrics::kSharedMetricsDirectory,
+ "Path to the shared metrics directory, used by "
+ "metrics_collector, metricsd and all metrics clients "
+ "(testing only)");
+
+ DEFINE_bool(logtostderr, false, "Log to standard error");
+ DEFINE_bool(logtosyslog, false, "Log to syslog");
+
+ brillo::FlagHelper::Init(argc, argv, "Chromium OS Metrics Daemon");
+
+ int logging_location = (FLAGS_foreground ? brillo::kLogToStderr
+ : brillo::kLogToSyslog);
+ if (FLAGS_logtosyslog)
+ logging_location = brillo::kLogToSyslog;
+
+ if (FLAGS_logtostderr)
+ logging_location = brillo::kLogToStderr;
+
+ // Also log to stderr when not running as daemon.
+ brillo::InitLog(logging_location | brillo::kLogHeader);
+
+ if (FLAGS_logtostderr && FLAGS_logtosyslog) {
+ LOG(ERROR) << "only one of --logtosyslog and --logtostderr can be set";
+ return 1;
+ }
+
+ if (!FLAGS_foreground && daemon(0, 0) != 0) {
+ return errno;
+ }
+
+ MetricsLibrary metrics_lib;
+ metrics_lib.InitWithNoCaching();
+ MetricsCollector daemon;
+ daemon.Init(false,
+ &metrics_lib,
+ MetricsMainDiskStatsPath(),
+ base::FilePath(FLAGS_private_directory),
+ base::FilePath(FLAGS_shared_directory));
+
+ daemon.Run();
+}
diff --git a/metricsd/metrics_collector_service_client.cc b/metricsd/metrics_collector_service_client.cc
new file mode 100644
index 0000000..08aaa4a
--- /dev/null
+++ b/metricsd/metrics_collector_service_client.cc
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Client interface to IMetricsCollectorService.
+
+#include "metrics/metrics_collector_service_client.h"
+
+#include <base/logging.h>
+#include <binder/IServiceManager.h>
+#include <utils/String16.h>
+
+#include "android/brillo/metrics/IMetricsCollectorService.h"
+
+namespace {
+const char kMetricsCollectorServiceName[] =
+ "android.brillo.metrics.IMetricsCollectorService";
+}
+
+bool MetricsCollectorServiceClient::Init() {
+ const android::String16 name(kMetricsCollectorServiceName);
+ metrics_collector_service_ = android::interface_cast<
+ android::brillo::metrics::IMetricsCollectorService>(
+ android::defaultServiceManager()->checkService(name));
+
+ if (metrics_collector_service_ == nullptr)
+ LOG(ERROR) << "Unable to lookup service " << kMetricsCollectorServiceName;
+
+ return metrics_collector_service_ != nullptr;
+}
+
+bool MetricsCollectorServiceClient::notifyUserCrash() {
+ if (metrics_collector_service_ == nullptr)
+ return false;
+
+ metrics_collector_service_->notifyUserCrash();
+ return true;
+}
diff --git a/metricsd/metrics_collector_service_impl.cc b/metricsd/metrics_collector_service_impl.cc
new file mode 100644
index 0000000..4d9a05a
--- /dev/null
+++ b/metricsd/metrics_collector_service_impl.cc
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "metrics_collector_service_impl.h"
+
+#include <binder/IServiceManager.h>
+#include <binder/Status.h>
+#include <utils/Errors.h>
+
+#include "metrics_collector.h"
+
+using namespace android;
+
+BnMetricsCollectorServiceImpl::BnMetricsCollectorServiceImpl(
+ MetricsCollector* metrics_collector)
+ : metrics_collector_(metrics_collector) {
+}
+
+android::binder::Status BnMetricsCollectorServiceImpl::notifyUserCrash() {
+ metrics_collector_->ProcessUserCrash();
+ return android::binder::Status::ok();
+}
diff --git a/metricsd/metrics_collector_service_impl.h b/metricsd/metrics_collector_service_impl.h
new file mode 100644
index 0000000..8db418a
--- /dev/null
+++ b/metricsd/metrics_collector_service_impl.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef METRICSD_METRICS_COLLECTOR_SERVICE_IMPL_H_
+#define METRICSD_METRICS_COLLECTOR_SERVICE_IMPL_H_
+
+// metrics_collector binder service implementation. Constructed by
+// MetricsCollector.
+
+#include "android/brillo/metrics/BnMetricsCollectorService.h"
+
+#include <binder/Status.h>
+
+class MetricsCollector;
+
+class BnMetricsCollectorServiceImpl
+ : public android::brillo::metrics::BnMetricsCollectorService {
+ public:
+ // Passed a this pointer from the MetricsCollector object that constructs us.
+ explicit BnMetricsCollectorServiceImpl(
+ MetricsCollector* metrics_collector_service);
+
+ virtual ~BnMetricsCollectorServiceImpl() = default;
+
+ // Called by crash_reporter to report a userspace crash event. We relay
+ // this to MetricsCollector.
+ android::binder::Status notifyUserCrash();
+
+ private:
+ // MetricsCollector object that constructs us, we use this to call back
+ // to it.
+ MetricsCollector* metrics_collector_;
+};
+
+#endif // METRICSD_METRICS_COLLECTOR_SERVICE_IMPL_H_
diff --git a/metricsd/metrics_daemon_test.cc b/metricsd/metrics_collector_test.cc
similarity index 66%
rename from metricsd/metrics_daemon_test.cc
rename to metricsd/metrics_collector_test.cc
index d3c9a23..5fb3ac8 100644
--- a/metricsd/metrics_daemon_test.cc
+++ b/metricsd/metrics_collector_test.cc
@@ -24,7 +24,7 @@
#include <gtest/gtest.h>
#include "constants.h"
-#include "metrics_daemon.h"
+#include "metrics_collector.h"
#include "metrics/metrics_library_mock.h"
#include "persistent_integer_mock.h"
@@ -40,29 +40,19 @@
using chromeos_metrics::PersistentIntegerMock;
-class MetricsDaemonTest : public testing::Test {
+class MetricsCollectorTest : public testing::Test {
protected:
virtual void SetUp() {
brillo::FlagHelper::Init(0, nullptr, "");
EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
- scaling_max_freq_path_ = temp_dir_.path().Append("scaling_max");
- cpu_max_freq_path_ = temp_dir_.path().Append("cpu_freq_max");
- CreateUint64ValueFile(cpu_max_freq_path_, 10000000);
- CreateUint64ValueFile(scaling_max_freq_path_, 10000000);
+ base::FilePath private_dir = temp_dir_.path().Append("private");
+ base::FilePath shared_dir = temp_dir_.path().Append("shared");
- chromeos_metrics::PersistentInteger::SetMetricsDirectory(
- temp_dir_.path().value());
- daemon_.Init(true,
- false,
- true,
- &metrics_lib_,
- "",
- scaling_max_freq_path_.value(),
- cpu_max_freq_path_.value(),
- base::TimeDelta::FromMinutes(30),
- metrics::kMetricsServer,
- temp_dir_.path());
+ EXPECT_TRUE(base::CreateDirectory(private_dir));
+ EXPECT_TRUE(base::CreateDirectory(shared_dir));
+
+ daemon_.Init(true, &metrics_lib_, "", private_dir, shared_dir);
}
// Adds a metrics library mock expectation that the specified metric
@@ -114,59 +104,24 @@
value_string.length()));
}
- // The MetricsDaemon under test.
- MetricsDaemon daemon_;
+ // The MetricsCollector under test.
+ MetricsCollector daemon_;
// Temporary directory used for tests.
base::ScopedTempDir temp_dir_;
- // Path for the fake files.
- base::FilePath scaling_max_freq_path_;
- base::FilePath cpu_max_freq_path_;
-
// Mocks. They are strict mock so that all unexpected
// calls are marked as failures.
StrictMock<MetricsLibraryMock> metrics_lib_;
};
-TEST_F(MetricsDaemonTest, MessageFilter) {
- // Ignore calls to SendToUMA.
- EXPECT_CALL(metrics_lib_, SendToUMA(_, _, _, _, _)).Times(AnyNumber());
-
- DBusMessage* msg = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_CALL);
- DBusHandlerResult res =
- MetricsDaemon::MessageFilter(/* connection */ nullptr, msg, &daemon_);
- EXPECT_EQ(DBUS_HANDLER_RESULT_NOT_YET_HANDLED, res);
- DeleteDBusMessage(msg);
-
- vector<string> signal_args;
- msg = NewDBusSignalString("/",
- "org.chromium.CrashReporter",
- "UserCrash",
- signal_args);
- res = MetricsDaemon::MessageFilter(/* connection */ nullptr, msg, &daemon_);
- EXPECT_EQ(DBUS_HANDLER_RESULT_HANDLED, res);
- DeleteDBusMessage(msg);
-
- signal_args.clear();
- signal_args.push_back("randomstate");
- signal_args.push_back("bob"); // arbitrary username
- msg = NewDBusSignalString("/",
- "org.chromium.UnknownService.Manager",
- "StateChanged",
- signal_args);
- res = MetricsDaemon::MessageFilter(/* connection */ nullptr, msg, &daemon_);
- EXPECT_EQ(DBUS_HANDLER_RESULT_NOT_YET_HANDLED, res);
- DeleteDBusMessage(msg);
-}
-
-TEST_F(MetricsDaemonTest, SendSample) {
+TEST_F(MetricsCollectorTest, SendSample) {
ExpectSample("Dummy.Metric", 3);
daemon_.SendSample("Dummy.Metric", /* sample */ 3,
/* min */ 1, /* max */ 100, /* buckets */ 50);
}
-TEST_F(MetricsDaemonTest, ProcessMeminfo) {
+TEST_F(MetricsCollectorTest, ProcessMeminfo) {
string meminfo =
"MemTotal: 2000000 kB\nMemFree: 500000 kB\n"
"Buffers: 1000000 kB\nCached: 213652 kB\n"
@@ -203,40 +158,13 @@
EXPECT_TRUE(daemon_.ProcessMeminfo(meminfo));
}
-TEST_F(MetricsDaemonTest, ProcessMeminfo2) {
+TEST_F(MetricsCollectorTest, ProcessMeminfo2) {
string meminfo = "MemTotal: 2000000 kB\nMemFree: 1000000 kB\n";
// Not enough fields.
EXPECT_FALSE(daemon_.ProcessMeminfo(meminfo));
}
-TEST_F(MetricsDaemonTest, ReadFreqToInt) {
- const int fake_scaled_freq = 1666999;
- const int fake_max_freq = 2000000;
- int scaled_freq = 0;
- int max_freq = 0;
- CreateUint64ValueFile(scaling_max_freq_path_, fake_scaled_freq);
- CreateUint64ValueFile(cpu_max_freq_path_, fake_max_freq);
- EXPECT_TRUE(daemon_.testing_);
- EXPECT_TRUE(daemon_.ReadFreqToInt(scaling_max_freq_path_.value(),
- &scaled_freq));
- EXPECT_TRUE(daemon_.ReadFreqToInt(cpu_max_freq_path_.value(), &max_freq));
- EXPECT_EQ(fake_scaled_freq, scaled_freq);
- EXPECT_EQ(fake_max_freq, max_freq);
-}
-
-TEST_F(MetricsDaemonTest, SendCpuThrottleMetrics) {
- CreateUint64ValueFile(cpu_max_freq_path_, 2001000);
- // Test the 101% and 100% cases.
- CreateUint64ValueFile(scaling_max_freq_path_, 2001000);
- EXPECT_TRUE(daemon_.testing_);
- EXPECT_CALL(metrics_lib_, SendEnumToUMA(_, 101, 101));
- daemon_.SendCpuThrottleMetrics();
- CreateUint64ValueFile(scaling_max_freq_path_, 2000000);
- EXPECT_CALL(metrics_lib_, SendEnumToUMA(_, 100, 101));
- daemon_.SendCpuThrottleMetrics();
-}
-
-TEST_F(MetricsDaemonTest, SendZramMetrics) {
+TEST_F(MetricsCollectorTest, SendZramMetrics) {
EXPECT_TRUE(daemon_.testing_);
// |compr_data_size| is the size in bytes of compressed data.
@@ -248,13 +176,13 @@
const uint64_t zero_pages = 10 * 1000 * 1000 / page_size;
CreateUint64ValueFile(
- temp_dir_.path().Append(MetricsDaemon::kComprDataSizeName),
+ temp_dir_.path().Append(MetricsCollector::kComprDataSizeName),
compr_data_size);
CreateUint64ValueFile(
- temp_dir_.path().Append(MetricsDaemon::kOrigDataSizeName),
+ temp_dir_.path().Append(MetricsCollector::kOrigDataSizeName),
orig_data_size);
CreateUint64ValueFile(
- temp_dir_.path().Append(MetricsDaemon::kZeroPagesName), zero_pages);
+ temp_dir_.path().Append(MetricsCollector::kZeroPagesName), zero_pages);
const uint64_t real_orig_size = orig_data_size + zero_pages * page_size;
const uint64_t zero_ratio_percent =
diff --git a/metricsd/metrics_daemon.cc b/metricsd/metrics_daemon.cc
deleted file mode 100644
index ed786e1..0000000
--- a/metricsd/metrics_daemon.cc
+++ /dev/null
@@ -1,945 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "metrics_daemon.h"
-
-#include <sysexits.h>
-#include <time.h>
-
-#include <base/bind.h>
-#include <base/files/file_path.h>
-#include <base/files/file_util.h>
-#include <base/hash.h>
-#include <base/logging.h>
-#include <base/strings/string_number_conversions.h>
-#include <base/strings/string_split.h>
-#include <base/strings/string_util.h>
-#include <base/strings/stringprintf.h>
-#include <brillo/osrelease_reader.h>
-#include <dbus/dbus.h>
-#include <dbus/message.h>
-
-#include "constants.h"
-#include "uploader/upload_service.h"
-
-using base::FilePath;
-using base::StringPrintf;
-using base::Time;
-using base::TimeDelta;
-using base::TimeTicks;
-using chromeos_metrics::PersistentInteger;
-using com::android::Weave::CommandProxy;
-using com::android::Weave::ManagerProxy;
-using std::map;
-using std::string;
-using std::vector;
-
-namespace {
-
-const char kCrashReporterInterface[] = "org.chromium.CrashReporter";
-const char kCrashReporterUserCrashSignal[] = "UserCrash";
-const char kCrashReporterMatchRule[] =
- "type='signal',interface='%s',path='/',member='%s'";
-
-const int kSecondsPerMinute = 60;
-const int kMinutesPerHour = 60;
-const int kHoursPerDay = 24;
-const int kMinutesPerDay = kHoursPerDay * kMinutesPerHour;
-const int kSecondsPerDay = kSecondsPerMinute * kMinutesPerDay;
-const int kDaysPerWeek = 7;
-const int kSecondsPerWeek = kSecondsPerDay * kDaysPerWeek;
-
-// Interval between calls to UpdateStats().
-const uint32_t kUpdateStatsIntervalMs = 300000;
-
-const char kKernelCrashDetectedFile[] = "/var/run/kernel-crash-detected";
-const char kUncleanShutdownDetectedFile[] =
- "/var/run/unclean-shutdown-detected";
-
-const int kMetricMeminfoInterval = 30; // seconds
-
-const char kMetricsProcStatFileName[] = "/proc/stat";
-const char kMeminfoFileName[] = "/proc/meminfo";
-const char kVmStatFileName[] = "/proc/vmstat";
-const int kMetricsProcStatFirstLineItemsCount = 11;
-
-// Thermal CPU throttling.
-
-const char kMetricScaledCpuFrequencyName[] =
- "Platform.CpuFrequencyThermalScaling";
-
-} // namespace
-
-// Zram sysfs entries.
-
-const char MetricsDaemon::kComprDataSizeName[] = "compr_data_size";
-const char MetricsDaemon::kOrigDataSizeName[] = "orig_data_size";
-const char MetricsDaemon::kZeroPagesName[] = "zero_pages";
-
-// Memory use stats collection intervals. We collect some memory use interval
-// at these intervals after boot, and we stop collecting after the last one,
-// with the assumption that in most cases the memory use won't change much
-// after that.
-static const int kMemuseIntervals[] = {
- 1 * kSecondsPerMinute, // 1 minute mark
- 4 * kSecondsPerMinute, // 5 minute mark
- 25 * kSecondsPerMinute, // 0.5 hour mark
- 120 * kSecondsPerMinute, // 2.5 hour mark
- 600 * kSecondsPerMinute, // 12.5 hour mark
-};
-
-MetricsDaemon::MetricsDaemon()
- : memuse_final_time_(0),
- memuse_interval_index_(0),
- ticks_per_second_(0),
- latest_cpu_use_ticks_(0) {}
-
-MetricsDaemon::~MetricsDaemon() {
-}
-
-// static
-double MetricsDaemon::GetActiveTime() {
- struct timespec ts;
- int r = clock_gettime(CLOCK_MONOTONIC, &ts);
- if (r < 0) {
- PLOG(WARNING) << "clock_gettime(CLOCK_MONOTONIC) failed";
- return 0;
- } else {
- return ts.tv_sec + static_cast<double>(ts.tv_nsec) / (1000 * 1000 * 1000);
- }
-}
-
-int MetricsDaemon::Run() {
- if (CheckSystemCrash(kKernelCrashDetectedFile)) {
- ProcessKernelCrash();
- }
-
- if (CheckSystemCrash(kUncleanShutdownDetectedFile)) {
- ProcessUncleanShutdown();
- }
-
- // On OS version change, clear version stats (which are reported daily).
- int32_t version = GetOsVersionHash();
- if (version_cycle_->Get() != version) {
- version_cycle_->Set(version);
- kernel_crashes_version_count_->Set(0);
- version_cumulative_active_use_->Set(0);
- version_cumulative_cpu_use_->Set(0);
- }
-
- return brillo::DBusDaemon::Run();
-}
-
-void MetricsDaemon::RunUploaderTest() {
- upload_service_.reset(new UploadService(
- new SystemProfileCache(true, metrics_directory_),
- metrics_lib_,
- server_));
- upload_service_->Init(upload_interval_, metrics_directory_);
- upload_service_->UploadEvent();
-}
-
-uint32_t MetricsDaemon::GetOsVersionHash() {
- brillo::OsReleaseReader reader;
- reader.Load();
- string version;
- if (!reader.GetString(metrics::kProductVersion, &version)) {
- LOG(ERROR) << "failed to read the product version.";
- version = metrics::kDefaultVersion;
- }
-
- uint32_t version_hash = base::Hash(version);
- if (testing_) {
- version_hash = 42; // return any plausible value for the hash
- }
- return version_hash;
-}
-
-void MetricsDaemon::Init(bool testing,
- bool uploader_active,
- bool dbus_enabled,
- MetricsLibraryInterface* metrics_lib,
- const string& diskstats_path,
- const string& scaling_max_freq_path,
- const string& cpuinfo_max_freq_path,
- const base::TimeDelta& upload_interval,
- const string& server,
- const base::FilePath& metrics_directory) {
- CHECK(metrics_lib);
- testing_ = testing;
- uploader_active_ = uploader_active;
- dbus_enabled_ = dbus_enabled;
- metrics_directory_ = metrics_directory;
- metrics_lib_ = metrics_lib;
-
- upload_interval_ = upload_interval;
- server_ = server;
-
- // Get ticks per second (HZ) on this system.
- // Sysconf cannot fail, so no sanity checks are needed.
- ticks_per_second_ = sysconf(_SC_CLK_TCK);
-
- daily_active_use_.reset(
- new PersistentInteger("Platform.UseTime.PerDay"));
- version_cumulative_active_use_.reset(
- new PersistentInteger("Platform.CumulativeUseTime"));
- version_cumulative_cpu_use_.reset(
- new PersistentInteger("Platform.CumulativeCpuTime"));
-
- kernel_crash_interval_.reset(
- new PersistentInteger("Platform.KernelCrashInterval"));
- unclean_shutdown_interval_.reset(
- new PersistentInteger("Platform.UncleanShutdownInterval"));
- user_crash_interval_.reset(
- new PersistentInteger("Platform.UserCrashInterval"));
-
- any_crashes_daily_count_.reset(
- new PersistentInteger("Platform.AnyCrashes.PerDay"));
- any_crashes_weekly_count_.reset(
- new PersistentInteger("Platform.AnyCrashes.PerWeek"));
- user_crashes_daily_count_.reset(
- new PersistentInteger("Platform.UserCrashes.PerDay"));
- user_crashes_weekly_count_.reset(
- new PersistentInteger("Platform.UserCrashes.PerWeek"));
- kernel_crashes_daily_count_.reset(
- new PersistentInteger("Platform.KernelCrashes.PerDay"));
- kernel_crashes_weekly_count_.reset(
- new PersistentInteger("Platform.KernelCrashes.PerWeek"));
- kernel_crashes_version_count_.reset(
- new PersistentInteger("Platform.KernelCrashesSinceUpdate"));
- unclean_shutdowns_daily_count_.reset(
- new PersistentInteger("Platform.UncleanShutdown.PerDay"));
- unclean_shutdowns_weekly_count_.reset(
- new PersistentInteger("Platform.UncleanShutdowns.PerWeek"));
-
- daily_cycle_.reset(new PersistentInteger("daily.cycle"));
- weekly_cycle_.reset(new PersistentInteger("weekly.cycle"));
- version_cycle_.reset(new PersistentInteger("version.cycle"));
-
- scaling_max_freq_path_ = scaling_max_freq_path;
- cpuinfo_max_freq_path_ = cpuinfo_max_freq_path;
- disk_usage_collector_.reset(new DiskUsageCollector(metrics_lib_));
- averaged_stats_collector_.reset(
- new AveragedStatisticsCollector(metrics_lib_, diskstats_path,
- kVmStatFileName));
-}
-
-int MetricsDaemon::OnInit() {
- int return_code = dbus_enabled_ ? brillo::DBusDaemon::OnInit() :
- brillo::Daemon::OnInit();
- if (return_code != EX_OK)
- return return_code;
-
- StatsReporterInit();
-
- // Start collecting meminfo stats.
- ScheduleMeminfoCallback(kMetricMeminfoInterval);
- memuse_final_time_ = GetActiveTime() + kMemuseIntervals[0];
- ScheduleMemuseCallback(kMemuseIntervals[0]);
-
- if (testing_)
- return EX_OK;
-
- if (dbus_enabled_) {
- bus_->AssertOnDBusThread();
- CHECK(bus_->SetUpAsyncOperations());
-
- if (bus_->is_connected()) {
- const std::string match_rule =
- base::StringPrintf(kCrashReporterMatchRule,
- kCrashReporterInterface,
- kCrashReporterUserCrashSignal);
-
- bus_->AddFilterFunction(&MetricsDaemon::MessageFilter, this);
-
- DBusError error;
- dbus_error_init(&error);
- bus_->AddMatch(match_rule, &error);
-
- if (dbus_error_is_set(&error)) {
- LOG(ERROR) << "Failed to add match rule \"" << match_rule << "\". Got "
- << error.name << ": " << error.message;
- return EX_SOFTWARE;
- }
- } else {
- LOG(ERROR) << "DBus isn't connected.";
- return EX_UNAVAILABLE;
- }
-
- device_ = weaved::Device::CreateInstance(
- bus_,
- base::Bind(&MetricsDaemon::UpdateWeaveState, base::Unretained(this)));
- device_->AddCommandHandler(
- "_metrics._enableAnalyticsReporting",
- base::Bind(&MetricsDaemon::OnEnableMetrics, base::Unretained(this)));
- device_->AddCommandHandler(
- "_metrics._disableAnalyticsReporting",
- base::Bind(&MetricsDaemon::OnDisableMetrics, base::Unretained(this)));
- }
-
- base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
- base::Bind(&MetricsDaemon::HandleUpdateStatsTimeout,
- base::Unretained(this)),
- base::TimeDelta::FromMilliseconds(kUpdateStatsIntervalMs));
-
- if (uploader_active_) {
- upload_service_.reset(
- new UploadService(new SystemProfileCache(), metrics_lib_, server_));
- upload_service_->Init(upload_interval_, metrics_directory_);
- }
-
- return EX_OK;
-}
-
-void MetricsDaemon::OnShutdown(int* return_code) {
- if (!testing_ && dbus_enabled_ && bus_->is_connected()) {
- const std::string match_rule =
- base::StringPrintf(kCrashReporterMatchRule,
- kCrashReporterInterface,
- kCrashReporterUserCrashSignal);
-
- bus_->RemoveFilterFunction(&MetricsDaemon::MessageFilter, this);
-
- DBusError error;
- dbus_error_init(&error);
- bus_->RemoveMatch(match_rule, &error);
-
- if (dbus_error_is_set(&error)) {
- LOG(ERROR) << "Failed to remove match rule \"" << match_rule << "\". Got "
- << error.name << ": " << error.message;
- }
- }
- brillo::DBusDaemon::OnShutdown(return_code);
-}
-
-void MetricsDaemon::OnEnableMetrics(const std::weak_ptr<weaved::Command>& cmd) {
- auto command = cmd.lock();
- if (!command)
- return;
-
- if (base::WriteFile(metrics_directory_.Append(metrics::kConsentFileName),
- "", 0) != 0) {
- PLOG(ERROR) << "Could not create the consent file.";
- command->Abort("metrics_error", "Could not create the consent file",
- nullptr);
- return;
- }
-
- UpdateWeaveState();
- command->Complete({}, nullptr);
-}
-
-void MetricsDaemon::OnDisableMetrics(
- const std::weak_ptr<weaved::Command>& cmd) {
- auto command = cmd.lock();
- if (!command)
- return;
-
- if (!base::DeleteFile(metrics_directory_.Append(metrics::kConsentFileName),
- false)) {
- PLOG(ERROR) << "Could not delete the consent file.";
- command->Abort("metrics_error", "Could not delete the consent file",
- nullptr);
- return;
- }
-
- UpdateWeaveState();
- command->Complete({}, nullptr);
-}
-
-void MetricsDaemon::UpdateWeaveState() {
- if (!device_)
- return;
-
- brillo::VariantDictionary state_change{
- { "_metrics._AnalyticsReportingState",
- metrics_lib_->AreMetricsEnabled() ? "enabled" : "disabled" }
- };
-
- if (!device_->SetStateProperties(state_change, nullptr)) {
- LOG(ERROR) << "failed to update weave's state";
- }
-}
-
-// static
-DBusHandlerResult MetricsDaemon::MessageFilter(DBusConnection* connection,
- DBusMessage* message,
- void* user_data) {
- int message_type = dbus_message_get_type(message);
- if (message_type != DBUS_MESSAGE_TYPE_SIGNAL) {
- DLOG(WARNING) << "unexpected message type " << message_type;
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
- }
-
- // Signal messages always have interfaces.
- const std::string interface(dbus_message_get_interface(message));
- const std::string member(dbus_message_get_member(message));
- DLOG(INFO) << "Got " << interface << "." << member << " D-Bus signal";
-
- MetricsDaemon* daemon = static_cast<MetricsDaemon*>(user_data);
-
- DBusMessageIter iter;
- dbus_message_iter_init(message, &iter);
- if (interface == kCrashReporterInterface) {
- CHECK_EQ(member, kCrashReporterUserCrashSignal);
- daemon->ProcessUserCrash();
- } else {
- // Ignore messages from the bus itself.
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
- }
-
- return DBUS_HANDLER_RESULT_HANDLED;
-}
-
-// One might argue that parts of this should go into
-// chromium/src/base/sys_info_chromeos.c instead, but put it here for now.
-
-TimeDelta MetricsDaemon::GetIncrementalCpuUse() {
- FilePath proc_stat_path = FilePath(kMetricsProcStatFileName);
- std::string proc_stat_string;
- if (!base::ReadFileToString(proc_stat_path, &proc_stat_string)) {
- LOG(WARNING) << "cannot open " << kMetricsProcStatFileName;
- return TimeDelta();
- }
-
- std::vector<std::string> proc_stat_lines;
- base::SplitString(proc_stat_string, '\n', &proc_stat_lines);
- if (proc_stat_lines.empty()) {
- LOG(WARNING) << "cannot parse " << kMetricsProcStatFileName
- << ": " << proc_stat_string;
- return TimeDelta();
- }
- std::vector<std::string> proc_stat_totals;
- base::SplitStringAlongWhitespace(proc_stat_lines[0], &proc_stat_totals);
-
- uint64_t user_ticks, user_nice_ticks, system_ticks;
- if (proc_stat_totals.size() != kMetricsProcStatFirstLineItemsCount ||
- proc_stat_totals[0] != "cpu" ||
- !base::StringToUint64(proc_stat_totals[1], &user_ticks) ||
- !base::StringToUint64(proc_stat_totals[2], &user_nice_ticks) ||
- !base::StringToUint64(proc_stat_totals[3], &system_ticks)) {
- LOG(WARNING) << "cannot parse first line: " << proc_stat_lines[0];
- return TimeDelta(base::TimeDelta::FromSeconds(0));
- }
-
- uint64_t total_cpu_use_ticks = user_ticks + user_nice_ticks + system_ticks;
-
- // Sanity check.
- if (total_cpu_use_ticks < latest_cpu_use_ticks_) {
- LOG(WARNING) << "CPU time decreasing from " << latest_cpu_use_ticks_
- << " to " << total_cpu_use_ticks;
- return TimeDelta();
- }
-
- uint64_t diff = total_cpu_use_ticks - latest_cpu_use_ticks_;
- latest_cpu_use_ticks_ = total_cpu_use_ticks;
- // Use microseconds to avoid significant truncations.
- return base::TimeDelta::FromMicroseconds(
- diff * 1000 * 1000 / ticks_per_second_);
-}
-
-void MetricsDaemon::ProcessUserCrash() {
- // Counts the active time up to now.
- UpdateStats(TimeTicks::Now(), Time::Now());
-
- // Reports the active use time since the last crash and resets it.
- SendAndResetCrashIntervalSample(user_crash_interval_);
-
- any_crashes_daily_count_->Add(1);
- any_crashes_weekly_count_->Add(1);
- user_crashes_daily_count_->Add(1);
- user_crashes_weekly_count_->Add(1);
-}
-
-void MetricsDaemon::ProcessKernelCrash() {
- // Counts the active time up to now.
- UpdateStats(TimeTicks::Now(), Time::Now());
-
- // Reports the active use time since the last crash and resets it.
- SendAndResetCrashIntervalSample(kernel_crash_interval_);
-
- any_crashes_daily_count_->Add(1);
- any_crashes_weekly_count_->Add(1);
- kernel_crashes_daily_count_->Add(1);
- kernel_crashes_weekly_count_->Add(1);
-
- kernel_crashes_version_count_->Add(1);
-}
-
-void MetricsDaemon::ProcessUncleanShutdown() {
- // Counts the active time up to now.
- UpdateStats(TimeTicks::Now(), Time::Now());
-
- // Reports the active use time since the last crash and resets it.
- SendAndResetCrashIntervalSample(unclean_shutdown_interval_);
-
- unclean_shutdowns_daily_count_->Add(1);
- unclean_shutdowns_weekly_count_->Add(1);
- any_crashes_daily_count_->Add(1);
- any_crashes_weekly_count_->Add(1);
-}
-
-bool MetricsDaemon::CheckSystemCrash(const string& crash_file) {
- FilePath crash_detected(crash_file);
- if (!base::PathExists(crash_detected))
- return false;
-
- // Deletes the crash-detected file so that the daemon doesn't report
- // another kernel crash in case it's restarted.
- base::DeleteFile(crash_detected, false); // not recursive
- return true;
-}
-
-void MetricsDaemon::StatsReporterInit() {
- disk_usage_collector_->Schedule();
-
- // Don't start a collection cycle during the first run to avoid delaying the
- // boot.
- averaged_stats_collector_->ScheduleWait();
-}
-
-
-bool MetricsDaemon::ReadFreqToInt(const string& sysfs_file_name, int* value) {
- const FilePath sysfs_path(sysfs_file_name);
- string value_string;
- if (!base::ReadFileToString(sysfs_path, &value_string)) {
- LOG(WARNING) << "cannot read " << sysfs_path.value().c_str();
- return false;
- }
- if (!base::RemoveChars(value_string, "\n", &value_string)) {
- LOG(WARNING) << "no newline in " << value_string;
- // Continue even though the lack of newline is suspicious.
- }
- if (!base::StringToInt(value_string, value)) {
- LOG(WARNING) << "cannot convert " << value_string << " to int";
- return false;
- }
- return true;
-}
-
-void MetricsDaemon::SendCpuThrottleMetrics() {
- // |max_freq| is 0 only the first time through.
- static int max_freq = 0;
- if (max_freq == -1)
- // Give up, as sysfs did not report max_freq correctly.
- return;
- if (max_freq == 0 || testing_) {
- // One-time initialization of max_freq. (Every time when testing.)
- if (!ReadFreqToInt(cpuinfo_max_freq_path_, &max_freq)) {
- max_freq = -1;
- return;
- }
- if (max_freq == 0) {
- LOG(WARNING) << "sysfs reports 0 max CPU frequency\n";
- max_freq = -1;
- return;
- }
- if (max_freq % 10000 == 1000) {
- // Special case: system has turbo mode, and max non-turbo frequency is
- // max_freq - 1000. This relies on "normal" (non-turbo) frequencies
- // being multiples of (at least) 10 MHz. Although there is no guarantee
- // of this, it seems a fairly reasonable assumption. Otherwise we should
- // read scaling_available_frequencies, sort the frequencies, compare the
- // two highest ones, and check if they differ by 1000 (kHz) (and that's a
- // hack too, no telling when it will change).
- max_freq -= 1000;
- }
- }
- int scaled_freq = 0;
- if (!ReadFreqToInt(scaling_max_freq_path_, &scaled_freq))
- return;
- // Frequencies are in kHz. If scaled_freq > max_freq, turbo is on, but
- // scaled_freq is not the actual turbo frequency. We indicate this situation
- // with a 101% value.
- int percent = scaled_freq > max_freq ? 101 : scaled_freq / (max_freq / 100);
- SendLinearSample(kMetricScaledCpuFrequencyName, percent, 101, 102);
-}
-
-void MetricsDaemon::ScheduleMeminfoCallback(int wait) {
- if (testing_) {
- return;
- }
- base::TimeDelta waitDelta = base::TimeDelta::FromSeconds(wait);
- base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
- base::Bind(&MetricsDaemon::MeminfoCallback, base::Unretained(this),
- waitDelta),
- waitDelta);
-}
-
-void MetricsDaemon::MeminfoCallback(base::TimeDelta wait) {
- string meminfo_raw;
- const FilePath meminfo_path(kMeminfoFileName);
- if (!base::ReadFileToString(meminfo_path, &meminfo_raw)) {
- LOG(WARNING) << "cannot read " << meminfo_path.value().c_str();
- return;
- }
- // Make both calls even if the first one fails.
- if (ProcessMeminfo(meminfo_raw)) {
- base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
- base::Bind(&MetricsDaemon::MeminfoCallback, base::Unretained(this),
- wait),
- wait);
- }
-}
-
-// static
-bool MetricsDaemon::ReadFileToUint64(const base::FilePath& path,
- uint64_t* value) {
- std::string content;
- if (!base::ReadFileToString(path, &content)) {
- PLOG(WARNING) << "cannot read " << path.MaybeAsASCII();
- return false;
- }
- // Remove final newline.
- base::TrimWhitespaceASCII(content, base::TRIM_TRAILING, &content);
- if (!base::StringToUint64(content, value)) {
- LOG(WARNING) << "invalid integer: " << content;
- return false;
- }
- return true;
-}
-
-bool MetricsDaemon::ReportZram(const base::FilePath& zram_dir) {
- // Data sizes are in bytes. |zero_pages| is in number of pages.
- uint64_t compr_data_size, orig_data_size, zero_pages;
- const size_t page_size = 4096;
-
- if (!ReadFileToUint64(zram_dir.Append(kComprDataSizeName),
- &compr_data_size) ||
- !ReadFileToUint64(zram_dir.Append(kOrigDataSizeName), &orig_data_size) ||
- !ReadFileToUint64(zram_dir.Append(kZeroPagesName), &zero_pages)) {
- return false;
- }
-
- // |orig_data_size| does not include zero-filled pages.
- orig_data_size += zero_pages * page_size;
-
- const int compr_data_size_mb = compr_data_size >> 20;
- const int savings_mb = (orig_data_size - compr_data_size) >> 20;
- const int zero_ratio_percent = zero_pages * page_size * 100 / orig_data_size;
-
- // Report compressed size in megabytes. 100 MB or less has little impact.
- SendSample("Platform.ZramCompressedSize", compr_data_size_mb, 100, 4000, 50);
- SendSample("Platform.ZramSavings", savings_mb, 100, 4000, 50);
- // The compression ratio is multiplied by 100 for better resolution. The
- // ratios of interest are between 1 and 6 (100% and 600% as reported). We
- // don't want samples when very little memory is being compressed.
- if (compr_data_size_mb >= 1) {
- SendSample("Platform.ZramCompressionRatioPercent",
- orig_data_size * 100 / compr_data_size, 100, 600, 50);
- }
- // The values of interest for zero_pages are between 1MB and 1GB. The units
- // are number of pages.
- SendSample("Platform.ZramZeroPages", zero_pages, 256, 256 * 1024, 50);
- SendSample("Platform.ZramZeroRatioPercent", zero_ratio_percent, 1, 50, 50);
-
- return true;
-}
-
-bool MetricsDaemon::ProcessMeminfo(const string& meminfo_raw) {
- static const MeminfoRecord fields_array[] = {
- { "MemTotal", "MemTotal" }, // SPECIAL CASE: total system memory
- { "MemFree", "MemFree" },
- { "Buffers", "Buffers" },
- { "Cached", "Cached" },
- // { "SwapCached", "SwapCached" },
- { "Active", "Active" },
- { "Inactive", "Inactive" },
- { "ActiveAnon", "Active(anon)" },
- { "InactiveAnon", "Inactive(anon)" },
- { "ActiveFile" , "Active(file)" },
- { "InactiveFile", "Inactive(file)" },
- { "Unevictable", "Unevictable", kMeminfoOp_HistLog },
- // { "Mlocked", "Mlocked" },
- { "SwapTotal", "SwapTotal", kMeminfoOp_SwapTotal },
- { "SwapFree", "SwapFree", kMeminfoOp_SwapFree },
- // { "Dirty", "Dirty" },
- // { "Writeback", "Writeback" },
- { "AnonPages", "AnonPages" },
- { "Mapped", "Mapped" },
- { "Shmem", "Shmem", kMeminfoOp_HistLog },
- { "Slab", "Slab", kMeminfoOp_HistLog },
- // { "SReclaimable", "SReclaimable" },
- // { "SUnreclaim", "SUnreclaim" },
- };
- vector<MeminfoRecord> fields(fields_array,
- fields_array + arraysize(fields_array));
- if (!FillMeminfo(meminfo_raw, &fields)) {
- return false;
- }
- int total_memory = fields[0].value;
- if (total_memory == 0) {
- // this "cannot happen"
- LOG(WARNING) << "borked meminfo parser";
- return false;
- }
- int swap_total = 0;
- int swap_free = 0;
- // Send all fields retrieved, except total memory.
- for (unsigned int i = 1; i < fields.size(); i++) {
- string metrics_name = base::StringPrintf("Platform.Meminfo%s",
- fields[i].name);
- int percent;
- switch (fields[i].op) {
- case kMeminfoOp_HistPercent:
- // report value as percent of total memory
- percent = fields[i].value * 100 / total_memory;
- SendLinearSample(metrics_name, percent, 100, 101);
- break;
- case kMeminfoOp_HistLog:
- // report value in kbytes, log scale, 4Gb max
- SendSample(metrics_name, fields[i].value, 1, 4 * 1000 * 1000, 100);
- break;
- case kMeminfoOp_SwapTotal:
- swap_total = fields[i].value;
- case kMeminfoOp_SwapFree:
- swap_free = fields[i].value;
- break;
- }
- }
- if (swap_total > 0) {
- int swap_used = swap_total - swap_free;
- int swap_used_percent = swap_used * 100 / swap_total;
- SendSample("Platform.MeminfoSwapUsed", swap_used, 1, 8 * 1000 * 1000, 100);
- SendLinearSample("Platform.MeminfoSwapUsed.Percent", swap_used_percent,
- 100, 101);
- }
- return true;
-}
-
-bool MetricsDaemon::FillMeminfo(const string& meminfo_raw,
- vector<MeminfoRecord>* fields) {
- vector<string> lines;
- unsigned int nlines = Tokenize(meminfo_raw, "\n", &lines);
-
- // Scan meminfo output and collect field values. Each field name has to
- // match a meminfo entry (case insensitive) after removing non-alpha
- // characters from the entry.
- unsigned int ifield = 0;
- for (unsigned int iline = 0;
- iline < nlines && ifield < fields->size();
- iline++) {
- vector<string> tokens;
- Tokenize(lines[iline], ": ", &tokens);
- if (strcmp((*fields)[ifield].match, tokens[0].c_str()) == 0) {
- // Name matches. Parse value and save.
- if (!base::StringToInt(tokens[1], &(*fields)[ifield].value)) {
- LOG(WARNING) << "Cound not convert " << tokens[1] << " to int";
- return false;
- }
- ifield++;
- }
- }
- if (ifield < fields->size()) {
- // End of input reached while scanning.
- LOG(WARNING) << "cannot find field " << (*fields)[ifield].match
- << " and following";
- return false;
- }
- return true;
-}
-
-void MetricsDaemon::ScheduleMemuseCallback(double interval) {
- if (testing_) {
- return;
- }
- base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
- base::Bind(&MetricsDaemon::MemuseCallback, base::Unretained(this)),
- base::TimeDelta::FromSeconds(interval));
-}
-
-void MetricsDaemon::MemuseCallback() {
- // Since we only care about active time (i.e. uptime minus sleep time) but
- // the callbacks are driven by real time (uptime), we check if we should
- // reschedule this callback due to intervening sleep periods.
- double now = GetActiveTime();
- // Avoid intervals of less than one second.
- double remaining_time = ceil(memuse_final_time_ - now);
- if (remaining_time > 0) {
- ScheduleMemuseCallback(remaining_time);
- } else {
- // Report stats and advance the measurement interval unless there are
- // errors or we've completed the last interval.
- if (MemuseCallbackWork() &&
- memuse_interval_index_ < arraysize(kMemuseIntervals)) {
- double interval = kMemuseIntervals[memuse_interval_index_++];
- memuse_final_time_ = now + interval;
- ScheduleMemuseCallback(interval);
- }
- }
-}
-
-bool MetricsDaemon::MemuseCallbackWork() {
- string meminfo_raw;
- const FilePath meminfo_path(kMeminfoFileName);
- if (!base::ReadFileToString(meminfo_path, &meminfo_raw)) {
- LOG(WARNING) << "cannot read " << meminfo_path.value().c_str();
- return false;
- }
- return ProcessMemuse(meminfo_raw);
-}
-
-bool MetricsDaemon::ProcessMemuse(const string& meminfo_raw) {
- static const MeminfoRecord fields_array[] = {
- { "MemTotal", "MemTotal" }, // SPECIAL CASE: total system memory
- { "ActiveAnon", "Active(anon)" },
- { "InactiveAnon", "Inactive(anon)" },
- };
- vector<MeminfoRecord> fields(fields_array,
- fields_array + arraysize(fields_array));
- if (!FillMeminfo(meminfo_raw, &fields)) {
- return false;
- }
- int total = fields[0].value;
- int active_anon = fields[1].value;
- int inactive_anon = fields[2].value;
- if (total == 0) {
- // this "cannot happen"
- LOG(WARNING) << "borked meminfo parser";
- return false;
- }
- string metrics_name = base::StringPrintf("Platform.MemuseAnon%d",
- memuse_interval_index_);
- SendLinearSample(metrics_name, (active_anon + inactive_anon) * 100 / total,
- 100, 101);
- return true;
-}
-
-void MetricsDaemon::SendSample(const string& name, int sample,
- int min, int max, int nbuckets) {
- metrics_lib_->SendToUMA(name, sample, min, max, nbuckets);
-}
-
-void MetricsDaemon::SendKernelCrashesCumulativeCountStats() {
- // Report the number of crashes for this OS version, but don't clear the
- // counter. It is cleared elsewhere on version change.
- int64_t crashes_count = kernel_crashes_version_count_->Get();
- SendSample(kernel_crashes_version_count_->Name(),
- crashes_count,
- 1, // value of first bucket
- 500, // value of last bucket
- 100); // number of buckets
-
-
- int64_t cpu_use_ms = version_cumulative_cpu_use_->Get();
- SendSample(version_cumulative_cpu_use_->Name(),
- cpu_use_ms / 1000, // stat is in seconds
- 1, // device may be used very little...
- 8 * 1000 * 1000, // ... or a lot (a little over 90 days)
- 100);
-
- // On the first run after an autoupdate, cpu_use_ms and active_use_seconds
- // can be zero. Avoid division by zero.
- if (cpu_use_ms > 0) {
- // Send the crash frequency since update in number of crashes per CPU year.
- SendSample("Logging.KernelCrashesPerCpuYear",
- crashes_count * kSecondsPerDay * 365 * 1000 / cpu_use_ms,
- 1,
- 1000 * 1000, // about one crash every 30s of CPU time
- 100);
- }
-
- int64_t active_use_seconds = version_cumulative_active_use_->Get();
- if (active_use_seconds > 0) {
- SendSample(version_cumulative_active_use_->Name(),
- active_use_seconds,
- 1, // device may be used very little...
- 8 * 1000 * 1000, // ... or a lot (about 90 days)
- 100);
- // Same as above, but per year of active time.
- SendSample("Logging.KernelCrashesPerActiveYear",
- crashes_count * kSecondsPerDay * 365 / active_use_seconds,
- 1,
- 1000 * 1000, // about one crash every 30s of active time
- 100);
- }
-}
-
-void MetricsDaemon::SendAndResetDailyUseSample(
- const scoped_ptr<PersistentInteger>& use) {
- SendSample(use->Name(),
- use->GetAndClear(),
- 1, // value of first bucket
- kSecondsPerDay, // value of last bucket
- 50); // number of buckets
-}
-
-void MetricsDaemon::SendAndResetCrashIntervalSample(
- const scoped_ptr<PersistentInteger>& interval) {
- SendSample(interval->Name(),
- interval->GetAndClear(),
- 1, // value of first bucket
- 4 * kSecondsPerWeek, // value of last bucket
- 50); // number of buckets
-}
-
-void MetricsDaemon::SendAndResetCrashFrequencySample(
- const scoped_ptr<PersistentInteger>& frequency) {
- SendSample(frequency->Name(),
- frequency->GetAndClear(),
- 1, // value of first bucket
- 100, // value of last bucket
- 50); // number of buckets
-}
-
-void MetricsDaemon::SendLinearSample(const string& name, int sample,
- int max, int nbuckets) {
- // TODO(semenzato): add a proper linear histogram to the Chrome external
- // metrics API.
- LOG_IF(FATAL, nbuckets != max + 1) << "unsupported histogram scale";
- metrics_lib_->SendEnumToUMA(name, sample, max);
-}
-
-void MetricsDaemon::UpdateStats(TimeTicks now_ticks,
- Time now_wall_time) {
- const int elapsed_seconds = (now_ticks - last_update_stats_time_).InSeconds();
- daily_active_use_->Add(elapsed_seconds);
- version_cumulative_active_use_->Add(elapsed_seconds);
- user_crash_interval_->Add(elapsed_seconds);
- kernel_crash_interval_->Add(elapsed_seconds);
- version_cumulative_cpu_use_->Add(GetIncrementalCpuUse().InMilliseconds());
- last_update_stats_time_ = now_ticks;
-
- const TimeDelta since_epoch = now_wall_time - Time::UnixEpoch();
- const int day = since_epoch.InDays();
- const int week = day / 7;
-
- if (daily_cycle_->Get() != day) {
- daily_cycle_->Set(day);
- SendAndResetDailyUseSample(daily_active_use_);
- SendAndResetCrashFrequencySample(any_crashes_daily_count_);
- SendAndResetCrashFrequencySample(user_crashes_daily_count_);
- SendAndResetCrashFrequencySample(kernel_crashes_daily_count_);
- SendAndResetCrashFrequencySample(unclean_shutdowns_daily_count_);
- SendKernelCrashesCumulativeCountStats();
- }
-
- if (weekly_cycle_->Get() != week) {
- weekly_cycle_->Set(week);
- SendAndResetCrashFrequencySample(any_crashes_weekly_count_);
- SendAndResetCrashFrequencySample(user_crashes_weekly_count_);
- SendAndResetCrashFrequencySample(kernel_crashes_weekly_count_);
- SendAndResetCrashFrequencySample(unclean_shutdowns_weekly_count_);
- }
-}
-
-void MetricsDaemon::HandleUpdateStatsTimeout() {
- UpdateStats(TimeTicks::Now(), Time::Now());
- base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
- base::Bind(&MetricsDaemon::HandleUpdateStatsTimeout,
- base::Unretained(this)),
- base::TimeDelta::FromMilliseconds(kUpdateStatsIntervalMs));
-}
diff --git a/metricsd/metrics_daemon.rc b/metricsd/metrics_daemon.rc
deleted file mode 100644
index 0ee577e..0000000
--- a/metricsd/metrics_daemon.rc
+++ /dev/null
@@ -1,7 +0,0 @@
-on post-fs-data
- mkdir /data/misc/metrics 0770 system system
-
-service metrics_daemon /system/bin/metrics_daemon --uploader -nodaemon
- class late_start
- user system
- group system dbus inet
diff --git a/metricsd/metrics_daemon_main.cc b/metricsd/metrics_daemon_main.cc
deleted file mode 100644
index 50c279d..0000000
--- a/metricsd/metrics_daemon_main.cc
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <base/at_exit.h>
-#include <base/command_line.h>
-#include <base/logging.h>
-#include <base/strings/string_util.h>
-#include <brillo/flag_helper.h>
-#include <brillo/syslog_logging.h>
-#include <rootdev.h>
-
-#include "constants.h"
-#include "metrics_daemon.h"
-
-const char kScalingMaxFreqPath[] =
- "/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq";
-const char kCpuinfoMaxFreqPath[] =
- "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq";
-
-// Returns the path to the disk stats in the sysfs. Returns the null string if
-// it cannot find the disk stats file.
-static
-const std::string MetricsMainDiskStatsPath() {
- char dev_path_cstr[PATH_MAX];
- std::string dev_prefix = "/dev/block/";
- std::string dev_path;
-
- int ret = rootdev(dev_path_cstr, sizeof(dev_path_cstr), true, true);
- if (ret != 0) {
- LOG(WARNING) << "error " << ret << " determining root device";
- return "";
- }
- dev_path = dev_path_cstr;
- // Check that rootdev begins with "/dev/block/".
- if (!base::StartsWithASCII(dev_path, dev_prefix, false)) {
- LOG(WARNING) << "unexpected root device " << dev_path;
- return "";
- }
- return "/sys/class/block/" + dev_path.substr(dev_prefix.length()) + "/stat";
-}
-
-int main(int argc, char** argv) {
- DEFINE_bool(daemon, true, "run as daemon (use -nodaemon for debugging)");
-
- // The uploader is disabled by default on ChromeOS as Chrome is responsible
- // for sending the metrics.
- DEFINE_bool(uploader, false, "activate the uploader");
-
- // Upload the metrics once and exit. (used for testing)
- DEFINE_bool(uploader_test,
- false,
- "run the uploader once and exit");
-
- // Enable dbus.
- DEFINE_bool(withdbus, true, "Enable dbus");
-
- // Upload Service flags.
- DEFINE_int32(upload_interval_secs,
- 1800,
- "Interval at which metrics_daemon sends the metrics. (needs "
- "-uploader)");
- DEFINE_string(server,
- metrics::kMetricsServer,
- "Server to upload the metrics to. (needs -uploader)");
- DEFINE_string(metrics_directory,
- metrics::kMetricsDirectory,
- "Root of the configuration files (testing only)");
-
- brillo::FlagHelper::Init(argc, argv, "Chromium OS Metrics Daemon");
-
- // Also log to stderr when not running as daemon.
- brillo::InitLog(brillo::kLogToSyslog | brillo::kLogHeader |
- (FLAGS_daemon ? 0 : brillo::kLogToStderr));
-
- if (FLAGS_daemon && daemon(0, 0) != 0) {
- return errno;
- }
-
- MetricsLibrary metrics_lib;
- metrics_lib.InitWithNoCaching();
- MetricsDaemon daemon;
- daemon.Init(FLAGS_uploader_test,
- FLAGS_uploader | FLAGS_uploader_test,
- FLAGS_withdbus,
- &metrics_lib,
- MetricsMainDiskStatsPath(),
- kScalingMaxFreqPath,
- kCpuinfoMaxFreqPath,
- base::TimeDelta::FromSeconds(FLAGS_upload_interval_secs),
- FLAGS_server,
- base::FilePath(FLAGS_metrics_directory));
-
- if (FLAGS_uploader_test) {
- daemon.RunUploaderTest();
- return 0;
- }
-
- daemon.Run();
-}
diff --git a/metricsd/metrics_library.cc b/metricsd/metrics_library.cc
index a651b76..d211ab4 100644
--- a/metricsd/metrics_library.cc
+++ b/metricsd/metrics_library.cc
@@ -18,19 +18,21 @@
#include <base/logging.h>
#include <base/strings/stringprintf.h>
+#include <binder/IServiceManager.h>
#include <errno.h>
#include <sys/file.h>
#include <sys/stat.h>
+#include <utils/String16.h>
#include <cstdio>
#include <cstring>
+#include "android/brillo/metrics/IMetricsd.h"
#include "constants.h"
-#include "serialization/metric_sample.h"
-#include "serialization/serialization_utils.h"
static const char kCrosEventHistogramName[] = "Platform.CrOSEvent";
static const int kCrosEventHistogramMax = 100;
+static const char kMetricsServiceName[] = "android.brillo.metrics.IMetricsd";
/* Add new cros events here.
*
@@ -53,6 +55,10 @@
"TPM.EarlyResetDuringCommand", // 12
};
+using android::binder::Status;
+using android::brillo::metrics::IMetricsd;
+using android::String16;
+
MetricsLibrary::MetricsLibrary() {}
MetricsLibrary::~MetricsLibrary() {}
@@ -123,6 +129,17 @@
return result && (access("/var/run/state/logged-in", F_OK) == 0);
}
+bool MetricsLibrary::CheckService() {
+ if (metricsd_proxy_.get() &&
+ android::IInterface::asBinder(metricsd_proxy_)->isBinderAlive())
+ return true;
+
+ const String16 name(kMetricsServiceName);
+ metricsd_proxy_ = android::interface_cast<IMetricsd>(
+ android::defaultServiceManager()->checkService(name));
+ return metricsd_proxy_.get();
+}
+
bool MetricsLibrary::AreMetricsEnabled() {
static struct stat stat_buffer;
time_t this_check_time = time(nullptr);
@@ -134,8 +151,7 @@
}
void MetricsLibrary::Init() {
- base::FilePath dir = base::FilePath(metrics::kMetricsDirectory);
- uma_events_file_ = dir.Append(metrics::kMetricsEventsFileName);
+ base::FilePath dir = base::FilePath(metrics::kSharedMetricsDirectory);
consent_file_ = dir.Append(metrics::kConsentFileName);
cached_enabled_ = false;
cached_enabled_time_ = 0;
@@ -148,47 +164,45 @@
}
void MetricsLibrary::InitForTest(const base::FilePath& metrics_directory) {
- uma_events_file_ = metrics_directory.Append(metrics::kMetricsEventsFileName);
consent_file_ = metrics_directory.Append(metrics::kConsentFileName);
cached_enabled_ = false;
cached_enabled_time_ = 0;
use_caching_ = true;
}
-bool MetricsLibrary::SendToUMA(const std::string& name,
- int sample,
- int min,
- int max,
- int nbuckets) {
- return metrics::SerializationUtils::WriteMetricToFile(
- *metrics::MetricSample::HistogramSample(name, sample, min, max, nbuckets)
- .get(),
- uma_events_file_.value());
+bool MetricsLibrary::SendToUMA(
+ const std::string& name, int sample, int min, int max, int nbuckets) {
+ return CheckService() &&
+ metricsd_proxy_->recordHistogram(String16(name.c_str()), sample, min,
+ max, nbuckets)
+ .isOk();
}
-bool MetricsLibrary::SendEnumToUMA(const std::string& name, int sample,
+bool MetricsLibrary::SendEnumToUMA(const std::string& name,
+ int sample,
int max) {
- return metrics::SerializationUtils::WriteMetricToFile(
- *metrics::MetricSample::LinearHistogramSample(name, sample, max).get(),
- uma_events_file_.value());
+ return CheckService() &&
+ metricsd_proxy_->recordLinearHistogram(String16(name.c_str()), sample,
+ max)
+ .isOk();
+}
+
+bool MetricsLibrary::SendBoolToUMA(const std::string& name, bool sample) {
+ return CheckService() &&
+ metricsd_proxy_->recordLinearHistogram(String16(name.c_str()),
+ sample ? 1 : 0, 2)
+ .isOk();
}
bool MetricsLibrary::SendSparseToUMA(const std::string& name, int sample) {
- return metrics::SerializationUtils::WriteMetricToFile(
- *metrics::MetricSample::SparseHistogramSample(name, sample).get(),
- uma_events_file_.value());
+ return CheckService() &&
+ metricsd_proxy_->recordSparseHistogram(String16(name.c_str()), sample)
+ .isOk();
}
-bool MetricsLibrary::SendUserActionToUMA(const std::string& action) {
- return metrics::SerializationUtils::WriteMetricToFile(
- *metrics::MetricSample::UserActionSample(action).get(),
- uma_events_file_.value());
-}
-
-bool MetricsLibrary::SendCrashToUMA(const char *crash_kind) {
- return metrics::SerializationUtils::WriteMetricToFile(
- *metrics::MetricSample::CrashSample(crash_kind).get(),
- uma_events_file_.value());
+bool MetricsLibrary::SendCrashToUMA(const char* crash_kind) {
+ return CheckService() &&
+ metricsd_proxy_->recordCrash(String16(crash_kind)).isOk();
}
bool MetricsLibrary::SendCrosEventToUMA(const std::string& event) {
@@ -199,3 +213,14 @@
}
return false;
}
+
+bool MetricsLibrary::GetHistogramsDump(std::string* dump) {
+ android::String16 temp_dump;
+ if (!CheckService() ||
+ !metricsd_proxy_->getHistogramsDump(&temp_dump).isOk()) {
+ return false;
+ }
+
+ *dump = android::String8(temp_dump).string();
+ return true;
+}
diff --git a/metricsd/metrics_library_test.cc b/metricsd/metrics_library_test.cc
index be8a4bb..52fcce3 100644
--- a/metricsd/metrics_library_test.cc
+++ b/metricsd/metrics_library_test.cc
@@ -29,7 +29,6 @@
virtual void SetUp() {
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
lib_.InitForTest(temp_dir_.path());
- EXPECT_EQ(0, WriteFile(lib_.uma_events_file_, "", 0));
// Defeat metrics enabled caching between tests.
lib_.cached_enabled_time_ = 0;
}
diff --git a/metricsd/metricsd.rc b/metricsd/metricsd.rc
new file mode 100644
index 0000000..359d0d1
--- /dev/null
+++ b/metricsd/metricsd.rc
@@ -0,0 +1,9 @@
+on post-fs-data
+ mkdir /data/misc/metrics 0770 system system
+ mkdir /data/misc/metricsd 0700 system system
+ mkdir /data/misc/metrics_collector 0700 system system
+
+service metricsd /system/bin/metricsd --foreground --logtosyslog
+ class late_start
+ user system
+ group system dbus inet
diff --git a/metricsd/metricsd_main.cc b/metricsd/metricsd_main.cc
new file mode 100644
index 0000000..f460268
--- /dev/null
+++ b/metricsd/metricsd_main.cc
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <thread>
+
+#include <base/at_exit.h>
+#include <base/command_line.h>
+#include <base/files/file_path.h>
+#include <base/logging.h>
+#include <base/metrics/statistics_recorder.h>
+#include <base/strings/string_util.h>
+#include <base/time/time.h>
+#include <brillo/flag_helper.h>
+#include <brillo/syslog_logging.h>
+
+#include "constants.h"
+#include "uploader/bn_metricsd_impl.h"
+#include "uploader/crash_counters.h"
+#include "uploader/upload_service.h"
+
+int main(int argc, char** argv) {
+ DEFINE_bool(foreground, false, "Don't daemonize");
+
+ // Upload the metrics once and exit. (used for testing)
+ DEFINE_bool(uploader_test, false, "run the uploader once and exit");
+
+ // Upload Service flags.
+ DEFINE_int32(upload_interval_secs, 1800,
+ "Interval at which metrics_daemon sends the metrics. (needs "
+ "-uploader)");
+ DEFINE_string(server, metrics::kMetricsServer,
+ "Server to upload the metrics to. (needs -uploader)");
+ DEFINE_string(private_directory, metrics::kMetricsdDirectory,
+ "Path to the private directory used by metricsd "
+ "(testing only)");
+ DEFINE_string(shared_directory, metrics::kSharedMetricsDirectory,
+ "Path to the shared metrics directory, used by "
+ "metrics_collector, metricsd and all metrics clients "
+ "(testing only)");
+
+ DEFINE_bool(logtostderr, false, "Log to standard error");
+ DEFINE_bool(logtosyslog, false, "Log to syslog");
+
+ brillo::FlagHelper::Init(argc, argv, "Brillo metrics daemon.");
+
+ int logging_location =
+ (FLAGS_foreground ? brillo::kLogToStderr : brillo::kLogToSyslog);
+ if (FLAGS_logtosyslog)
+ logging_location = brillo::kLogToSyslog;
+
+ if (FLAGS_logtostderr)
+ logging_location = brillo::kLogToStderr;
+
+ // Also log to stderr when not running as daemon.
+ brillo::InitLog(logging_location | brillo::kLogHeader);
+
+ if (FLAGS_logtostderr && FLAGS_logtosyslog) {
+ LOG(ERROR) << "only one of --logtosyslog and --logtostderr can be set";
+ return 1;
+ }
+
+ if (!FLAGS_foreground && daemon(0, 0) != 0) {
+ return errno;
+ }
+
+ std::shared_ptr<CrashCounters> counters(new CrashCounters);
+
+ UploadService upload_service(
+ FLAGS_server, base::TimeDelta::FromSeconds(FLAGS_upload_interval_secs),
+ base::FilePath(FLAGS_private_directory),
+ base::FilePath(FLAGS_shared_directory), counters);
+
+ base::StatisticsRecorder::Initialize();
+
+ // Create and start the binder thread.
+ BnMetricsdImpl binder_service(counters);
+ std::thread binder_thread(&BnMetricsdImpl::Run, &binder_service);
+
+ upload_service.Run();
+}
diff --git a/metricsd/persistent_integer.cc b/metricsd/persistent_integer.cc
index e849f00..7fe355e 100644
--- a/metricsd/persistent_integer.cc
+++ b/metricsd/persistent_integer.cc
@@ -22,21 +22,16 @@
#include <base/posix/eintr_wrapper.h>
#include "constants.h"
-#include "metrics/metrics_library.h"
-
namespace chromeos_metrics {
-// Static class member instantiation.
-std::string PersistentInteger::metrics_directory_ = metrics::kMetricsDirectory;
-
-PersistentInteger::PersistentInteger(const std::string& name) :
- value_(0),
+PersistentInteger::PersistentInteger(const std::string& name,
+ const base::FilePath& directory)
+ : value_(0),
version_(kVersion),
name_(name),
- synced_(false) {
- backing_file_name_ = metrics_directory_ + name_;
-}
+ backing_file_path_(directory.Append(name_)),
+ synced_(false) {}
PersistentInteger::~PersistentInteger() {}
@@ -63,23 +58,25 @@
}
void PersistentInteger::Write() {
- int fd = HANDLE_EINTR(open(backing_file_name_.c_str(),
+ int fd = HANDLE_EINTR(open(backing_file_path_.value().c_str(),
O_WRONLY | O_CREAT | O_TRUNC,
S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH));
- PCHECK(fd >= 0) << "cannot open " << backing_file_name_ << " for writing";
+ PCHECK(fd >= 0) << "cannot open " << backing_file_path_.value()
+ << " for writing";
PCHECK((HANDLE_EINTR(write(fd, &version_, sizeof(version_))) ==
sizeof(version_)) &&
(HANDLE_EINTR(write(fd, &value_, sizeof(value_))) ==
sizeof(value_)))
- << "cannot write to " << backing_file_name_;
+ << "cannot write to " << backing_file_path_.value();
close(fd);
synced_ = true;
}
bool PersistentInteger::Read() {
- int fd = HANDLE_EINTR(open(backing_file_name_.c_str(), O_RDONLY));
+ int fd = HANDLE_EINTR(open(backing_file_path_.value().c_str(), O_RDONLY));
if (fd < 0) {
- PLOG(WARNING) << "cannot open " << backing_file_name_ << " for reading";
+ PLOG(WARNING) << "cannot open " << backing_file_path_.value()
+ << " for reading";
return false;
}
int32_t version;
@@ -96,9 +93,4 @@
return read_succeeded;
}
-void PersistentInteger::SetMetricsDirectory(const std::string& directory) {
- metrics_directory_ = directory;
-}
-
-
} // namespace chromeos_metrics
diff --git a/metricsd/persistent_integer.h b/metricsd/persistent_integer.h
index ecef3d1..96d9fc0 100644
--- a/metricsd/persistent_integer.h
+++ b/metricsd/persistent_integer.h
@@ -21,6 +21,8 @@
#include <string>
+#include <base/files/file_path.h>
+
namespace chromeos_metrics {
// PersistentIntegers is a named 64-bit integer value backed by a file.
@@ -29,7 +31,7 @@
class PersistentInteger {
public:
- explicit PersistentInteger(const std::string& name);
+ PersistentInteger(const std::string& name, const base::FilePath& directory);
// Virtual only because of mock.
virtual ~PersistentInteger();
@@ -50,10 +52,6 @@
// Virtual only because of mock.
virtual void Add(int64_t x);
- // Sets the directory path for all persistent integers.
- // This is used in unittests to change where the counters are stored.
- static void SetMetricsDirectory(const std::string& directory);
-
private:
static const int kVersion = 1001;
@@ -68,8 +66,7 @@
int64_t value_;
int32_t version_;
std::string name_;
- std::string backing_file_name_;
- static std::string metrics_directory_;
+ base::FilePath backing_file_path_;
bool synced_;
};
diff --git a/metricsd/persistent_integer_mock.h b/metricsd/persistent_integer_mock.h
index acc5389..0be54d4 100644
--- a/metricsd/persistent_integer_mock.h
+++ b/metricsd/persistent_integer_mock.h
@@ -27,9 +27,10 @@
class PersistentIntegerMock : public PersistentInteger {
public:
- explicit PersistentIntegerMock(const std::string& name)
- : PersistentInteger(name) {}
- MOCK_METHOD1(Add, void(int64_t count));
+ explicit PersistentIntegerMock(const std::string& name,
+ const base::FilePath& directory)
+ : PersistentInteger(name, directory) {}
+ MOCK_METHOD1(Add, void(int64_t count));
};
} // namespace chromeos_metrics
diff --git a/metricsd/persistent_integer_test.cc b/metricsd/persistent_integer_test.cc
index 5e2067f..bf76261 100644
--- a/metricsd/persistent_integer_test.cc
+++ b/metricsd/persistent_integer_test.cc
@@ -14,17 +14,17 @@
* limitations under the License.
*/
-#include <gtest/gtest.h>
+#include <memory>
#include <base/compiler_specific.h>
#include <base/files/file_enumerator.h>
#include <base/files/file_util.h>
#include <base/files/scoped_temp_dir.h>
+#include <gtest/gtest.h>
#include "persistent_integer.h"
const char kBackingFileName[] = "1.pibakf";
-const char kBackingFilePattern[] = "*.pibakf";
using chromeos_metrics::PersistentInteger;
@@ -32,28 +32,15 @@
void SetUp() override {
// Set testing mode.
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
- chromeos_metrics::PersistentInteger::SetMetricsDirectory(
- temp_dir_.path().value());
}
- void TearDown() override {
- // Remove backing files. The convention is that they all end in ".pibakf".
- base::FileEnumerator f_enum(base::FilePath("."),
- false,
- base::FileEnumerator::FILES,
- FILE_PATH_LITERAL(kBackingFilePattern));
- for (base::FilePath name = f_enum.Next();
- !name.empty();
- name = f_enum.Next()) {
- base::DeleteFile(name, false);
- }
- }
-
+ protected:
base::ScopedTempDir temp_dir_;
};
TEST_F(PersistentIntegerTest, BasicChecks) {
- scoped_ptr<PersistentInteger> pi(new PersistentInteger(kBackingFileName));
+ std::unique_ptr<PersistentInteger> pi(
+ new PersistentInteger(kBackingFileName, temp_dir_.path()));
// Test initialization.
EXPECT_EQ(0, pi->Get());
@@ -65,7 +52,7 @@
EXPECT_EQ(5, pi->Get());
// Test persistence.
- pi.reset(new PersistentInteger(kBackingFileName));
+ pi.reset(new PersistentInteger(kBackingFileName, temp_dir_.path()));
EXPECT_EQ(5, pi->Get());
// Test GetAndClear.
@@ -73,6 +60,6 @@
EXPECT_EQ(pi->Get(), 0);
// Another persistence test.
- pi.reset(new PersistentInteger(kBackingFileName));
+ pi.reset(new PersistentInteger(kBackingFileName, temp_dir_.path()));
EXPECT_EQ(0, pi->Get());
}
diff --git a/metricsd/serialization/metric_sample.cc b/metricsd/serialization/metric_sample.cc
deleted file mode 100644
index 76a47c0..0000000
--- a/metricsd/serialization/metric_sample.cc
+++ /dev/null
@@ -1,209 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "serialization/metric_sample.h"
-
-#include <string>
-#include <vector>
-
-#include "base/logging.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_split.h"
-#include "base/strings/stringprintf.h"
-
-namespace metrics {
-
-MetricSample::MetricSample(MetricSample::SampleType sample_type,
- const std::string& metric_name,
- int sample,
- int min,
- int max,
- int bucket_count)
- : type_(sample_type),
- name_(metric_name),
- sample_(sample),
- min_(min),
- max_(max),
- bucket_count_(bucket_count) {
-}
-
-MetricSample::~MetricSample() {
-}
-
-bool MetricSample::IsValid() const {
- return name().find(' ') == std::string::npos &&
- name().find('\0') == std::string::npos && !name().empty();
-}
-
-std::string MetricSample::ToString() const {
- if (type_ == CRASH) {
- return base::StringPrintf("crash%c%s%c",
- '\0',
- name().c_str(),
- '\0');
- } else if (type_ == SPARSE_HISTOGRAM) {
- return base::StringPrintf("sparsehistogram%c%s %d%c",
- '\0',
- name().c_str(),
- sample_,
- '\0');
- } else if (type_ == LINEAR_HISTOGRAM) {
- return base::StringPrintf("linearhistogram%c%s %d %d%c",
- '\0',
- name().c_str(),
- sample_,
- max_,
- '\0');
- } else if (type_ == HISTOGRAM) {
- return base::StringPrintf("histogram%c%s %d %d %d %d%c",
- '\0',
- name().c_str(),
- sample_,
- min_,
- max_,
- bucket_count_,
- '\0');
- } else {
- // The type can only be USER_ACTION.
- CHECK_EQ(type_, USER_ACTION);
- return base::StringPrintf("useraction%c%s%c",
- '\0',
- name().c_str(),
- '\0');
- }
-}
-
-int MetricSample::sample() const {
- CHECK_NE(type_, USER_ACTION);
- CHECK_NE(type_, CRASH);
- return sample_;
-}
-
-int MetricSample::min() const {
- CHECK_EQ(type_, HISTOGRAM);
- return min_;
-}
-
-int MetricSample::max() const {
- CHECK_NE(type_, CRASH);
- CHECK_NE(type_, USER_ACTION);
- CHECK_NE(type_, SPARSE_HISTOGRAM);
- return max_;
-}
-
-int MetricSample::bucket_count() const {
- CHECK_EQ(type_, HISTOGRAM);
- return bucket_count_;
-}
-
-// static
-scoped_ptr<MetricSample> MetricSample::CrashSample(
- const std::string& crash_name) {
- return scoped_ptr<MetricSample>(
- new MetricSample(CRASH, crash_name, 0, 0, 0, 0));
-}
-
-// static
-scoped_ptr<MetricSample> MetricSample::HistogramSample(
- const std::string& histogram_name,
- int sample,
- int min,
- int max,
- int bucket_count) {
- return scoped_ptr<MetricSample>(new MetricSample(
- HISTOGRAM, histogram_name, sample, min, max, bucket_count));
-}
-
-// static
-scoped_ptr<MetricSample> MetricSample::ParseHistogram(
- const std::string& serialized_histogram) {
- std::vector<std::string> parts;
- base::SplitString(serialized_histogram, ' ', &parts);
-
- if (parts.size() != 5)
- return scoped_ptr<MetricSample>();
- int sample, min, max, bucket_count;
- if (parts[0].empty() || !base::StringToInt(parts[1], &sample) ||
- !base::StringToInt(parts[2], &min) ||
- !base::StringToInt(parts[3], &max) ||
- !base::StringToInt(parts[4], &bucket_count)) {
- return scoped_ptr<MetricSample>();
- }
-
- return HistogramSample(parts[0], sample, min, max, bucket_count);
-}
-
-// static
-scoped_ptr<MetricSample> MetricSample::SparseHistogramSample(
- const std::string& histogram_name,
- int sample) {
- return scoped_ptr<MetricSample>(
- new MetricSample(SPARSE_HISTOGRAM, histogram_name, sample, 0, 0, 0));
-}
-
-// static
-scoped_ptr<MetricSample> MetricSample::ParseSparseHistogram(
- const std::string& serialized_histogram) {
- std::vector<std::string> parts;
- base::SplitString(serialized_histogram, ' ', &parts);
- if (parts.size() != 2)
- return scoped_ptr<MetricSample>();
- int sample;
- if (parts[0].empty() || !base::StringToInt(parts[1], &sample))
- return scoped_ptr<MetricSample>();
-
- return SparseHistogramSample(parts[0], sample);
-}
-
-// static
-scoped_ptr<MetricSample> MetricSample::LinearHistogramSample(
- const std::string& histogram_name,
- int sample,
- int max) {
- return scoped_ptr<MetricSample>(
- new MetricSample(LINEAR_HISTOGRAM, histogram_name, sample, 0, max, 0));
-}
-
-// static
-scoped_ptr<MetricSample> MetricSample::ParseLinearHistogram(
- const std::string& serialized_histogram) {
- std::vector<std::string> parts;
- int sample, max;
- base::SplitString(serialized_histogram, ' ', &parts);
- if (parts.size() != 3)
- return scoped_ptr<MetricSample>();
- if (parts[0].empty() || !base::StringToInt(parts[1], &sample) ||
- !base::StringToInt(parts[2], &max)) {
- return scoped_ptr<MetricSample>();
- }
-
- return LinearHistogramSample(parts[0], sample, max);
-}
-
-// static
-scoped_ptr<MetricSample> MetricSample::UserActionSample(
- const std::string& action_name) {
- return scoped_ptr<MetricSample>(
- new MetricSample(USER_ACTION, action_name, 0, 0, 0, 0));
-}
-
-bool MetricSample::IsEqual(const MetricSample& metric) {
- return type_ == metric.type_ && name_ == metric.name_ &&
- sample_ == metric.sample_ && min_ == metric.min_ &&
- max_ == metric.max_ && bucket_count_ == metric.bucket_count_;
-}
-
-} // namespace metrics
diff --git a/metricsd/serialization/metric_sample.h b/metricsd/serialization/metric_sample.h
deleted file mode 100644
index 5a4e4ae..0000000
--- a/metricsd/serialization/metric_sample.h
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef METRICS_SERIALIZATION_METRIC_SAMPLE_H_
-#define METRICS_SERIALIZATION_METRIC_SAMPLE_H_
-
-#include <string>
-
-#include "base/gtest_prod_util.h"
-#include "base/macros.h"
-#include "base/memory/scoped_ptr.h"
-
-namespace metrics {
-
-// This class is used by libmetrics (ChromeOS) to serialize
-// and deserialize measurements to send them to a metrics sending service.
-// It is meant to be a simple container with serialization functions.
-class MetricSample {
- public:
- // Types of metric sample used.
- enum SampleType {
- CRASH,
- HISTOGRAM,
- LINEAR_HISTOGRAM,
- SPARSE_HISTOGRAM,
- USER_ACTION
- };
-
- ~MetricSample();
-
- // Returns true if the sample is valid (can be serialized without ambiguity).
- //
- // This function should be used to filter bad samples before serializing them.
- bool IsValid() const;
-
- // Getters for type and name. All types of metrics have these so we do not
- // need to check the type.
- SampleType type() const { return type_; }
- const std::string& name() const { return name_; }
-
- // Getters for sample, min, max, bucket_count.
- // Check the metric type to make sure the request make sense. (ex: a crash
- // sample does not have a bucket_count so we crash if we call bucket_count()
- // on it.)
- int sample() const;
- int min() const;
- int max() const;
- int bucket_count() const;
-
- // Returns a serialized version of the sample.
- //
- // The serialized message for each type is:
- // crash: crash\0|name_|\0
- // user action: useraction\0|name_|\0
- // histogram: histogram\0|name_| |sample_| |min_| |max_| |bucket_count_|\0
- // sparsehistogram: sparsehistogram\0|name_| |sample_|\0
- // linearhistogram: linearhistogram\0|name_| |sample_| |max_|\0
- std::string ToString() const;
-
- // Builds a crash sample.
- static scoped_ptr<MetricSample> CrashSample(const std::string& crash_name);
-
- // Builds a histogram sample.
- static scoped_ptr<MetricSample> HistogramSample(
- const std::string& histogram_name,
- int sample,
- int min,
- int max,
- int bucket_count);
- // Deserializes a histogram sample.
- static scoped_ptr<MetricSample> ParseHistogram(const std::string& serialized);
-
- // Builds a sparse histogram sample.
- static scoped_ptr<MetricSample> SparseHistogramSample(
- const std::string& histogram_name,
- int sample);
- // Deserializes a sparse histogram sample.
- static scoped_ptr<MetricSample> ParseSparseHistogram(
- const std::string& serialized);
-
- // Builds a linear histogram sample.
- static scoped_ptr<MetricSample> LinearHistogramSample(
- const std::string& histogram_name,
- int sample,
- int max);
- // Deserializes a linear histogram sample.
- static scoped_ptr<MetricSample> ParseLinearHistogram(
- const std::string& serialized);
-
- // Builds a user action sample.
- static scoped_ptr<MetricSample> UserActionSample(
- const std::string& action_name);
-
- // Returns true if sample and this object represent the same sample (type,
- // name, sample, min, max, bucket_count match).
- bool IsEqual(const MetricSample& sample);
-
- private:
- MetricSample(SampleType sample_type,
- const std::string& metric_name,
- const int sample,
- const int min,
- const int max,
- const int bucket_count);
-
- const SampleType type_;
- const std::string name_;
- const int sample_;
- const int min_;
- const int max_;
- const int bucket_count_;
-
- DISALLOW_COPY_AND_ASSIGN(MetricSample);
-};
-
-} // namespace metrics
-
-#endif // METRICS_SERIALIZATION_METRIC_SAMPLE_H_
diff --git a/metricsd/serialization/serialization_utils.cc b/metricsd/serialization/serialization_utils.cc
deleted file mode 100644
index 102c940..0000000
--- a/metricsd/serialization/serialization_utils.cc
+++ /dev/null
@@ -1,265 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "serialization/serialization_utils.h"
-
-#include <sys/file.h>
-
-#include <string>
-#include <vector>
-
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/files/scoped_file.h"
-#include "base/logging.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/memory/scoped_vector.h"
-#include "base/strings/string_split.h"
-#include "base/strings/string_util.h"
-#include "serialization/metric_sample.h"
-
-#define READ_WRITE_ALL_FILE_FLAGS \
- (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)
-
-namespace metrics {
-namespace {
-
-// Reads the next message from |file_descriptor| into |message|.
-//
-// |message| will be set to the empty string if no message could be read (EOF)
-// or the message was badly constructed.
-//
-// Returns false if no message can be read from this file anymore (EOF or
-// unrecoverable error).
-bool ReadMessage(int fd, std::string* message) {
- CHECK(message);
-
- int result;
- int32_t message_size;
- const int32_t message_hdr_size = sizeof(message_size);
- // The file containing the metrics do not leave the device so the writer and
- // the reader will always have the same endianness.
- result = HANDLE_EINTR(read(fd, &message_size, sizeof(message_size)));
- if (result < 0) {
- DPLOG(ERROR) << "reading metrics message header";
- return false;
- }
- if (result == 0) {
- // This indicates a normal EOF.
- return false;
- }
- if (result < message_hdr_size) {
- DLOG(ERROR) << "bad read size " << result << ", expecting "
- << sizeof(message_size);
- return false;
- }
-
- // kMessageMaxLength applies to the entire message: the 4-byte
- // length field and the content.
- if (message_size > SerializationUtils::kMessageMaxLength) {
- DLOG(ERROR) << "message too long : " << message_size;
- if (HANDLE_EINTR(lseek(fd, message_size - 4, SEEK_CUR)) == -1) {
- DLOG(ERROR) << "error while skipping message. abort";
- return false;
- }
- // Badly formatted message was skipped. Treat the badly formatted sample as
- // an empty sample.
- message->clear();
- return true;
- }
-
- if (message_size < message_hdr_size) {
- DLOG(ERROR) << "message too short : " << message_size;
- return false;
- }
-
- message_size -= message_hdr_size; // The message size includes itself.
- char buffer[SerializationUtils::kMessageMaxLength];
- if (!base::ReadFromFD(fd, buffer, message_size)) {
- DPLOG(ERROR) << "reading metrics message body";
- return false;
- }
- *message = std::string(buffer, message_size);
- return true;
-}
-
-
-// Opens the metrics log file at |filename| in the given |mode|.
-//
-// Returns the file descriptor wrapped in a valid ScopedFD on success.
-base::ScopedFD OpenMetricsFile(const std::string& filename, mode_t mode) {
- struct stat stat_buf;
- int result;
-
- result = stat(filename.c_str(), &stat_buf);
- if (result < 0) {
- if (errno != ENOENT)
- DPLOG(ERROR) << filename << ": bad metrics file stat";
-
- // Nothing to collect---try later.
- return base::ScopedFD();
- }
- if (stat_buf.st_size == 0) {
- // Also nothing to collect.
- return base::ScopedFD();
- }
- base::ScopedFD fd(open(filename.c_str(), mode));
- if (fd.get() < 0) {
- DPLOG(ERROR) << filename << ": cannot open";
- return base::ScopedFD();
- }
-
- return fd.Pass();
-}
-
-
-// Parses the contents of the metrics log file descriptor |fd| into |metrics|.
-void ReadAllMetricsFromFd(int fd, ScopedVector<MetricSample>* metrics) {
- for (;;) {
- std::string message;
-
- if (!ReadMessage(fd, &message))
- break;
-
- scoped_ptr<MetricSample> sample = SerializationUtils::ParseSample(message);
- if (sample)
- metrics->push_back(sample.release());
- }
-}
-
-} // namespace
-
-scoped_ptr<MetricSample> SerializationUtils::ParseSample(
- const std::string& sample) {
- if (sample.empty())
- return scoped_ptr<MetricSample>();
-
- std::vector<std::string> parts;
- base::SplitString(sample, '\0', &parts);
- // We should have two null terminated strings so split should produce
- // three chunks.
- if (parts.size() != 3) {
- DLOG(ERROR) << "splitting message on \\0 produced " << parts.size()
- << " parts (expected 3)";
- return scoped_ptr<MetricSample>();
- }
- const std::string& name = parts[0];
- const std::string& value = parts[1];
-
- if (base::LowerCaseEqualsASCII(name, "crash")) {
- return MetricSample::CrashSample(value);
- } else if (base::LowerCaseEqualsASCII(name, "histogram")) {
- return MetricSample::ParseHistogram(value);
- } else if (base::LowerCaseEqualsASCII(name, "linearhistogram")) {
- return MetricSample::ParseLinearHistogram(value);
- } else if (base::LowerCaseEqualsASCII(name, "sparsehistogram")) {
- return MetricSample::ParseSparseHistogram(value);
- } else if (base::LowerCaseEqualsASCII(name, "useraction")) {
- return MetricSample::UserActionSample(value);
- } else {
- DLOG(ERROR) << "invalid event type: " << name << ", value: " << value;
- }
- return scoped_ptr<MetricSample>();
-}
-
-void SerializationUtils::ReadMetricsFromFile(
- const std::string& filename,
- ScopedVector<MetricSample>* metrics) {
- base::ScopedFD fd(OpenMetricsFile(filename, O_RDONLY));
- if (!fd.is_valid()) {
- return;
- }
-
- // This processes all messages in the log.
- ReadAllMetricsFromFd(fd.get(), metrics);
-}
-
-void SerializationUtils::ReadAndTruncateMetricsFromFile(
- const std::string& filename,
- ScopedVector<MetricSample>* metrics) {
- base::ScopedFD fd(OpenMetricsFile(filename, O_RDWR));
- if (!fd.is_valid()) {
- return;
- }
-
- int result = flock(fd.get(), LOCK_EX);
- if (result < 0) {
- DPLOG(ERROR) << filename << ": cannot lock";
- return;
- }
-
- // This processes all messages in the log. When all messages are
- // read and processed, or an error occurs, truncate the file to zero size.
- ReadAllMetricsFromFd(fd.get(), metrics);
-
- result = ftruncate(fd.get(), 0);
- if (result < 0)
- DPLOG(ERROR) << "truncate metrics log";
-
- result = flock(fd.get(), LOCK_UN);
- if (result < 0)
- DPLOG(ERROR) << "unlock metrics log";
-}
-
-bool SerializationUtils::WriteMetricToFile(const MetricSample& sample,
- const std::string& filename) {
- if (!sample.IsValid())
- return false;
-
- base::ScopedFD file_descriptor(open(filename.c_str(),
- O_WRONLY | O_APPEND | O_CREAT,
- READ_WRITE_ALL_FILE_FLAGS));
-
- if (file_descriptor.get() < 0) {
- DPLOG(ERROR) << filename << ": cannot open";
- return false;
- }
-
- fchmod(file_descriptor.get(), READ_WRITE_ALL_FILE_FLAGS);
- // Grab a lock to avoid chrome truncating the file
- // underneath us. Keep the file locked as briefly as possible.
- // Freeing file_descriptor will close the file and and remove the lock.
- if (HANDLE_EINTR(flock(file_descriptor.get(), LOCK_EX)) < 0) {
- DPLOG(ERROR) << filename << ": cannot lock";
- return false;
- }
-
- std::string msg = sample.ToString();
- int32 size = msg.length() + sizeof(int32);
- if (size > kMessageMaxLength) {
- DLOG(ERROR) << "cannot write message: too long";
- return false;
- }
-
- // The file containing the metrics samples will only be read by programs on
- // the same device so we do not check endianness.
- if (!base::WriteFileDescriptor(file_descriptor.get(),
- reinterpret_cast<char*>(&size),
- sizeof(size))) {
- DPLOG(ERROR) << "error writing message length";
- return false;
- }
-
- if (!base::WriteFileDescriptor(
- file_descriptor.get(), msg.c_str(), msg.size())) {
- DPLOG(ERROR) << "error writing message";
- return false;
- }
-
- return true;
-}
-
-} // namespace metrics
diff --git a/metricsd/serialization/serialization_utils.h b/metricsd/serialization/serialization_utils.h
deleted file mode 100644
index 655652d..0000000
--- a/metricsd/serialization/serialization_utils.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef METRICS_SERIALIZATION_SERIALIZATION_UTILS_H_
-#define METRICS_SERIALIZATION_SERIALIZATION_UTILS_H_
-
-#include <string>
-
-#include "base/memory/scoped_ptr.h"
-#include "base/memory/scoped_vector.h"
-
-namespace metrics {
-
-class MetricSample;
-
-// Metrics helpers to serialize and deserialize metrics collected by
-// ChromeOS.
-namespace SerializationUtils {
-
-// Deserializes a sample passed as a string and return a sample.
-// The return value will either be a scoped_ptr to a Metric sample (if the
-// deserialization was successful) or a NULL scoped_ptr.
-scoped_ptr<MetricSample> ParseSample(const std::string& sample);
-
-// Reads all samples from a file. The file contents remain unchanged.
-void ReadMetricsFromFile(const std::string& filename,
- ScopedVector<MetricSample>* metrics);
-
-// Reads all samples from a file and truncates the file when done.
-void ReadAndTruncateMetricsFromFile(const std::string& filename,
- ScopedVector<MetricSample>* metrics);
-
-// Serializes a sample and write it to filename.
-// The format for the message is:
-// message_size, serialized_message
-// where
-// * message_size is the total length of the message (message_size +
-// serialized_message) on 4 bytes
-// * serialized_message is the serialized version of sample (using ToString)
-//
-// NB: the file will never leave the device so message_size will be written
-// with the architecture's endianness.
-bool WriteMetricToFile(const MetricSample& sample, const std::string& filename);
-
-// Maximum length of a serialized message
-static const int kMessageMaxLength = 1024;
-
-} // namespace SerializationUtils
-} // namespace metrics
-
-#endif // METRICS_SERIALIZATION_SERIALIZATION_UTILS_H_
diff --git a/metricsd/serialization/serialization_utils_unittest.cc b/metricsd/serialization/serialization_utils_unittest.cc
deleted file mode 100644
index 7a572de..0000000
--- a/metricsd/serialization/serialization_utils_unittest.cc
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "serialization/serialization_utils.h"
-
-#include <base/files/file_util.h>
-#include <base/files/scoped_temp_dir.h>
-#include <base/logging.h>
-#include <base/strings/stringprintf.h>
-#include <gtest/gtest.h>
-
-#include "serialization/metric_sample.h"
-
-namespace metrics {
-namespace {
-
-class SerializationUtilsTest : public testing::Test {
- protected:
- SerializationUtilsTest() {
- bool success = temporary_dir.CreateUniqueTempDir();
- if (success) {
- base::FilePath dir_path = temporary_dir.path();
- filename = dir_path.value() + "chromeossampletest";
- filepath = base::FilePath(filename);
- }
- }
-
- void SetUp() override { base::DeleteFile(filepath, false); }
-
- void TestSerialization(MetricSample* sample) {
- std::string serialized(sample->ToString());
- ASSERT_EQ('\0', serialized[serialized.length() - 1]);
- scoped_ptr<MetricSample> deserialized =
- SerializationUtils::ParseSample(serialized);
- ASSERT_TRUE(deserialized);
- EXPECT_TRUE(sample->IsEqual(*deserialized.get()));
- }
-
- std::string filename;
- base::ScopedTempDir temporary_dir;
- base::FilePath filepath;
-};
-
-TEST_F(SerializationUtilsTest, CrashSerializeTest) {
- TestSerialization(MetricSample::CrashSample("test").get());
-}
-
-TEST_F(SerializationUtilsTest, HistogramSerializeTest) {
- TestSerialization(
- MetricSample::HistogramSample("myhist", 13, 1, 100, 10).get());
-}
-
-TEST_F(SerializationUtilsTest, LinearSerializeTest) {
- TestSerialization(
- MetricSample::LinearHistogramSample("linearhist", 12, 30).get());
-}
-
-TEST_F(SerializationUtilsTest, SparseSerializeTest) {
- TestSerialization(MetricSample::SparseHistogramSample("mysparse", 30).get());
-}
-
-TEST_F(SerializationUtilsTest, UserActionSerializeTest) {
- TestSerialization(MetricSample::UserActionSample("myaction").get());
-}
-
-TEST_F(SerializationUtilsTest, IllegalNameAreFilteredTest) {
- scoped_ptr<MetricSample> sample1 =
- MetricSample::SparseHistogramSample("no space", 10);
- scoped_ptr<MetricSample> sample2 = MetricSample::LinearHistogramSample(
- base::StringPrintf("here%cbhe", '\0'), 1, 3);
-
- EXPECT_FALSE(SerializationUtils::WriteMetricToFile(*sample1.get(), filename));
- EXPECT_FALSE(SerializationUtils::WriteMetricToFile(*sample2.get(), filename));
- int64 size = 0;
-
- ASSERT_TRUE(!PathExists(filepath) || base::GetFileSize(filepath, &size));
-
- EXPECT_EQ(0, size);
-}
-
-TEST_F(SerializationUtilsTest, BadInputIsCaughtTest) {
- std::string input(
- base::StringPrintf("sparsehistogram%cname foo%c", '\0', '\0'));
- EXPECT_EQ(NULL, MetricSample::ParseSparseHistogram(input).get());
-}
-
-TEST_F(SerializationUtilsTest, MessageSeparatedByZero) {
- scoped_ptr<MetricSample> crash = MetricSample::CrashSample("mycrash");
-
- SerializationUtils::WriteMetricToFile(*crash.get(), filename);
- int64 size = 0;
- ASSERT_TRUE(base::GetFileSize(filepath, &size));
- // 4 bytes for the size
- // 5 bytes for crash
- // 7 bytes for mycrash
- // 2 bytes for the \0
- // -> total of 18
- EXPECT_EQ(size, 18);
-}
-
-TEST_F(SerializationUtilsTest, MessagesTooLongAreDiscardedTest) {
- // Creates a message that is bigger than the maximum allowed size.
- // As we are adding extra character (crash, \0s, etc), if the name is
- // kMessageMaxLength long, it will be too long.
- std::string name(SerializationUtils::kMessageMaxLength, 'c');
-
- scoped_ptr<MetricSample> crash = MetricSample::CrashSample(name);
- EXPECT_FALSE(SerializationUtils::WriteMetricToFile(*crash.get(), filename));
- int64 size = 0;
- ASSERT_TRUE(base::GetFileSize(filepath, &size));
- EXPECT_EQ(0, size);
-}
-
-TEST_F(SerializationUtilsTest, ReadLongMessageTest) {
- base::File test_file(filepath,
- base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_APPEND);
- std::string message(SerializationUtils::kMessageMaxLength + 1, 'c');
-
- int32 message_size = message.length() + sizeof(int32);
- test_file.WriteAtCurrentPos(reinterpret_cast<const char*>(&message_size),
- sizeof(message_size));
- test_file.WriteAtCurrentPos(message.c_str(), message.length());
- test_file.Close();
-
- scoped_ptr<MetricSample> crash = MetricSample::CrashSample("test");
- SerializationUtils::WriteMetricToFile(*crash.get(), filename);
-
- ScopedVector<MetricSample> samples;
- SerializationUtils::ReadAndTruncateMetricsFromFile(filename, &samples);
- ASSERT_EQ(size_t(1), samples.size());
- ASSERT_TRUE(samples[0] != NULL);
- EXPECT_TRUE(crash->IsEqual(*samples[0]));
-}
-
-TEST_F(SerializationUtilsTest, WriteReadTest) {
- scoped_ptr<MetricSample> hist =
- MetricSample::HistogramSample("myhist", 1, 2, 3, 4);
- scoped_ptr<MetricSample> crash = MetricSample::CrashSample("mycrash");
- scoped_ptr<MetricSample> lhist =
- MetricSample::LinearHistogramSample("linear", 1, 10);
- scoped_ptr<MetricSample> shist =
- MetricSample::SparseHistogramSample("mysparse", 30);
- scoped_ptr<MetricSample> action = MetricSample::UserActionSample("myaction");
-
- SerializationUtils::WriteMetricToFile(*hist.get(), filename);
- SerializationUtils::WriteMetricToFile(*crash.get(), filename);
- SerializationUtils::WriteMetricToFile(*lhist.get(), filename);
- SerializationUtils::WriteMetricToFile(*shist.get(), filename);
- SerializationUtils::WriteMetricToFile(*action.get(), filename);
- ScopedVector<MetricSample> vect;
- SerializationUtils::ReadAndTruncateMetricsFromFile(filename, &vect);
- ASSERT_EQ(vect.size(), size_t(5));
- for (int i = 0; i < 5; i++) {
- ASSERT_TRUE(vect[0] != NULL);
- }
- EXPECT_TRUE(hist->IsEqual(*vect[0]));
- EXPECT_TRUE(crash->IsEqual(*vect[1]));
- EXPECT_TRUE(lhist->IsEqual(*vect[2]));
- EXPECT_TRUE(shist->IsEqual(*vect[3]));
- EXPECT_TRUE(action->IsEqual(*vect[4]));
-
- int64 size = 0;
- ASSERT_TRUE(base::GetFileSize(filepath, &size));
- ASSERT_EQ(0, size);
-}
-
-} // namespace
-} // namespace metrics
diff --git a/metricsd/timer.cc b/metricsd/timer.cc
index 0c2c119..06fc336 100644
--- a/metricsd/timer.cc
+++ b/metricsd/timer.cc
@@ -18,8 +18,6 @@
#include <string>
-#include <base/memory/scoped_ptr.h>
-
#include "metrics/metrics_library.h"
namespace chromeos_metrics {
diff --git a/metricsd/timer_test.cc b/metricsd/timer_test.cc
index bc7a2a1..7a67e11 100644
--- a/metricsd/timer_test.cc
+++ b/metricsd/timer_test.cc
@@ -16,9 +16,9 @@
#include <stdint.h>
-#include <base/memory/scoped_ptr.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
+#include <memory>
#include "metrics/metrics_library_mock.h"
#include "metrics/timer.h"
@@ -61,7 +61,7 @@
virtual void TearDown() {}
Timer timer_;
- scoped_ptr<ClockWrapperMock> clock_wrapper_mock_;
+ std::unique_ptr<ClockWrapperMock> clock_wrapper_mock_;
base::TimeTicks stime, etime, stime2, etime2, stime3, etime3;
};
@@ -436,7 +436,7 @@
TimerReporter timer_reporter_;
MetricsLibraryMock lib_;
- scoped_ptr<ClockWrapperMock> clock_wrapper_mock_;
+ std::unique_ptr<ClockWrapperMock> clock_wrapper_mock_;
base::TimeTicks stime, etime;
};
diff --git a/metricsd/uploader/bn_metricsd_impl.cc b/metricsd/uploader/bn_metricsd_impl.cc
new file mode 100644
index 0000000..2cbc2da
--- /dev/null
+++ b/metricsd/uploader/bn_metricsd_impl.cc
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "uploader/bn_metricsd_impl.h"
+
+#include <base/metrics/histogram.h>
+#include <base/metrics/sparse_histogram.h>
+#include <base/metrics/statistics_recorder.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <utils/Errors.h>
+#include <utils/String16.h>
+#include <utils/String8.h>
+
+using android::binder::Status;
+using android::String16;
+
+static const char16_t kCrashTypeKernel[] = u"kernel";
+static const char16_t kCrashTypeUncleanShutdown[] = u"uncleanshutdown";
+static const char16_t kCrashTypeUser[] = u"user";
+
+BnMetricsdImpl::BnMetricsdImpl(const std::shared_ptr<CrashCounters>& counters)
+ : counters_(counters) {
+ CHECK(counters_) << "Invalid counters argument to constructor";
+}
+
+void BnMetricsdImpl::Run() {
+ android::status_t status =
+ android::defaultServiceManager()->addService(getInterfaceDescriptor(),
+ this);
+ CHECK(status == android::OK) << "Metricsd service registration failed";
+ android::ProcessState::self()->setThreadPoolMaxThreadCount(0);
+ android::IPCThreadState::self()->disableBackgroundScheduling(true);
+ android::IPCThreadState::self()->joinThreadPool();
+}
+
+Status BnMetricsdImpl::recordHistogram(
+ const String16& name, int sample, int min, int max, int nbuckets) {
+ base::HistogramBase* histogram = base::Histogram::FactoryGet(
+ android::String8(name).string(), min, max, nbuckets,
+ base::Histogram::kUmaTargetedHistogramFlag);
+ // |histogram| may be null if a client reports two contradicting histograms
+ // with the same name but different limits.
+ // FactoryGet will print a useful message if that is the case.
+ if (histogram) {
+ histogram->Add(sample);
+ }
+ return Status::ok();
+}
+
+Status BnMetricsdImpl::recordLinearHistogram(const String16& name,
+ int sample,
+ int max) {
+ base::HistogramBase* histogram = base::LinearHistogram::FactoryGet(
+ android::String8(name).string(), 1, max, max + 1,
+ base::Histogram::kUmaTargetedHistogramFlag);
+ // |histogram| may be null if a client reports two contradicting histograms
+ // with the same name but different limits.
+ // FactoryGet will print a useful message if that is the case.
+ if (histogram) {
+ histogram->Add(sample);
+ }
+ return Status::ok();
+}
+
+Status BnMetricsdImpl::recordSparseHistogram(const String16& name, int sample) {
+ base::HistogramBase* histogram = base::SparseHistogram::FactoryGet(
+ android::String8(name).string(),
+ base::Histogram::kUmaTargetedHistogramFlag);
+ // |histogram| may be null if a client reports two contradicting histograms
+ // with the same name but different limits.
+ // FactoryGet will print a useful message if that is the case.
+ if (histogram) {
+ histogram->Add(sample);
+ }
+ return Status::ok();
+}
+
+Status BnMetricsdImpl::recordCrash(const String16& type) {
+ if (type == kCrashTypeUser) {
+ counters_->IncrementUserCrashCount();
+ } else if (type == kCrashTypeKernel) {
+ counters_->IncrementKernelCrashCount();
+ } else if (type == kCrashTypeUncleanShutdown) {
+ counters_->IncrementUncleanShutdownCount();
+ } else {
+ LOG(ERROR) << "Unknown crash type received: " << type;
+ }
+ return Status::ok();
+}
+
+Status BnMetricsdImpl::getHistogramsDump(String16* dump) {
+ std::string str_dump;
+ base::StatisticsRecorder::WriteGraph(std::string(), &str_dump);
+ *dump = String16(str_dump.c_str());
+ return Status::ok();
+}
diff --git a/metricsd/uploader/bn_metricsd_impl.h b/metricsd/uploader/bn_metricsd_impl.h
new file mode 100644
index 0000000..016ccb6
--- /dev/null
+++ b/metricsd/uploader/bn_metricsd_impl.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef METRICSD_UPLOADER_BN_METRICSD_IMPL_H_
+#define METRICSD_UPLOADER_BN_METRICSD_IMPL_H_
+
+#include "android/brillo/metrics/BnMetricsd.h"
+#include "uploader/crash_counters.h"
+
+class BnMetricsdImpl : public android::brillo::metrics::BnMetricsd {
+ public:
+ explicit BnMetricsdImpl(const std::shared_ptr<CrashCounters>& counters);
+ virtual ~BnMetricsdImpl() = default;
+
+ // Starts the binder main loop.
+ void Run();
+
+ // Records a histogram.
+ android::binder::Status recordHistogram(const android::String16& name,
+ int sample,
+ int min,
+ int max,
+ int nbuckets) override;
+
+ // Records a linear histogram.
+ android::binder::Status recordLinearHistogram(const android::String16& name,
+ int sample,
+ int max) override;
+
+ // Records a sparse histogram.
+ android::binder::Status recordSparseHistogram(const android::String16& name,
+ int sample) override;
+
+ // Records a crash.
+ android::binder::Status recordCrash(const android::String16& type) override;
+
+ // Returns a dump of the histograms aggregated in memory.
+ android::binder::Status getHistogramsDump(android::String16* dump) override;
+
+ private:
+ std::shared_ptr<CrashCounters> counters_;
+};
+
+#endif // METRICSD_UPLOADER_BN_METRICSD_IMPL_H_
diff --git a/metricsd/uploader/crash_counters.cc b/metricsd/uploader/crash_counters.cc
new file mode 100644
index 0000000..1478b9a
--- /dev/null
+++ b/metricsd/uploader/crash_counters.cc
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "uploader/crash_counters.h"
+
+CrashCounters::CrashCounters()
+ : kernel_crashes_(0), unclean_shutdowns_(0), user_crashes_(0) {}
+
+void CrashCounters::IncrementKernelCrashCount() {
+ kernel_crashes_++;
+}
+
+unsigned int CrashCounters::GetAndResetKernelCrashCount() {
+ return kernel_crashes_.exchange(0);
+}
+
+void CrashCounters::IncrementUncleanShutdownCount() {
+ unclean_shutdowns_++;
+}
+
+unsigned int CrashCounters::GetAndResetUncleanShutdownCount() {
+ return unclean_shutdowns_.exchange(0);
+}
+
+void CrashCounters::IncrementUserCrashCount() {
+ user_crashes_++;
+}
+
+unsigned int CrashCounters::GetAndResetUserCrashCount() {
+ return user_crashes_.exchange(0);
+}
diff --git a/metricsd/uploader/crash_counters.h b/metricsd/uploader/crash_counters.h
new file mode 100644
index 0000000..3fdbf3f
--- /dev/null
+++ b/metricsd/uploader/crash_counters.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef METRICSD_UPLOADER_CRASH_COUNTERS_H_
+#define METRICSD_UPLOADER_CRASH_COUNTERS_H_
+
+#include <atomic>
+
+// This class is used to keep track of the crash counters.
+// An instance of it will be used by both the binder thread (to increment the
+// counters) and the uploader thread (to gather and reset the counters).
+// As such, the internal counters are atomic uints to allow concurrent access.
+class CrashCounters {
+ public:
+ CrashCounters();
+
+ void IncrementKernelCrashCount();
+ unsigned int GetAndResetKernelCrashCount();
+
+ void IncrementUserCrashCount();
+ unsigned int GetAndResetUserCrashCount();
+
+ void IncrementUncleanShutdownCount();
+ unsigned int GetAndResetUncleanShutdownCount();
+
+ private:
+ std::atomic_uint kernel_crashes_;
+ std::atomic_uint unclean_shutdowns_;
+ std::atomic_uint user_crashes_;
+};
+
+#endif // METRICSD_UPLOADER_CRASH_COUNTERS_H_
diff --git a/metricsd/uploader/metrics_log.cc b/metricsd/uploader/metrics_log.cc
index 1f16ca1..a01b5da 100644
--- a/metricsd/uploader/metrics_log.cc
+++ b/metricsd/uploader/metrics_log.cc
@@ -27,25 +27,25 @@
: MetricsLogBase("", 0, metrics::MetricsLogBase::ONGOING_LOG, "") {
}
-void MetricsLog::IncrementUserCrashCount() {
+void MetricsLog::IncrementUserCrashCount(unsigned int count) {
metrics::SystemProfileProto::Stability* stability(
uma_proto()->mutable_system_profile()->mutable_stability());
int current = stability->other_user_crash_count();
- stability->set_other_user_crash_count(current + 1);
+ stability->set_other_user_crash_count(current + count);
}
-void MetricsLog::IncrementKernelCrashCount() {
+void MetricsLog::IncrementKernelCrashCount(unsigned int count) {
metrics::SystemProfileProto::Stability* stability(
uma_proto()->mutable_system_profile()->mutable_stability());
int current = stability->kernel_crash_count();
- stability->set_kernel_crash_count(current + 1);
+ stability->set_kernel_crash_count(current + count);
}
-void MetricsLog::IncrementUncleanShutdownCount() {
+void MetricsLog::IncrementUncleanShutdownCount(unsigned int count) {
metrics::SystemProfileProto::Stability* stability(
uma_proto()->mutable_system_profile()->mutable_stability());
int current = stability->unclean_system_shutdown_count();
- stability->set_unclean_system_shutdown_count(current + 1);
+ stability->set_unclean_system_shutdown_count(current + count);
}
bool MetricsLog::PopulateSystemProfile(SystemProfileSetter* profile_setter) {
diff --git a/metricsd/uploader/metrics_log.h b/metricsd/uploader/metrics_log.h
index 5e09070..b76cd72 100644
--- a/metricsd/uploader/metrics_log.h
+++ b/metricsd/uploader/metrics_log.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef METRICS_UPLOADER_METRICS_LOG_H_
-#define METRICS_UPLOADER_METRICS_LOG_H_
+#ifndef METRICSD_UPLOADER_METRICS_LOG_H_
+#define METRICSD_UPLOADER_METRICS_LOG_H_
#include <string>
@@ -34,15 +34,20 @@
// SystemProfileSetter.
MetricsLog();
- void IncrementUserCrashCount();
- void IncrementKernelCrashCount();
- void IncrementUncleanShutdownCount();
+ // Increment the crash counters in the protobuf.
+ // These methods don't have to be thread safe as metrics logs are only
+ // accessed by the uploader thread.
+ void IncrementUserCrashCount(unsigned int count);
+ void IncrementKernelCrashCount(unsigned int count);
+ void IncrementUncleanShutdownCount(unsigned int count);
// Populate the system profile with system information using setter.
bool PopulateSystemProfile(SystemProfileSetter* setter);
private:
+ friend class UploadServiceTest;
FRIEND_TEST(UploadServiceTest, LogContainsAggregatedValues);
+ FRIEND_TEST(UploadServiceTest, LogContainsCrashCounts);
FRIEND_TEST(UploadServiceTest, LogKernelCrash);
FRIEND_TEST(UploadServiceTest, LogUncleanShutdown);
FRIEND_TEST(UploadServiceTest, LogUserCrash);
@@ -51,4 +56,4 @@
DISALLOW_COPY_AND_ASSIGN(MetricsLog);
};
-#endif // METRICS_UPLOADER_METRICS_LOG_H_
+#endif // METRICSD_UPLOADER_METRICS_LOG_H_
diff --git a/metricsd/uploader/system_profile_cache.cc b/metricsd/uploader/system_profile_cache.cc
index f7060a2..70f6afd 100644
--- a/metricsd/uploader/system_profile_cache.cc
+++ b/metricsd/uploader/system_profile_cache.cc
@@ -55,11 +55,10 @@
SystemProfileCache::SystemProfileCache()
: initialized_(false),
- testing_(false),
- metrics_directory_(metrics::kMetricsDirectory),
- session_id_(new chromeos_metrics::PersistentInteger(
- kPersistentSessionIdFilename)) {
-}
+ testing_(false),
+ metrics_directory_(metrics::kMetricsdDirectory),
+ session_id_(new chromeos_metrics::PersistentInteger(
+ kPersistentSessionIdFilename, metrics_directory_)) {}
SystemProfileCache::SystemProfileCache(bool testing,
const base::FilePath& metrics_directory)
@@ -67,8 +66,7 @@
testing_(testing),
metrics_directory_(metrics_directory),
session_id_(new chromeos_metrics::PersistentInteger(
- kPersistentSessionIdFilename)) {
-}
+ kPersistentSessionIdFilename, metrics_directory)) {}
bool SystemProfileCache::Initialize() {
CHECK(!initialized_)
@@ -84,10 +82,12 @@
auto client = update_engine::UpdateEngineClient::CreateInstance();
if (!client->GetChannel(&channel)) {
LOG(ERROR) << "failed to read the current channel from update engine.";
+ return false;
}
}
- if (!reader.GetString(metrics::kProductId, &profile_.product_id)) {
+ if (!reader.GetString(metrics::kProductId, &profile_.product_id)
+ || profile_.product_id.empty()) {
LOG(ERROR) << "product_id is not set.";
return false;
}
@@ -179,13 +179,13 @@
metrics::SystemProfileProto_Channel SystemProfileCache::ProtoChannelFromString(
const std::string& channel) {
- if (channel == "stable") {
+ if (channel == "stable-channel") {
return metrics::SystemProfileProto::CHANNEL_STABLE;
- } else if (channel == "dev") {
+ } else if (channel == "dev-channel") {
return metrics::SystemProfileProto::CHANNEL_DEV;
- } else if (channel == "beta") {
+ } else if (channel == "beta-channel") {
return metrics::SystemProfileProto::CHANNEL_BETA;
- } else if (channel == "canary") {
+ } else if (channel == "canary-channel") {
return metrics::SystemProfileProto::CHANNEL_CANARY;
}
diff --git a/metricsd/uploader/system_profile_cache.h b/metricsd/uploader/system_profile_cache.h
index ae54a2a..f9c484c 100644
--- a/metricsd/uploader/system_profile_cache.h
+++ b/metricsd/uploader/system_profile_cache.h
@@ -19,12 +19,12 @@
#include <stdint.h>
+#include <memory>
#include <string>
#include "base/compiler_specific.h"
#include "base/files/file_path.h"
#include "base/gtest_prod_util.h"
-#include "base/memory/scoped_ptr.h"
#include "persistent_integer.h"
#include "uploader/proto/system_profile.pb.h"
#include "uploader/system_profile_setter.h"
@@ -69,6 +69,7 @@
FRIEND_TEST(UploadServiceTest, ReadKeyValueFromFile);
FRIEND_TEST(UploadServiceTest, SessionIdIncrementedAtInitialization);
FRIEND_TEST(UploadServiceTest, ValuesInConfigFileAreSent);
+ FRIEND_TEST(UploadServiceTest, ProductIdMandatory);
// Fetches all informations and populates |profile_|
bool Initialize();
@@ -79,7 +80,7 @@
bool initialized_;
bool testing_;
base::FilePath metrics_directory_;
- scoped_ptr<chromeos_metrics::PersistentInteger> session_id_;
+ std::unique_ptr<chromeos_metrics::PersistentInteger> session_id_;
SystemProfile profile_;
};
diff --git a/metricsd/uploader/upload_service.cc b/metricsd/uploader/upload_service.cc
index b630cec..2fb30c3 100644
--- a/metricsd/uploader/upload_service.cc
+++ b/metricsd/uploader/upload_service.cc
@@ -16,6 +16,9 @@
#include "uploader/upload_service.h"
+#include <sysexits.h>
+
+#include <memory>
#include <string>
#include <base/bind.h>
@@ -31,46 +34,40 @@
#include <base/sha1.h>
#include "constants.h"
-#include "serialization/metric_sample.h"
-#include "serialization/serialization_utils.h"
#include "uploader/metrics_log.h"
#include "uploader/sender_http.h"
#include "uploader/system_profile_setter.h"
const int UploadService::kMaxFailedUpload = 10;
-UploadService::UploadService(SystemProfileSetter* setter,
- MetricsLibraryInterface* metrics_lib,
- const std::string& server)
- : system_profile_setter_(setter),
- metrics_lib_(metrics_lib),
- histogram_snapshot_manager_(this),
+UploadService::UploadService(const std::string& server,
+ const base::TimeDelta& upload_interval,
+ const base::FilePath& private_metrics_directory,
+ const base::FilePath& shared_metrics_directory,
+ const std::shared_ptr<CrashCounters> counters)
+ : histogram_snapshot_manager_(this),
sender_(new HttpSender(server)),
- failed_upload_count_(metrics::kFailedUploadCountName),
- testing_(false) {
+ failed_upload_count_(metrics::kFailedUploadCountName,
+ private_metrics_directory),
+ counters_(counters),
+ upload_interval_(upload_interval) {
+ staged_log_path_ = private_metrics_directory.Append(metrics::kStagedLogName);
+ consent_file_ = shared_metrics_directory.Append(metrics::kConsentFileName);
}
-UploadService::UploadService(SystemProfileSetter* setter,
- MetricsLibraryInterface* metrics_lib,
- const std::string& server,
- bool testing)
- : UploadService(setter, metrics_lib, server) {
- testing_ = testing;
+int UploadService::OnInit() {
+ system_profile_setter_.reset(new SystemProfileCache());
+
+ base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
+ base::Bind(&UploadService::UploadEventCallback,
+ base::Unretained(this),
+ upload_interval_),
+ upload_interval_);
+ return EX_OK;
}
-void UploadService::Init(const base::TimeDelta& upload_interval,
- const base::FilePath& metrics_directory) {
- base::StatisticsRecorder::Initialize();
- metrics_file_ = metrics_directory.Append(metrics::kMetricsEventsFileName);
- staged_log_path_ = metrics_directory.Append(metrics::kStagedLogName);
-
- if (!testing_) {
- base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
- base::Bind(&UploadService::UploadEventCallback,
- base::Unretained(this),
- upload_interval),
- upload_interval);
- }
+void UploadService::InitForTest(SystemProfileSetter* setter) {
+ system_profile_setter_.reset(setter);
}
void UploadService::StartNewLog() {
@@ -101,8 +98,7 @@
return;
}
- // Previous upload successful, reading metrics sample from the file.
- ReadMetrics();
+ // Previous upload successful, stage another log.
GatherHistograms();
StageCurrentLog();
@@ -114,7 +110,7 @@
void UploadService::SendStagedLog() {
// If metrics are not enabled, discard the log and exit.
- if (!metrics_lib_->AreMetricsEnabled()) {
+ if (!AreMetricsEnabled()) {
LOG(INFO) << "Metrics disabled. Don't upload metrics samples.";
base::DeleteFile(staged_log_path_, false);
return;
@@ -142,76 +138,24 @@
failed_upload_count_.Set(0);
}
-void UploadService::ReadMetrics() {
- CHECK(!HasStagedLog()) << "cannot read metrics until the old logs have been "
- << "discarded";
-
- ScopedVector<metrics::MetricSample> vector;
- metrics::SerializationUtils::ReadAndTruncateMetricsFromFile(
- metrics_file_.value(), &vector);
-
- int i = 0;
- for (ScopedVector<metrics::MetricSample>::iterator it = vector.begin();
- it != vector.end(); it++) {
- metrics::MetricSample* sample = *it;
- AddSample(*sample);
- i++;
- }
- VLOG(1) << i << " samples read";
-}
-
-void UploadService::AddSample(const metrics::MetricSample& sample) {
- base::HistogramBase* counter;
- switch (sample.type()) {
- case metrics::MetricSample::CRASH:
- AddCrash(sample.name());
- break;
- case metrics::MetricSample::HISTOGRAM:
- counter = base::Histogram::FactoryGet(
- sample.name(), sample.min(), sample.max(), sample.bucket_count(),
- base::Histogram::kUmaTargetedHistogramFlag);
- counter->Add(sample.sample());
- break;
- case metrics::MetricSample::SPARSE_HISTOGRAM:
- counter = base::SparseHistogram::FactoryGet(
- sample.name(), base::HistogramBase::kUmaTargetedHistogramFlag);
- counter->Add(sample.sample());
- break;
- case metrics::MetricSample::LINEAR_HISTOGRAM:
- counter = base::LinearHistogram::FactoryGet(
- sample.name(),
- 1,
- sample.max(),
- sample.max() + 1,
- base::Histogram::kUmaTargetedHistogramFlag);
- counter->Add(sample.sample());
- break;
- case metrics::MetricSample::USER_ACTION:
- GetOrCreateCurrentLog()->RecordUserAction(sample.name());
- break;
- default:
- break;
- }
-}
-
-void UploadService::AddCrash(const std::string& crash_name) {
- if (crash_name == "user") {
- GetOrCreateCurrentLog()->IncrementUserCrashCount();
- } else if (crash_name == "kernel") {
- GetOrCreateCurrentLog()->IncrementKernelCrashCount();
- } else if (crash_name == "uncleanshutdown") {
- GetOrCreateCurrentLog()->IncrementUncleanShutdownCount();
- } else {
- DLOG(ERROR) << "crash name unknown" << crash_name;
- }
-}
-
void UploadService::GatherHistograms() {
base::StatisticsRecorder::Histograms histograms;
base::StatisticsRecorder::GetHistograms(&histograms);
histogram_snapshot_manager_.PrepareDeltas(
base::Histogram::kNoFlags, base::Histogram::kUmaTargetedHistogramFlag);
+
+ // Gather and reset the crash counters, shared with the binder threads.
+ unsigned int kernel_crashes = counters_->GetAndResetKernelCrashCount();
+ unsigned int unclean_shutdowns = counters_->GetAndResetUncleanShutdownCount();
+ unsigned int user_crashes = counters_->GetAndResetUserCrashCount();
+
+ // Only create a log if the counters have changed.
+ if (kernel_crashes > 0 || unclean_shutdowns > 0 || user_crashes > 0) {
+ GetOrCreateCurrentLog()->IncrementKernelCrashCount(kernel_crashes);
+ GetOrCreateCurrentLog()->IncrementUncleanShutdownCount(unclean_shutdowns);
+ GetOrCreateCurrentLog()->IncrementUserCrashCount(user_crashes);
+ }
}
void UploadService::RecordDelta(const base::HistogramBase& histogram,
@@ -226,7 +170,7 @@
if (!current_log_)
return;
- scoped_ptr<MetricsLog> staged_log;
+ std::unique_ptr<MetricsLog> staged_log;
staged_log.swap(current_log_);
staged_log->CloseLog();
if (!staged_log->PopulateSystemProfile(system_profile_setter_.get())) {
@@ -263,3 +207,7 @@
failed_upload_count_.Set(0);
}
}
+
+bool UploadService::AreMetricsEnabled() {
+ return base::PathExists(consent_file_);
+}
diff --git a/metricsd/uploader/upload_service.h b/metricsd/uploader/upload_service.h
index 77df74b..1d36121 100644
--- a/metricsd/uploader/upload_service.h
+++ b/metricsd/uploader/upload_service.h
@@ -17,28 +17,21 @@
#ifndef METRICS_UPLOADER_UPLOAD_SERVICE_H_
#define METRICS_UPLOADER_UPLOAD_SERVICE_H_
+#include <memory>
#include <string>
-#include "base/metrics/histogram_base.h"
-#include "base/metrics/histogram_flattener.h"
-#include "base/metrics/histogram_snapshot_manager.h"
+#include <base/metrics/histogram_base.h>
+#include <base/metrics/histogram_flattener.h>
+#include <base/metrics/histogram_snapshot_manager.h>
+#include <brillo/daemons/daemon.h>
-#include "metrics/metrics_library.h"
#include "persistent_integer.h"
+#include "uploader/crash_counters.h"
#include "uploader/metrics_log.h"
+#include "uploader/proto/chrome_user_metrics_extension.pb.h"
#include "uploader/sender.h"
#include "uploader/system_profile_cache.h"
-namespace metrics {
-class ChromeUserMetricsExtension;
-class CrashSample;
-class HistogramSample;
-class LinearHistogramSample;
-class MetricSample;
-class SparseHistogramSample;
-class UserActionSample;
-}
-
class SystemProfileSetter;
// Service responsible for uploading the metrics periodically to the server.
@@ -57,24 +50,24 @@
// - if the upload is successful, we discard the log (therefore
// transitioning back to no staged log)
// - if the upload fails, we keep the log to try again later.
-// We do not try to read the metrics that are stored on
-// the disk as we want to avoid storing the metrics in memory.
//
// * if no staged logs are present:
-// Read all metrics from the disk, aggregate them and try to send them.
+// Take a snapshot of the aggregated metrics, save it to disk and try to send
+// it:
// - if the upload succeeds, we discard the staged log (transitioning back
// to the no staged log state)
-// - if the upload fails, we keep the staged log in memory to retry
-// uploading later.
+// - if the upload fails, we continue and will retry to upload later.
//
-class UploadService : public base::HistogramFlattener {
+class UploadService : public base::HistogramFlattener, public brillo::Daemon {
public:
- explicit UploadService(SystemProfileSetter* setter,
- MetricsLibraryInterface* metrics_lib,
- const std::string& server);
+ UploadService(const std::string& server,
+ const base::TimeDelta& upload_interval,
+ const base::FilePath& private_metrics_directory,
+ const base::FilePath& shared_metrics_directory,
+ const std::shared_ptr<CrashCounters> counters);
- void Init(const base::TimeDelta& upload_interval,
- const base::FilePath& metrics_directory);
+ // Initializes the upload service.
+ int OnInit();
// Starts a new log. The log needs to be regenerated after each successful
// launch as it is destroyed when staging the log.
@@ -105,6 +98,7 @@
FRIEND_TEST(UploadServiceTest, EmptyLogsAreNotSent);
FRIEND_TEST(UploadServiceTest, FailedSendAreRetried);
FRIEND_TEST(UploadServiceTest, LogContainsAggregatedValues);
+ FRIEND_TEST(UploadServiceTest, LogContainsCrashCounts);
FRIEND_TEST(UploadServiceTest, LogEmptyAfterUpload);
FRIEND_TEST(UploadServiceTest, LogEmptyByDefault);
FRIEND_TEST(UploadServiceTest, LogFromTheMetricsLibrary);
@@ -114,11 +108,8 @@
FRIEND_TEST(UploadServiceTest, UnknownCrashIgnored);
FRIEND_TEST(UploadServiceTest, ValuesInConfigFileAreSent);
- // Private constructor for use in unit testing.
- UploadService(SystemProfileSetter* setter,
- MetricsLibraryInterface* metrics_lib,
- const std::string& server,
- bool testing);
+ // Initializes the upload service for testing.
+ void InitForTest(SystemProfileSetter* setter);
// If a staged log fails to upload more than kMaxFailedUpload times, it
// will be discarded.
@@ -127,14 +118,8 @@
// Resets the internal state.
void Reset();
- // Reads all the metrics from the disk.
- void ReadMetrics();
-
- // Adds a generic sample to the current log.
- void AddSample(const metrics::MetricSample& sample);
-
- // Adds a crash to the current log.
- void AddCrash(const std::string& crash_name);
+ // Returns true iff metrics reporting is enabled.
+ bool AreMetricsEnabled();
// Aggregates all histogram available in memory and store them in the current
// log.
@@ -157,14 +142,16 @@
// Returns the current log. If there is no current log, creates it first.
MetricsLog* GetOrCreateCurrentLog();
- scoped_ptr<SystemProfileSetter> system_profile_setter_;
- MetricsLibraryInterface* metrics_lib_;
+ std::unique_ptr<SystemProfileSetter> system_profile_setter_;
base::HistogramSnapshotManager histogram_snapshot_manager_;
- scoped_ptr<Sender> sender_;
+ std::unique_ptr<Sender> sender_;
chromeos_metrics::PersistentInteger failed_upload_count_;
- scoped_ptr<MetricsLog> current_log_;
+ std::unique_ptr<MetricsLog> current_log_;
+ std::shared_ptr<CrashCounters> counters_;
- base::FilePath metrics_file_;
+ base::TimeDelta upload_interval_;
+
+ base::FilePath consent_file_;
base::FilePath staged_log_path_;
bool testing_;
diff --git a/metricsd/uploader/upload_service_test.cc b/metricsd/uploader/upload_service_test.cc
index 236376a..ec507e8 100644
--- a/metricsd/uploader/upload_service_test.cc
+++ b/metricsd/uploader/upload_service_test.cc
@@ -14,18 +14,19 @@
* limitations under the License.
*/
-#include <gtest/gtest.h>
+#include <memory>
#include <base/at_exit.h>
#include <base/files/file_util.h>
#include <base/files/scoped_temp_dir.h>
#include <base/logging.h>
+#include <base/metrics/sparse_histogram.h>
+#include <base/metrics/statistics_recorder.h>
#include <base/sys_info.h>
+#include <gtest/gtest.h>
#include "constants.h"
-#include "metrics/metrics_library_mock.h"
#include "persistent_integer.h"
-#include "serialization/metric_sample.h"
#include "uploader/metrics_log.h"
#include "uploader/mock/mock_system_profile_setter.h"
#include "uploader/mock/sender_mock.h"
@@ -39,83 +40,72 @@
protected:
virtual void SetUp() {
CHECK(dir_.CreateUniqueTempDir());
- chromeos_metrics::PersistentInteger::SetMetricsDirectory(
- dir_.path().value());
- metrics_lib_.InitForTest(dir_.path());
- ASSERT_EQ(0, base::WriteFile(
- dir_.path().Append(metrics::kConsentFileName), "", 0));
- upload_service_.reset(new UploadService(new MockSystemProfileSetter(),
- &metrics_lib_, "", true));
+ // Make sure the statistics recorder is inactive (contains no metrics) then
+ // initialize it.
+ ASSERT_FALSE(base::StatisticsRecorder::IsActive());
+ base::StatisticsRecorder::Initialize();
+
+ base::FilePath private_dir = dir_.path().Append("private");
+ base::FilePath shared_dir = dir_.path().Append("shared");
+
+ EXPECT_TRUE(base::CreateDirectory(private_dir));
+ EXPECT_TRUE(base::CreateDirectory(shared_dir));
+
+ ASSERT_EQ(0, base::WriteFile(shared_dir.Append(metrics::kConsentFileName),
+ "", 0));
+ counters_.reset(new CrashCounters);
+
+ upload_service_.reset(new UploadService("", base::TimeDelta(), private_dir,
+ shared_dir, counters_));
upload_service_->sender_.reset(new SenderMock);
- upload_service_->Init(base::TimeDelta::FromMinutes(30), dir_.path());
+ upload_service_->InitForTest(new MockSystemProfileSetter);
upload_service_->GatherHistograms();
upload_service_->Reset();
}
- scoped_ptr<metrics::MetricSample> Crash(const std::string& name) {
- return metrics::MetricSample::CrashSample(name);
+ void SendSparseHistogram(const std::string& name, int sample) {
+ base::HistogramBase* histogram = base::SparseHistogram::FactoryGet(
+ name, base::Histogram::kUmaTargetedHistogramFlag);
+ histogram->Add(sample);
+ }
+
+ void SendHistogram(
+ const std::string& name, int sample, int min, int max, int nbuckets) {
+ base::HistogramBase* histogram = base::Histogram::FactoryGet(
+ name, min, max, nbuckets, base::Histogram::kUmaTargetedHistogramFlag);
+ histogram->Add(sample);
}
void SetTestingProperty(const std::string& name, const std::string& value) {
- base::FilePath filepath = dir_.path().Append("etc/os-release.d").Append(name);
+ base::FilePath filepath =
+ dir_.path().Append("etc/os-release.d").Append(name);
ASSERT_TRUE(base::CreateDirectory(filepath.DirName()));
ASSERT_EQ(
value.size(),
base::WriteFile(filepath, value.data(), value.size()));
}
+ const metrics::SystemProfileProto_Stability GetCurrentStability() {
+ EXPECT_TRUE(upload_service_->current_log_.get());
+
+ return upload_service_->current_log_->uma_proto()->system_profile().stability();
+ }
+
base::ScopedTempDir dir_;
- scoped_ptr<UploadService> upload_service_;
- MetricsLibrary metrics_lib_;
+ std::unique_ptr<UploadService> upload_service_;
- scoped_ptr<base::AtExitManager> exit_manager_;
+ std::unique_ptr<base::AtExitManager> exit_manager_;
+ std::shared_ptr<CrashCounters> counters_;
};
-// Tests that the right crash increments a values.
-TEST_F(UploadServiceTest, LogUserCrash) {
- upload_service_->AddSample(*Crash("user").get());
-
- MetricsLog* log = upload_service_->current_log_.get();
- metrics::ChromeUserMetricsExtension* proto = log->uma_proto();
-
- EXPECT_EQ(1, proto->system_profile().stability().other_user_crash_count());
-}
-
-TEST_F(UploadServiceTest, LogUncleanShutdown) {
- upload_service_->AddSample(*Crash("uncleanshutdown"));
-
- EXPECT_EQ(1, upload_service_->current_log_
- ->uma_proto()
- ->system_profile()
- .stability()
- .unclean_system_shutdown_count());
-}
-
-TEST_F(UploadServiceTest, LogKernelCrash) {
- upload_service_->AddSample(*Crash("kernel"));
-
- EXPECT_EQ(1, upload_service_->current_log_
- ->uma_proto()
- ->system_profile()
- .stability()
- .kernel_crash_count());
-}
-
-TEST_F(UploadServiceTest, UnknownCrashIgnored) {
- upload_service_->AddSample(*Crash("foo"));
-
- // The log should be empty.
- EXPECT_FALSE(upload_service_->current_log_);
-}
-
TEST_F(UploadServiceTest, FailedSendAreRetried) {
SenderMock* sender = new SenderMock();
upload_service_->sender_.reset(sender);
sender->set_should_succeed(false);
- upload_service_->AddSample(*Crash("user"));
+ SendSparseHistogram("hello", 1);
upload_service_->UploadEvent();
EXPECT_EQ(1, sender->send_call_count());
std::string sent_string = sender->last_message();
@@ -131,7 +121,7 @@
sender->set_should_succeed(false);
- upload_service_->AddSample(*Crash("user"));
+ SendSparseHistogram("hello", 1);
for (int i = 0; i < UploadService::kMaxFailedUpload; i++) {
upload_service_->UploadEvent();
@@ -142,7 +132,7 @@
EXPECT_FALSE(upload_service_->HasStagedLog());
// Log a new sample. The failed upload counter should be reset.
- upload_service_->AddSample(*Crash("user"));
+ SendSparseHistogram("hello", 1);
for (int i = 0; i < UploadService::kMaxFailedUpload; i++) {
upload_service_->UploadEvent();
}
@@ -159,8 +149,8 @@
}
TEST_F(UploadServiceTest, LogEmptyByDefault) {
- UploadService upload_service(new MockSystemProfileSetter(), &metrics_lib_,
- "");
+ UploadService upload_service("", base::TimeDelta(), dir_.path(), dir_.path(),
+ std::make_shared<CrashCounters>());
// current_log_ should be initialized later as it needs AtExitManager to exit
// in order to gather system information from SysInfo.
@@ -171,35 +161,28 @@
SenderMock* sender = new SenderMock();
upload_service_->sender_.reset(sender);
- upload_service_->AddSample(*Crash("user"));
+ SendSparseHistogram("hello", 1);
+
upload_service_->UploadEvent();
std::string first_message = sender->last_message();
+ SendSparseHistogram("hello", 2);
- upload_service_->AddSample(*Crash("kernel"));
upload_service_->UploadEvent();
EXPECT_NE(first_message, sender->last_message());
}
TEST_F(UploadServiceTest, LogEmptyAfterUpload) {
- upload_service_->AddSample(*Crash("user"));
-
- EXPECT_TRUE(upload_service_->current_log_);
+ SendSparseHistogram("hello", 2);
upload_service_->UploadEvent();
EXPECT_FALSE(upload_service_->current_log_);
}
TEST_F(UploadServiceTest, LogContainsAggregatedValues) {
- scoped_ptr<metrics::MetricSample> histogram =
- metrics::MetricSample::HistogramSample("foo", 10, 0, 42, 10);
- upload_service_->AddSample(*histogram.get());
-
-
- scoped_ptr<metrics::MetricSample> histogram2 =
- metrics::MetricSample::HistogramSample("foo", 11, 0, 42, 10);
- upload_service_->AddSample(*histogram2.get());
+ SendHistogram("foo", 11, 0, 42, 10);
+ SendHistogram("foo", 12, 0, 42, 10);
upload_service_->GatherHistograms();
metrics::ChromeUserMetricsExtension* proto =
@@ -207,6 +190,37 @@
EXPECT_EQ(1, proto->histogram_event().size());
}
+TEST_F(UploadServiceTest, LogContainsCrashCounts) {
+ // By default, there is no current log.
+ upload_service_->GatherHistograms();
+ EXPECT_FALSE(upload_service_->current_log_);
+
+ // If the user crash counter is incremented, we add the count to the current
+ // log.
+ counters_->IncrementUserCrashCount();
+ upload_service_->GatherHistograms();
+ EXPECT_EQ(1, GetCurrentStability().other_user_crash_count());
+
+ // If the kernel crash counter is incremented, we add the count to the current
+ // log.
+ counters_->IncrementKernelCrashCount();
+ upload_service_->GatherHistograms();
+ EXPECT_EQ(1, GetCurrentStability().kernel_crash_count());
+
+ // If the kernel crash counter is incremented, we add the count to the current
+ // log.
+ counters_->IncrementUncleanShutdownCount();
+ counters_->IncrementUncleanShutdownCount();
+ upload_service_->GatherHistograms();
+ EXPECT_EQ(2, GetCurrentStability().unclean_system_shutdown_count());
+
+ // If no counter is incremented, the reported numbers don't change.
+ upload_service_->GatherHistograms();
+ EXPECT_EQ(1, GetCurrentStability().other_user_crash_count());
+ EXPECT_EQ(1, GetCurrentStability().kernel_crash_count());
+ EXPECT_EQ(2, GetCurrentStability().unclean_system_shutdown_count());
+}
+
TEST_F(UploadServiceTest, ExtractChannelFromString) {
EXPECT_EQ(
SystemProfileCache::ProtoChannelFromString(
@@ -214,10 +228,10 @@
metrics::SystemProfileProto::CHANNEL_UNKNOWN);
EXPECT_EQ(metrics::SystemProfileProto::CHANNEL_DEV,
- SystemProfileCache::ProtoChannelFromString("dev"));
+ SystemProfileCache::ProtoChannelFromString("dev-channel"));
EXPECT_EQ(metrics::SystemProfileProto::CHANNEL_STABLE,
- SystemProfileCache::ProtoChannelFromString("stable"));
+ SystemProfileCache::ProtoChannelFromString("stable-channel"));
EXPECT_EQ(metrics::SystemProfileProto::CHANNEL_UNKNOWN,
SystemProfileCache::ProtoChannelFromString("this is a test"));
@@ -230,13 +244,12 @@
SetTestingProperty(metrics::kProductId, "hello");
SetTestingProperty(metrics::kProductVersion, "1.2.3.4");
- scoped_ptr<metrics::MetricSample> histogram =
- metrics::MetricSample::SparseHistogramSample("myhistogram", 1);
+ SendSparseHistogram("hello", 1);
+
// Reset to create the new log with the profile setter.
upload_service_->system_profile_setter_.reset(
new SystemProfileCache(true, dir_.path()));
upload_service_->Reset();
- upload_service_->AddSample(*histogram.get());
upload_service_->UploadEvent();
EXPECT_EQ(1, sender->send_call_count());
@@ -277,17 +290,13 @@
EXPECT_EQ(cache.profile_.session_id, session_id + 1);
}
-// Test that we can log metrics from the metrics library and have the uploader
-// upload them.
-TEST_F(UploadServiceTest, LogFromTheMetricsLibrary) {
- SenderMock* sender = new SenderMock();
- upload_service_->sender_.reset(sender);
-
- upload_service_->UploadEvent();
- EXPECT_EQ(0, sender->send_call_count());
-
- metrics_lib_.SendEnumToUMA("testname", 2, 10);
- upload_service_->UploadEvent();
-
- EXPECT_EQ(1, sender->send_call_count());
+// The product id must be set for metrics to be uploaded.
+// If it is not set, the system profile cache should fail to initialize.
+TEST_F(UploadServiceTest, ProductIdMandatory) {
+ SystemProfileCache cache(true, dir_.path());
+ ASSERT_FALSE(cache.Initialize());
+ SetTestingProperty(metrics::kProductId, "");
+ ASSERT_FALSE(cache.Initialize());
+ SetTestingProperty(metrics::kProductId, "hello");
+ ASSERT_TRUE(cache.Initialize());
}
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index b134f93..895a25d 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -48,6 +48,8 @@
ln -sf /storage/self/primary $(TARGET_ROOT_OUT)/sdcard
ifdef BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE
LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/vendor
+else
+ LOCAL_POST_INSTALL_CMD += ; ln -sf /system/vendor $(TARGET_ROOT_OUT)/vendor
endif
ifdef BOARD_ROOT_EXTRA_SYMLINKS
# BOARD_ROOT_EXTRA_SYMLINKS is a list of <target>:<link_name>.
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 38c686d..a52d87d 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -7,6 +7,7 @@
import /init.environ.rc
import /init.usb.rc
import /init.${ro.hardware}.rc
+import /init.usb.configfs.rc
import /init.${ro.zygote}.rc
on early-init
@@ -86,10 +87,17 @@
write /proc/sys/kernel/panic_on_oops 1
write /proc/sys/kernel/hung_task_timeout_secs 0
write /proc/cpu/alignment 4
+
+ # scheduler tunables
+ # Disable auto-scaling of scheduler tunables with hotplug. The tunables
+ # will vary across devices in unpredictable ways if allowed to scale with
+ # cpu cores.
+ write /proc/sys/kernel/sched_tunable_scaling 0
write /proc/sys/kernel/sched_latency_ns 10000000
write /proc/sys/kernel/sched_wakeup_granularity_ns 2000000
write /proc/sys/kernel/sched_compat_yield 1
write /proc/sys/kernel/sched_child_runs_first 0
+
write /proc/sys/kernel/randomize_va_space 2
write /proc/sys/kernel/kptr_restrict 2
write /proc/sys/vm/mmap_min_addr 32768
@@ -130,21 +138,37 @@
# sets up initial cpusets for ActivityManager
mkdir /dev/cpuset
mount cpuset none /dev/cpuset
- mkdir /dev/cpuset/foreground
- mkdir /dev/cpuset/background
+
# this ensures that the cpusets are present and usable, but the device's
# init.rc must actually set the correct cpus
+ mkdir /dev/cpuset/foreground
write /dev/cpuset/foreground/cpus 0
- write /dev/cpuset/background/cpus 0
write /dev/cpuset/foreground/mems 0
+ mkdir /dev/cpuset/foreground/boost
+ write /dev/cpuset/foreground/boost/cpus 0
+ write /dev/cpuset/foreground/boost/mems 0
+ mkdir /dev/cpuset/background
+ write /dev/cpuset/background/cpus 0
write /dev/cpuset/background/mems 0
+
+ # system-background is for system tasks that should only run on
+ # little cores, not on bigs
+ # to be used only by init, so don't change system-bg permissions
+ mkdir /dev/cpuset/system-background
+ write /dev/cpuset/system-background/cpus 0
+ write /dev/cpuset/system-background/mems 0
+
+ # change permissions for all cpusets we'll touch at runtime
chown system system /dev/cpuset
chown system system /dev/cpuset/foreground
+ chown system system /dev/cpuset/foreground/boost
chown system system /dev/cpuset/background
chown system system /dev/cpuset/tasks
chown system system /dev/cpuset/foreground/tasks
+ chown system system /dev/cpuset/foreground/boost/tasks
chown system system /dev/cpuset/background/tasks
chmod 0664 /dev/cpuset/foreground/tasks
+ chmod 0664 /dev/cpuset/foreground/boost/tasks
chmod 0664 /dev/cpuset/background/tasks
chmod 0664 /dev/cpuset/tasks
@@ -180,8 +204,11 @@
trigger late-init
# Load properties from /system/ + /factory after fs mount.
-on load_all_props_action
- load_all_props
+on load_system_props_action
+ load_system_props
+
+on load_persist_props_action
+ load_persist_props
start logd
start logd-reinit
@@ -194,12 +221,16 @@
trigger early-fs
trigger fs
trigger post-fs
- trigger post-fs-data
# Load properties from /system/ + /factory after fs mount. Place
# this in another action so that the load will be scheduled after the prior
# issued fs triggers have completed.
- trigger load_all_props_action
+ trigger load_system_props_action
+
+ # Now we can mount /data. File encryption requires keymaster to decrypt
+ # /data, which in turn can only be loaded when system properties are present
+ trigger post-fs-data
+ trigger load_persist_props_action
# Remove a file to wake up anything waiting for firmware.
trigger firmware_mounts_complete
@@ -217,6 +248,9 @@
# Mount default storage into root namespace
mount none /mnt/runtime/default /storage slave bind rec
+ # Make sure /sys/kernel/debug (if present) is labeled properly
+ restorecon_recursive /sys/kernel/debug
+
# We chown/chmod /cache again so because mount is run as root + defaults
chown system cache /cache
chmod 0770 /cache
@@ -300,6 +334,7 @@
mkdir /data/misc/media 0700 media media
mkdir /data/misc/boottrace 0771 system shell
mkdir /data/misc/update_engine 0700 root root
+ mkdir /data/misc/trace 0700 root root
# For security reasons, /data/local/tmp should always be empty.
# Do not place files or directories in /data/local/tmp
@@ -314,7 +349,6 @@
# create dalvik-cache, so as to enforce our permissions
mkdir /data/dalvik-cache 0771 root root
- mkdir /data/dalvik-cache/profiles 0711 system system
# create resource-cache and double-check the perms
mkdir /data/resource-cache 0771 system system
@@ -349,6 +383,8 @@
mkdir /data/system/heapdump 0700 system system
mkdir /data/user 0711 system system
+ setusercryptopolicies /data/user
+
# Reload policy from /data/security if present.
setprop selinux.reload_policy 1
@@ -396,8 +432,8 @@
chown system system /sys/power/autosleep
chown system system /sys/power/state
chown system system /sys/power/wakeup_count
- chown radio system /sys/power/wake_lock
- chown radio system /sys/power/wake_unlock
+ chown radio wakelock /sys/power/wake_lock
+ chown radio wakelock /sys/power/wake_unlock
chmod 0660 /sys/power/state
chmod 0660 /sys/power/wake_lock
chmod 0660 /sys/power/wake_unlock
@@ -457,6 +493,8 @@
class_start core
on nonencrypted
+ # A/B update verifier that marks a successful boot.
+ exec - root -- /system/bin/update_verifier nonencrypted
class_start main
class_start late_start
@@ -485,9 +523,13 @@
trigger post-fs-data
on property:vold.decrypt=trigger_restart_min_framework
+ # A/B update verifier that marks a successful boot.
+ exec - root -- /system/bin/update_verifier trigger_restart_min_framework
class_start main
on property:vold.decrypt=trigger_restart_framework
+ # A/B update verifier that marks a successful boot.
+ exec - root -- /system/bin/update_verifier trigger_restart_framework
class_start main
class_start late_start
@@ -520,17 +562,20 @@
class core
critical
seclabel u:r:healthd:s0
- group root system
+ group root system wakelock
service console /system/bin/sh
class core
console
disabled
user shell
- group shell log
+ group shell log readproc
seclabel u:r:shell:s0
on property:ro.debuggable=1
+ # Give writes to anyone for the trace folder on debug builds.
+ # The folder is used to store method traces.
+ chmod 0773 /data/misc/trace
start console
service flash_recovery /system/bin/install-recovery.sh
diff --git a/rootdir/init.usb.configfs.rc b/rootdir/init.usb.configfs.rc
new file mode 100644
index 0000000..186384b
--- /dev/null
+++ b/rootdir/init.usb.configfs.rc
@@ -0,0 +1,175 @@
+on property:sys.usb.config=none && property:sys.usb.configfs=1
+ write /config/usb_gadget/g1/UDC "none"
+ stop adbd
+ write /config/usb_gadget/g1/bDeviceClass 0
+ write /config/usb_gadget/g1/bDeviceSubClass 0
+ write /config/usb_gadget/g1/bDeviceProtocol 0
+ setprop sys.usb.state ${sys.usb.config}
+
+on property:sys.usb.config=adb && property:sys.usb.configfs=1
+ start adbd
+
+on property:sys.usb.ffs.ready=1 && property:sys.usb.config=adb && property:sys.usb.configfs=1
+ write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "adb"
+ rm /config/usb_gadget/g1/configs/b.1/f1
+ rm /config/usb_gadget/g1/configs/b.1/f2
+ rm /config/usb_gadget/g1/configs/b.1/f3
+ symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f1
+ write /config/usb_gadget/g1/UDC ${sys.usb.controller}
+ setprop sys.usb.state ${sys.usb.config}
+
+on property:sys.usb.config=mtp && property:sys.usb.configfs=1
+ write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "mtp"
+ rm /config/usb_gadget/g1/configs/b.1/f1
+ rm /config/usb_gadget/g1/configs/b.1/f2
+ rm /config/usb_gadget/g1/configs/b.1/f3
+ symlink /config/usb_gadget/g1/functions/mtp.gs0 /config/usb_gadget/g1/configs/b.1/f1
+ write /config/usb_gadget/g1/UDC ${sys.usb.controller}
+ setprop sys.usb.state ${sys.usb.config}
+
+on property:sys.usb.config=mtp,adb && property:sys.usb.configfs=1
+ start adbd
+
+on property:sys.usb.ffs.ready=1 && property:sys.usb.config=mtp,adb && property:sys.usb.configfs=1
+ write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "mtp_adb"
+ rm /config/usb_gadget/g1/configs/b.1/f1
+ rm /config/usb_gadget/g1/configs/b.1/f2
+ rm /config/usb_gadget/g1/configs/b.1/f3
+ symlink /config/usb_gadget/g1/functions/mtp.gs0 /config/usb_gadget/g1/configs/b.1/f1
+ symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f2
+ write /config/usb_gadget/g1/UDC ${sys.usb.controller}
+ setprop sys.usb.state ${sys.usb.config}
+
+on property:sys.usb.config=ptp && property:sys.usb.configfs=1
+ write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "ptp"
+ rm /config/usb_gadget/g1/configs/b.1/f1
+ rm /config/usb_gadget/g1/configs/b.1/f2
+ rm /config/usb_gadget/g1/configs/b.1/f3
+ symlink /config/usb_gadget/g1/functions/ptp.gs1 /config/usb_gadget/g1/configs/b.1/f1
+ write /config/usb_gadget/g1/UDC ${sys.usb.controller}
+ setprop sys.usb.state ${sys.usb.config}
+
+on property:sys.usb.config=ptp,adb && property:sys.usb.configfs=1
+ start adbd
+
+on property:sys.usb.ffs.ready=1 && property:sys.usb.config=ptp,adb && property:sys.usb.configfs=1
+ write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "ptp_adb"
+ rm /config/usb_gadget/g1/configs/b.1/f1
+ rm /config/usb_gadget/g1/configs/b.1/f2
+ rm /config/usb_gadget/g1/configs/b.1/f3
+ symlink /config/usb_gadget/g1/functions/ptp.gs1 /config/usb_gadget/g1/configs/b.1/f1
+ symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f2
+ write /config/usb_gadget/g1/UDC ${sys.usb.controller}
+ setprop sys.usb.state ${sys.usb.config}
+
+on property:sys.usb.config=accessory && property:sys.usb.configfs=1
+ write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "accessory"
+ rm /config/usb_gadget/g1/configs/b.1/f1
+ rm /config/usb_gadget/g1/configs/b.1/f2
+ rm /config/usb_gadget/g1/configs/b.1/f3
+ symlink /config/usb_gadget/g1/functions/accessory.gs2 /config/usb_gadget/g1/configs/b.1/f1
+ write /config/usb_gadget/g1/UDC ${sys.usb.controller}
+ setprop sys.usb.state ${sys.usb.config}
+
+on property:sys.usb.config=accessory,adb && property:sys.usb.configfs=1
+ start adbd
+
+on property:sys.usb.ffs.ready=1 && property:sys.usb.config=accessory,adb && property:sys.usb.configfs=1
+ write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "accessory_adb"
+ rm /config/usb_gadget/g1/configs/b.1/f1
+ rm /config/usb_gadget/g1/configs/b.1/f2
+ rm /config/usb_gadget/g1/configs/b.1/f3
+ symlink /config/usb_gadget/g1/functions/accessory.gs2 /config/usb_gadget/g1/configs/b.1/f1
+ symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f2
+ write /config/usb_gadget/g1/UDC ${sys.usb.controller}
+ setprop sys.usb.state ${sys.usb.config}
+
+on property:sys.usb.config=audio_source && property:sys.usb.configfs=1
+ write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "audiosource"
+ rm /config/usb_gadget/g1/configs/b.1/f1
+ rm /config/usb_gadget/g1/configs/b.1/f2
+ rm /config/usb_gadget/g1/configs/b.1/f3
+ symlink /config/usb_gadget/g1/functions/audio_source.gs2 /config/usb_gadget/g1/configs/b.1/f1
+ write /config/usb_gadget/g1/UDC ${sys.usb.controller}
+ setprop sys.usb.state ${sys.usb.config}
+
+on property:sys.usb.config=audio_source,adb && property:sys.usb.configfs=1
+ start adbd
+
+on property:sys.usb.ffs.ready=1 && property:sys.usb.config=audio_source,adb && property:sys.usb.configfs=1
+ write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "audiosource_adb"
+ rm /config/usb_gadget/g1/configs/b.1/f1
+ rm /config/usb_gadget/g1/configs/b.1/f2
+ rm /config/usb_gadget/g1/configs/b.1/f3
+ symlink /config/usb_gadget/g1/functions/audio_source.gs2 /config/usb_gadget/g1/configs/b.1/f1
+ symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f2
+ write /config/usb_gadget/g1/UDC ${sys.usb.controller}
+ setprop sys.usb.state ${sys.usb.config}
+
+on property:sys.usb.config=accessory,audio_source && property:sys.usb.configfs=1
+ write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "accessory_audiosource"
+ rm /config/usb_gadget/g1/configs/b.1/f1
+ rm /config/usb_gadget/g1/configs/b.1/f2
+ rm /config/usb_gadget/g1/configs/b.1/f3
+ symlink /config/usb_gadget/g1/functions/accessory.gs2 /config/usb_gadget/g1/configs/b.1/f1
+ symlink /config/usb_gadget/g1/functions/audio_source.gs3 /config/usb_gadget/g1/configs/b.1/f2
+ write /config/usb_gadget/g1/UDC ${sys.usb.controller}
+ setprop sys.usb.state ${sys.usb.config}
+
+on property:sys.usb.config=accessory,audio_source,adb && property:sys.usb.configfs=1
+ start adbd
+
+on property:sys.usb.ffs.ready=1 && property:sys.usb.config=accessory,audio_source,adb && property:sys.usb.configfs=1
+ write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "accessory_audiosource_adb"
+ rm /config/usb_gadget/g1/configs/b.1/f1
+ rm /config/usb_gadget/g1/configs/b.1/f2
+ rm /config/usb_gadget/g1/configs/b.1/f3
+ symlink /config/usb_gadget/g1/functions/accessory.gs2 /config/usb_gadget/g1/configs/b.1/f1
+ symlink /config/usb_gadget/g1/functions/audio_source.gs3 /config/usb_gadget/g1/configs/b.1/f2
+ symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f3
+ write /config/usb_gadget/g1/UDC ${sys.usb.controller}
+ setprop sys.usb.state ${sys.usb.config}
+
+on property:sys.usb.config=midi && property:sys.usb.configfs=1
+ write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "midi"
+ rm /config/usb_gadget/g1/configs/b.1/f1
+ rm /config/usb_gadget/g1/configs/b.1/f2
+ rm /config/usb_gadget/g1/configs/b.1/f3
+ symlink /config/usb_gadget/g1/functions/midi.gs5 /config/usb_gadget/g1/configs/b.1/f1
+ write /config/usb_gadget/g1/UDC ${sys.usb.controller}
+ setprop sys.usb.state ${sys.usb.config}
+
+on property:sys.usb.config=midi,adb && property:sys.usb.configfs=1
+ start adbd
+
+on property:sys.usb.ffs.ready=1 && property:sys.usb.config=midi,adb && property:sys.usb.configfs=1
+ write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "midi_adb"
+ rm /config/usb_gadget/g1/configs/b.1/f1
+ rm /config/usb_gadget/g1/configs/b.1/f2
+ rm /config/usb_gadget/g1/configs/b.1/f3
+ symlink /config/usb_gadget/g1/functions/midi.gs5 /config/usb_gadget/g1/configs/b.1/f1
+ symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f2
+ write /config/usb_gadget/g1/UDC ${sys.usb.controller}
+ setprop sys.usb.state ${sys.usb.config}
+
+on property:sys.usb.config=rndis && property:sys.usb.configfs=1
+ write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "rndis"
+ rm /config/usb_gadget/g1/configs/b.1/f1
+ rm /config/usb_gadget/g1/configs/b.1/f2
+ rm /config/usb_gadget/g1/configs/b.1/f3
+ symlink /config/usb_gadget/g1/functions/rndis.gs4 /config/usb_gadget/g1/configs/b.1/f1
+ write /config/usb_gadget/g1/UDC ${sys.usb.controller}
+ setprop sys.usb.state ${sys.usb.config}
+
+on property:sys.usb.config=rndis,adb && property:sys.usb.configfs=1
+ start adbd
+
+on property:sys.usb.ffs.ready=1 && property:sys.usb.config=rndis,adb && property:sys.usb.configfs=1
+ write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "rndis_adb"
+ rm /config/usb_gadget/g1/configs/b.1/f1
+ rm /config/usb_gadget/g1/configs/b.1/f2
+ rm /config/usb_gadget/g1/configs/b.1/f3
+ symlink /config/usb_gadget/g1/functions/rndis.gs4 /config/usb_gadget/g1/configs/b.1/f1
+ symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f2
+ write /config/usb_gadget/g1/UDC ${sys.usb.controller}
+ setprop sys.usb.state ${sys.usb.config}
diff --git a/rootdir/init.usb.rc b/rootdir/init.usb.rc
index 4e6f2a8..1fd1e2a 100644
--- a/rootdir/init.usb.rc
+++ b/rootdir/init.usb.rc
@@ -22,8 +22,11 @@
on property:ro.kernel.qemu=1
start adbd
+on boot
+ setprop sys.usb.configfs 0
+
# Used to disable USB when switching states
-on property:sys.usb.config=none
+on property:sys.usb.config=none && property:sys.usb.configfs=0
stop adbd
write /sys/class/android_usb/android0/enable 0
write /sys/class/android_usb/android0/bDeviceClass 0
@@ -32,7 +35,7 @@
# adb only USB configuration
# This is the fallback configuration if the
# USB manager fails to set a standard configuration
-on property:sys.usb.config=adb
+on property:sys.usb.config=adb && property:sys.usb.configfs=0
write /sys/class/android_usb/android0/enable 0
write /sys/class/android_usb/android0/idVendor 18d1
write /sys/class/android_usb/android0/idProduct 4EE7
@@ -42,7 +45,7 @@
setprop sys.usb.state ${sys.usb.config}
# USB accessory configuration
-on property:sys.usb.config=accessory
+on property:sys.usb.config=accessory && property:sys.usb.configfs=0
write /sys/class/android_usb/android0/enable 0
write /sys/class/android_usb/android0/idVendor 18d1
write /sys/class/android_usb/android0/idProduct 2d00
@@ -51,7 +54,7 @@
setprop sys.usb.state ${sys.usb.config}
# USB accessory configuration, with adb
-on property:sys.usb.config=accessory,adb
+on property:sys.usb.config=accessory,adb && property:sys.usb.configfs=0
write /sys/class/android_usb/android0/enable 0
write /sys/class/android_usb/android0/idVendor 18d1
write /sys/class/android_usb/android0/idProduct 2d01
@@ -61,7 +64,7 @@
setprop sys.usb.state ${sys.usb.config}
# audio accessory configuration
-on property:sys.usb.config=audio_source
+on property:sys.usb.config=audio_source && property:sys.usb.configfs=0
write /sys/class/android_usb/android0/enable 0
write /sys/class/android_usb/android0/idVendor 18d1
write /sys/class/android_usb/android0/idProduct 2d02
@@ -70,7 +73,7 @@
setprop sys.usb.state ${sys.usb.config}
# audio accessory configuration, with adb
-on property:sys.usb.config=audio_source,adb
+on property:sys.usb.config=audio_source,adb && property:sys.usb.configfs=0
write /sys/class/android_usb/android0/enable 0
write /sys/class/android_usb/android0/idVendor 18d1
write /sys/class/android_usb/android0/idProduct 2d03
@@ -80,7 +83,7 @@
setprop sys.usb.state ${sys.usb.config}
# USB and audio accessory configuration
-on property:sys.usb.config=accessory,audio_source
+on property:sys.usb.config=accessory,audio_source && property:sys.usb.configfs=0
write /sys/class/android_usb/android0/enable 0
write /sys/class/android_usb/android0/idVendor 18d1
write /sys/class/android_usb/android0/idProduct 2d04
@@ -89,7 +92,7 @@
setprop sys.usb.state ${sys.usb.config}
# USB and audio accessory configuration, with adb
-on property:sys.usb.config=accessory,audio_source,adb
+on property:sys.usb.config=accessory,audio_source,adb && property:sys.usb.configfs=0
write /sys/class/android_usb/android0/enable 0
write /sys/class/android_usb/android0/idVendor 18d1
write /sys/class/android_usb/android0/idProduct 2d05
@@ -102,3 +105,34 @@
# when changing the default configuration
on property:persist.sys.usb.config=*
setprop sys.usb.config ${persist.sys.usb.config}
+
+#
+# USB type C
+#
+
+# USB mode changes
+on property:sys.usb.typec.mode=dfp
+ write /sys/class/dual_role_usb/otg_default/mode ${sys.usb.typec.mode}
+ setprop sys.usb.typec.state ${sys.usb.typec.mode}
+
+on property:sys.usb.typec.mode=ufp
+ write /sys/class/dual_role_usb/otg_default/mode ${sys.usb.typec.mode}
+ setprop sys.usb.typec.state ${sys.usb.typec.mode}
+
+# USB data role changes
+on property:sys.usb.typec.data_role=device
+ write /sys/class/dual_role_usb/otg_default/data_role ${sys.usb.typec.data_role}
+ setprop sys.usb.typec.state ${sys.usb.typec.data_role}
+
+on property:sys.usb.typec.data_role=host
+ write /sys/class/dual_role_usb/otg_default/data_role ${sys.usb.typec.data_role}
+ setprop sys.usb.typec.state ${sys.usb.typec.data_role}
+
+# USB power role changes
+on property:sys.usb.typec.power_role=source
+ write /sys/class/dual_role_usb/otg_default/power_role ${sys.usb.typec.power_role}
+ setprop sys.usb.typec.state ${sys.usb.typec.power_role}
+
+on property:sys.usb.typec.power_role=sink
+ write /sys/class/dual_role_usb/otg_default/power_role ${sys.usb.typec.power_role}
+ setprop sys.usb.typec.state ${sys.usb.typec.power_role}
diff --git a/rootdir/init.zygote32.rc b/rootdir/init.zygote32.rc
index 75961e6..ff25ac2 100644
--- a/rootdir/init.zygote32.rc
+++ b/rootdir/init.zygote32.rc
@@ -5,4 +5,4 @@
onrestart write /sys/power/state on
onrestart restart media
onrestart restart netd
-
+ writepid /dev/cpuset/foreground/tasks
diff --git a/rootdir/init.zygote32_64.rc b/rootdir/init.zygote32_64.rc
index 68c0668..29bb1cf 100644
--- a/rootdir/init.zygote32_64.rc
+++ b/rootdir/init.zygote32_64.rc
@@ -5,8 +5,10 @@
onrestart write /sys/power/state on
onrestart restart media
onrestart restart netd
+ writepid /dev/cpuset/foreground/tasks
service zygote_secondary /system/bin/app_process64 -Xzygote /system/bin --zygote --socket-name=zygote_secondary
class main
socket zygote_secondary stream 660 root system
onrestart restart zygote
+ writepid /dev/cpuset/foreground/tasks
\ No newline at end of file
diff --git a/rootdir/init.zygote64.rc b/rootdir/init.zygote64.rc
index afb6d63..5497524 100644
--- a/rootdir/init.zygote64.rc
+++ b/rootdir/init.zygote64.rc
@@ -5,4 +5,4 @@
onrestart write /sys/power/state on
onrestart restart media
onrestart restart netd
-
+ writepid /dev/cpuset/foreground/tasks
diff --git a/rootdir/init.zygote64_32.rc b/rootdir/init.zygote64_32.rc
index 979ab3b..8ed5e9e 100644
--- a/rootdir/init.zygote64_32.rc
+++ b/rootdir/init.zygote64_32.rc
@@ -5,8 +5,10 @@
onrestart write /sys/power/state on
onrestart restart media
onrestart restart netd
+ writepid /dev/cpuset/foreground/tasks
service zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary
class main
socket zygote_secondary stream 660 root system
onrestart restart zygote
+ writepid /dev/cpuset/foreground/tasks
\ No newline at end of file
diff --git a/run-as/run-as.c b/run-as/run-as.c
index 3f32e7d..f0fd2fe 100644
--- a/run-as/run-as.c
+++ b/run-as/run-as.c
@@ -20,6 +20,8 @@
#include <dirent.h>
#include <errno.h>
+#include <paths.h>
+#include <pwd.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
@@ -193,10 +195,21 @@
panic("Could not set SELinux security context: %s\n", strerror(errno));
}
- /* cd into the data directory */
+ // cd into the data directory, and set $HOME correspondingly.
if (TEMP_FAILURE_RETRY(chdir(info.dataDir)) < 0) {
panic("Could not cd to package's data directory: %s\n", strerror(errno));
}
+ setenv("HOME", info.dataDir, 1);
+
+ // Reset parts of the environment, like su would.
+ setenv("PATH", _PATH_DEFPATH, 1);
+ unsetenv("IFS");
+
+ // Set the user-specific parts for this user.
+ struct passwd* pw = getpwuid(uid);
+ setenv("LOGNAME", pw->pw_name, 1);
+ setenv("SHELL", pw->pw_shell, 1);
+ setenv("USER", pw->pw_name, 1);
/* User specified command for exec. */
if ((argc >= commandArgvOfs + 1) &&
diff --git a/sdcard/sdcard.c b/sdcard/sdcard.c
index b6bbe7e..45efe36 100644
--- a/sdcard/sdcard.c
+++ b/sdcard/sdcard.c
@@ -507,6 +507,16 @@
}
}
+static void derive_permissions_recursive_locked(struct fuse* fuse, struct node *parent) {
+ struct node *node;
+ for (node = parent->child; node; node = node->next) {
+ derive_permissions_locked(fuse, parent, node);
+ if (node->child) {
+ derive_permissions_recursive_locked(fuse, node);
+ }
+ }
+}
+
/* Kernel has already enforced everything we returned through
* derive_permissions_locked(), so this is used to lock down access
* even further, such as enforcing that apps hold sdcard_rw. */
@@ -1145,6 +1155,8 @@
res = rename_node_locked(child_node, new_name, new_actual_name);
if (!res) {
remove_node_from_parent_locked(child_node);
+ derive_permissions_locked(fuse, new_parent_node, child_node);
+ derive_permissions_recursive_locked(fuse, child_node);
add_node_to_parent_locked(child_node, new_parent_node);
}
goto done;
@@ -1654,6 +1666,9 @@
TRACE("read_package_list: found %zu packages\n",
hashmapSize(global->package_to_appid));
+ /* Regenerate ownership details using newly loaded mapping */
+ derive_permissions_recursive_locked(global->fuse_default, &global->root);
+
pthread_mutex_unlock(&global->lock);
return rc;
diff --git a/toolbox/top.c b/toolbox/top.c
index 1e99d4c..0ea5a5e 100644
--- a/toolbox/top.c
+++ b/toolbox/top.c
@@ -158,7 +158,7 @@
fprintf(stderr, "Invalid argument \"%s\" for option -s.\n", argv[i]);
exit(EXIT_FAILURE);
}
- if (!strcmp(argv[i], "-t")) { threads = 1; continue; }
+ if (!strcmp(argv[i], "-H") || !strcmp(argv[i], "-t")) { threads = 1; continue; }
if (!strcmp(argv[i], "-h")) {
usage(argv[0]);
exit(EXIT_SUCCESS);
@@ -187,6 +187,7 @@
read_procs();
print_procs();
free_old_procs();
+ fflush(stdout);
}
return 0;
@@ -566,7 +567,7 @@
" -n num Updates to show before exiting.\n"
" -d num Seconds to wait between updates.\n"
" -s col Column to sort by (cpu,vss,rss,thr).\n"
- " -t Show threads instead of processes.\n"
+ " -H Show threads instead of processes.\n"
" -h Display this help screen.\n",
cmd);
}
diff --git a/tzdatacheck/tzdatacheck.cpp b/tzdatacheck/tzdatacheck.cpp
index 31f7b55..c1ab2ac 100644
--- a/tzdatacheck/tzdatacheck.cpp
+++ b/tzdatacheck/tzdatacheck.cpp
@@ -27,7 +27,7 @@
#include <string>
#include <vector>
-#include "base/logging.h"
+#include "android-base/logging.h"
static const char* TZDATA_FILENAME = "/tzdata";
// tzdata file header (as much as we need for the version):