Merge "Add audioserver uid and restart service when needed"
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 c507905..c38cf93 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 \
@@ -155,6 +169,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 \
@@ -189,6 +204,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 +220,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
@@ -213,12 +228,13 @@
LOCAL_REQUIRED_MODULES_windows := AdbWinApi AdbWinUsbApi
LOCAL_SRC_FILES := \
+ adb_client.cpp \
client/main.cpp \
console.cpp \
commandline.cpp \
- adb_client.cpp \
- services.cpp \
file_sync_client.cpp \
+ line_printer.cpp \
+ services.cpp \
shell_service_protocol.cpp \
LOCAL_CFLAGS += \
@@ -229,6 +245,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
@@ -238,9 +261,12 @@
libadb \
libbase \
libcrypto_static \
- libcutils \
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
@@ -272,6 +298,7 @@
LOCAL_CFLAGS := \
$(ADB_COMMON_CFLAGS) \
+ $(ADB_COMMON_linux_CFLAGS) \
-DADB_HOST=0 \
-D_GNU_SOURCE \
-Wno-deprecated-declarations \
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 1eb3a3c..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"
@@ -963,9 +963,11 @@
fflush(stdout);
SendOkay(reply_fd);
- // At least on Windows, if we exit() without shutdown(SD_SEND) or
- // closesocket(), the client's next recv() will error-out with
- // WSAECONNRESET and they'll never read the OKAY.
+ // On Windows, if the process exits with open sockets that
+ // shutdown(SD_SEND) has not been called on, TCP RST segments will be
+ // sent to the peers which will cause their next recv() to error-out
+ // with WSAECONNRESET. In the case of this code, that means the client
+ // may not read the OKAY sent above.
adb_shutdown(reply_fd);
exit(0);
diff --git a/adb/adb.h b/adb/adb.h
index c284c8c..774215e 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,18 +214,23 @@
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
+// 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();
+
int adb_commandline(int argc, const char **argv);
ConnectionState connection_state(atransport *t);
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 fa8ce9e..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"
@@ -206,6 +206,7 @@
return -1;
}
+ ReadOrderlyShutdown(fd);
adb_close(fd);
if (sscanf(&version_string[0], "%04x", &version) != 1) {
@@ -227,6 +228,7 @@
version, ADB_SERVER_VERSION);
fd = _adb_connect("host:kill", error);
if (fd >= 0) {
+ ReadOrderlyShutdown(fd);
adb_close(fd);
} else {
// If we couldn't connect to the server or had some other error,
@@ -271,6 +273,8 @@
return false;
}
+ ReadOrderlyShutdown(fd);
+ adb_close(fd);
return true;
}
@@ -286,5 +290,8 @@
adb_close(fd);
return false;
}
+
+ ReadOrderlyShutdown(fd);
+ adb_close(fd);
return true;
}
diff --git a/adb/adb_io.cpp b/adb/adb_io.cpp
index 7788996..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"
@@ -137,3 +137,43 @@
return WriteFdExactly(fd, str);
}
+
+bool ReadOrderlyShutdown(int fd) {
+ char buf[16];
+
+ // Only call this function if you're sure that the peer does
+ // orderly/graceful shutdown of the socket, closing the socket so that
+ // adb_read() will return 0. If the peer keeps the socket open, adb_read()
+ // will never return.
+ int result = adb_read(fd, buf, sizeof(buf));
+ if (result == -1) {
+ // If errno is EAGAIN, that means this function was called on a
+ // nonblocking socket and it would have blocked (which would be bad
+ // because we'd probably block the main thread where nonblocking IO is
+ // done). Don't do that. If you have a nonblocking socket, use the
+ // fdevent APIs to get called on FDE_READ, and then call this function
+ // if you really need to, but it shouldn't be needed for server sockets.
+ CHECK_NE(errno, EAGAIN);
+
+ // Note that on Windows, orderly shutdown sometimes causes
+ // recv() == SOCKET_ERROR && WSAGetLastError() == WSAECONNRESET. That
+ // can be ignored.
+ return false;
+ } else if (result == 0) {
+ // Peer has performed an orderly/graceful shutdown.
+ return true;
+ } else {
+ // Unexpectedly received data. This is essentially a protocol error
+ // because you should not call this function unless you expect no more
+ // data. We don't repeatedly call adb_read() until we get zero because
+ // we don't know how long that would take, but we do know that the
+ // caller wants to close the socket soon.
+ VLOG(RWX) << "ReadOrderlyShutdown(" << fd << ") unexpectedly read "
+ << dump_hex(buf, result);
+ // Shutdown the socket to prevent the caller from reading or writing to
+ // it which doesn't make sense if we just read and discarded some data.
+ adb_shutdown(fd);
+ errno = EINVAL;
+ return false;
+ }
+}
diff --git a/adb/adb_io.h b/adb/adb_io.h
index 9c3b2a5..aa550af 100644
--- a/adb/adb_io.h
+++ b/adb/adb_io.h
@@ -41,6 +41,24 @@
// If this function fails, the contents of buf are undefined.
bool ReadFdExactly(int fd, void* buf, size_t len);
+// Given a client socket, wait for orderly/graceful shutdown. Call this:
+//
+// * Before closing a client socket.
+// * Only when no more data is expected to come in.
+// * Only when the server is not waiting for data from the client (because then
+// the client and server will deadlock waiting for each other).
+// * Only when the server is expected to close its socket right now.
+// * Don't call shutdown(SHUT_WR) before calling this because that will shutdown
+// the client socket early, defeating the purpose of calling this.
+//
+// Waiting for orderly/graceful shutdown of the server socket will cause the
+// server socket to close before the client socket. That prevents the client
+// socket from staying in TIME_WAIT which eventually causes subsequent
+// connect()s from the client to fail with WSAEADDRINUSE on Windows.
+// Returns true if it is sure that orderly/graceful shutdown has occurred with
+// no additional data read from the server.
+bool ReadOrderlyShutdown(int fd);
+
// Writes exactly len bytes from buf to fd.
//
// Returns false if there is an error or if the fd was closed before the write
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 354273c..8575c86 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,20 +105,20 @@
" will disconnect from all connected TCP/IP devices.\n"
"\n"
"device commands:\n"
- " adb push [-p] <local> <remote>\n"
- " - copy file/dir to device\n"
- " ('-p' to display the transfer progress)\n"
- " adb pull [-p] [-a] <remote> [<local>]\n"
- " - copy file/dir from device\n"
- " ('-p' to display the transfer progress)\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"
@@ -140,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"
@@ -248,17 +252,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);
@@ -266,11 +270,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
@@ -359,7 +363,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) {
@@ -401,7 +405,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");
@@ -421,47 +425,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
@@ -472,36 +539,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) {
@@ -525,6 +592,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);
@@ -546,6 +618,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);
@@ -571,57 +644,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,
@@ -651,6 +807,7 @@
std::string error;
adb_status(fd, &error);
fprintf(stderr,"* failed to write data '%s' *\n", error.c_str());
+ adb_close(fd);
return -1;
}
sz -= xfer;
@@ -666,6 +823,7 @@
if (!adb_status(fd, &error)) {
fprintf(stderr,"* error response '%s' *\n", error.c_str());
+ adb_close(fd);
return -1;
}
@@ -882,8 +1040,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) {
@@ -933,8 +1090,8 @@
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 +1102,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 +1129,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,32 +1227,35 @@
return path;
}
-static void parse_push_pull_args(const char **arg, int narg, char const **path1,
- char const **path2, bool* show_progress,
- int *copy_attrs) {
- *show_progress = false;
- *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")) {
- *show_progress = true;
- } 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();
}
}
@@ -1137,6 +1302,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
@@ -1268,7 +1438,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);
}
@@ -1339,94 +1509,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");
@@ -1471,6 +1555,7 @@
} else {
// Successfully connected, kill command sent, okay status came back.
// Server should exit() in a moment, if not already.
+ ReadOrderlyShutdown(fd);
adb_close(fd);
return 0;
}
@@ -1561,26 +1646,30 @@
return do_sync_ls(argv[1]) ? 0 : 1;
}
else if (!strcmp(argv[0], "push")) {
- bool show_progress = false;
- 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, &show_progress, ©_attrs);
- if (!lpath || !rpath || copy_attrs != 0) return usage();
- return do_sync_push(lpath, rpath, show_progress) ? 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")) {
- bool show_progress = false;
- 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, &show_progress, ©_attrs);
- if (!rpath) return usage();
- return do_sync_pull(rpath, lpath, show_progress, 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();
@@ -1588,7 +1677,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;
@@ -1696,85 +1789,83 @@
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(), false)) 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 write %s\n", file);
+ fputs(buf, stderr);
+ return 1;
+ }
+ fputs(buf, stderr);
+ return 0;
}
static int install_multiple_app(TransportType transport, const char* serial, int argc,
@@ -1793,7 +1884,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;
@@ -1818,7 +1909,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));
@@ -1836,7 +1927,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
@@ -1891,7 +1982,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);
@@ -1902,6 +1993,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 ba5a72b..15c6abd 100644
--- a/adb/console.cpp
+++ b/adb/console.cpp
@@ -18,13 +18,14 @@
#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"
#include "adb_client.h"
+#include "adb_io.h"
// Return the console port of the currently connected emulator (if any) or -1 if
// there is no emulator, and -2 if there is more than one.
@@ -87,14 +88,20 @@
return 1;
}
+ std::string commands;
+
for (int i = 1; i < argc; i++) {
- adb_write(fd, argv[i], strlen(argv[i]));
- adb_write(fd, i == argc - 1 ? "\n" : " ", 1);
+ commands.append(argv[i]);
+ commands.append(i == argc - 1 ? "\n" : " ");
}
- const char disconnect_command[] = "quit\n";
- if (adb_write(fd, disconnect_command, sizeof(disconnect_command) - 1) == -1) {
- LOG(FATAL) << "Could not finalize emulator command";
+ commands.append("quit\n");
+
+ if (!WriteFdExactly(fd, commands)) {
+ fprintf(stderr, "error: cannot write to emulator: %s\n",
+ strerror(errno));
+ adb_close(fd);
+ return 1;
}
// Drain output that the emulator console has sent us to prevent a problem
@@ -106,11 +113,14 @@
do {
char buf[BUFSIZ];
result = adb_read(fd, buf, sizeof(buf));
- // Keep reading until zero bytes (EOF) or an error. If 'adb emu kill'
- // is executed, the emulator calls exit() which causes adb to get
- // ECONNRESET. Any other emu command is followed by the quit command
- // that we sent above, and that causes the emulator to close the socket
- // which should cause zero bytes (EOF) to be returned.
+ // Keep reading until zero bytes (orderly/graceful shutdown) or an
+ // error. If 'adb emu kill' is executed, the emulator calls exit() with
+ // the socket open (and shutdown(SD_SEND) was not called), which causes
+ // Windows to send a TCP RST segment which causes adb to get ECONNRESET.
+ // Any other emu command is followed by the quit command that we
+ // appended above, and that causes the emulator to close the socket
+ // which should cause zero bytes (orderly/graceful shutdown) to be
+ // returned.
} while (result > 0);
adb_close(fd);
diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp
index 8c3ca63..feea7a3 100644
--- a/adb/daemon/main.cpp
+++ b/adb/daemon/main.cpp
@@ -25,8 +25,8 @@
#include <getopt.h>
#include <sys/prctl.h>
-#include "base/logging.h"
-#include "base/stringprintf.h"
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
#include "cutils/properties.h"
#include "private/android_filesystem_config.h"
#include "selinux/selinux.h"
@@ -34,6 +34,7 @@
#include "adb.h"
#include "adb_auth.h"
#include "adb_listeners.h"
+#include "adb_utils.h"
#include "transport.h"
static const char* root_seclabel = nullptr;
@@ -106,6 +107,56 @@
#endif // ALLOW_ADBD_ROOT
}
+static void drop_privileges(int server_port) {
+ // 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 (setgroups(sizeof(groups) / sizeof(groups[0]), groups) != 0) {
+ PLOG(FATAL) << "Could not set supplemental 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;
+ }
+ }
+}
+
int adbd_main(int server_port) {
umask(0);
@@ -133,51 +184,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 +222,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/fdevent.cpp b/adb/fdevent.cpp
index ddd15a2..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,21 @@
// 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;
+
+static void check_main_thread() {
+ if (main_thread_valid) {
+ CHECK_NE(0, pthread_equal(main_thread, pthread_self()));
+ }
+}
+
+static void set_main_thread() {
+ main_thread_valid = true;
+ main_thread = pthread_self();
+}
static std::string dump_fde(const fdevent* fde) {
std::string state;
@@ -101,6 +114,7 @@
fdevent *fdevent_create(int fd, fd_func func, void *arg)
{
+ check_main_thread();
fdevent *fde = (fdevent*) malloc(sizeof(fdevent));
if(fde == 0) return 0;
fdevent_install(fde, fd, func, arg);
@@ -110,6 +124,7 @@
void fdevent_destroy(fdevent *fde)
{
+ check_main_thread();
if(fde == 0) return;
if(!(fde->state & FDE_CREATED)) {
LOG(FATAL) << "destroying fde not created by fdevent_create(): " << dump_fde(fde);
@@ -119,6 +134,7 @@
}
void fdevent_install(fdevent* fde, int fd, fd_func func, void* arg) {
+ check_main_thread();
CHECK_GE(fd, 0);
memset(fde, 0, sizeof(fdevent));
fde->state = FDE_ACTIVE;
@@ -137,6 +153,7 @@
}
void fdevent_remove(fdevent* fde) {
+ check_main_thread();
D("fdevent_remove %s", dump_fde(fde).c_str());
if (fde->state & FDE_ACTIVE) {
g_poll_node_map.erase(fde->fd);
@@ -171,6 +188,7 @@
}
void fdevent_set(fdevent* fde, unsigned events) {
+ check_main_thread();
events &= FDE_EVENTMASK;
if ((fde->state & FDE_EVENTMASK) == events) {
return;
@@ -190,10 +208,12 @@
}
void fdevent_add(fdevent* fde, unsigned events) {
+ check_main_thread();
fdevent_set(fde, (fde->state & FDE_EVENTMASK) | events);
}
void fdevent_del(fdevent* fde, unsigned events) {
+ check_main_thread();
fdevent_set(fde, (fde->state & FDE_EVENTMASK) & ~events);
}
@@ -335,6 +355,7 @@
void fdevent_loop()
{
+ set_main_thread();
#if !ADB_HOST
fdevent_subproc_setup();
#endif // !ADB_HOST
@@ -359,4 +380,5 @@
void fdevent_reset() {
g_poll_node_map.clear();
g_pending_list.clear();
+ main_thread_valid = false;
}
diff --git a/adb/file_sync_client.cpp b/adb/file_sync_client.cpp
index 1d668bc..239aaf8 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>
@@ -38,10 +39,11 @@
#include "adb_io.h"
#include "adb_utils.h"
#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;
@@ -49,45 +51,33 @@
char data[SYNC_DATA_MAX];
};
-static long long NOW() {
- struct timeval tv;
- gettimeofday(&tv, 0);
- return ((long long) tv.tv_usec) + 1000000LL * ((long long) tv.tv_sec);
-}
-
-static void print_transfer_progress(uint64_t bytes_current,
- uint64_t bytes_total) {
- if (bytes_total == 0) return;
-
- fprintf(stderr, "\rTransferring: %" PRIu64 "/%" PRIu64 " (%d%%)",
- bytes_current, bytes_total,
- (int) (bytes_current * 100 / bytes_total));
-
- if (bytes_current == bytes_total) {
- fputc('\n', stderr);
- }
-
- fflush(stderr);
-}
-
class SyncConnection {
public:
- SyncConnection() : total_bytes(0), start_time_(NOW()) {
+ SyncConnection() : total_bytes(0), start_time_ms_(CurrentTimeMs()) {
max = SYNC_DATA_MAX; // TODO: decide at runtime.
std::string error;
fd = adb_connect("sync:", &error);
if (fd < 0) {
- fprintf(stderr, "adb: error: %s\n", error.c_str());
+ Error("connect failed: %s", error.c_str());
}
}
~SyncConnection() {
if (!IsValid()) return;
- SendQuit();
- ShowTransferRate();
+ if (SendQuit()) {
+ // We sent a quit command, so the server should be doing orderly
+ // shutdown soon. But if we encountered an error while we were using
+ // the connection, the server might still be sending data (before
+ // doing orderly shutdown), in which case we won't wait for all of
+ // the data nor the coming orderly shutdown. In the common success
+ // case, this will wait for the server to do orderly shutdown.
+ ReadOrderlyShutdown(fd);
+ }
adb_close(fd);
+
+ line_printer_.KeepInfoLine();
}
bool IsValid() { return fd >= 0; }
@@ -95,7 +85,7 @@
bool SendRequest(int id, const char* path_and_mode) {
size_t path_length = strlen(path_and_mode);
if (path_length > 1024) {
- fprintf(stderr, "adb: SendRequest failed: path too long: %zu\n", path_length);
+ Error("SendRequest failed: path too long: %zu", path_length);
errno = ENAMETOOLONG;
return false;
}
@@ -115,18 +105,21 @@
// 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* data, size_t data_length,
- unsigned mtime) {
+ const char* lpath, const char* rpath,
+ unsigned mtime,
+ const char* data, size_t data_length) {
+ Print(rpath);
+
size_t path_length = strlen(path_and_mode);
if (path_length > 1024) {
- fprintf(stderr, "adb: SendSmallFile failed: path too long: %zu\n", path_length);
+ Error("SendSmallFile failed: path too long: %zu", path_length);
errno = ENAMETOOLONG;
return false;
}
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);
@@ -148,25 +141,82 @@
req_done->path_length = mtime;
p += sizeof(SyncRequest);
- if (!WriteFdExactly(fd, &buf[0], (p - &buf[0]))) return false;
-
+ 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;
+
+ if (total_size == 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.
+ Printf("%s: ?%%", rpath);
+ } else {
+ int percentage = static_cast<int>(bytes_copied * 100 / total_size);
+ Printf("%s: %d%%", rpath, percentage);
+ }
+ }
+
+ 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))) {
- fprintf(stderr, "adb: failed to copy '%s' to '%s': no ID_DONE: %s\n",
- from, to, strerror(errno));
+ Error("failed to copy '%s' to '%s': no ID_DONE: %s", from, to, strerror(errno));
return false;
}
if (msg.status.id == ID_OKAY) {
return true;
}
if (msg.status.id != ID_FAIL) {
- fprintf(stderr, "adb: failed to copy '%s' to '%s': unknown reason %d\n",
- from, to, msg.status.id);
+ Error("failed to copy '%s' to '%s': unknown reason %d", from, to, msg.status.id);
return false;
}
return ReportCopyFailure(from, to, msg);
@@ -175,15 +225,61 @@
bool ReportCopyFailure(const char* from, const char* to, const syncmsg& msg) {
std::vector<char> buf(msg.status.msglen + 1);
if (!ReadFdExactly(fd, &buf[0], msg.status.msglen)) {
- fprintf(stderr, "adb: failed to copy '%s' to '%s'; failed to read reason (!): %s\n",
- from, to, strerror(errno));
+ Error("failed to copy '%s' to '%s'; failed to read reason (!): %s",
+ from, to, strerror(errno));
return false;
}
buf[msg.status.msglen] = 0;
- fprintf(stderr, "adb: failed to copy '%s' to '%s': %s\n", from, to, &buf[0]);
+ Error("failed to copy '%s' to '%s': %s", from, to, &buf[0]);
return false;
}
+ std::string TransferRate() {
+ uint64_t ms = CurrentTimeMs() - start_time_ms_;
+ if (total_bytes == 0 || ms == 0) return "";
+
+ double s = static_cast<double>(ms) / 1000LL;
+ 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);
+ }
+
+ void Print(const std::string& s) {
+ line_printer_.Print(s, LinePrinter::INFO);
+ }
+
+ 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);
+
+ Print(s);
+ }
+
+ void Error(const char* fmt, ...) __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 3))) {
+ std::string s = "adb: error: ";
+
+ va_list ap;
+ va_start(ap, fmt);
+ android::base::StringAppendV(&s, fmt, ap);
+ va_end(ap);
+
+ line_printer_.Print(s, LinePrinter::ERROR);
+ }
+
+ 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);
+ }
+
uint64_t total_bytes;
// TODO: add a char[max] buffer here, to replace syncsendbuf...
@@ -191,25 +287,46 @@
size_t max;
private:
- uint64_t start_time_;
+ uint64_t start_time_ms_;
- void SendQuit() {
- SendRequest(ID_QUIT, ""); // TODO: add a SendResponse?
+ LinePrinter line_printer_;
+
+ bool SendQuit() {
+ return SendRequest(ID_QUIT, ""); // TODO: add a SendResponse?
}
- void ShowTransferRate() {
- uint64_t t = NOW() - start_time_;
- if (total_bytes == 0 || t == 0) return;
+ 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;
+ }
- fprintf(stderr, "%lld KB/s (%" PRId64 " bytes in %lld.%03llds)\n",
- ((total_bytes * 1000000LL) / t) / 1024LL,
- total_bytes, (t / 1000000LL), (t % 1000000LL) / 1000LL);
+ static uint64_t CurrentTimeMs() {
+ struct timeval tv;
+ gettimeofday(&tv, 0); // (Not clock_gettime because of Mac/Windows.)
+ return static_cast<uint64_t>(tv.tv_sec) * 1000 + tv.tv_usec / 1000;
}
};
-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) {
@@ -226,7 +343,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);
}
}
@@ -249,73 +366,8 @@
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* path,
- unsigned mtime, bool show_progress) {
- if (!sc.SendRequest(ID_SEND, path_and_mode)) {
- fprintf(stderr, "adb: failed to send ID_SEND message '%s': %s\n",
- path_and_mode, strerror(errno));
- return false;
- }
-
- unsigned long long size = 0;
- if (show_progress) {
- // Determine local file size.
- struct stat st;
- if (stat(path, &st) == -1) {
- fprintf(stderr, "adb: cannot stat '%s': %s\n", path, strerror(errno));
- return false;
- }
-
- size = st.st_size;
- }
-
- int lfd = adb_open(path, O_RDONLY);
- if (lfd < 0) {
- fprintf(stderr, "adb: cannot open '%s': %s\n", path, 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) {
- fprintf(stderr, "adb: cannot read '%s': %s\n", path, 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;
-
- if (show_progress) {
- print_transfer_progress(sc.total_bytes, size);
- }
- }
-
- adb_close(lfd);
-
- syncmsg msg;
- msg.data.id = ID_DONE;
- msg.data.size = mtime;
- if (!WriteFdExactly(sc.fd, &msg.data, sizeof(msg.data))) {
- fprintf(stderr, "adb: failed to send ID_DONE message for '%s': %s\n",
- path, strerror(errno));
- return false;
- }
-
- return true;
-}
-
static bool sync_send(SyncConnection& sc, const char* lpath, const char* rpath,
- unsigned mtime, mode_t mode, bool show_progress)
+ unsigned mtime, mode_t mode)
{
std::string path_and_mode = android::base::StringPrintf("%s,%d", rpath, mode);
@@ -324,55 +376,68 @@
char buf[PATH_MAX];
ssize_t data_length = readlink(lpath, buf, PATH_MAX - 1);
if (data_length == -1) {
- fprintf(stderr, "adb: readlink '%s' failed: %s\n", lpath, strerror(errno));
+ sc.Error("readlink '%s' failed: %s", lpath, strerror(errno));
return false;
}
buf[data_length++] = '\0';
- if (!sc.SendSmallFile(path_and_mode.c_str(), 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
}
if (!S_ISREG(mode)) {
- fprintf(stderr, "adb: local file '%s' has unsupported mode: 0o%o\n", lpath, mode);
+ sc.Error("local file '%s' has unsupported mode: 0o%o", lpath, mode);
return false;
}
struct stat st;
if (stat(lpath, &st) == -1) {
- fprintf(stderr, "adb: failed to stat local file '%s': %s\n", lpath, strerror(errno));
+ sc.Error("failed to stat local file '%s': %s", lpath, strerror(errno));
return false;
}
if (st.st_size < SYNC_DATA_MAX) {
std::string data;
if (!android::base::ReadFileToString(lpath, &data)) {
- fprintf(stderr, "adb: failed to read all of '%s': %s\n", lpath, strerror(errno));
+ sc.Error("failed to read all of '%s': %s", lpath, strerror(errno));
return false;
}
- if (!sc.SendSmallFile(path_and_mode.c_str(), data.data(), data.size(), mtime)) return false;
+ 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, mtime, show_progress)) return false;
+ if (!sc.SendLargeFile(path_and_mode.c_str(), lpath, rpath, mtime)) {
+ return false;
+ }
}
return sc.CopyDone(lpath, rpath);
}
-static bool sync_recv(SyncConnection& sc, const char* rpath, const char* lpath, bool show_progress) {
+static bool sync_recv(SyncConnection& sc, const char* rpath, const char* lpath) {
+ sc.Print(rpath);
+
unsigned size = 0;
- if (show_progress) {
- if (!sync_stat(sc, rpath, nullptr, nullptr, &size)) return false;
- }
+ if (!sync_stat(sc, rpath, nullptr, nullptr, &size)) return false;
if (!sc.SendRequest(ID_RECV, rpath)) return false;
adb_unlink(lpath);
- mkdirs(lpath);
- int lfd = adb_creat(lpath, 0644);
- if (lfd < 0) {
- fprintf(stderr, "adb: cannot create '%s': %s\n", lpath, strerror(errno));
+ 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));
+ return false;
+ }
+
+ uint64_t bytes_copied = 0;
while (true) {
syncmsg msg;
if (!ReadFdExactly(sc.fd, &msg.data, sizeof(msg.data))) {
@@ -391,7 +456,7 @@
}
if (msg.data.size > sc.max) {
- fprintf(stderr, "adb: msg.data.size too large: %u (max %zu)\n", msg.data.size, sc.max);
+ sc.Error("msg.data.size too large: %u (max %zu)", msg.data.size, sc.max);
adb_close(lfd);
adb_unlink(lpath);
return false;
@@ -405,7 +470,7 @@
}
if (!WriteFdExactly(lfd, buffer, msg.data.size)) {
- fprintf(stderr, "adb: cannot write '%s': %s\n", lpath, strerror(errno));
+ sc.Error("cannot write '%s': %s", lpath, strerror(errno));
adb_close(lfd);
adb_unlink(lpath);
return false;
@@ -413,8 +478,15 @@
sc.total_bytes += msg.data.size;
- if (show_progress) {
- print_transfer_progress(sc.total_bytes, size);
+ bytes_copied += msg.data.size;
+
+ if (size == 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.
+ sc.Printf("%s: ?%%", rpath);
+ } else {
+ int percentage = static_cast<int>(bytes_copied * 100 / size);
+ sc.Printf("%s: %d%%", rpath, percentage);
}
}
@@ -422,369 +494,485 @@
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);
+ return sync_ls(sc, path, [](unsigned mode, unsigned size, unsigned time,
+ const char* name) {
+ printf("%08x %08x %08x %s\n", mode, size, time, name);
+ });
}
struct copyinfo
{
- copyinfo *next;
- const char *src;
- const char *dst;
+ std::string lpath;
+ std::string rpath;
unsigned int time;
unsigned int mode;
uint64_t size;
- int flag;
+ bool skip;
};
-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;
+static void ensure_trailing_separator(std::string& lpath, std::string& rpath) {
+ if (!adb_is_separator(lpath.back())) {
+ lpath.push_back(OS_PATH_SEPARATOR);
+ }
+ if (rpath.back() != '/') {
+ rpath.push_back('/');
+ }
+}
- copyinfo *ci = reinterpret_cast<copyinfo*>(malloc(sizeof(copyinfo) + ssize + dsize));
- if(ci == 0) {
- fprintf(stderr, "out of memory\n");
- abort();
+static copyinfo mkcopyinfo(std::string lpath, std::string rpath,
+ const std::string& name, unsigned int mode) {
+ copyinfo result;
+ result.lpath = std::move(lpath);
+ result.rpath = std::move(rpath);
+ ensure_trailing_separator(result.lpath, result.rpath);
+ result.lpath.append(name);
+ result.rpath.append(name);
+
+ if (S_ISDIR(mode)) {
+ ensure_trailing_separator(result.lpath, result.rpath);
}
- 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;
+ result.time = 0;
+ result.mode = mode;
+ result.size = 0;
+ result.skip = false;
+ return result;
}
static bool IsDotOrDotDot(const char* name) {
return name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'));
}
-static int local_build_list(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>* filelist,
+ 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) {
- fprintf(stderr, "adb: cannot open '%s': %s\n", lpath, strerror(errno));
- return -1;
+ sc.Error("cannot open '%s': %s", lpath.c_str(), strerror(errno));
+ return false;
}
- dirent *de;
+ 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)) {
- fprintf(stderr, "adb: skipping long path '%s%s'\n", 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)) {
- fprintf(stderr, "adb: skipping special file '%s'\n", 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 = mkcopyinfo(lpath, rpath, de->d_name, st.st_mode);
+ if (S_ISDIR(st.st_mode)) {
+ dirlist.push_back(ci);
} else {
- fprintf(stderr, "adb: cannot lstat '%s': %s\n",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;
+ }
+ filelist->push_back(ci);
}
}
// Close this directory and recurse.
dir.reset();
- for (ci = dirlist; ci != 0; ci = next) {
- next = ci->next;
- local_build_list(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 = mkcopyinfo(adb_dirname(lpath), adb_dirname(rpath),
+ adb_basename(lpath), S_IFDIR);
+ ci.skip = true;
+ filelist->push_back(ci);
+ return true;
}
- return 0;
+ for (const copyinfo& ci : dirlist) {
+ local_build_list(sc, filelist, 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_separator(lpath, rpath);
+
+ // Recursively build the list of files to copy.
+ std::vector<copyinfo> filelist;
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(&filelist, lpath, rpath)) {
+ if (!local_build_list(sc, &filelist, 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 : filelist) {
+ if (!sc.SendRequest(ID_STAT, ci.rpath.c_str())) {
+ return false;
+ }
}
- for(ci = filelist; ci != 0; ci = ci->next) {
+ for (copyinfo& ci : filelist) {
unsigned int timestamp, mode, size;
- if (!sync_finish_stat(sc, ×tamp, &mode, &size)) return false;
- if (size == ci->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 ((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) {
- fprintf(stderr, "%spush: %s -> %s\n", list_only ? "would " : "", ci->src, ci->dst);
- if (!list_only && !sync_send(sc, ci->src, ci->dst, ci->time, ci->mode, false)) {
- return false;
+
+ for (const copyinfo& ci : filelist) {
+ if (!ci.skip) {
+ if (list_only) {
+ sc.Error("would push: %s -> %s", ci.lpath.c_str(),
+ ci.rpath.c_str());
+ } else {
+ if (!sync_send(sc, ci.lpath.c_str(), ci.rpath.c_str(), ci.time,
+ ci.mode)) {
+ return false;
+ }
}
pushed++;
} else {
skipped++;
}
- free(ci);
}
- fprintf(stderr, "%d file%s pushed. %d file%s skipped.\n",
- pushed, (pushed == 1) ? "" : "s",
- skipped, (skipped == 1) ? "" : "s");
-
+ 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 show_progress) {
+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)) {
- fprintf(stderr, "adb: cannot stat '%s': %s\n", lpath, strerror(errno));
- return false;
+ 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;
+ }
+ }
}
- if (S_ISDIR(st.st_mode)) {
- return copy_local_dir_remote(sc, lpath, rpath, false, 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();
+ }
+ success &= sync_send(sc, src_path, dst_path, st.st_mtime, st.st_mode);
}
- 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();
- }
- return sync_send(sc, lpath, rpath, st.st_mtime, st.st_mode, show_progress);
+ return success;
}
-
-struct sync_ls_build_list_cb_args {
- 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 = (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 {
- fprintf(stderr, "adb: skipping special file '%s'\n", name);
- }
-}
-
-static bool remote_build_list(SyncConnection& sc, copyinfo **filelist,
- const char *rpath, const char *lpath) {
- copyinfo *dirlist = NULL;
- sync_ls_build_list_cb_args args;
-
- args.filelist = filelist;
- args.dirlist = &dirlist;
- args.rpath = rpath;
- args.lpath = lpath;
+static bool remote_build_list(SyncConnection& sc,
+ std::vector<copyinfo>* filelist,
+ const std::string& rpath,
+ const std::string& lpath) {
+ std::vector<copyinfo> dirlist;
+ bool empty_dir = true;
// Put the files/dirs in rpath on the lists.
- if (!sync_ls(sc, rpath, sync_ls_build_list_cb, (void *)&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 = mkcopyinfo(lpath, rpath, name, mode);
+ if (S_ISDIR(mode)) {
+ dirlist.push_back(ci);
+ } else {
+ if (S_ISREG(mode)) {
+ ci.time = time;
+ ci.size = size;
+ } else if (S_ISLNK(mode)) {
+ sc.Warning("skipping symlink '%s'", name);
+ ci.skip = true;
+ } else {
+ sc.Warning("skipping special file '%s'", name);
+ ci.skip = true;
+ }
+ filelist->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) {
+ filelist->push_back(mkcopyinfo(adb_dirname(lpath), adb_dirname(rpath),
+ adb_basename(rpath), S_IFDIR));
+ return true;
+ }
+
// 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, filelist, 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_separator(lpath, rpath);
// Recursively build the list of files to copy.
- fprintf(stderr, "pull: building file list...\n");
- copyinfo* filelist = nullptr;
- if (!remote_build_list(sc, &filelist, rpath_clean.c_str(), lpath_clean.c_str())) return false;
+ sc.Print("pull: building file list...");
+ std::vector<copyinfo> filelist;
+ if (!remote_build_list(sc, &filelist, rpath.c_str(), lpath.c_str())) {
+ return false;
+ }
int pulled = 0;
int skipped = 0;
- copyinfo* ci = filelist;
- while (ci) {
- copyinfo* next = ci->next;
- if (ci->flag == 0) {
- fprintf(stderr, "pull: %s -> %s\n", ci->src, ci->dst);
- if (!sync_recv(sc, ci->src, ci->dst, false)) {
+ for (const copyinfo &ci : filelist) {
+ if (!ci.skip) {
+ sc.Printf("pull: %s -> %s", ci.rpath.c_str(), ci.lpath.c_str());
+
+ 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;
}
- fprintf(stderr, "%d file%s pulled. %d file%s skipped.\n",
- pulled, (pulled == 1) ? "" : "s",
- skipped, (skipped == 1) ? "" : "s");
+ 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, bool show_progress, 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) {
- fprintf(stderr, "adb: remote object '%s' does not exist\n", 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, show_progress)) {
+ 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;
}
}
- return true;
- } else if (S_ISDIR(mode)) {
- return copy_remote_dir_local(sc, rpath, lpath, copy_attrs);
}
- fprintf(stderr, "adb: remote object '%s' not a file or directory\n", rpath);
- return false;
+ for (const char* src_path : srcs) {
+ const char* dst_path = dst;
+ unsigned src_mode, src_time;
+ if (!sync_stat(sc, src_path, &src_time, &src_mode, nullptr)) {
+ return false;
+ }
+ if (src_mode == 0) {
+ sc.Error("remote object '%s' does not exist", src_path);
+ success = false;
+ continue;
+ }
+
+ if (S_ISREG(src_mode)) {
+ 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();
+ }
+ if (!sync_recv(sc, src_path, dst_path)) {
+ success = false;
+ continue;
+ } else {
+ if (copy_attrs &&
+ set_time_and_mode(dst_path, src_time, src_mode) != 0) {
+ success = false;
+ continue;
+ }
+ }
+ } else if (S_ISDIR(src_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;
+ }
+ 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 {
+ sc.Error("remote object '%s' not a file or directory", src_path);
+ success = false;
+ continue;
+ }
+ }
+
+ return success;
}
bool do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only) {
- fprintf(stderr, "syncing %s...\n", rpath.c_str());
-
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 67ed3fc..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 show_progress);
+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, bool show_progress, 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
new file mode 100644
index 0000000..e8fe6c9
--- /dev/null
+++ b/adb/line_printer.cpp
@@ -0,0 +1,127 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// 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 "line_printer.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <termios.h>
+#include <sys/time.h>
+#endif
+
+// Make sure printf is really adb_printf which works for UTF-8 on Windows.
+#include <sysdeps.h>
+
+// Stuff from ninja's util.h that's needed below.
+#include <vector>
+using namespace std;
+string ElideMiddle(const string& str, size_t width) {
+ const int kMargin = 3; // Space for "...".
+ string result = str;
+ if (result.size() + kMargin > width) {
+ size_t elide_size = (width - kMargin) / 2;
+ result = result.substr(0, elide_size)
+ + "..."
+ + result.substr(result.size() - elide_size, elide_size);
+ }
+ return result;
+}
+
+LinePrinter::LinePrinter() : have_blank_line_(true) {
+#ifndef _WIN32
+ const char* term = getenv("TERM");
+ 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
+ // buffering. However, for Win32, the behavior is the same as _IOFBF
+ // - Full Buffering."
+ setvbuf(stdout, NULL, _IONBF, 0);
+ console_ = GetStdHandle(STD_OUTPUT_HANDLE);
+ CONSOLE_SCREEN_BUFFER_INFO csbi;
+ smart_terminal_ = GetConsoleScreenBufferInfo(console_, &csbi);
+#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 (!smart_terminal_) {
+ Out(to_print);
+ return;
+ }
+
+ // 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 (type == INFO) {
+#ifdef _WIN32
+ CONSOLE_SCREEN_BUFFER_INFO csbi;
+ GetConsoleScreenBufferInfo(console_, &csbi);
+
+ // 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
+ // printf use WriteConsoleOutput which updates the contents of the buffer,
+ // but doesn't move the cursor position.
+ COORD buf_size = { csbi.dwSize.X, 1 };
+ COORD zero_zero = { 0, 0 };
+ SMALL_RECT target = {
+ csbi.dwCursorPosition.X, csbi.dwCursorPosition.Y,
+ static_cast<SHORT>(csbi.dwCursorPosition.X + csbi.dwSize.X - 1),
+ csbi.dwCursorPosition.Y
+ };
+ vector<CHAR_INFO> char_data(csbi.dwSize.X);
+ for (size_t i = 0; i < static_cast<size_t>(csbi.dwSize.X); ++i) {
+ // TODO: UnicodeChar instead of AsciiChar, to_print_wide[i].
+ char_data[i].Char.AsciiChar = i < to_print.size() ? to_print[i] : ' ';
+ char_data[i].Attributes = csbi.wAttributes;
+ }
+ // TODO: WriteConsoleOutputW.
+ WriteConsoleOutput(console_, &char_data[0], buf_size, zero_zero, &target);
+#else
+ // Limit output to width of the terminal if provided so we don't cause
+ // line-wrapping.
+ winsize size;
+ if ((ioctl(0, TIOCGWINSZ, &size) == 0) && size.ws_col) {
+ to_print = ElideMiddle(to_print, size.ws_col);
+ }
+ Out(to_print);
+ printf("\x1B[K"); // Clear to end of line.
+ fflush(stdout);
+#endif
+
+ have_blank_line_ = false;
+ } else {
+ Out(to_print);
+ Out("\n");
+ have_blank_line_ = true;
+ }
+}
+
+void LinePrinter::KeepInfoLine() {
+ if (!have_blank_line_) Out("\n");
+}
diff --git a/adb/line_printer.h b/adb/line_printer.h
new file mode 100644
index 0000000..42345e2
--- /dev/null
+++ b/adb/line_printer.h
@@ -0,0 +1,50 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// 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 NINJA_LINE_PRINTER_H_
+#define NINJA_LINE_PRINTER_H_
+
+#include <stddef.h>
+#include <string>
+
+/// Prints lines of text, possibly overprinting previously printed lines
+/// if the terminal supports it.
+struct LinePrinter {
+ LinePrinter();
+
+ bool is_smart_terminal() const { return smart_terminal_; }
+ void set_smart_terminal(bool smart) { smart_terminal_ = smart; }
+
+ 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);
+
+ /// If there's an INFO line, keep it. If not, do nothing.
+ void KeepInfoLine();
+
+ private:
+ /// Whether we can do fancy terminal control codes.
+ bool smart_terminal_;
+
+ /// Whether the caret is at the beginning of a blank line.
+ bool have_blank_line_;
+
+#ifdef _WIN32
+ void* console_;
+#endif
+};
+
+#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 e24b470..523353a 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
@@ -184,15 +184,18 @@
adb_close(fd);
}
-void reverse_service(int fd, void* arg)
-{
- const char* command = reinterpret_cast<const char*>(arg);
-
- if (handle_forward_request(command, kTransportAny, NULL, fd) < 0) {
- SendFail(fd, "not a reverse forwarding command");
+int reverse_service(const char* command) {
+ int s[2];
+ if (adb_socketpair(s)) {
+ PLOG(ERROR) << "cannot create service socket pair.";
+ return -1;
}
- free(arg);
- adb_close(fd);
+ VLOG(SERVICES) << "service socketpair: " << s[0] << ", " << s[1];
+ if (handle_forward_request(command, kTransportAny, nullptr, s[1]) < 0) {
+ SendFail(s[1], "not a reverse forwarding command");
+ }
+ adb_close(s[1]);
+ return s[0];
}
// Shell service string can look like:
@@ -210,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) {
@@ -221,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
@@ -305,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)) {
@@ -322,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;
@@ -335,15 +340,7 @@
} else if(!strncmp(name, "usb:", 4)) {
ret = create_service_thread(restart_usb_service, NULL);
} else if (!strncmp(name, "reverse:", 8)) {
- char* cookie = strdup(name + 8);
- if (cookie == NULL) {
- ret = -1;
- } else {
- ret = create_service_thread(reverse_service, cookie);
- if (ret < 0) {
- free(cookie);
- }
- }
+ ret = reverse_service(name + 8);
} else if(!strncmp(name, "disable-verity:", 15)) {
ret = create_service_thread(set_verity_enabled_state_service, (void*)0);
} else if(!strncmp(name, "enable-verity:", 15)) {
diff --git a/adb/shell_service.cpp b/adb/shell_service.cpp
index 544afce..3fc70b0 100644
--- a/adb/shell_service.cpp
+++ b/adb/shell_service.cpp
@@ -83,13 +83,14 @@
#include <errno.h>
#include <pty.h>
+#include <pwd.h>
#include <sys/select.h>
#include <termios.h>
#include <memory>
-#include <base/logging.h>
-#include <base/stringprintf.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
#include <paths.h>
#include "adb.h"
@@ -174,8 +175,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_; }
@@ -206,6 +207,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;
@@ -219,9 +221,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() {
@@ -281,6 +286,18 @@
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 (!terminal_type_.empty()) {
+ setenv("TERM", terminal_type_.c_str(), 1);
+ }
+
if (is_interactive()) {
execl(_PATH_BSHELL, _PATH_BSHELL, "-", nullptr);
} else {
@@ -503,6 +520,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;
@@ -521,8 +550,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;
}
@@ -623,13 +651,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_adb.py b/adb/test_adb.py
index 5bda22f..0f1b034 100644
--- a/adb/test_adb.py
+++ b/adb/test_adb.py
@@ -21,8 +21,11 @@
"""
from __future__ import print_function
+import contextlib
import os
import random
+import socket
+import struct
import subprocess
import threading
import unittest
@@ -140,6 +143,71 @@
subprocess.check_output(['adb', '-P', str(port), 'kill-server'],
stderr=subprocess.STDOUT)
+ # Use SO_LINGER to cause TCP RST segment to be sent on socket close.
+ def _reset_socket_on_close(self, sock):
+ # The linger structure is two shorts on Windows, but two ints on Unix.
+ linger_format = 'hh' if os.name == 'nt' else 'ii'
+ l_onoff = 1
+ l_linger = 0
+
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER,
+ struct.pack(linger_format, l_onoff, l_linger))
+ # Verify that we set the linger structure properly by retrieving it.
+ linger = sock.getsockopt(socket.SOL_SOCKET, socket.SO_LINGER, 16)
+ self.assertEqual((l_onoff, l_linger),
+ struct.unpack_from(linger_format, linger))
+
+ def test_emu_kill(self):
+ """Ensure that adb emu kill works.
+
+ Bug: https://code.google.com/p/android/issues/detail?id=21021
+ """
+ port = 12345
+
+ with contextlib.closing(
+ socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as listener:
+ # Use SO_REUSEADDR so subsequent runs of the test can grab the port
+ # even if it is in TIME_WAIT.
+ listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ listener.bind(('127.0.0.1', port))
+ listener.listen(4)
+
+ # Now that listening has started, start adb emu kill, telling it to
+ # connect to our mock emulator.
+ p = subprocess.Popen(
+ ['adb', '-s', 'emulator-' + str(port), 'emu', 'kill'],
+ stderr=subprocess.STDOUT)
+
+ accepted_connection, addr = listener.accept()
+ with contextlib.closing(accepted_connection) as conn:
+ # If WSAECONNABORTED (10053) is raised by any socket calls,
+ # then adb probably isn't reading the data that we sent it.
+ conn.sendall('Android Console: type \'help\' for a list ' +
+ 'of commands\r\n')
+ conn.sendall('OK\r\n')
+
+ with contextlib.closing(conn.makefile()) as f:
+ self.assertEqual('kill\n', f.readline())
+ self.assertEqual('quit\n', f.readline())
+
+ conn.sendall('OK: killing emulator, bye bye\r\n')
+
+ # Use SO_LINGER to send TCP RST segment to test whether adb
+ # ignores WSAECONNRESET on Windows. This happens with the
+ # real emulator because it just calls exit() without closing
+ # the socket or calling shutdown(SD_SEND). At process
+ # termination, Windows sends a TCP RST segment for every
+ # open socket that shutdown(SD_SEND) wasn't used on.
+ self._reset_socket_on_close(conn)
+
+ # Wait for adb to finish, so we can check return code.
+ p.communicate()
+
+ # If this fails, adb probably isn't ignoring WSAECONNRESET when
+ # reading the response from the adb emu kill command (on Windows).
+ self.assertEqual(0, p.returncode)
+
+
def main():
random.seed(0)
if len(adb.get_devices()) > 0:
diff --git a/adb/test_device.py b/adb/test_device.py
new file mode 100644
index 0000000..955b67a
--- /dev/null
+++ b/adb/test_device.py
@@ -0,0 +1,951 @@
+#!/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_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..988007d 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -26,22 +26,26 @@
#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"
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 +674,11 @@
adb_mutex_lock(&transport_lock);
for (const auto& t : transport_list) {
if (t->connection_state == kCsNoPerm) {
- *error_out = "insufficient permissions for device";
+ *error_out = UsbNoPermissionsLongHelpText();
+ // If we couldn't figure out a reasonable help message default to something generic.
+ if (error_out->empty()) {
+ *error_out = "insufficient permissions for device";
+ }
continue;
}
@@ -744,17 +752,20 @@
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: {
+ std::string message = UsbNoPermissionsShortHelpText();
+ return message.empty() ? "no permissions" : message;
+ }
+ case kCsSideload: return "sideload";
+ case kCsUnauthorized: return "unauthorized";
+ default: return "unknown";
}
}
@@ -780,7 +791,8 @@
const FeatureSet& supported_features() {
// Local static allocation to avoid global non-POD variables.
static const FeatureSet* features = new FeatureSet{
- kFeatureShell2
+ kFeatureShell2,
+ 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 +871,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..e887a94 100644
--- a/adb/usb_linux.cpp
+++ b/adb/usb_linux.cpp
@@ -22,6 +22,7 @@
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
+#include <grp.h>
#include <linux/usb/ch9.h>
#include <linux/usbdevice_fs.h>
#include <linux/version.h>
@@ -39,9 +40,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 +82,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);
@@ -595,3 +596,54 @@
fatal_errno("cannot create device_poll thread");
}
}
+
+static const char kPermissionsHelpUrl[] = "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() {
+ errno = 0;
+ group* plugdev_group = getgrnam("plugdev");
+
+ if (plugdev_group == nullptr) {
+ if (errno != 0) {
+ D("failed to read plugdev group info: %s", strerror(errno));
+ }
+ // 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";
+}
+
+// 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 = "USB permission failure";
+
+ 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/usb_linux_client.cpp b/adb/usb_linux_client.cpp
index 3495a71..c5e7452 100644
--- a/adb/usb_linux_client.cpp
+++ b/adb/usb_linux_client.cpp
@@ -456,56 +456,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;
+
+ // Writes larger than 16k fail on some devices (seed with 3.10.49-g209ea2f in particular).
+ const char* buf = static_cast<const char*>(data);
+ while (len > 0) {
+ int write_len = (len > 16384) ? 16384 : 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 n = adb_read(h->bulk_out, buf, 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;
}
@@ -587,3 +571,12 @@
{
h->kick(h);
}
+
+// kCsNoPerm is a host-side issue, we can just ignore it here.
+std::string UsbNoPermissionsShortHelpText() {
+ return "";
+}
+
+std::string UsbNoPermissionsLongHelpText() {
+ return "";
+}
diff --git a/adb/usb_osx.cpp b/adb/usb_osx.cpp
index e0dcc756..f494795 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"
@@ -552,3 +552,12 @@
handle->interface = 0;
}
}
+
+// kCsNoPerm is Linux-only.
+std::string UsbNoPermissionsShortHelpText() {
+ return "";
+}
+
+std::string UsbNoPermissionsLongHelpText() {
+ return "";
+}
diff --git a/adb/usb_windows.cpp b/adb/usb_windows.cpp
index d811b24..9124685 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);
}
@@ -666,3 +659,12 @@
}
adb_mutex_unlock(&usb_lock);
}
+
+// kCsNoPerm is Linux-only.
+std::string UsbNoPermissionsShortHelpText() {
+ return "";
+}
+
+std::string UsbNoPermissionsLongHelpText() {
+ return "";
+}
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 89%
rename from base/include/base/parseint.h
rename to base/include/android-base/parseint.h
index 9ecbfbc..0543795 100644
--- a/base/include/base/parseint.h
+++ b/base/include/android-base/parseint.h
@@ -31,9 +31,10 @@
template <typename T>
bool ParseUint(const char* s, T* out,
T max = std::numeric_limits<T>::max()) {
+ int base = (s[0] == '0' && s[1] == 'x') ? 16 : 10;
errno = 0;
char* end;
- unsigned long long int result = strtoull(s, &end, 10);
+ unsigned long long int result = strtoull(s, &end, base);
if (errno != 0 || s == end || *end != '\0') {
return false;
}
@@ -52,9 +53,10 @@
bool ParseInt(const char* s, T* out,
T min = std::numeric_limits<T>::min(),
T max = std::numeric_limits<T>::max()) {
+ int base = (s[0] == '0' && s[1] == 'x') ? 16 : 10;
errno = 0;
char* end;
- long long int result = strtoll(s, &end, 10);
+ long long int result = strtoll(s, &end, base);
if (errno != 0 || s == end || *end != '\0') {
return false;
}
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 86%
rename from base/include/base/strings.h
rename to base/include/android-base/strings.h
index 638f845..20da144 100644
--- a/base/include/base/strings.h
+++ b/base/include/android-base/strings.h
@@ -36,8 +36,8 @@
std::string Trim(const std::string& s);
// Joins a container of things into a single string, using the given separator.
-template <typename ContainerT>
-std::string Join(const ContainerT& things, char separator) {
+template <typename ContainerT, typename SeparatorT>
+std::string Join(const ContainerT& things, SeparatorT separator) {
if (things.empty()) {
return "";
}
@@ -53,6 +53,8 @@
// We instantiate the common cases in strings.cpp.
extern template std::string Join(const std::vector<std::string>&, char);
extern template std::string Join(const std::vector<const char*>&, char);
+extern template std::string Join(const std::vector<std::string>&, const std::string&);
+extern template std::string Join(const std::vector<const char*>&, const std::string&);
// Tests whether 's' starts with 'prefix'.
bool StartsWith(const std::string& s, const char* prefix);
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 e19c6e3..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>
@@ -66,3 +66,13 @@
ASSERT_TRUE(android::base::ParseUint("0123", &u));
ASSERT_EQ(123u, u);
}
+
+TEST(parseint, explicit_hex) {
+ int i;
+ ASSERT_TRUE(android::base::ParseInt("0x123", &i));
+ ASSERT_EQ(0x123, i);
+
+ unsigned int u;
+ ASSERT_TRUE(android::base::ParseUint("0x123", &u));
+ ASSERT_EQ(0x123u, u);
+}
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 bac983b..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>
@@ -83,6 +83,8 @@
// aid compile time and binary size.
template std::string Join(const std::vector<std::string>&, char);
template std::string Join(const std::vector<const char*>&, char);
+template std::string Join(const std::vector<std::string>&, const std::string&);
+template std::string Join(const std::vector<const char*>&, const std::string&);
bool StartsWith(const std::string& s, const char* prefix) {
return s.compare(0, strlen(prefix), prefix) == 0;
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..565963c 100644
--- a/crash_reporter/Android.mk
+++ b/crash_reporter/Android.mk
@@ -43,11 +43,12 @@
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 +61,19 @@
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 +104,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 +142,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..0afa895 100644
--- a/debuggerd/debuggerd.cpp
+++ b/debuggerd/debuggerd.cpp
@@ -34,9 +34,10 @@
#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,160 +337,146 @@
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;
- }
+ // 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));
+ 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);
- }
+ 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));
+ return;
+ }
- 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);
- }
- }
-
- // resume stopped process (so it can crash in peace).
- kill(request.pid, SIGCONT);
-
- // 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);
- }
+ std::unique_ptr<char> tombstone_path;
+ 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");
+ tombstone_path.reset(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:
+#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.reset(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;
}
- if (fd >= 0) {
- close(fd);
+
+ if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) {
+ if (tombstone_path) {
+ write(fd, tombstone_path.get(), strlen(tombstone_path.get()));
+ }
+ }
+
+ 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);
+ }
+ }
+
+ // resume stopped process (so it can crash in peace).
+ kill(request.pid, SIGCONT);
+
+ // 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);
}
}
@@ -531,11 +518,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 4be2e5d..e43fe96 100644
--- a/debuggerd/debuggerd.rc
+++ b/debuggerd/debuggerd.rc
@@ -1,3 +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 c6e7bf2..35b5af3 100644
--- a/debuggerd/debuggerd64.rc
+++ b/debuggerd/debuggerd64.rc
@@ -1,3 +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 65c1904..9959f2e 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..5f422e3 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>
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..5ca88cb 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -49,7 +49,7 @@
libutils \
liblog \
libz \
- libbase
+ libbase \
LOCAL_STATIC_LIBRARIES_darwin := libselinux
LOCAL_STATIC_LIBRARIES_linux := libselinux
@@ -83,5 +83,6 @@
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
diff --git a/fastboot/engine.cpp b/fastboot/engine.cpp
index a0e990a..ac5a17a 100644
--- a/fastboot/engine.cpp
+++ b/fastboot/engine.cpp
@@ -75,41 +75,17 @@
-int fb_getvar(struct usb_handle *usb, char *response, const char *fmt, ...)
-{
- char cmd[CMD_SIZE] = "getvar:";
- int getvar_len = strlen(cmd);
- va_list args;
+bool fb_getvar(Transport* transport, const std::string& key, std::string* value) {
+ std::string cmd = "getvar:";
+ cmd += key;
- response[FB_RESPONSE_SZ] = '\0';
- va_start(args, fmt);
- vsnprintf(cmd + getvar_len, sizeof(cmd) - getvar_len, fmt, args);
- va_end(args);
- cmd[CMD_SIZE - 1] = '\0';
- return fb_command_response(usb, cmd, response);
-}
-
-
-/* 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.
- */
-int fb_format_supported(usb_handle *usb, const char *partition, const char *type_override)
-{
- char fs_type[FB_RESPONSE_SZ + 1] = {0,};
- int status;
-
- if (type_override) {
- return !!fs_get_generator(type_override);
+ char buf[FB_RESPONSE_SZ + 1];
+ memset(buf, 0, sizeof(buf));
+ if (fb_command_response(transport, cmd.c_str(), buf)) {
+ return false;
}
- status = fb_getvar(usb, fs_type, "partition-type:%s", partition);
- if (status) {
- return 0;
- }
- return !!fs_get_generator(fs_type);
+ *value = buf;
+ return true;
}
static int cb_default(Action* a, int status, const char* resp) {
@@ -154,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;
@@ -347,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];
@@ -367,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");
}
@@ -394,8 +377,3 @@
fprintf(stderr,"finished. total time: %.3fs\n", (now() - start));
return status;
}
-
-int fb_queue_is_empty(void)
-{
- return (action_list == nullptr);
-}
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 0901310..0f1d22f 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -42,13 +42,21 @@
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
+#include <functional>
+#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 "fastboot.h"
#include "fs.h"
+#include "transport.h"
+#include "usb.h"
#ifndef O_BINARY
#define O_BINARY 0
@@ -73,6 +81,8 @@
static unsigned second_offset = 0x00f00000;
static unsigned tags_offset = 0x00000100;
+static const std::string convert_fbe_marker_filename("convert_fbe");
+
enum fb_buffer_type {
FB_BUFFER,
FB_BUFFER_SPARSE,
@@ -216,16 +226,16 @@
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");
}
@@ -271,6 +281,10 @@
" 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"
@@ -291,12 +305,38 @@
" -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"
+#if !defined(_WIN32)
+ " --wipe-and-use-fbe On devices which support it,\n"
+ " erase userdata and cache, and\n"
+ " enable file-based encryption\n"
+#endif
+ " --unbuffered Do not buffer input or output.\n"
+ " --version Display version.\n"
+ " -h, --help show this message.\n"
);
}
@@ -419,8 +459,60 @@
#define tmpfile win32_tmpfile
+static std::string make_temporary_directory() {
+ fprintf(stderr, "make_temporary_directory not supported under Windows, sorry!");
+ return "";
+}
+
+#else
+
+static std::string make_temporary_directory() {
+ const char *tmpdir = getenv("TMPDIR");
+ if (tmpdir == nullptr) {
+ tmpdir = P_tmpdir;
+ }
+ std::string result = std::string(tmpdir) + "/fastboot_userdata_XXXXXX";
+ if (mkdtemp(&result[0]) == NULL) {
+ fprintf(stderr, "Unable to create temporary directory: %s\n",
+ strerror(errno));
+ return "";
+ }
+ return result;
+}
+
#endif
+static std::string create_fbemarker_tmpdir() {
+ std::string dir = make_temporary_directory();
+ if (dir.empty()) {
+ fprintf(stderr, "Unable to create local temp directory for FBE marker\n");
+ return "";
+ }
+ std::string marker_file = dir + "/" + convert_fbe_marker_filename;
+ int fd = open(marker_file.c_str(), O_CREAT | O_WRONLY | O_CLOEXEC, 0666);
+ if (fd == -1) {
+ fprintf(stderr, "Unable to create FBE marker file %s locally: %d, %s\n",
+ marker_file.c_str(), errno, strerror(errno));
+ return "";
+ }
+ close(fd);
+ return dir;
+}
+
+static void delete_fbemarker_tmpdir(const std::string& dir) {
+ std::string marker_file = dir + "/" + convert_fbe_marker_filename;
+ if (unlink(marker_file.c_str()) == -1) {
+ fprintf(stderr, "Unable to delete FBE marker file %s locally: %d, %s\n",
+ marker_file.c_str(), errno, strerror(errno));
+ return;
+ }
+ if (rmdir(dir.c_str()) == -1) {
+ fprintf(stderr, "Unable to delete FBE marker directory %s locally: %d, %s\n",
+ dir.c_str(), errno, strerror(errno));
+ return;
+ }
+}
+
static int unzip_to_file(ZipArchiveHandle zip, char* entry_name) {
FILE* fp = tmpfile();
if (fp == nullptr) {
@@ -572,25 +664,29 @@
return out_s;
}
-static int64_t get_target_sparse_limit(struct usb_handle *usb)
-{
- int64_t limit = 0;
- char response[FB_RESPONSE_SZ + 1];
- int status = fb_getvar(usb, response, "max-download-size");
-
- if (!status) {
- limit = strtoul(response, nullptr, 0);
- if (limit > 0) {
- fprintf(stderr, "target reported max download size of %" PRId64 " bytes\n",
- limit);
- }
+static int64_t get_target_sparse_limit(Transport* transport) {
+ std::string 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());
+ return 0;
+ }
+ if (limit > 0) {
+ fprintf(stderr, "target reported max download size of %" PRId64 " bytes\n", limit);
+ }
return limit;
}
-static int64_t get_sparse_limit(struct usb_handle *usb, int64_t size)
-{
+static int64_t get_sparse_limit(Transport* transport, int64_t size) {
int64_t limit;
if (sparse_limit == 0) {
@@ -599,7 +695,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;
@@ -615,26 +711,25 @@
return 0;
}
-/* 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 int needs_erase(usb_handle* usb, const char *part)
-{
- /* The function fb_format_supported() currently returns the value
- * we want, so just call it.
- */
- return fb_format_supported(usb, part, nullptr);
+// 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(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) {
@@ -653,8 +748,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;
@@ -663,7 +757,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)
@@ -686,10 +780,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);
@@ -703,7 +889,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));
@@ -734,17 +920,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);
@@ -766,7 +956,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));
@@ -783,16 +973,20 @@
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);
}
}
@@ -877,125 +1071,145 @@
return num;
}
-static void fb_perform_format(usb_handle* usb,
- const char *partition, int skip_if_not_supported,
- const char *type_override, const char *size_override) {
- char pTypeBuff[FB_RESPONSE_SZ + 1], pSizeBuff[FB_RESPONSE_SZ + 1];
- char *pType = pTypeBuff;
- char *pSize = pSizeBuff;
- unsigned int limit = INT_MAX;
+static void fb_perform_format(Transport* transport,
+ const char* partition, int skip_if_not_supported,
+ const char* type_override, const char* size_override,
+ const std::string& initial_dir) {
+ std::string partition_type, partition_size;
+
struct fastboot_buffer buf;
- const char *errMsg = nullptr;
- const struct fs_generator *gen;
- uint64_t pSz;
- int status;
+ const char* errMsg = nullptr;
+ const struct fs_generator* gen = nullptr;
int fd;
- if (target_sparse_limit > 0 && target_sparse_limit < limit)
+ unsigned int limit = INT_MAX;
+ if (target_sparse_limit > 0 && target_sparse_limit < limit) {
limit = target_sparse_limit;
- if (sparse_limit > 0 && sparse_limit < limit)
+ }
+ if (sparse_limit > 0 && sparse_limit < limit) {
limit = sparse_limit;
+ }
- status = fb_getvar(usb, pType, "partition-type:%s", partition);
- if (status) {
+ if (!fb_getvar(transport, std::string("partition-type:") + partition, &partition_type)) {
errMsg = "Can't determine partition type.\n";
goto failed;
}
if (type_override) {
- if (strcmp(type_override, pType)) {
- fprintf(stderr,
- "Warning: %s type is %s, but %s was requested for formating.\n",
- partition, pType, type_override);
+ if (partition_type != type_override) {
+ fprintf(stderr, "Warning: %s type is %s, but %s was requested for formatting.\n",
+ partition, partition_type.c_str(), type_override);
}
- pType = (char *)type_override;
+ partition_type = type_override;
}
- status = fb_getvar(usb, pSize, "partition-size:%s", partition);
- if (status) {
+ if (!fb_getvar(transport, std::string("partition-size:") + partition, &partition_size)) {
errMsg = "Unable to get partition size\n";
goto failed;
}
if (size_override) {
- if (strcmp(size_override, pSize)) {
- fprintf(stderr,
- "Warning: %s size is %s, but %s was requested for formating.\n",
- partition, pSize, size_override);
+ if (partition_size != size_override) {
+ fprintf(stderr, "Warning: %s size is %s, but %s was requested for formatting.\n",
+ partition, partition_size.c_str(), size_override);
}
- pSize = (char *)size_override;
+ 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(pType);
+ gen = fs_get_generator(partition_type);
if (!gen) {
if (skip_if_not_supported) {
fprintf(stderr, "Erase successful, but not automatically formatting.\n");
- fprintf(stderr, "File system type %s not supported.\n", pType);
+ fprintf(stderr, "File system type %s not supported.\n", partition_type.c_str());
return;
}
- fprintf(stderr, "Formatting is not supported for filesystem with type '%s'.\n", pType);
+ fprintf(stderr, "Formatting is not supported for file system with type '%s'.\n",
+ partition_type.c_str());
return;
}
- pSz = strtoll(pSize, (char **)nullptr, 16);
+ int64_t size;
+ if (!android::base::ParseInt(partition_size.c_str(), &size)) {
+ fprintf(stderr, "Couldn't parse partition size '%s'.\n", partition_size.c_str());
+ return;
+ }
fd = fileno(tmpfile());
- if (fs_generator_generate(gen, fd, pSz)) {
+ if (fs_generator_generate(gen, fd, size, initial_dir)) {
+ fprintf(stderr, "Cannot generate image: %s\n", strerror(errno));
close(fd);
- fprintf(stderr, "Cannot generate image.\n");
return;
}
- if (load_buf_fd(usb, fd, &buf)) {
- fprintf(stderr, "Cannot read image.\n");
+ if (load_buf_fd(transport, fd, &buf)) {
+ fprintf(stderr, "Cannot read image: %s\n", strerror(errno));
close(fd);
return;
}
flash_buf(partition, &buf);
-
return;
-
failed:
if (skip_if_not_supported) {
fprintf(stderr, "Erase successful, but not automatically formatting.\n");
- if (errMsg)
- fprintf(stderr, "%s", errMsg);
+ if (errMsg) fprintf(stderr, "%s", errMsg);
}
fprintf(stderr,"FAILED (%s)\n", fb_get_error());
}
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;
+ bool set_fbe_marker = false;
void *data;
int64_t sz;
- int status;
- int c;
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'},
+#if !defined(_WIN32)
+ {"wipe-and-use-fbe", no_argument, 0, 0},
+#endif
{0, 0, 0, 0}
};
serial = getenv("ANDROID_SERIAL");
while (1) {
- 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;
@@ -1047,7 +1261,7 @@
erase_first = false;
break;
case 'w':
- wants_wipe = 1;
+ wants_wipe = true;
break;
case '?':
return 1;
@@ -1058,6 +1272,17 @@
} 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);
+#if !defined(_WIN32)
+ } else if (strcmp("wipe-and-use-fbe", longopts[longindex].name) == 0) {
+ wants_wipe = true;
+ set_fbe_marker = true;
+#endif
+ } else {
+ fprintf(stderr, "Internal error in options processing for %s\n",
+ longopts[longindex].name);
+ return 1;
}
break;
default:
@@ -1068,7 +1293,7 @@
argc -= optind;
argv += optind;
- if (argc == 0 && !wants_wipe) {
+ if (argc == 0 && !wants_wipe && !wants_set_active) {
usage();
return 1;
}
@@ -1084,21 +1309,41 @@
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")) {
+ if (!strcmp(*argv, "getvar")) {
require(2);
fb_queue_display(argv[1], argv[1]);
skip(2);
} 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;
@@ -1126,10 +1371,15 @@
}
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);
@@ -1140,18 +1390,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");
@@ -1189,12 +1439,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;
@@ -1210,20 +1463,29 @@
}
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")) {
@@ -1249,10 +1511,29 @@
}
if (wants_wipe) {
+ fprintf(stderr, "wiping userdata...\n");
fb_queue_erase("userdata");
- fb_perform_format(usb, "userdata", 1, nullptr, nullptr);
- fb_queue_erase("cache");
- fb_perform_format(usb, "cache", 1, nullptr, nullptr);
+ if (set_fbe_marker) {
+ fprintf(stderr, "setting FBE marker...\n");
+ std::string initial_userdata_dir = create_fbemarker_tmpdir();
+ if (initial_userdata_dir.empty()) {
+ return 1;
+ }
+ fb_perform_format(transport, "userdata", 1, nullptr, nullptr, initial_userdata_dir);
+ delete_fbemarker_tmpdir(initial_userdata_dir);
+ } else {
+ fb_perform_format(transport, "userdata", 1, nullptr, nullptr, "");
+ }
+
+ std::string cache_type;
+ if (fb_getvar(transport, "partition-type:cache", &cache_type) && !cache_type.empty()) {
+ fprintf(stderr, "wiping cache...\n");
+ fb_queue_erase("cache");
+ fb_perform_format(transport, "cache", 1, nullptr, nullptr, "");
+ }
+ }
+ if (wants_set_active) {
+ fb_set_active(next_active.c_str());
}
if (wants_reboot) {
fb_queue_reboot();
@@ -1262,9 +1543,5 @@
fb_queue_wait_for_disconnect();
}
- if (fb_queue_is_empty())
- return 0;
-
- status = fb_execute_queue(usb);
- return (status) ? 1 : 0;
+ return fb_execute_queue(transport) ? EXIT_FAILURE : EXIT_SUCCESS;
}
diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h
index 091a70f..acfbc13 100644
--- a/fastboot/fastboot.h
+++ b/fastboot/fastboot.h
@@ -32,23 +32,24 @@
#include <inttypes.h>
#include <stdlib.h>
-#include "usb.h"
+#include <string>
+
+#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 */
-int fb_getvar(struct usb_handle *usb, char *response, const char *fmt, ...);
-int 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);
@@ -62,8 +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_queue_is_empty(void);
+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..8539e23 100644
--- a/fastboot/fs.cpp
+++ b/fastboot/fs.cpp
@@ -1,7 +1,8 @@
+#include "fs.h"
+
#include "fastboot.h"
#include "make_ext4fs.h"
#include "make_f2fs.h"
-#include "fs.h"
#include <errno.h>
#include <stdio.h>
@@ -13,24 +14,32 @@
#include <sparse/sparse.h>
-static int generate_ext4_image(int fd, long long partSize)
+static int generate_ext4_image(int fd, long long partSize, const std::string& initial_dir)
{
- make_ext4fs_sparse_fd(fd, partSize, NULL, NULL);
-
+ if (initial_dir.empty()) {
+ make_ext4fs_sparse_fd(fd, partSize, NULL, NULL);
+ } else {
+ make_ext4fs_sparse_fd_directory(fd, partSize, NULL, NULL, initial_dir.c_str());
+ }
return 0;
}
#ifdef USE_F2FS
-static int generate_f2fs_image(int fd, long long partSize)
+static int generate_f2fs_image(int fd, long long partSize, const std::string& initial_dir)
{
+ if (!initial_dir.empty()) {
+ fprintf(stderr, "Unable to set initial directory on F2FS filesystem\n");
+ return -1;
+ }
return make_f2fs_sparse_fd(fd, partSize, NULL, NULL);
}
#endif
static const struct fs_generator {
-
const char* fs_type; //must match what fastboot reports for partition type
- int (*generate)(int fd, long long partSize); //returns 0 or error value
+
+ //returns 0 or error value
+ int (*generate)(int fd, long long partSize, const std::string& initial_dir);
} generators[] = {
{ "ext4", generate_ext4_image},
@@ -39,16 +48,17 @@
#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;
}
}
return nullptr;
}
-int fs_generator_generate(const struct fs_generator* gen, int tmpFileNo, long long partSize)
+int fs_generator_generate(const struct fs_generator* gen, int tmpFileNo, long long partSize,
+ const std::string& initial_dir)
{
- return gen->generate(tmpFileNo, partSize);
+ return gen->generate(tmpFileNo, partSize, initial_dir);
}
diff --git a/fastboot/fs.h b/fastboot/fs.h
index 8444081..0a68507 100644
--- a/fastboot/fs.h
+++ b/fastboot/fs.h
@@ -1,12 +1,13 @@
#ifndef _FS_H_
#define _FS_H_
+#include <string>
#include <stdint.h>
struct fs_generator;
-const struct fs_generator* fs_get_generator(const char *fs_type);
-int fs_generator_generate(const struct fs_generator* gen, int tmpFileNo, long long partSize);
+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,
+ const std::string& initial_dir);
#endif
-
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/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/fingerprintd/FingerprintDaemonProxy.cpp b/fingerprintd/FingerprintDaemonProxy.cpp
index beb95de..1c7da30 100644
--- a/fingerprintd/FingerprintDaemonProxy.cpp
+++ b/fingerprintd/FingerprintDaemonProxy.cpp
@@ -88,6 +88,16 @@
msg->data.removed.finger.fid,
msg->data.removed.finger.gid);
break;
+ case FINGERPRINT_TEMPLATE_ENUMERATING:
+ ALOGD("onEnumerate(fid=%d, gid=%d, rem=%d)",
+ msg->data.enumerated.finger.fid,
+ msg->data.enumerated.finger.gid,
+ msg->data.enumerated.remaining_templates);
+ callback->onEnumerate(device,
+ msg->data.enumerated.finger.fid,
+ msg->data.enumerated.finger.gid,
+ msg->data.enumerated.remaining_templates);
+ break;
default:
ALOGE("invalid msg type: %d", msg->type);
return;
@@ -158,6 +168,11 @@
return mDevice->remove(mDevice, groupId, fingerId);
}
+int32_t FingerprintDaemonProxy::enumerate() {
+ ALOG(LOG_VERBOSE, LOG_TAG, "enumerate()\n");
+ return mDevice->enumerate(mDevice);
+}
+
uint64_t FingerprintDaemonProxy::getAuthenticatorId() {
return mDevice->get_authenticator_id(mDevice);
}
diff --git a/fingerprintd/FingerprintDaemonProxy.h b/fingerprintd/FingerprintDaemonProxy.h
index 871c0e6..145b4c9 100644
--- a/fingerprintd/FingerprintDaemonProxy.h
+++ b/fingerprintd/FingerprintDaemonProxy.h
@@ -40,6 +40,7 @@
virtual int32_t authenticate(uint64_t sessionId, uint32_t groupId);
virtual int32_t stopAuthentication();
virtual int32_t remove(int32_t fingerId, int32_t groupId);
+ virtual int32_t enumerate();
virtual uint64_t getAuthenticatorId();
virtual int32_t setActiveGroup(int32_t groupId, const uint8_t* path, ssize_t pathLen);
virtual int64_t openHal();
diff --git a/fingerprintd/IFingerprintDaemon.cpp b/fingerprintd/IFingerprintDaemon.cpp
index 7131793..bc4af56 100644
--- a/fingerprintd/IFingerprintDaemon.cpp
+++ b/fingerprintd/IFingerprintDaemon.cpp
@@ -125,6 +125,16 @@
reply->writeInt32(ret);
return NO_ERROR;
}
+ case ENUMERATE: {
+ CHECK_INTERFACE(IFingerprintDaemon, data, reply);
+ if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) {
+ return PERMISSION_DENIED;
+ }
+ const int32_t ret = enumerate();
+ reply->writeNoException();
+ reply->writeInt32(ret);
+ return NO_ERROR;
+ }
case GET_AUTHENTICATOR_ID: {
CHECK_INTERFACE(IFingerprintDaemon, data, reply);
if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) {
diff --git a/fingerprintd/IFingerprintDaemon.h b/fingerprintd/IFingerprintDaemon.h
index 1eb4ac1..23c36ff 100644
--- a/fingerprintd/IFingerprintDaemon.h
+++ b/fingerprintd/IFingerprintDaemon.h
@@ -44,6 +44,7 @@
CLOSE_HAL = IBinder::FIRST_CALL_TRANSACTION + 9,
INIT = IBinder::FIRST_CALL_TRANSACTION + 10,
POST_ENROLL = IBinder::FIRST_CALL_TRANSACTION + 11,
+ ENUMERATE = IBinder::FIRST_CALL_TRANSACTION + 12,
};
IFingerprintDaemon() { }
@@ -60,6 +61,7 @@
virtual int32_t authenticate(uint64_t sessionId, uint32_t groupId) = 0;
virtual int32_t stopAuthentication() = 0;
virtual int32_t remove(int32_t fingerId, int32_t groupId) = 0;
+ virtual int32_t enumerate() = 0;
virtual uint64_t getAuthenticatorId() = 0;
virtual int32_t setActiveGroup(int32_t groupId, const uint8_t* path, ssize_t pathLen) = 0;
virtual int64_t openHal() = 0;
diff --git a/fingerprintd/IFingerprintDaemonCallback.cpp b/fingerprintd/IFingerprintDaemonCallback.cpp
index 44d8020..19838c9 100644
--- a/fingerprintd/IFingerprintDaemonCallback.cpp
+++ b/fingerprintd/IFingerprintDaemonCallback.cpp
@@ -74,13 +74,13 @@
return remote()->transact(ON_REMOVED, data, &reply, IBinder::FLAG_ONEWAY);
}
- virtual status_t onEnumerate(int64_t devId, const int32_t* fpIds, const int32_t* gpIds,
- int32_t sz) {
+ virtual status_t onEnumerate(int64_t devId, int32_t fpId, int32_t gpId, int32_t rem) {
Parcel data, reply;
data.writeInterfaceToken(IFingerprintDaemonCallback::getInterfaceDescriptor());
data.writeInt64(devId);
- data.writeInt32Array(sz, fpIds);
- data.writeInt32Array(sz, gpIds);
+ data.writeInt32(fpId);
+ data.writeInt32(gpId);
+ data.writeInt32(rem);
return remote()->transact(ON_ENUMERATE, data, &reply, IBinder::FLAG_ONEWAY);
}
};
diff --git a/fingerprintd/IFingerprintDaemonCallback.h b/fingerprintd/IFingerprintDaemonCallback.h
index 6e32213..e343cb4 100644
--- a/fingerprintd/IFingerprintDaemonCallback.h
+++ b/fingerprintd/IFingerprintDaemonCallback.h
@@ -44,8 +44,7 @@
virtual status_t onAuthenticated(int64_t devId, int32_t fingerId, int32_t groupId) = 0;
virtual status_t onError(int64_t devId, int32_t error) = 0;
virtual status_t onRemoved(int64_t devId, int32_t fingerId, int32_t groupId) = 0;
- virtual status_t onEnumerate(int64_t devId, const int32_t* fpIds, const int32_t* gpIds,
- int32_t sz) = 0;
+ virtual status_t onEnumerate(int64_t devId, int32_t fingerId, int32_t groupId, int32_t rem) = 0;
DECLARE_META_INTERFACE(FingerprintDaemonCallback);
};
diff --git a/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.c
index c47a585..2ff6273 100644
--- a/fs_mgr/fs_mgr.c
+++ b/fs_mgr/fs_mgr.c
@@ -101,6 +101,7 @@
char tmpmnt_opts[64] = "errors=remount-ro";
char *e2fsck_argv[] = {
E2FSCK_BIN,
+ "-f",
"-y",
blk_device
};
@@ -442,8 +443,22 @@
// Check to see if a mountable volume has encryption requirements
static int handle_encryptable(struct fstab *fstab, const struct fstab_rec* rec)
{
+ /* Check for existence of convert_fbe breadcrumb file */
+ char convert_fbe_name[PATH_MAX];
+ snprintf(convert_fbe_name, sizeof(convert_fbe_name),
+ "%s/convert_fbe", rec->mount_point);
+ bool convert_fbe = (access(convert_fbe_name, F_OK) == 0);
+
+ /* Check for existence of convert_fbe breadcrumb file */
+ char convert_fde_name[PATH_MAX];
+ snprintf(convert_fde_name, sizeof(convert_fbe_name),
+ "%s/misc/vold/convert_fde", rec->mount_point);
+ bool convert_fde = (access(convert_fde_name, F_OK) == 0);
+
/* If this is block encryptable, need to trigger encryption */
if ( (rec->fs_mgr_flags & MF_FORCECRYPT)
+ || ((rec->fs_mgr_flags & MF_CRYPT) && convert_fde)
+ || ((rec->fs_mgr_flags & MF_FORCEFDEORFBE) && !convert_fbe)
|| (device_is_force_encrypted() && fs_mgr_is_encryptable(rec))) {
if (umount(rec->mount_point) == 0) {
return FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION;
@@ -455,7 +470,8 @@
}
// Deal with file level encryption
- if (rec->fs_mgr_flags & MF_FILEENCRYPTION) {
+ if ( (rec->fs_mgr_flags & MF_FILEENCRYPTION)
+ || ((rec->fs_mgr_flags & MF_FORCEFDEORFBE) && convert_fbe)) {
// Default or not yet initialized encryption requires no more work here
if (!e4crypt_non_default_key(rec->mount_point)) {
INFO("%s is default file encrypted\n", rec->mount_point);
@@ -881,7 +897,8 @@
if (fstab->recs[i].fs_mgr_flags & MF_VOLDMANAGED) {
continue;
}
- if (!(fstab->recs[i].fs_mgr_flags & (MF_CRYPT | MF_FORCECRYPT))) {
+ if (!(fstab->recs[i].fs_mgr_flags
+ & (MF_CRYPT | MF_FORCECRYPT | MF_FORCEFDEORFBE))) {
continue;
}
diff --git a/fs_mgr/fs_mgr_fstab.c b/fs_mgr/fs_mgr_fstab.c
index cf35b3f..c8c624d 100644
--- a/fs_mgr/fs_mgr_fstab.c
+++ b/fs_mgr/fs_mgr_fstab.c
@@ -64,6 +64,7 @@
{ "encryptable=",MF_CRYPT },
{ "forceencrypt=",MF_FORCECRYPT },
{ "fileencryption",MF_FILEENCRYPTION },
+ { "forcefdeorfbe=",MF_FORCEFDEORFBE },
{ "nonremovable",MF_NONREMOVABLE },
{ "voldmanaged=",MF_VOLDMANAGED},
{ "length=", MF_LENGTH },
@@ -140,6 +141,11 @@
* location of the keys. Get it and return it.
*/
flag_vals->key_loc = strdup(strchr(p, '=') + 1);
+ } else if ((fl[i].flag == MF_FORCEFDEORFBE) && flag_vals) {
+ /* The forcefdeorfbe flag is followed by an = and the
+ * location of the keys. Get it and return it.
+ */
+ flag_vals->key_loc = strdup(strchr(p, '=') + 1);
} else if ((fl[i].flag == MF_LENGTH) && flag_vals) {
/* The length flag is followed by an = and the
* size of the partition. Get it and return it.
@@ -464,7 +470,7 @@
int fs_mgr_is_encryptable(const struct fstab_rec *fstab)
{
- return fstab->fs_mgr_flags & (MF_CRYPT | MF_FORCECRYPT);
+ return fstab->fs_mgr_flags & (MF_CRYPT | MF_FORCECRYPT | MF_FORCEFDEORFBE);
}
int fs_mgr_is_file_encrypted(const struct fstab_rec *fstab)
@@ -472,6 +478,11 @@
return fstab->fs_mgr_flags & MF_FILEENCRYPTION;
}
+int fs_mgr_is_convertible_to_fbe(const struct fstab_rec *fstab)
+{
+ return fstab->fs_mgr_flags & MF_FORCEFDEORFBE;
+}
+
int fs_mgr_is_noemulatedsd(const struct fstab_rec *fstab)
{
return fstab->fs_mgr_flags & MF_NOEMULATEDSD;
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index ba0e097..181b6cd 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -82,6 +82,7 @@
#define MF_FILEENCRYPTION 0x2000
#define MF_FORMATTABLE 0x4000
#define MF_SLOTSELECT 0x8000
+#define MF_FORCEFDEORFBE 0x10000
#define DM_BUF_SIZE 4096
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/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index 27fccf7..eb0a7fc 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -102,6 +102,7 @@
int fs_mgr_is_verified(const struct fstab_rec *fstab);
int fs_mgr_is_encryptable(const struct fstab_rec *fstab);
int fs_mgr_is_file_encrypted(const struct fstab_rec *fstab);
+int fs_mgr_is_convertible_to_fbe(const struct fstab_rec *fstab);
int fs_mgr_is_noemulatedsd(const struct fstab_rec *fstab);
int fs_mgr_is_notrim(struct fstab_rec *fstab);
int fs_mgr_is_formattable(struct fstab_rec *fstab);
diff --git a/gatekeeperd/SoftGateKeeper.h b/gatekeeperd/SoftGateKeeper.h
index c8010ca..8b15d72 100644
--- a/gatekeeperd/SoftGateKeeper.h
+++ b/gatekeeperd/SoftGateKeeper.h
@@ -25,7 +25,7 @@
#include <crypto_scrypt.h>
}
-#include <base/memory.h>
+#include <android-base/memory.h>
#include <UniquePtr.h>
#include <gatekeeper/gatekeeper.h>
@@ -180,4 +180,3 @@
}
#endif // SOFT_GATEKEEPER_H_
-
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index ddfdd88..3e618c0 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -137,6 +137,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 },
};
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..e7c69d6
--- /dev/null
+++ b/include/nativeloader/native_loader.h
@@ -0,0 +1,31 @@
+/*
+ * 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, 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 a256b66..d236938 100644
--- a/include/private/android_filesystem_config.h
+++ b/include/private/android_filesystem_config.h
@@ -103,6 +103,7 @@
#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 */
/* The range 5000-5999 is also reserved for OEM, and must never be used here. */
#define AID_OEM_RESERVED_2_START 5000
@@ -195,6 +196,7 @@
{ "net_bw_stats", AID_NET_BW_STATS, },
{ "net_bw_acct", AID_NET_BW_ACCT, },
{ "net_bt_stack", AID_NET_BT_STACK, },
+ { "readproc", AID_READPROC, },
{ "everybody", AID_EVERYBODY, },
{ "misc", AID_MISC, },
diff --git a/include/system/radio.h b/include/system/radio.h
index a088526..9e291c8 100644
--- a/include/system/radio.h
+++ b/include/system/radio.h
@@ -94,6 +94,7 @@
radio_rds_t rds; /* RDS variants supported */
bool ta; /* Traffic Announcement supported */
bool af; /* Alternate Frequency supported */
+ bool ea; /* Emergency announcements supported */
} radio_hal_fm_band_config_t;
/* Additional attributes for an AM band configuration */
@@ -184,6 +185,7 @@
RADIO_EVENT_METADATA = 4, /* New meta data received */
RADIO_EVENT_TA = 5, /* Traffic announcement start or stop */
RADIO_EVENT_AF_SWITCH = 6, /* Switch to Alternate Frequency */
+ RADIO_EVENT_EA = 7, /* Emergency announcement start or stop */
// begin framework only events
RADIO_EVENT_CONTROL = 100, /* loss/gain of tuner control */
RADIO_EVENT_SERVER_DIED = 101, /* radio service died */
@@ -195,7 +197,8 @@
radio_event_type_t type; /* event type */
int status; /* used by RADIO_EVENT_CONFIG, RADIO_EVENT_TUNED */
union {
- bool on; /* RADIO_EVENT_ANTENNA, RADIO_EVENT_TA */
+ /* RADIO_EVENT_ANTENNA, RADIO_EVENT_TA, RADIO_EVENT_EA */
+ bool on;
radio_hal_band_config_t config; /* RADIO_EVENT_CONFIG */
radio_program_info_t info; /* RADIO_EVENT_TUNED, RADIO_EVENT_AF_SWITCH */
radio_metadata_t *metadata; /* RADIO_EVENT_METADATA */
diff --git a/include/system/window.h b/include/system/window.h
index 508ce00..9f6e951 100644
--- a/include/system/window.h
+++ b/include/system/window.h
@@ -25,6 +25,7 @@
#include <sys/cdefs.h>
#include <system/graphics.h>
#include <unistd.h>
+#include <stdbool.h>
#ifndef __UNUSED
#define __UNUSED __attribute__((__unused__))
@@ -311,6 +312,7 @@
NATIVE_WINDOW_SET_SIDEBAND_STREAM = 18,
NATIVE_WINDOW_SET_BUFFERS_DATASPACE = 19,
NATIVE_WINDOW_SET_SURFACE_DAMAGE = 20, /* private */
+ NATIVE_WINDOW_SET_SINGLE_BUFFER_MODE = 21,
};
/* parameter for NATIVE_WINDOW_[API_][DIS]CONNECT */
@@ -949,6 +951,18 @@
rects, numRects);
}
+/*
+ * native_window_set_single_buffer_mode(..., bool singleBufferMode)
+ * Enable/disable single buffer mode
+ */
+static inline int native_window_set_single_buffer_mode(
+ struct ANativeWindow* window,
+ bool singleBufferMode)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_SINGLE_BUFFER_MODE,
+ singleBufferMode);
+}
+
__END_DECLS
#endif /* SYSTEM_CORE_INCLUDE_ANDROID_WINDOW_H */
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/utils/StrongPointer.h b/include/utils/StrongPointer.h
index aba9577..50fde35 100644
--- a/include/utils/StrongPointer.h
+++ b/include/utils/StrongPointer.h
@@ -62,8 +62,10 @@
sp(T* other);
sp(const sp<T>& other);
+ sp(sp<T>&& other);
template<typename U> sp(U* other);
template<typename U> sp(const sp<U>& other);
+ template<typename U> sp(sp<U>&& other);
~sp();
@@ -71,8 +73,10 @@
sp& operator = (T* other);
sp& operator = (const sp<T>& other);
+ sp& operator = (sp<T>&& other);
template<typename U> sp& operator = (const sp<U>& other);
+ template<typename U> sp& operator = (sp<U>&& other);
template<typename U> sp& operator = (U* other);
//! Special optimization for use by ProcessState (and nobody else).
@@ -123,6 +127,12 @@
m_ptr->incStrong(this);
}
+template<typename T>
+sp<T>::sp(sp<T>&& other)
+ : m_ptr(other.m_ptr) {
+ other.m_ptr = nullptr;
+}
+
template<typename T> template<typename U>
sp<T>::sp(U* other)
: m_ptr(other) {
@@ -137,6 +147,12 @@
m_ptr->incStrong(this);
}
+template<typename T> template<typename U>
+sp<T>::sp(sp<U>&& other)
+ : m_ptr(other.m_ptr) {
+ other.m_ptr = nullptr;
+}
+
template<typename T>
sp<T>::~sp() {
if (m_ptr)
@@ -155,6 +171,15 @@
}
template<typename T>
+sp<T>& sp<T>::operator =(sp<T>&& other) {
+ if (m_ptr)
+ m_ptr->decStrong(this);
+ m_ptr = other.m_ptr;
+ other.m_ptr = nullptr;
+ return *this;
+}
+
+template<typename T>
sp<T>& sp<T>::operator =(T* other) {
if (other)
other->incStrong(this);
@@ -176,6 +201,15 @@
}
template<typename T> template<typename U>
+sp<T>& sp<T>::operator =(sp<U>&& other) {
+ if (m_ptr)
+ m_ptr->decStrong(this);
+ m_ptr = other.m_ptr;
+ other.m_ptr = nullptr;
+ return *this;
+}
+
+template<typename T> template<typename U>
sp<T>& sp<T>::operator =(U* other) {
if (other)
((T*) other)->incStrong(this);
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 36ecbb8..dddd6e4 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;
@@ -838,6 +838,15 @@
return e4crypt_set_user_crypto_policies(args[1].c_str());
}
+static int do_createuserkey(const std::vector<std::string>& args) {
+ if (!is_file_crypto()) {
+ return 0;
+ }
+ return e4crypt_create_user_key(atoi(args[1].c_str()),
+ atoi(args[2].c_str()),
+ atoi(args[3].c_str()));
+}
+
BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
static const Map builtin_functions = {
@@ -848,6 +857,7 @@
{"class_start", {1, 1, do_class_start}},
{"class_stop", {1, 1, do_class_stop}},
{"copy", {2, 2, do_copy}},
+ {"createuserkey", {3, 3, do_createuserkey}},
{"domainname", {1, 1, do_domainname}},
{"enable", {1, 1, do_enable}},
{"exec", {1, kMax, do_exec}},
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 96b4a37..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");
diff --git a/init/property_service.h b/init/property_service.h
index 8b76d2b..dbaed34 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -32,10 +32,8 @@
extern void load_persist_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/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/libcutils/Android.mk b/libcutils/Android.mk
index 3ca544f..1f68290 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 \
@@ -115,6 +113,9 @@
ifneq ($(ENABLE_CPUSETS),)
LOCAL_CFLAGS += -DUSE_CPUSETS
endif
+ifneq ($(ENABLE_SCHEDBOOST),)
+LOCAL_CFLAGS += -DUSE_SCHEDBOOST
+endif
LOCAL_CFLAGS += -Werror -Wall -Wextra -std=gnu90
LOCAL_CLANG := true
LOCAL_SANITIZE := integer
@@ -129,6 +130,9 @@
ifneq ($(ENABLE_CPUSETS),)
LOCAL_CFLAGS += -DUSE_CPUSETS
endif
+ifneq ($(ENABLE_SCHEDBOOST),)
+LOCAL_CFLAGS += -DUSE_SCHEDBOOST
+endif
LOCAL_CFLAGS += -Werror -Wall -Wextra
LOCAL_C_INCLUDES := $(libcutils_c_includes)
LOCAL_CLANG := true
diff --git a/libcutils/fs_config.c b/libcutils/fs_config.c
index 970b386..645edd1 100644
--- a/libcutils/fs_config.c
+++ b/libcutils/fs_config.c
@@ -79,6 +79,7 @@
{ 00500, AID_ROOT, AID_ROOT, 0, "config" },
{ 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/app" },
{ 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/app-private" },
+ { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/app-ephemeral" },
{ 00771, AID_ROOT, AID_ROOT, 0, "data/dalvik-cache" },
{ 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/data" },
{ 00771, AID_SHELL, AID_SHELL, 0, "data/local/tmp" },
@@ -88,6 +89,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" },
@@ -124,14 +127,17 @@
{ 00644, AID_SYSTEM, AID_SYSTEM, 0, "data/app/*" },
{ 00644, AID_MEDIA_RW, AID_MEDIA_RW, 0, "data/media/*" },
{ 00644, AID_SYSTEM, AID_SYSTEM, 0, "data/app-private/*" },
+ { 00644, AID_SYSTEM, AID_SYSTEM, 0, "data/app-ephemeral/*" },
{ 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/sched_policy.c b/libcutils/sched_policy.c
index 298e3da..6bba3a7 100644
--- a/libcutils/sched_policy.c
+++ b/libcutils/sched_policy.c
@@ -64,6 +64,8 @@
// File descriptors open to /dev/cpuset/../tasks, setup by initialize, or -1 on error
static int bg_cpuset_fd = -1;
static int fg_cpuset_fd = -1;
+static int bg_schedboost_fd = -1;
+static int fg_schedboost_fd = -1;
#endif
/* Add tid to the scheduling group defined by the policy */
@@ -128,6 +130,12 @@
fg_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
filename = "/dev/cpuset/background/tasks";
bg_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
+#ifdef USE_SCHEDBOOST
+ filename = "/sys/fs/cgroup/stune/foreground/tasks";
+ fg_schedboost_fd = open(filename, O_WRONLY | O_CLOEXEC);
+ filename = "/sys/fs/cgroup/stune/tasks";
+ bg_schedboost_fd = open(filename, O_WRONLY | O_CLOEXEC);
+#endif
}
#endif
@@ -253,17 +261,20 @@
pthread_once(&the_once, __initialize);
int fd;
+ int boost_fd;
switch (policy) {
case SP_BACKGROUND:
fd = bg_cpuset_fd;
+ boost_fd = bg_schedboost_fd;
break;
case SP_FOREGROUND:
case SP_AUDIO_APP:
case SP_AUDIO_SYS:
fd = fg_cpuset_fd;
+ boost_fd = fg_schedboost_fd;
break;
default:
- fd = -1;
+ boost_fd = fd = -1;
break;
}
@@ -272,6 +283,11 @@
return -errno;
}
+ if (boost_fd > 0 && add_tid_to_cgroup(tid, boost_fd) != 0) {
+ if (errno != ESRCH && errno != ENOENT)
+ return -errno;
+ }
+
return 0;
#endif
}
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/liblog/Android.bp b/liblog/Android.bp
index 34e7f92..878feb8 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"],
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..b4711d2 100644
--- a/liblog/log_is_loggable.c
+++ b/liblog/log_is_loggable.c
@@ -23,12 +23,31 @@
#include <android/log.h>
+static pthread_mutex_t lock_loggable = 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(&lock_loggable);
+}
+
+static void unlock()
+{
+ pthread_mutex_unlock(&lock_loggable);
+}
+
struct cache {
const prop_info *pinfo;
uint32_t serial;
- char c;
+ unsigned char c;
};
+#define BOOLEAN_TRUE 0xFF
+#define BOOLEAN_FALSE 0xFE
+
static void refresh_cache(struct cache *cache, const char *key)
{
uint32_t serial;
@@ -46,12 +65,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.";
@@ -86,7 +112,7 @@
strcpy(key, log_namespace);
- pthread_mutex_lock(&lock);
+ lock();
current_global_serial = __system_property_area_serial();
@@ -133,6 +159,7 @@
case 'F': /* Not officially supported */
case 'A':
case 'S':
+ case BOOLEAN_FALSE: /* Not officially supported */
break;
default:
/* clear '.' after log.tag */
@@ -156,7 +183,7 @@
global_serial = current_global_serial;
- pthread_mutex_unlock(&lock);
+ unlock();
switch (toupper(c)) {
case 'V': return ANDROID_LOG_VERBOSE;
@@ -166,38 +193,83 @@
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()
+/*
+ * Timestamp state generally remains constant, since a change is
+ * rare, we can accept a trylock failure gracefully. Use a separate
+ * lock from is_loggable to keep contention down b/25563384.
+ */
+static pthread_mutex_t lock_clockid = PTHREAD_MUTEX_INITIALIZER;
+
+clockid_t android_log_clockid()
{
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;
+ char c;
- pthread_mutex_lock(&lock);
+ if (pthread_mutex_trylock(&lock_clockid)) {
+ /* We are willing to accept some race in this context */
+ if (!(c = p_time_cache.c)) {
+ c = r_time_cache.c;
+ }
+ } else {
+ static uint32_t serial;
+ uint32_t 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 (!(c = p_time_cache.c)) {
+ c = r_time_cache.c;
+ }
- 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 (!(retval = p_time_cache.c)) {
- retval = r_time_cache.c;
+ pthread_mutex_unlock(&lock_clockid);
}
- pthread_mutex_unlock(&lock);
+ return (tolower(c) == 'm') ? CLOCK_MONOTONIC : CLOCK_REALTIME;
+}
- return tolower(retval ?: 'r');
+/*
+ * security state generally remains constant, since a change is
+ * rare, we can accept a trylock failure gracefully.
+ */
+static pthread_mutex_t lock_security = PTHREAD_MUTEX_INITIALIZER;
+
+int __android_log_security()
+{
+ static struct cache r_do_cache = { NULL, -1, BOOLEAN_FALSE };
+ static struct cache p_security_cache = { NULL, -1, BOOLEAN_FALSE };
+ int retval;
+
+ if (pthread_mutex_trylock(&lock_security)) {
+ /* We are willing to accept some race in this context */
+ retval = (r_do_cache.c != BOOLEAN_FALSE) && r_do_cache.c &&
+ (p_security_cache.c == BOOLEAN_TRUE);
+ } else {
+ static uint32_t serial;
+ uint32_t current_serial = __system_property_area_serial();
+ if (current_serial != serial) {
+ refresh_cache(&r_do_cache, "ro.device_owner");
+ refresh_cache(&p_security_cache, "persist.logd.security");
+ serial = current_serial;
+ }
+ retval = (r_do_cache.c != BOOLEAN_FALSE) && r_do_cache.c &&
+ (p_security_cache.c == BOOLEAN_TRUE);
+
+ pthread_mutex_unlock(&lock_security);
+ }
+
+ return retval;
}
diff --git a/liblog/log_read.c b/liblog/log_read.c
index cfc8a7a..8aa887b 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",
};
@@ -634,6 +635,7 @@
android_log_header_t l;
} buf;
static uint8_t preread_count;
+ bool is_system;
memset(log_msg, 0, sizeof(*log_msg));
@@ -690,12 +692,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 +709,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 +807,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);
diff --git a/liblog/logd_write.c b/liblog/logd_write.c
index bdee28f..c2b0ec2 100644
--- a/liblog/logd_write.c
+++ b/liblog/logd_write.c
@@ -54,16 +54,37 @@
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 void unlock()
+{
+ pthread_mutex_unlock(&log_init_lock);
+}
+
+#else /* !defined(_WIN32) */
+
+#define lock() ((void)0)
+#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 +181,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 +193,23 @@
if (last_pid == (pid_t) -1) {
last_pid = getpid();
}
+ if (log_id == LOG_ID_SECURITY) {
+ 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()) {
+ return -EPERM;
+ }
+ }
/*
* struct {
* // what we provide to pstore
@@ -191,7 +230,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,7 +247,26 @@
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_event_int_t buffer;
@@ -222,7 +280,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 +332,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 +353,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 +370,7 @@
[LOG_ID_EVENTS] = "events",
[LOG_ID_SYSTEM] = "system",
[LOG_ID_CRASH] = "crash",
+ [LOG_ID_SECURITY] = "security",
[LOG_ID_KERNEL] = "kernel",
};
@@ -325,18 +385,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 +404,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 +526,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..746eb8a 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,6 +745,7 @@
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;
@@ -744,6 +756,9 @@
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_v4)) {
+ entry->uid = ((struct logger_entry_v4 *)buf)->uid;
+ }
}
inCount = buf->len;
if (inCount < 4)
@@ -908,7 +923,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 +1253,7 @@
char prefixBuf[128], suffixBuf[128];
char priChar;
int prefixSuffixIsHeaderFooter = 0;
- char *ret = NULL;
+ char *ret;
time_t now;
unsigned long nsec;
@@ -1261,10 +1276,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 +1325,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 +1361,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 +1377,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 +1404,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_test.cpp b/liblog/tests/liblog_test.cpp
index c987041..621101c 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));
}
@@ -968,7 +1098,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 +1169,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..cef2d75
--- /dev/null
+++ b/libnativeloader/native_loader.cpp
@@ -0,0 +1,159 @@
+/*
+ * 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,
+ 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;
+ }
+
+ android_namespace_t* ns =
+ android_create_namespace("classloader-namespace",
+ nullptr,
+ library_path.c_str(),
+ true,
+ 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, 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, 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,
+ 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 65a3fdf..9a937f8 100644
--- a/libpixelflinger/Android.mk
+++ b/libpixelflinger/Android.mk
@@ -50,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
#
@@ -59,6 +67,7 @@
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) \
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/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/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..a5305cc
--- /dev/null
+++ b/libpixelflinger/codeflinger/MIPS64Assembler.cpp
@@ -0,0 +1,1452 @@
+/* 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(NULL, NULL)
+{
+ mBase = mPC = (uint32_t *)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..daa231f 100644
--- a/libpixelflinger/codeflinger/MIPSAssembler.cpp
+++ b/libpixelflinger/codeflinger/MIPSAssembler.cpp
@@ -1358,7 +1358,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 +1407,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 8fea8cb..06cb0d0 100644
--- a/libpixelflinger/codeflinger/MIPSAssembler.h
+++ b/libpixelflinger/codeflinger/MIPSAssembler.h
@@ -244,20 +244,20 @@
MIPSAssembler(const sp<Assembly>& assembly, ArmToMipsAssembler *parent);
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 +399,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 +537,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/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/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/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/tests/Android.mk b/libutils/tests/Android.mk
index 47415ab..8f07f1a 100644
--- a/libutils/tests/Android.mk
+++ b/libutils/tests/Android.mk
@@ -27,6 +27,7 @@
Looper_test.cpp \
LruCache_test.cpp \
String8_test.cpp \
+ StrongPointer_test.cpp \
Unicode_test.cpp \
Vector_test.cpp \
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/StrongPointer_test.cpp b/libutils/tests/StrongPointer_test.cpp
new file mode 100644
index 0000000..f46d6d1
--- /dev/null
+++ b/libutils/tests/StrongPointer_test.cpp
@@ -0,0 +1,58 @@
+/*
+ * 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 <utils/StrongPointer.h>
+#include <utils/RefBase.h>
+
+using namespace android;
+
+class Foo : public LightRefBase<Foo> {
+public:
+ Foo(bool* deleted_check) : mDeleted(deleted_check) {
+ *mDeleted = false;
+ }
+
+ ~Foo() {
+ *mDeleted = true;
+ }
+private:
+ bool* mDeleted;
+};
+
+TEST(StrongPointer, move) {
+ bool isDeleted;
+ Foo* foo = new Foo(&isDeleted);
+ ASSERT_EQ(0, foo->getStrongCount());
+ ASSERT_FALSE(isDeleted) << "Already deleted...?";
+ sp<Foo> sp1(foo);
+ ASSERT_EQ(1, foo->getStrongCount());
+ {
+ sp<Foo> sp2 = std::move(sp1);
+ ASSERT_EQ(1, foo->getStrongCount()) << "std::move failed, incremented refcnt";
+ ASSERT_EQ(nullptr, sp1.get()) << "std::move failed, sp1 is still valid";
+ // The strong count isn't increasing, let's double check the old object
+ // is properly reset and doesn't early delete
+ sp1 = std::move(sp2);
+ }
+ ASSERT_FALSE(isDeleted) << "deleted too early! still has a reference!";
+ {
+ // Now let's double check it deletes on time
+ sp<Foo> sp2 = std::move(sp1);
+ }
+ ASSERT_TRUE(isDeleted) << "foo was leaked!";
+}
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 cb9598e..ebcebd6 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -397,7 +397,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;
@@ -406,8 +407,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);
diff --git a/lmkd/lmkd.rc b/lmkd/lmkd.rc
index 7d6cb11..3bb84ab 100644
--- a/lmkd/lmkd.rc
+++ b/lmkd/lmkd.rc
@@ -1,5 +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/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index 610a6ec..61b020c 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);
@@ -601,6 +612,9 @@
}
}
pclose(fp);
+ if ((count != 7) && (count != 8)) {
+ fprintf(stderr, "count=%d\n", count);
+ }
EXPECT_TRUE(count == 7 || count == 8);
}
}
diff --git a/logd/CommandListener.cpp b/logd/CommandListener.cpp
index eafa28f..e103359 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*/) :
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..1e4f3b0 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -28,6 +28,7 @@
#include <log/logger.h>
#include "LogBuffer.h"
+#include "LogKlog.h"
#include "LogReader.h"
// Default
@@ -126,9 +127,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 +199,24 @@
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) {
+ 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;
+ }
}
pthread_mutex_lock(&mLogElementsLock);
@@ -389,7 +447,10 @@
while(t != mTimes.end()) {
LogTimeEntry *entry = (*t);
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++;
@@ -408,8 +469,12 @@
}
if (oldest && (oldest->mStart <= e->getSequence())) {
- oldest->triggerSkip_Locked(id, pruneRows);
busy = true;
+ if (oldest->mTimeout.tv_sec || oldest->mTimeout.tv_nsec) {
+ oldest->triggerReader_Locked();
+ } else {
+ oldest->triggerSkip_Locked(id, pruneRows);
+ }
break;
}
@@ -421,7 +486,7 @@
}
// prune by worst offender by uid
- bool hasBlacklist = mPrune.naughty();
+ 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;
@@ -436,7 +501,10 @@
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) {
@@ -480,6 +548,9 @@
if (oldest && (oldest->mStart <= e->getSequence())) {
busy = true;
+ if (oldest->mTimeout.tv_sec || oldest->mTimeout.tv_nsec) {
+ oldest->triggerReader_Locked();
+ }
break;
}
@@ -585,7 +656,7 @@
}
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;
@@ -605,6 +676,8 @@
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);
}
@@ -637,6 +710,8 @@
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 +848,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;
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index 7ed92e9..c1fec73 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -32,6 +32,21 @@
#include "LogStatistics.h"
#include "LogWhiteBlackList.h"
+//
+// 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.
+//
+namespace android {
+
+static bool isMonotonic(const log_time &mono) {
+ static const uint32_t EPOCH_PLUS_10_YEARS = 10 * 1461 / 4 * 24 * 60 * 60;
+
+ return mono.tv_sec < EPOCH_PLUS_10_YEARS;
+}
+
+}
+
typedef std::list<LogBufferElement *> LogBufferElementCollection;
class LogBuffer {
@@ -49,11 +64,14 @@
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,
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..db7e682 100644
--- a/logd/LogKlog.cpp
+++ b/logd/LogKlog.cpp
@@ -330,6 +330,10 @@
}
*buf = cp;
+ if (isMonotonic()) {
+ return;
+ }
+
const char *b;
if (((b = strnstr(cp, len, suspendStr)))
&& ((size_t)((b += sizeof(suspendStr) - 1) - cp) < len)) {
@@ -376,7 +380,11 @@
convertMonotonicToReal(now);
} else {
- now = log_time(CLOCK_REALTIME);
+ if (isMonotonic()) {
+ now = log_time(CLOCK_MONOTONIC);
+ } else {
+ now = log_time(CLOCK_REALTIME);
+ }
}
}
@@ -574,7 +582,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 +596,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 +620,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 +696,7 @@
p = cp + 1;
}
}
- }
+ } /* else no tag */
size = etag - tag;
if ((size <= 1)
// register names like x9
@@ -713,8 +723,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 +741,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 +778,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..9bbf9e8 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),
@@ -92,6 +93,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 fdb2576..bf0e30b 100644
--- a/logd/LogStatistics.cpp
+++ b/logd/LogStatistics.cpp
@@ -85,7 +85,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);
+ }
}
}
@@ -113,7 +117,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);
+ }
}
}
@@ -190,13 +198,13 @@
return formatLine(android::base::StringPrintf(
name.c_str(), android_log_id_to_name(id)),
std::string("Size"),
- std::string(isprune ? "Pruned" : ""))
+ std::string(isprune ? "+/- Pruned" : ""))
+ formatLine(std::string("UID PACKAGE"),
std::string("BYTES"),
std::string(isprune ? "NUM" : ""));
}
-std::string UidEntry::format(const LogStatistics &stat, log_id_t /* id */) const {
+std::string UidEntry::format(const LogStatistics &stat, log_id_t id) const {
uid_t uid = getKey();
std::string name = android::base::StringPrintf("%u", uid);
const char *nameTmp = stat.uidToName(uid);
@@ -210,9 +218,59 @@
std::string size = android::base::StringPrintf("%zu", getSizes());
std::string pruned = "";
- size_t dropped = getDropped();
- if (dropped) {
- pruned = android::base::StringPrintf("%zu", dropped);
+ if (worstUidEnabledForLogid(id)) {
+ size_t totalDropped = 0;
+ for (LogStatistics::uidTable_t::const_iterator it = stat.uidTable[id].begin();
+ it != stat.uidTable[id].end(); ++it) {
+ totalDropped += it->second.getDropped();
+ }
+ size_t sizes = stat.sizes(id);
+ size_t totalSize = stat.sizesTotal(id);
+ size_t totalElements = stat.elementsTotal(id);
+ float totalVirtualSize = (float)sizes + (float)totalDropped * totalSize
+ / totalElements;
+ size_t entrySize = getSizes();
+ float virtualEntrySize = entrySize;
+ int realPermille = virtualEntrySize * 1000.0 / sizes;
+ size_t dropped = getDropped();
+ if (dropped) {
+ pruned = android::base::StringPrintf("%zu", dropped);
+ virtualEntrySize += (float)dropped * totalSize / totalElements;
+ }
+ int virtualPermille = virtualEntrySize * 1000.0 / totalVirtualSize;
+ int permille = (realPermille - virtualPermille) * 1000L
+ / (virtualPermille ?: 1);
+ if ((permille < -1) || (1 < permille)) {
+ std::string change;
+ const char *units = "%";
+ const char *prefix = (permille > 0) ? "+" : "";
+
+ if (permille > 999) {
+ permille = (permille + 1000) / 100; // Now tenths fold
+ units = "X";
+ prefix = "";
+ }
+ if ((-99 < permille) && (permille < 99)) {
+ change = android::base::StringPrintf("%s%d.%u%s",
+ prefix,
+ permille / 10,
+ ((permille < 0) ? (-permille % 10) : (permille % 10)),
+ units);
+ } else {
+ change = android::base::StringPrintf("%s%d%s",
+ prefix,
+ (permille + 5) / 10, units);
+ }
+ ssize_t spaces = EntryBaseConstants::pruned_len
+ - 2 - pruned.length() - change.length();
+ if ((spaces <= 0) && pruned.length()) {
+ spaces = 1;
+ }
+ if (spaces > 0) {
+ change += android::base::StringPrintf("%*s", (int)spaces, "");
+ }
+ pruned = change + pruned;
+ }
}
return formatLine(name, size, pruned);
@@ -418,6 +476,11 @@
output += tagTable.format(*this, uid, name, LOG_ID_EVENTS);
}
+ if (enable && (logMask & (1 << LOG_ID_SECURITY))) {
+ name = "Chattiest security log buffer TAGs:";
+ output += securityTagTable.format(*this, uid, name, LOG_ID_SECURITY);
+ }
+
return output;
}
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index 6943820..8558b06 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>
@@ -372,6 +372,8 @@
// Log Statistics
class LogStatistics {
+ friend UidEntry;
+
size_t mSizes[LOG_ID_MAX];
size_t mElements[LOG_ID_MAX];
size_t mDroppedElements[LOG_ID_MAX];
@@ -395,6 +397,9 @@
typedef LogHashtable<uint32_t, TagEntry> tagTable_t;
tagTable_t tagTable;
+ // security tag list
+ tagTable_t securityTagTable;
+
public:
LogStatistics();
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..b591f28 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,12 @@
}
+// Furnished in LogCommand.cpp
+bool clientHasLogCredentials(uid_t uid, gid_t gid, pid_t pid);
+bool clientHasLogCredentials(SocketClient *cli);
+
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 ad005ec..29e637e 100644
--- a/logd/LogWhiteBlackList.cpp
+++ b/logd/LogWhiteBlackList.cpp
@@ -16,7 +16,8 @@
#include <ctype.h>
-#include <base/stringprintf.h>
+#include <android-base/stringprintf.h>
+#include <cutils/properties.h>
#include "LogWhiteBlackList.h"
@@ -49,7 +50,8 @@
return std::string("/");
}
-PruneList::PruneList() : mWorstUidEnabled(true) {
+PruneList::PruneList() {
+ init(NULL);
}
PruneList::~PruneList() {
@@ -72,13 +74,44 @@
it = mNaughty.erase(it);
}
- if (!str) {
- return 0;
+ static const char _default[] = "default";
+ // default here means take ro.logd.filter, persist.logd.filter then
+ // internal default in that order.
+ if (str && !strcmp(str, _default)) {
+ str = NULL;
+ }
+ static const char _disable[] = "disable";
+ if (str && !strcmp(str, _disable)) {
+ str = "";
+ }
+
+ std::string filter;
+
+ if (str) {
+ filter = str;
+ } else {
+ char property[PROPERTY_VALUE_MAX];
+ property_get("ro.logd.filter", property, _default);
+ filter = property;
+ property_get("persist.logd.filter", property, filter.c_str());
+ // default here means take ro.logd.filter
+ if (strcmp(property, _default)) {
+ filter = property;
+ }
+ }
+
+ // default here means take internal default.
+ if (filter == _default) {
+ // See README.property for description of filter format
+ filter = "~!";
+ }
+ if (filter == _disable) {
+ filter = "";
}
mWorstUidEnabled = false;
- for(; *str; ++str) {
+ for(str = filter.c_str(); *str; ++str) {
if (isspace(*str)) {
continue;
}
diff --git a/logd/README.property b/logd/README.property
index a472efd..e4b23a9 100644
--- a/logd/README.property
+++ b/logd/README.property
@@ -12,16 +12,28 @@
default false
persist.logd.logpersistd string Enable logpersist daemon, "logcatd"
turns on logcat -f in logd context
-persist.logd.size number 256K default size of the buffer for all
- log ids at initial startup, at runtime
- use: logcat -b all -G <value>
+persist.logd.size number 256K 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>"
+persist.logd.timestamp string The recording timestamp source. Default
+ is ro.logd.timestamp. "m[onotonic]" is
+ the only supported key character,
+ otherwise assumes realtime.
NB:
-- number support multipliers (K or M) for convenience. Range is limited
- to between 64K and 256M for log buffer sizes. Individual logs override the
- global default.
+- 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.
diff --git a/logd/logd.rc b/logd/logd.rc
index ecd2f0a..10f3553 100644
--- a/logd/logd.rc
+++ b/logd/logd.rc
@@ -3,7 +3,7 @@
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
diff --git a/logd/main.cpp b/logd/main.cpp
index 13dda78..0f55d60 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;
}
@@ -212,6 +214,7 @@
// Anything that reads persist.<property>
if (logBuf) {
logBuf->init();
+ logBuf->initPrune(NULL);
}
}
@@ -297,7 +300,7 @@
}
buf[--len] = '\0';
- if (kl) {
+ if (kl && kl->isMonotonic()) {
kl->synchronize(buf.get(), len);
}
@@ -346,7 +349,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;
}
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
index 44fa95c..4472c1d 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/tests/logd_test.cpp
@@ -112,18 +112,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;
@@ -223,6 +243,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 +445,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 +525,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..2149a4b 100644
--- a/metricsd/Android.mk
+++ b/metricsd/Android.mk
@@ -18,43 +18,47 @@
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 \
+ metrics_collector_service_trampoline.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 +68,50 @@
-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_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_impl.cc \
+ metrics_collector_service_client.cc
+LOCAL_RTTI_FLAG := -fno-rtti
+include $(BUILD_STATIC_LIBRARY)
# Shared library for metrics.
# ========================================================
@@ -89,6 +126,7 @@
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 +141,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 +155,70 @@
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_INIT_RC := metrics_collector.rc
+LOCAL_REQUIRED_MODULES := metrics.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_SHARED_LIBRARIES := $(metrics_collector_shared_libraries)
+LOCAL_SRC_FILES := $(metrics_collector_common) \
+ metrics_collector_main.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_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_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 := $(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 +226,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/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 7e1e116..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";
@@ -30,6 +32,10 @@
// Build time properties name.
static const char kProductId[] = "product_id";
static const char kProductVersion[] = "product_version";
+
+// Weave configuration.
+static const char kWeaveConfigurationFile[] = "/system/etc/weaved/weaved.conf";
+static const char kModelManifestId[] = "model_id";
} // namespace metrics
#endif // METRICS_CONSTANTS_H_
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..7d17c77
--- /dev/null
+++ b/metricsd/etc/weaved/traits/metrics.json
@@ -0,0 +1,18 @@
+{
+ "_metrics": {
+ "commands": {
+ "_enableAnalyticsReporting": {
+ "minimalRole": "manager"
+ },
+ "_disableAnalyticsReporting": {
+ "minimalRole": "manager"
+ }
+ },
+ "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/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..b5c2289
--- /dev/null
+++ b/metricsd/metrics_collector.cc
@@ -0,0 +1,744 @@
+/*
+ * 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 <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 "metrics_collector_service_trampoline.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 via trampoline
+ MetricsCollectorServiceTrampoline metricscollectorservice_trampoline(this);
+ metricscollectorservice_trampoline.Run();
+
+ 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 scoped_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 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 MetricsCollector::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 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 74%
rename from metricsd/metrics_daemon.h
rename to metricsd/metrics_collector.h
index 3d691c5..422ed7c 100644
--- a/metricsd/metrics_daemon.h
+++ b/metricsd/metrics_collector.h
@@ -14,8 +14,8 @@
* 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>
@@ -32,29 +32,24 @@
#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;
-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 +60,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 +74,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 +112,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 +121,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,10 +149,6 @@
// 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(
@@ -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,12 +236,9 @@
// 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_;
@@ -302,17 +267,12 @@
scoped_ptr<PersistentInteger> kernel_crashes_version_count_;
scoped_ptr<PersistentInteger> unclean_shutdowns_daily_count_;
scoped_ptr<PersistentInteger> unclean_shutdowns_weekly_count_;
+
+ scoped_ptr<CpuUsageCollector> cpu_usage_collector_;
scoped_ptr<DiskUsageCollector> disk_usage_collector_;
scoped_ptr<AveragedStatisticsCollector> averaged_stats_collector_;
- std::string scaling_max_freq_path_;
- std::string cpuinfo_max_freq_path_;
-
- 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..dbb0578
--- /dev/null
+++ b/metricsd/metrics_collector_service_impl.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 "metrics_collector_service_impl.h"
+
+#include <binder/IServiceManager.h>
+#include <binder/Status.h>
+#include <brillo/binder_watcher.h>
+#include <utils/Errors.h>
+
+#include "metrics_collector_service_trampoline.h"
+
+using namespace android;
+
+BnMetricsCollectorServiceImpl::BnMetricsCollectorServiceImpl(
+ MetricsCollectorServiceTrampoline* metrics_collector_service_trampoline) {
+ metrics_collector_service_trampoline_ = metrics_collector_service_trampoline;
+}
+
+void BnMetricsCollectorServiceImpl::Run() {
+ status_t status =
+ defaultServiceManager()->addService(getInterfaceDescriptor(), this);
+ CHECK(status == OK) << "libmetricscollectorservice: failed to add service";
+ binder_watcher_.reset(new ::brillo::BinderWatcher);
+ CHECK(binder_watcher_->Init()) << "Binder FD watcher init failed";
+}
+
+android::binder::Status BnMetricsCollectorServiceImpl::notifyUserCrash() {
+ metrics_collector_service_trampoline_->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..bdcab50
--- /dev/null
+++ b/metricsd/metrics_collector_service_impl.h
@@ -0,0 +1,62 @@
+/*
+ * 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
+// MetricsCollectorServiceTrampoline, which we use to call back into
+// MetricsCollector. The trampoline isolates us from the -frtti code of
+// metrics_collector / libbrillo.
+
+#include "android/brillo/metrics/BnMetricsCollectorService.h"
+
+#include <memory>
+
+#include <binder/Status.h>
+#include <brillo/binder_watcher.h>
+
+class MetricsCollectorServiceTrampoline;
+
+//#include "metrics_collector_service_trampoline.h"
+
+class BnMetricsCollectorServiceImpl
+ : public android::brillo::metrics::BnMetricsCollectorService {
+ public:
+ // Passed a this pointer from the MetricsCollectorServiceTrampoline
+ // object that constructs us.
+ explicit BnMetricsCollectorServiceImpl(
+ MetricsCollectorServiceTrampoline* metrics_collector_service_trampoline);
+
+ virtual ~BnMetricsCollectorServiceImpl() = default;
+
+ // Starts the binder main loop.
+ void Run();
+
+ // Called by crash_reporter to report a userspace crash event. We relay
+ // this to MetricsCollector using the trampoline.
+ android::binder::Status notifyUserCrash();
+
+ private:
+ // Trampoline object that constructs us, we use this to call MetricsCollector
+ // methods via the trampoline.
+ MetricsCollectorServiceTrampoline* metrics_collector_service_trampoline_;
+
+ // BinderWatcher object we construct for handling Binder traffic
+ std::unique_ptr<brillo::BinderWatcher> binder_watcher_;
+};
+
+#endif // METRICSD_METRICS_COLLECTOR_SERVICE_IMPL_H_
diff --git a/metricsd/metrics_collector_service_trampoline.cc b/metricsd/metrics_collector_service_trampoline.cc
new file mode 100644
index 0000000..12b80a1
--- /dev/null
+++ b/metricsd/metrics_collector_service_trampoline.cc
@@ -0,0 +1,34 @@
+/*
+ * 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_trampoline.h"
+#include "metrics_collector.h"
+#include "metrics_collector_service_impl.h"
+
+MetricsCollectorServiceTrampoline::MetricsCollectorServiceTrampoline(
+ MetricsCollector* metrics_collector) {
+ metrics_collector_ = metrics_collector;
+}
+
+void MetricsCollectorServiceTrampoline::Run() {
+ // Start metricscollectorservice binder service
+ metrics_collector_service.reset(new BnMetricsCollectorServiceImpl(this));
+ metrics_collector_service->Run();
+}
+
+void MetricsCollectorServiceTrampoline::ProcessUserCrash() {
+ metrics_collector_->ProcessUserCrash();
+}
diff --git a/metricsd/metrics_collector_service_trampoline.h b/metricsd/metrics_collector_service_trampoline.h
new file mode 100644
index 0000000..5da9fa5
--- /dev/null
+++ b/metricsd/metrics_collector_service_trampoline.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_METRICS_COLLECTOR_SERVICE_TRAMPOLINE_H_
+#define METRICSD_METRICS_COLLECTOR_SERVICE_TRAMPOLINE_H_
+
+// Trampoline between the -fno-rtti compile of libmetricsservice and the
+// -frtti compile of metrics_collector. MetricsCollectorServiceTrampoline
+// is called from MetricsCollector to run the IMetricsCollectorService
+// server, and acts as a go-between for calls from server back to
+// MetricsCollector.
+
+#include <memory>
+
+#include "metrics_collector_service_impl.h"
+
+// Forward declaration of MetricsCollector. Don't include the header file
+// for the class here, as it pulls in -frtti stuff.
+class MetricsCollector;
+
+class MetricsCollectorServiceTrampoline {
+ public:
+ // Constructor take a this pointer from the MetricsCollector class that
+ // constructs these objects.
+ explicit MetricsCollectorServiceTrampoline(
+ MetricsCollector* metrics_collector);
+
+ // Initialize and run the IMetricsCollectorService
+ void Run();
+
+ // Called from IMetricsCollectorService to trampoline into the
+ // MetricsCollector method of the same name.
+ void ProcessUserCrash();
+
+ private:
+ // The MetricsCollector object that constructs us, for which we act as
+ // the go-between for MetricsCollectorServiceImpl use.
+ MetricsCollector* metrics_collector_;
+
+ // The IMetricsCollectorService implementation we construct.
+ std::unique_ptr<BnMetricsCollectorServiceImpl> metrics_collector_service;
+};
+
+#endif // METRICSD_METRICS_COLLECTOR_SERVICE_TRAMPOLINE_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..55d6cbc 100644
--- a/metricsd/persistent_integer_test.cc
+++ b/metricsd/persistent_integer_test.cc
@@ -24,7 +24,6 @@
#include "persistent_integer.h"
const char kBackingFileName[] = "1.pibakf";
-const char kBackingFilePattern[] = "*.pibakf";
using chromeos_metrics::PersistentInteger;
@@ -32,28 +31,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));
+ scoped_ptr<PersistentInteger> pi(
+ new PersistentInteger(kBackingFileName, temp_dir_.path()));
// Test initialization.
EXPECT_EQ(0, pi->Get());
@@ -65,7 +51,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 +59,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/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 1995510..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;
}
@@ -108,7 +108,18 @@
profile_.client_id = testing_ ?
"client_id_test" :
GetPersistentGUID(guid_path);
- profile_.hardware_class = "unknown";
+ profile_.model_manifest_id = "unknown";
+ if (!testing_) {
+ brillo::KeyValueStore weave_config;
+ if (!weave_config.Load(base::FilePath(metrics::kWeaveConfigurationFile))) {
+ LOG(ERROR) << "Failed to load the weave configuration file.";
+ } else if (!weave_config.GetString(metrics::kModelManifestId,
+ &profile_.model_manifest_id)) {
+ LOG(ERROR) << "The model manifest id (model_id) is undefined in "
+ << metrics::kWeaveConfigurationFile;
+ }
+ }
+
profile_.channel = ProtoChannelFromString(channel);
// Increment the session_id everytime we initialize this. If metrics_daemon
@@ -143,7 +154,7 @@
metrics::SystemProfileProto* profile_proto =
metrics_proto->mutable_system_profile();
profile_proto->mutable_hardware()->set_hardware_class(
- profile_.hardware_class);
+ profile_.model_manifest_id);
profile_proto->set_app_version(profile_.version);
profile_proto->set_channel(profile_.channel);
metrics::SystemProfileProto_BrilloDeviceData* device_data =
@@ -168,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 97fb33a..0a21ad4 100644
--- a/metricsd/uploader/system_profile_cache.h
+++ b/metricsd/uploader/system_profile_cache.h
@@ -35,7 +35,7 @@
struct SystemProfile {
std::string version;
- std::string hardware_class;
+ std::string model_manifest_id;
std::string client_id;
int session_id;
metrics::SystemProfileProto::Channel channel;
@@ -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();
diff --git a/metricsd/uploader/upload_service.cc b/metricsd/uploader/upload_service.cc
index b630cec..ea8427a 100644
--- a/metricsd/uploader/upload_service.cc
+++ b/metricsd/uploader/upload_service.cc
@@ -16,6 +16,8 @@
#include "uploader/upload_service.h"
+#include <sysexits.h>
+
#include <string>
#include <base/bind.h>
@@ -31,46 +33,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 +97,7 @@
return;
}
- // Previous upload successful, reading metrics sample from the file.
- ReadMetrics();
+ // Previous upload successful, stage another log.
GatherHistograms();
StageCurrentLog();
@@ -114,7 +109,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 +137,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,
@@ -263,3 +206,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 a4d0a1e..fe064b8 100644
--- a/metricsd/uploader/upload_service.h
+++ b/metricsd/uploader/upload_service.h
@@ -19,26 +19,18 @@
#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.
@@ -46,8 +38,8 @@
//
// The two states are the presence or not of a staged log.
// A staged log is a compressed protobuffer containing both the aggregated
-// metrics and event and information about the client. (product, hardware id,
-// etc...).
+// metrics and event and information about the client. (product,
+// model_manifest_id, etc...).
//
// At regular intervals, the upload event will be triggered and the following
// will happen:
@@ -57,24 +49,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 +97,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 +107,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 +117,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.
@@ -158,13 +142,15 @@
MetricsLog* GetOrCreateCurrentLog();
scoped_ptr<SystemProfileSetter> system_profile_setter_;
- MetricsLibraryInterface* metrics_lib_;
base::HistogramSnapshotManager histogram_snapshot_manager_;
scoped_ptr<Sender> sender_;
chromeos_metrics::PersistentInteger failed_upload_count_;
scoped_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..0e2ba8f 100644
--- a/metricsd/uploader/upload_service_test.cc
+++ b/metricsd/uploader/upload_service_test.cc
@@ -20,12 +20,12 @@
#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 "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 +39,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_);
+
+ return upload_service_->current_log_->uma_proto()->system_profile().stability();
+ }
+
base::ScopedTempDir dir_;
scoped_ptr<UploadService> upload_service_;
- MetricsLibrary metrics_lib_;
scoped_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 +120,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 +131,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 +148,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 +160,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 +189,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 +227,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 +243,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 +289,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 8b16996..895a25d 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -41,17 +41,23 @@
# because init.rc is conditionally included.
#
# create some directories (some are mount points) and symlinks
-local_post_install_cmd_base := mkdir -p $(addprefix $(TARGET_ROOT_OUT)/, \
- sbin dev proc sys system data oem acct cache config storage mnt root); \
+LOCAL_POST_INSTALL_CMD := mkdir -p $(addprefix $(TARGET_ROOT_OUT)/, \
+ sbin dev proc sys system data oem acct cache config storage mnt root $(BOARD_ROOT_EXTRA_FOLDERS)); \
ln -sf /system/etc $(TARGET_ROOT_OUT)/etc; \
ln -sf /sys/kernel/debug $(TARGET_ROOT_OUT)/d; \
ln -sf /storage/self/primary $(TARGET_ROOT_OUT)/sdcard
ifdef BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE
- LOCAL_POST_INSTALL_CMD := $(local_post_install_cmd_base); mkdir -p $(TARGET_ROOT_OUT)/vendor
+ LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/vendor
else
- LOCAL_POST_INSTALL_CMD := $(local_post_install_cmd_base)
+ LOCAL_POST_INSTALL_CMD += ; ln -sf /system/vendor $(TARGET_ROOT_OUT)/vendor
endif
-local_post_install_cmd_base :=
+ifdef BOARD_ROOT_EXTRA_SYMLINKS
+# BOARD_ROOT_EXTRA_SYMLINKS is a list of <target>:<link_name>.
+ LOCAL_POST_INSTALL_CMD += $(foreach s, $(BOARD_ROOT_EXTRA_SYMLINKS),\
+ $(eval p := $(subst :,$(space),$(s)))\
+ ; mkdir -p $(dir $(TARGET_ROOT_OUT)/$(word 2,$(p))) \
+ ; ln -sf $(word 1,$(p)) $(TARGET_ROOT_OUT)/$(word 2,$(p)))
+endif
include $(BUILD_SYSTEM)/base_rules.mk
diff --git a/rootdir/init.rc b/rootdir/init.rc
index bdc4567..a902536 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -52,6 +52,17 @@
chown root system /sys/fs/cgroup/memory/sw/tasks
chmod 0660 /sys/fs/cgroup/memory/sw/tasks
+ # Create energy-aware scheduler tuning nodes
+ mkdir /sys/fs/cgroup/stune
+ mount cgroup none /sys/fs/cgroup/stune schedtune
+ mkdir /sys/fs/cgroup/stune/foreground
+ chown system system /sys/fs/cgroup/stune
+ chown system system /sys/fs/cgroup/stune/foreground
+ chown system system /sys/fs/cgroup/stune/tasks
+ chown system system /sys/fs/cgroup/stune/foreground/tasks
+ chmod 0664 /sys/fs/cgroup/stune/tasks
+ chmod 0664 /sys/fs/cgroup/stune/foreground/tasks
+
# Mount staging areas for devices managed by vold
# See storage config details at http://source.android.com/tech/storage/
mount tmpfs tmpfs /mnt mode=0755,uid=0,gid=1000
@@ -65,9 +76,9 @@
mkdir /mnt/user 0755 root root
mkdir /mnt/user/0 0755 root root
mkdir /mnt/expand 0771 system system
+ mkdir /mnt/appfuse 0711 root root
# Storage views to support runtime permissions
- mkdir /storage 0755 root root
mkdir /mnt/runtime 0700 root root
mkdir /mnt/runtime/default 0755 root root
mkdir /mnt/runtime/default/self 0755 root root
@@ -163,13 +174,16 @@
chown system system /dev/cpuset/foreground
chown system system /dev/cpuset/foreground/boost
chown system system /dev/cpuset/background
+ chown system system /dev/cpuset/system-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
+ chown system system /dev/cpuset/system-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/system-background/tasks
chmod 0664 /dev/cpuset/tasks
@@ -248,6 +262,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
@@ -333,12 +350,14 @@
mkdir /data/misc/vold 0700 root root
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
mkdir /data/local/tmp 0771 shell shell
mkdir /data/data 0771 system system
mkdir /data/app-private 0771 system system
+ mkdir /data/app-ephemeral 0771 system system
mkdir /data/app-asec 0700 root root
mkdir /data/app-lib 0771 system system
mkdir /data/app 0771 system system
@@ -347,7 +366,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
@@ -378,11 +396,18 @@
mkdir /data/backup 0700 system system
mkdir /data/media 0770 media_rw media_rw
mkdir /data/ss 0700 system system
+
mkdir /data/system 0775 system system
mkdir /data/system/heapdump 0700 system system
+ mkdir /data/system_ce 0770 system system
+ mkdir /data/system_ce/0 0770 system system
+
mkdir /data/user 0711 system system
+ mkdir /data/user_de 0711 system system
+ mkdir /data/user_de/0 0771 system system
setusercryptopolicies /data/user
+ createuserkey 0 0 0
# Reload policy from /data/security if present.
setprop selinux.reload_policy 1
@@ -492,6 +517,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
@@ -520,9 +547,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
@@ -562,12 +593,25 @@
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
class main
oneshot
+
+# bugreportplus is an enhanced version of bugreport that provides a better
+# user interface (like displaying progress and allowing user to enter details).
+# It's typically triggered by the power button or developer settings.
+# TODO: remove the -p option when screenshot is taken by Shell
+service bugreportplus /system/bin/dumpstate -d -p -B -P -z \
+ -o /data/data/com.android.shell/files/bugreports/bugreport
+ class main
+ disabled
+ oneshot
diff --git a/rootdir/init.zygote32.rc b/rootdir/init.zygote32.rc
index 7c81344..53cfcc2 100644
--- a/rootdir/init.zygote32.rc
+++ b/rootdir/init.zygote32.rc
@@ -6,4 +6,4 @@
onrestart restart audioserver
onrestart restart media
onrestart restart netd
- writepid /dev/cpuset/foreground/tasks
+ writepid /dev/cpuset/foreground/tasks /sys/fs/cgroup/stune/foreground/tasks
diff --git a/rootdir/init.zygote32_64.rc b/rootdir/init.zygote32_64.rc
index 2565202..a9a94d7 100644
--- a/rootdir/init.zygote32_64.rc
+++ b/rootdir/init.zygote32_64.rc
@@ -6,10 +6,10 @@
onrestart restart audioserver
onrestart restart media
onrestart restart netd
- writepid /dev/cpuset/foreground/tasks
+ writepid /dev/cpuset/foreground/tasks /sys/fs/cgroup/stune/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
+ writepid /dev/cpuset/foreground/tasks /sys/fs/cgroup/stune/foreground/tasks
diff --git a/rootdir/init.zygote64.rc b/rootdir/init.zygote64.rc
index 58ebfb9..8f47174 100644
--- a/rootdir/init.zygote64.rc
+++ b/rootdir/init.zygote64.rc
@@ -6,4 +6,4 @@
onrestart restart audioserver
onrestart restart media
onrestart restart netd
- writepid /dev/cpuset/foreground/tasks
+ writepid /dev/cpuset/foreground/tasks /sys/fs/cgroup/stune/foreground/tasks
diff --git a/rootdir/init.zygote64_32.rc b/rootdir/init.zygote64_32.rc
index 917a402..70a9678 100644
--- a/rootdir/init.zygote64_32.rc
+++ b/rootdir/init.zygote64_32.rc
@@ -6,10 +6,10 @@
onrestart restart audioserver
onrestart restart media
onrestart restart netd
- writepid /dev/cpuset/foreground/tasks
+ writepid /dev/cpuset/foreground/tasks /sys/fs/cgroup/stune/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
+ writepid /dev/cpuset/foreground/tasks /sys/fs/cgroup/stune/foreground/tasks
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/toolbox/ps.c b/toolbox/ps.c
index cf3f05a..3bc540d 100644
--- a/toolbox/ps.c
+++ b/toolbox/ps.c
@@ -41,14 +41,14 @@
static void print_exe_abi(int pid);
-static int ps_line(int pid, int tid, char *namefilter)
+static int ps_line(int pid, int tid)
{
char statline[1024];
char cmdline[1024];
char macline[1024];
char user[32];
struct stat stats;
- int fd, r;
+ int r;
char *ptr, *name, *state;
int ppid;
unsigned rss, vss;
@@ -68,7 +68,7 @@
sprintf(statline, "/proc/%d/stat", pid);
sprintf(cmdline, "/proc/%d/cmdline", pid);
snprintf(macline, sizeof(macline), "/proc/%d/attr/current", pid);
- fd = open(cmdline, O_RDONLY);
+ int fd = open(cmdline, O_RDONLY);
if(fd == 0) {
r = 0;
} else {
@@ -79,7 +79,7 @@
cmdline[r] = 0;
}
- fd = open(statline, O_RDONLY);
+ int fd = open(statline, O_RDONLY);
if(fd == 0) return -1;
r = read(fd, statline, 1023);
close(fd);
@@ -158,51 +158,48 @@
return 0;
}
- if(!namefilter || !strncmp(cmdline[0] ? cmdline : name, namefilter, strlen(namefilter))) {
- if (display_flags & SHOW_MACLABEL) {
- fd = open(macline, O_RDONLY);
- strcpy(macline, "-");
- if (fd >= 0) {
- r = read(fd, macline, sizeof(macline)-1);
- close(fd);
- if (r > 0)
- macline[r] = 0;
- }
- printf("%-30s %-9s %-5d %-5d %s\n", macline, user, pid, ppid, cmdline[0] ? cmdline : name);
- return 0;
+ if (display_flags & SHOW_MACLABEL) {
+ fd = open(macline, O_RDONLY);
+ strcpy(macline, "-");
+ if (fd >= 0) {
+ r = read(fd, macline, sizeof(macline)-1);
+ close(fd);
+ if (r > 0)
+ macline[r] = 0;
}
-
- printf("%-9s %-5d %-5d %-6d %-5d", user, pid, ppid, vss / 1024, rss * 4);
- if (display_flags & SHOW_CPU)
- printf(" %-2d", psr);
- if (display_flags & SHOW_PRIO)
- printf(" %-5d %-5d %-5d %-5d", prio, nice, rtprio, sched);
- if (display_flags & SHOW_POLICY) {
- SchedPolicy p;
- if (get_sched_policy(pid, &p) < 0)
- printf(" un ");
- else
- printf(" %.2s ", get_sched_policy_name(p));
- }
- char path[PATH_MAX];
- snprintf(path, sizeof(path), "/proc/%d/wchan", pid);
- char wchan[10];
- int fd = open(path, O_RDONLY);
- ssize_t wchan_len = read(fd, wchan, sizeof(wchan));
- if (wchan_len == -1) {
- wchan[wchan_len = 0] = '\0';
- }
- close(fd);
- printf(" %10.*s %0*" PRIxPTR " %s ", (int) wchan_len, wchan, (int) PC_WIDTH, eip, state);
- if (display_flags & SHOW_ABI) {
- print_exe_abi(pid);
- }
- printf("%s", cmdline[0] ? cmdline : name);
- if(display_flags&SHOW_TIME)
- printf(" (u:%d, s:%d)", utime, stime);
-
- printf("\n");
+ printf("%-30s ", macline);
}
+
+ printf("%-9s %-5d %-5d %-6d %-5d", user, pid, ppid, vss / 1024, rss * 4);
+ if (display_flags & SHOW_CPU)
+ printf(" %-2d", psr);
+ if (display_flags & SHOW_PRIO)
+ printf(" %-5d %-5d %-5d %-5d", prio, nice, rtprio, sched);
+ if (display_flags & SHOW_POLICY) {
+ SchedPolicy p;
+ if (get_sched_policy(pid, &p) < 0)
+ printf(" un ");
+ else
+ printf(" %.2s ", get_sched_policy_name(p));
+ }
+ char path[PATH_MAX];
+ snprintf(path, sizeof(path), "/proc/%d/wchan", pid);
+ char wchan[10];
+ fd = open(path, O_RDONLY);
+ ssize_t wchan_len = read(fd, wchan, sizeof(wchan));
+ if (wchan_len == -1) {
+ wchan[wchan_len = 0] = '\0';
+ }
+ close(fd);
+ printf(" %10.*s %0*" PRIxPTR " %s ", (int) wchan_len, wchan, (int) PC_WIDTH, eip, state);
+ if (display_flags & SHOW_ABI) {
+ print_exe_abi(pid);
+ }
+ printf("%s", cmdline[0] ? cmdline : name);
+ if(display_flags&SHOW_TIME)
+ printf(" (u:%d, s:%d)", utime, stime);
+
+ printf("\n");
return 0;
}
@@ -240,7 +237,7 @@
}
}
-void ps_threads(int pid, char *namefilter)
+void ps_threads(int pid)
{
char tmp[128];
DIR *d;
@@ -254,7 +251,7 @@
if(isdigit(de->d_name[0])){
int tid = atoi(de->d_name);
if(tid == pid) continue;
- ps_line(pid, tid, namefilter);
+ ps_line(pid, tid);
}
}
closedir(d);
@@ -264,7 +261,6 @@
{
DIR *d;
struct dirent *de;
- char *namefilter = 0;
int pidfilter = 0;
int threads = 0;
@@ -290,33 +286,39 @@
display_flags |= SHOW_ABI;
} else if(!strcmp(argv[1],"--ppid")) {
ppid_filter = atoi(argv[2]);
+ if (ppid_filter == 0) {
+ fprintf(stderr, "bad ppid '%s'\n", argv[2]);
+ return 1;
+ }
argc--;
argv++;
- } else if(isdigit(argv[1][0])){
- pidfilter = atoi(argv[1]);
} else {
- namefilter = argv[1];
+ pidfilter = atoi(argv[1]);
+ if (pidfilter == 0) {
+ fprintf(stderr, "bad pid '%s'\n", argv[1]);
+ return 1;
+ }
}
argc--;
argv++;
}
if (display_flags & SHOW_MACLABEL) {
- printf("LABEL USER PID PPID NAME\n");
- } else {
- printf("USER PID PPID VSIZE RSS %s%s %sWCHAN %*s %sNAME\n",
- (display_flags&SHOW_CPU)?"CPU ":"",
- (display_flags&SHOW_PRIO)?"PRIO NICE RTPRI SCHED ":"",
- (display_flags&SHOW_POLICY)?"PCY " : "",
- (int) PC_WIDTH, "PC",
- (display_flags&SHOW_ABI)?"ABI " : "");
+ printf("LABEL ");
}
+ printf("USER PID PPID VSIZE RSS %s%s %sWCHAN %*s %sNAME\n",
+ (display_flags&SHOW_CPU)?"CPU ":"",
+ (display_flags&SHOW_PRIO)?"PRIO NICE RTPRI SCHED ":"",
+ (display_flags&SHOW_POLICY)?"PCY " : "",
+ (int) PC_WIDTH, "PC",
+ (display_flags&SHOW_ABI)?"ABI " : "");
+
while((de = readdir(d)) != 0){
if(isdigit(de->d_name[0])){
int pid = atoi(de->d_name);
if(!pidfilter || (pidfilter == pid)) {
- ps_line(pid, 0, namefilter);
- if(threads) ps_threads(pid, namefilter);
+ ps_line(pid, 0);
+ if(threads) ps_threads(pid);
}
}
}
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/trusty/gatekeeper/Android.mk b/trusty/gatekeeper/Android.mk
new file mode 100644
index 0000000..13e9a09
--- /dev/null
+++ b/trusty/gatekeeper/Android.mk
@@ -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.
+#
+
+# WARNING: Everything listed here will be built on ALL platforms,
+# including x86, the emulator, and the SDK. Modules must be uniquely
+# named (liblights.panda), and must build everywhere, or limit themselves
+# to only building on ARM if they include assembly. Individual makefiles
+# are responsible for having their own logic, for fine-grained control.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := gatekeeper.trusty
+
+LOCAL_MODULE_RELATIVE_PATH := hw
+
+LOCAL_SRC_FILES := \
+ module.cpp \
+ trusty_gatekeeper_ipc.c \
+ trusty_gatekeeper.cpp
+
+LOCAL_CLFAGS = -fvisibility=hidden -Wall -Werror
+
+LOCAL_SHARED_LIBRARIES := \
+ libgatekeeper \
+ liblog \
+ libcutils \
+ libtrusty
+
+LOCAL_MODULE_TAGS := optional
+
+# Symlink gatekeeper.trusty.so -> gatekeeper.<device>.so so libhardware can find it.
+LOCAL_POST_INSTALL_CMD = \
+ $(hide) ln -sf $(notdir $(LOCAL_INSTALLED_MODULE)) $(dir $(LOCAL_INSTALLED_MODULE))gatekeeper.$(TARGET_DEVICE).so
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/trusty/gatekeeper/gatekeeper_ipc.h b/trusty/gatekeeper/gatekeeper_ipc.h
new file mode 100644
index 0000000..b05dcd8
--- /dev/null
+++ b/trusty/gatekeeper/gatekeeper_ipc.h
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#define GATEKEEPER_PORT "com.android.trusty.gatekeeper"
+#define GATEKEEPER_MAX_BUFFER_LENGTH 1024
+
+enum gatekeeper_command {
+ GK_REQ_SHIFT = 1,
+ GK_RESP_BIT = 1,
+
+ GK_ENROLL = (0 << GK_REQ_SHIFT),
+ GK_VERIFY = (1 << GK_REQ_SHIFT),
+};
+
+/**
+ * gatekeeper_message - Serial header for communicating with GK server
+ * @cmd: the command, one of ENROLL, VERIFY. Payload must be a serialized
+ * buffer of the corresponding request object.
+ * @payload: start of the serialized command specific payload
+ */
+struct gatekeeper_message {
+ uint32_t cmd;
+ uint8_t payload[0];
+};
+
diff --git a/trusty/gatekeeper/module.cpp b/trusty/gatekeeper/module.cpp
new file mode 100644
index 0000000..0ee3c2f
--- /dev/null
+++ b/trusty/gatekeeper/module.cpp
@@ -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.
+ */
+
+#include <hardware/hardware.h>
+
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#include "trusty_gatekeeper.h"
+
+using gatekeeper::TrustyGateKeeperDevice;
+
+static int trusty_gatekeeper_open(const hw_module_t *module, const char *name,
+ hw_device_t **device) {
+
+ if (strcmp(name, HARDWARE_GATEKEEPER) != 0) {
+ return -EINVAL;
+ }
+
+ TrustyGateKeeperDevice *gatekeeper = new TrustyGateKeeperDevice(module);
+ if (gatekeeper == NULL) return -ENOMEM;
+ *device = gatekeeper->hw_device();
+
+ return 0;
+}
+
+static struct hw_module_methods_t gatekeeper_module_methods = {
+ .open = trusty_gatekeeper_open,
+};
+
+struct gatekeeper_module HAL_MODULE_INFO_SYM __attribute__((visibility("default"))) = {
+ .common = {
+ .tag = HARDWARE_MODULE_TAG,
+ .module_api_version = GATEKEEPER_MODULE_API_VERSION_0_1,
+ .hal_api_version = HARDWARE_HAL_API_VERSION,
+ .id = GATEKEEPER_HARDWARE_MODULE_ID,
+ .name = "Trusty GateKeeper HAL",
+ .author = "The Android Open Source Project",
+ .methods = &gatekeeper_module_methods,
+ .dso = 0,
+ .reserved = {}
+ },
+};
diff --git a/trusty/gatekeeper/trusty_gatekeeper.cpp b/trusty/gatekeeper/trusty_gatekeeper.cpp
new file mode 100644
index 0000000..d24f44f
--- /dev/null
+++ b/trusty/gatekeeper/trusty_gatekeeper.cpp
@@ -0,0 +1,230 @@
+/*
+ * 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 <errno.h>
+#include <stdio.h>
+#include <assert.h>
+#include <type_traits>
+
+#include "trusty_gatekeeper.h"
+#include "trusty_gatekeeper_ipc.h"
+#include "gatekeeper_ipc.h"
+
+#define LOG_TAG "TrustyGateKeeper"
+#include <cutils/log.h>
+
+namespace gatekeeper {
+
+const uint32_t SEND_BUF_SIZE = 8192;
+const uint32_t RECV_BUF_SIZE = 8192;
+
+TrustyGateKeeperDevice::TrustyGateKeeperDevice(const hw_module_t *module) {
+#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
+ static_assert(std::is_standard_layout<TrustyGateKeeperDevice>::value,
+ "TrustyGateKeeperDevice must be standard layout");
+ static_assert(offsetof(TrustyGateKeeperDevice, device_) == 0,
+ "device_ must be the first member of TrustyGateKeeperDevice");
+ static_assert(offsetof(TrustyGateKeeperDevice, device_.common) == 0,
+ "common must be the first member of gatekeeper_device");
+#else
+ assert(reinterpret_cast<gatekeeper_device_t *>(this) == &device_);
+ assert(reinterpret_cast<hw_device_t *>(this) == &(device_.common));
+#endif
+
+ memset(&device_, 0, sizeof(device_));
+ device_.common.tag = HARDWARE_DEVICE_TAG;
+ device_.common.version = 1;
+ device_.common.module = const_cast<hw_module_t *>(module);
+ device_.common.close = close_device;
+
+ device_.enroll = enroll;
+ device_.verify = verify;
+ device_.delete_user = nullptr;
+ device_.delete_all_users = nullptr;
+
+ int rc = trusty_gatekeeper_connect();
+ if (rc < 0) {
+ ALOGE("Error initializing trusty session: %d", rc);
+ }
+
+ error_ = rc;
+
+}
+
+hw_device_t* TrustyGateKeeperDevice::hw_device() {
+ return &device_.common;
+}
+
+int TrustyGateKeeperDevice::close_device(hw_device_t* dev) {
+ delete reinterpret_cast<TrustyGateKeeperDevice *>(dev);
+ return 0;
+}
+
+TrustyGateKeeperDevice::~TrustyGateKeeperDevice() {
+ trusty_gatekeeper_disconnect();
+}
+
+int TrustyGateKeeperDevice::Enroll(uint32_t uid, const uint8_t *current_password_handle,
+ uint32_t current_password_handle_length, const uint8_t *current_password,
+ uint32_t current_password_length, const uint8_t *desired_password,
+ uint32_t desired_password_length, uint8_t **enrolled_password_handle,
+ uint32_t *enrolled_password_handle_length) {
+
+ if (error_ != 0) {
+ return error_;
+ }
+
+ SizedBuffer desired_password_buffer(desired_password_length);
+ memcpy(desired_password_buffer.buffer.get(), desired_password, desired_password_length);
+
+ SizedBuffer current_password_handle_buffer(current_password_handle_length);
+ if (current_password_handle) {
+ memcpy(current_password_handle_buffer.buffer.get(), current_password_handle,
+ current_password_handle_length);
+ }
+
+ SizedBuffer current_password_buffer(current_password_length);
+ if (current_password) {
+ memcpy(current_password_buffer.buffer.get(), current_password, current_password_length);
+ }
+
+ EnrollRequest request(uid, ¤t_password_handle_buffer, &desired_password_buffer,
+ ¤t_password_buffer);
+ EnrollResponse response;
+
+ gatekeeper_error_t error = Send(request, &response);
+
+ if (error == ERROR_RETRY) {
+ return response.retry_timeout;
+ } else if (error != ERROR_NONE) {
+ return -EINVAL;
+ }
+
+ *enrolled_password_handle = response.enrolled_password_handle.buffer.release();
+ *enrolled_password_handle_length = response.enrolled_password_handle.length;
+
+
+ return 0;
+}
+
+int TrustyGateKeeperDevice::Verify(uint32_t uid, uint64_t challenge,
+ const uint8_t *enrolled_password_handle, uint32_t enrolled_password_handle_length,
+ const uint8_t *provided_password, uint32_t provided_password_length,
+ uint8_t **auth_token, uint32_t *auth_token_length, bool *request_reenroll) {
+ if (error_ != 0) {
+ return error_;
+ }
+
+ SizedBuffer password_handle_buffer(enrolled_password_handle_length);
+ memcpy(password_handle_buffer.buffer.get(), enrolled_password_handle,
+ enrolled_password_handle_length);
+ SizedBuffer provided_password_buffer(provided_password_length);
+ memcpy(provided_password_buffer.buffer.get(), provided_password, provided_password_length);
+
+ VerifyRequest request(uid, challenge, &password_handle_buffer, &provided_password_buffer);
+ VerifyResponse response;
+
+ gatekeeper_error_t error = Send(request, &response);
+
+ if (error == ERROR_RETRY) {
+ return response.retry_timeout;
+ } else if (error != ERROR_NONE) {
+ return -EINVAL;
+ }
+
+ if (auth_token != NULL && auth_token_length != NULL) {
+ *auth_token = response.auth_token.buffer.release();
+ *auth_token_length = response.auth_token.length;
+ }
+
+ if (request_reenroll != NULL) {
+ *request_reenroll = response.request_reenroll;
+ }
+
+ return 0;
+}
+
+gatekeeper_error_t TrustyGateKeeperDevice::Send(uint32_t command, const GateKeeperMessage& request,
+ GateKeeperMessage *response) {
+ uint32_t request_size = request.GetSerializedSize();
+ if (request_size > SEND_BUF_SIZE)
+ return ERROR_INVALID;
+ uint8_t send_buf[SEND_BUF_SIZE];
+ request.Serialize(send_buf, send_buf + request_size);
+
+ // Send it
+ uint8_t recv_buf[RECV_BUF_SIZE];
+ uint32_t response_size = RECV_BUF_SIZE;
+ int rc = trusty_gatekeeper_call(command, send_buf, request_size, recv_buf, &response_size);
+ if (rc < 0) {
+ ALOGE("error (%d) calling gatekeeper TA", rc);
+ return ERROR_INVALID;
+ }
+
+ const gatekeeper_message *msg = reinterpret_cast<gatekeeper_message *>(recv_buf);
+ const uint8_t *payload = msg->payload;
+
+ return response->Deserialize(payload, payload + response_size);
+}
+
+static inline TrustyGateKeeperDevice *convert_device(const gatekeeper_device *dev) {
+ return reinterpret_cast<TrustyGateKeeperDevice *>(const_cast<gatekeeper_device *>(dev));
+}
+
+/* static */
+int TrustyGateKeeperDevice::enroll(const struct gatekeeper_device *dev, uint32_t uid,
+ const uint8_t *current_password_handle, uint32_t current_password_handle_length,
+ const uint8_t *current_password, uint32_t current_password_length,
+ const uint8_t *desired_password, uint32_t desired_password_length,
+ uint8_t **enrolled_password_handle, uint32_t *enrolled_password_handle_length) {
+
+ if (dev == NULL ||
+ enrolled_password_handle == NULL || enrolled_password_handle_length == NULL ||
+ desired_password == NULL || desired_password_length == 0)
+ return -EINVAL;
+
+ // Current password and current password handle go together
+ if (current_password_handle == NULL || current_password_handle_length == 0 ||
+ current_password == NULL || current_password_length == 0) {
+ current_password_handle = NULL;
+ current_password_handle_length = 0;
+ current_password = NULL;
+ current_password_length = 0;
+ }
+
+ return convert_device(dev)->Enroll(uid, current_password_handle, current_password_handle_length,
+ current_password, current_password_length, desired_password, desired_password_length,
+ enrolled_password_handle, enrolled_password_handle_length);
+
+}
+
+/* static */
+int TrustyGateKeeperDevice::verify(const struct gatekeeper_device *dev, uint32_t uid,
+ uint64_t challenge, const uint8_t *enrolled_password_handle,
+ uint32_t enrolled_password_handle_length, const uint8_t *provided_password,
+ uint32_t provided_password_length, uint8_t **auth_token, uint32_t *auth_token_length,
+ bool *request_reenroll) {
+
+ if (dev == NULL || enrolled_password_handle == NULL ||
+ provided_password == NULL) {
+ return -EINVAL;
+ }
+
+ return convert_device(dev)->Verify(uid, challenge, enrolled_password_handle,
+ enrolled_password_handle_length, provided_password, provided_password_length,
+ auth_token, auth_token_length, request_reenroll);
+}
+};
diff --git a/trusty/gatekeeper/trusty_gatekeeper.h b/trusty/gatekeeper/trusty_gatekeeper.h
new file mode 100644
index 0000000..82108dc
--- /dev/null
+++ b/trusty/gatekeeper/trusty_gatekeeper.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TRUSTY_GATEKEEPER_H
+#define TRUSTY_GATEKEEPER_H
+
+#include <hardware/gatekeeper.h>
+#include <gatekeeper/gatekeeper_messages.h>
+
+#include "gatekeeper_ipc.h"
+
+namespace gatekeeper {
+
+class TrustyGateKeeperDevice {
+ public:
+
+ TrustyGateKeeperDevice(const hw_module_t* module);
+ ~TrustyGateKeeperDevice();
+
+ hw_device_t* hw_device();
+
+ /**
+ * Enrolls password_payload, which should be derived from a user selected pin or password,
+ * with the authentication factor private key used only for enrolling authentication
+ * factor data.
+ *
+ * Returns: 0 on success or an error code less than 0 on error.
+ * On error, enrolled_password will not be allocated.
+ */
+ int Enroll(uint32_t uid, const uint8_t *current_password_handle,
+ uint32_t current_password_handle_length, const uint8_t *current_password,
+ uint32_t current_password_length, const uint8_t *desired_password,
+ uint32_t desired_password_length, uint8_t **enrolled_password_handle,
+ uint32_t *enrolled_password_handle_length);
+
+ /**
+ * Verifies provided_password matches expected_password after enrolling
+ * with the authentication factor private key.
+ *
+ * Implementations of this module may retain the result of this call
+ * to attest to the recency of authentication.
+ *
+ * On success, writes the address of a verification token to verification_token,
+ *
+ * Returns: 0 on success or an error code less than 0 on error
+ * On error, verification token will not be allocated
+ */
+ int Verify(uint32_t uid, uint64_t challenge, const uint8_t *enrolled_password_handle,
+ uint32_t enrolled_password_handle_length, const uint8_t *provided_password,
+ uint32_t provided_password_length, uint8_t **auth_token, uint32_t *auth_token_length,
+ bool *request_reenroll);
+
+ private:
+
+ gatekeeper_error_t Send(uint32_t command, const GateKeeperMessage& request,
+ GateKeeperMessage* response);
+
+ gatekeeper_error_t Send(const EnrollRequest& request, EnrollResponse *response) {
+ return Send(GK_ENROLL, request, response);
+ }
+
+ gatekeeper_error_t Send(const VerifyRequest& request, VerifyResponse *response) {
+ return Send(GK_VERIFY, request, response);
+ }
+
+ // Static methods interfacing the HAL API with the TrustyGateKeeper device
+
+ /**
+ * Enrolls desired_password, which should be derived from a user selected pin or password,
+ * with the authentication factor private key used only for enrolling authentication
+ * factor data.
+ *
+ * If there was already a password enrolled, it should be provided in
+ * current_password_handle, along with the current password in current_password
+ * that should validate against current_password_handle.
+ *
+ * Returns: 0 on success or an error code less than 0 on error.
+ * On error, enrolled_password_handle will not be allocated.
+ */
+ static int enroll(const struct gatekeeper_device *dev, uint32_t uid,
+ const uint8_t *current_password_handle, uint32_t current_password_handle_length,
+ const uint8_t *current_password, uint32_t current_password_length,
+ const uint8_t *desired_password, uint32_t desired_password_length,
+ uint8_t **enrolled_password_handle, uint32_t *enrolled_password_handle_length);
+
+ /**
+ * Verifies provided_password matches enrolled_password_handle.
+ *
+ * Implementations of this module may retain the result of this call
+ * to attest to the recency of authentication.
+ *
+ * On success, writes the address of a verification token to auth_token,
+ * usable to attest password verification to other trusted services. Clients
+ * may pass NULL for this value.
+ *
+ * Returns: 0 on success or an error code less than 0 on error
+ * On error, verification token will not be allocated
+ */
+ static int verify(const struct gatekeeper_device *dev, uint32_t uid, uint64_t challenge,
+ const uint8_t *enrolled_password_handle, uint32_t enrolled_password_handle_length,
+ const uint8_t *provided_password, uint32_t provided_password_length,
+ uint8_t **auth_token, uint32_t *auth_token_length, bool *request_reenroll);
+
+ static int close_device(hw_device_t* dev);
+
+ gatekeeper_device device_;
+ int error_;
+
+};
+}
+
+#endif
+
diff --git a/trusty/gatekeeper/trusty_gatekeeper_ipc.c b/trusty/gatekeeper/trusty_gatekeeper_ipc.c
new file mode 100644
index 0000000..a1c319e
--- /dev/null
+++ b/trusty/gatekeeper/trusty_gatekeeper_ipc.c
@@ -0,0 +1,91 @@
+/*
+ * 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 <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define LOG_TAG "TrustyGateKeeper"
+#include <cutils/log.h>
+#include <trusty/tipc.h>
+
+#include "trusty_gatekeeper_ipc.h"
+#include "gatekeeper_ipc.h"
+
+#define TRUSTY_DEVICE_NAME "/dev/trusty-ipc-dev0"
+
+static int handle_ = 0;
+
+int trusty_gatekeeper_connect() {
+ int rc = tipc_connect(TRUSTY_DEVICE_NAME, GATEKEEPER_PORT);
+ if (rc < 0) {
+ return rc;
+ }
+
+ handle_ = rc;
+ return 0;
+}
+
+int trusty_gatekeeper_call(uint32_t cmd, void *in, uint32_t in_size, uint8_t *out,
+ uint32_t *out_size) {
+ if (handle_ == 0) {
+ ALOGE("not connected\n");
+ return -EINVAL;
+ }
+
+ size_t msg_size = in_size + sizeof(struct gatekeeper_message);
+ struct gatekeeper_message *msg = malloc(msg_size);
+ msg->cmd = cmd;
+ memcpy(msg->payload, in, in_size);
+
+ ssize_t rc = write(handle_, msg, msg_size);
+ free(msg);
+
+ if (rc < 0) {
+ ALOGE("failed to send cmd (%d) to %s: %s\n", cmd,
+ GATEKEEPER_PORT, strerror(errno));
+ return -errno;
+ }
+
+ rc = read(handle_, out, *out_size);
+ if (rc < 0) {
+ ALOGE("failed to retrieve response for cmd (%d) to %s: %s\n",
+ cmd, GATEKEEPER_PORT, strerror(errno));
+ return -errno;
+ }
+
+ if ((size_t) rc < sizeof(struct gatekeeper_message)) {
+ ALOGE("invalid response size (%d)\n", (int) rc);
+ return -EINVAL;
+ }
+
+ msg = (struct gatekeeper_message *) out;
+
+ if ((cmd | GK_RESP_BIT) != msg->cmd) {
+ ALOGE("invalid command (%d)\n", msg->cmd);
+ return -EINVAL;
+ }
+
+ *out_size = ((size_t) rc) - sizeof(struct gatekeeper_message);
+ return rc;
+}
+
+void trusty_gatekeeper_disconnect() {
+ if (handle_ != 0) {
+ tipc_close(handle_);
+ }
+}
+
diff --git a/trusty/gatekeeper/trusty_gatekeeper_ipc.h b/trusty/gatekeeper/trusty_gatekeeper_ipc.h
new file mode 100644
index 0000000..f8de7f8
--- /dev/null
+++ b/trusty/gatekeeper/trusty_gatekeeper_ipc.h
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+__BEGIN_DECLS
+
+int trusty_gatekeeper_connect();
+int trusty_gatekeeper_call(uint32_t cmd, void *in, uint32_t in_size, uint8_t *out,
+ uint32_t *out_size);
+void trusty_gatekeeper_disconnect();
+
+__END_DECLS
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):