Merge "Add explicit cast to shut off clang warnings."
diff --git a/adb/Android.mk b/adb/Android.mk
index 903d1e1..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
@@ -230,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
@@ -239,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
@@ -273,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.h b/adb/adb.h
index 491fff3..5187c81 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -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
diff --git a/adb/adb_auth_host.cpp b/adb/adb_auth_host.cpp
index 5309519..e11bff0 100644
--- a/adb/adb_auth_host.cpp
+++ b/adb/adb_auth_host.cpp
@@ -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/client/main.cpp b/adb/client/main.cpp
index a225a53..04b9882 100644
--- a/adb/client/main.cpp
+++ b/adb/client/main.cpp
@@ -58,7 +58,12 @@
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";
@@ -81,8 +86,7 @@
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 +102,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 +119,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 6e4c4e8..bd3813e 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -62,7 +62,7 @@
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) {
@@ -591,6 +591,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);
@@ -1029,8 +1034,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) {
@@ -1080,8 +1084,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; ) {
@@ -1092,14 +1096,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);
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:";
@@ -1115,15 +1123,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) {
@@ -1286,6 +1296,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
@@ -1417,7 +1432,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);
}
diff --git a/adb/fdevent.cpp b/adb/fdevent.cpp
index 06eb34d..46547b9 100644
--- a/adb/fdevent.cpp
+++ b/adb/fdevent.cpp
@@ -70,8 +70,8 @@
// All operations to fdevent should happen only in the main thread.
// That's why we don't need a lock for fdevent.
-static std::unordered_map<int, PollNode> g_poll_node_map;
-static std::list<fdevent*> g_pending_list;
+static auto& g_poll_node_map = *new std::unordered_map<int, PollNode>();
+static auto& g_pending_list = *new std::list<fdevent*>();
static bool main_thread_valid;
static pthread_t main_thread;
diff --git a/adb/file_sync_client.cpp b/adb/file_sync_client.cpp
index 3322763..ad38369 100644
--- a/adb/file_sync_client.cpp
+++ b/adb/file_sync_client.cpp
@@ -403,9 +403,9 @@
if (!sc.SendRequest(ID_RECV, rpath)) return false;
adb_unlink(lpath);
- if (!mkdirs(adb_dirname(lpath))) {
- sc.Error("failed to create parent directory '%s': %s",
- adb_dirname(lpath).c_str(), 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;
}
@@ -478,32 +478,34 @@
struct copyinfo
{
- std::string src;
- std::string dst;
+ std::string lpath;
+ std::string rpath;
unsigned int time;
unsigned int mode;
uint64_t size;
bool skip;
};
-static copyinfo mkcopyinfo(const std::string& spath, const std::string& dpath,
+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('/');
+ }
+}
+
+static copyinfo mkcopyinfo(std::string lpath, std::string rpath,
const std::string& name, unsigned int mode) {
copyinfo result;
- result.src = spath;
- result.dst = dpath;
- if (result.src.back() != '/') {
- result.src.push_back('/');
- }
- if (result.dst.back() != '/') {
- result.dst.push_back('/');
- }
- result.src.append(name);
- result.dst.append(name);
+ 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);
- bool isdir = S_ISDIR(mode);
- if (isdir) {
- result.src.push_back('/');
- result.dst.push_back('/');
+ if (S_ISDIR(mode)) {
+ ensure_trailing_separator(result.lpath, result.rpath);
}
result.time = 0;
@@ -538,22 +540,23 @@
std::string stat_path = lpath + de->d_name;
struct stat st;
- if (!lstat(stat_path.c_str(), &st)) {
- copyinfo ci = mkcopyinfo(lpath, rpath, de->d_name, st.st_mode);
- if (S_ISDIR(st.st_mode)) {
- dirlist.push_back(ci);
- } else {
- if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
- sc.Warning("skipping special file '%s'", lpath.c_str());
- } else {
- ci.time = st.st_mtime;
- ci.size = st.st_size;
- filelist->push_back(ci);
- }
- }
- } else {
+ 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 {
+ if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
+ sc.Error("skipping special file '%s'", lpath.c_str());
+ } else {
+ ci.time = st.st_mtime;
+ ci.size = st.st_size;
+ filelist->push_back(ci);
+ }
}
}
@@ -574,7 +577,7 @@
}
for (const copyinfo& ci : dirlist) {
- local_build_list(sc, filelist, ci.src.c_str(), ci.dst.c_str());
+ local_build_list(sc, filelist, ci.lpath, ci.rpath);
}
return true;
@@ -584,13 +587,8 @@
std::string rpath, bool check_timestamps,
bool list_only) {
// Make sure that both directory paths end in a slash.
- // Both paths are known to exist, so they cannot be empty.
- if (lpath.back() != '/') {
- lpath.push_back('/');
- }
- if (rpath.back() != '/') {
- rpath.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.
std::vector<copyinfo> filelist;
@@ -602,7 +600,7 @@
if (check_timestamps) {
for (const copyinfo& ci : filelist) {
- if (!sc.SendRequest(ID_STAT, ci.dst.c_str())) {
+ if (!sc.SendRequest(ID_STAT, ci.rpath.c_str())) {
return false;
}
}
@@ -624,10 +622,10 @@
for (const copyinfo& ci : filelist) {
if (!ci.skip) {
if (list_only) {
- sc.Error("would push: %s -> %s", ci.src.c_str(),
- ci.dst.c_str());
+ sc.Error("would push: %s -> %s", ci.lpath.c_str(),
+ ci.rpath.c_str());
} else {
- if (!sync_send(sc, ci.src.c_str(), ci.dst.c_str(), ci.time,
+ if (!sync_send(sc, ci.lpath.c_str(), ci.rpath.c_str(), ci.time,
ci.mode)) {
return false;
}
@@ -649,9 +647,10 @@
if (!sc.IsValid()) return false;
bool success = true;
- unsigned mode;
- if (!sync_stat(sc, dst, nullptr, &mode, nullptr)) return false;
- bool dst_isdir = mode != 0 && S_ISDIR(mode);
+ 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) {
@@ -659,7 +658,10 @@
return false;
} else {
size_t dst_len = strlen(dst);
- if (dst[dst_len - 1] == '/') {
+
+ // 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;
}
@@ -669,19 +671,37 @@
for (const char* src_path : srcs) {
const char* dst_path = dst;
struct stat st;
- if (stat(src_path, &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)) {
- success &= copy_local_dir_remote(sc, src_path, dst, false, false);
+ 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 (mode != 0 && S_ISDIR(mode)) {
+ 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(
@@ -712,7 +732,7 @@
// We found a child that isn't '.' or '..'.
empty_dir = false;
- copyinfo ci = mkcopyinfo(rpath, lpath, name, mode);
+ copyinfo ci = mkcopyinfo(lpath, rpath, name, mode);
if (S_ISDIR(mode)) {
dirlist.push_back(ci);
} else if (S_ISREG(mode) || S_ISLNK(mode)) {
@@ -731,11 +751,7 @@
// Add the current directory to the list if it was empty, to ensure that
// it gets created.
if (empty_dir) {
- auto rdname = adb_dirname(rpath);
- auto ldname = adb_dirname(lpath);
- auto rbasename = adb_basename(rpath);
- auto lbasename = adb_basename(lpath);
- filelist->push_back(mkcopyinfo(adb_dirname(rpath), adb_dirname(lpath),
+ filelist->push_back(mkcopyinfo(adb_dirname(lpath), adb_dirname(rpath),
adb_basename(rpath), S_IFDIR));
return true;
}
@@ -744,8 +760,7 @@
while (!dirlist.empty()) {
copyinfo current = dirlist.back();
dirlist.pop_back();
- if (!remote_build_list(sc, filelist, current.src.c_str(),
- current.dst.c_str())) {
+ if (!remote_build_list(sc, filelist, current.rpath, current.lpath)) {
return false;
}
}
@@ -753,15 +768,15 @@
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);
umask(mask);
- int r2 = chmod(lpath, mode & ~mask);
+ int r2 = chmod(lpath.c_str(), mode & ~mask);
return r1 ? r1 : r2;
}
@@ -769,13 +784,8 @@
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.
- // Both paths are known to exist, so they cannot be empty.
- if (rpath.back() != '/') {
- rpath.push_back('/');
- }
- if (lpath.back() != '/') {
- lpath.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.
sc.Print("pull: building file list...");
@@ -788,26 +798,25 @@
int skipped = 0;
for (const copyinfo &ci : filelist) {
if (!ci.skip) {
- sc.Printf("pull: %s -> %s", ci.src.c_str(), ci.dst.c_str());
+ 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.dst)) {
+ if (!mkdirs(ci.lpath)) {
sc.Error("failed to create directory '%s': %s",
- ci.dst.c_str(), strerror(errno));
+ ci.lpath.c_str(), strerror(errno));
return false;
}
pulled++;
continue;
}
- if (!sync_recv(sc, ci.src.c_str(), ci.dst.c_str())) {
+ if (!sync_recv(sc, ci.rpath.c_str(), ci.lpath.c_str())) {
return false;
}
- if (copy_attrs &&
- set_time_and_mode(ci.dst.c_str(), ci.time, ci.mode)) {
+ if (copy_attrs && set_time_and_mode(ci.lpath, ci.time, ci.mode)) {
return false;
}
pulled++;
@@ -828,25 +837,38 @@
if (!sc.IsValid()) return false;
bool success = true;
- unsigned mode, time;
struct stat st;
- if (stat(dst, &st)) {
- // If we're only pulling one file, the destination path might point to
+ 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) {
- sc.Error("cannot stat '%s': %s", dst, strerror(errno));
+ 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;
}
}
- bool dst_isdir = S_ISDIR(st.st_mode);
+ 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 {
size_t dst_len = strlen(dst);
- if (dst[dst_len - 1] == '/') {
+
+ // 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;
}
@@ -855,38 +877,55 @@
for (const char* src_path : srcs) {
const char* dst_path = dst;
- if (!sync_stat(sc, src_path, &time, &mode, nullptr)) return false;
- if (mode == 0) {
+ 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(mode) || S_ISLNK(mode)) {
- // TODO(b/25601283): symlinks shouldn't be handled as files.
+ if (S_ISREG(src_mode)) {
std::string path_holder;
- struct stat st;
- if (stat(dst_path, &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", dst_path, adb_basename(src_path).c_str());
- dst_path = path_holder.c_str();
- }
+ 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, time, mode)) {
+ if (copy_attrs &&
+ set_time_and_mode(dst_path, src_time, src_mode) != 0) {
success = false;
continue;
}
}
- } else if (S_ISDIR(mode)) {
- success &= copy_remote_dir_local(sc, src_path, dst_path, copy_attrs);
+ } 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);
diff --git a/adb/line_printer.cpp b/adb/line_printer.cpp
index aa332f7..4c57c7e 100644
--- a/adb/line_printer.cpp
+++ b/adb/line_printer.cpp
@@ -77,7 +77,7 @@
CONSOLE_SCREEN_BUFFER_INFO csbi;
GetConsoleScreenBufferInfo(console_, &csbi);
- // TODO: const std::wstring to_print_wide = widen(to_print);
+ // TODO: std::wstring to_print_wide; if (!android::base::UTF8ToWide(to_print, &to_print_wide)...
// TODO: wstring ElideMiddle.
to_print = ElideMiddle(to_print, static_cast<size_t>(csbi.dwSize.X));
// We don't want to have the cursor spamming back and forth, so instead of
diff --git a/adb/remount_service.cpp b/adb/remount_service.cpp
index 35ba056..8f1c9b0 100644
--- a/adb/remount_service.cpp
+++ b/adb/remount_service.cpp
@@ -35,8 +35,6 @@
#include "cutils/properties.h"
#include "fs_mgr.h"
-const std::string kFstab_Prefix = "/fstab.";
-
// Returns the device used to mount a directory in /proc/mounts.
static std::string find_proc_mount(const char* dir) {
std::unique_ptr<FILE, int(*)(FILE*)> fp(setmntent("/proc/mounts", "r"), endmntent);
@@ -58,7 +56,7 @@
char propbuf[PROPERTY_VALUE_MAX];
property_get("ro.hardware", propbuf, "");
- std::string fstab_filename = kFstab_Prefix + propbuf;
+ std::string fstab_filename = std::string("/fstab.") + propbuf;
struct fstab* fstab = fs_mgr_read_fstab(fstab_filename.c_str());
struct fstab_rec* rec = fs_mgr_get_entry_for_mount_point(fstab, dir);
std::string dev = rec ? std::string(rec->blk_device) : "";
diff --git a/adb/services.cpp b/adb/services.cpp
index 19a6726..41da4b8 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -213,9 +213,11 @@
// Defaults:
// PTY for interactive, raw for non-interactive.
// No protocol.
+ // $TERM set to "dumb".
SubprocessType type(command.empty() ? SubprocessType::kPty
: SubprocessType::kRaw);
SubprocessProtocol protocol = SubprocessProtocol::kNone;
+ std::string terminal_type = "dumb";
for (const std::string& arg : android::base::Split(service_args, ",")) {
if (arg == kShellServiceArgRaw) {
@@ -224,14 +226,15 @@
type = SubprocessType::kPty;
} else if (arg == kShellServiceArgShellProtocol) {
protocol = SubprocessProtocol::kShell;
- }
- else if (!arg.empty()) {
- LOG(ERROR) << "Unsupported shell service arguments: " << args;
- return -1;
+ } else if (android::base::StartsWith(arg, "TERM=")) {
+ terminal_type = arg.substr(5);
+ } else if (!arg.empty()) {
+ // This is not an error to allow for future expansion.
+ LOG(WARNING) << "Ignoring unknown shell service argument: " << arg;
}
}
- return StartSubprocess(command.c_str(), type, protocol);
+ return StartSubprocess(command.c_str(), terminal_type.c_str(), type, protocol);
}
#endif // !ADB_HOST
@@ -308,8 +311,7 @@
} else if(!strncmp(name, "shell", 5)) {
ret = ShellService(name + 5, transport);
} else if(!strncmp(name, "exec:", 5)) {
- ret = StartSubprocess(name + 5, SubprocessType::kRaw,
- SubprocessProtocol::kNone);
+ ret = StartSubprocess(name + 5, nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
} else if(!strncmp(name, "sync:", 5)) {
ret = create_service_thread(file_sync_service, NULL);
} else if(!strncmp(name, "remount:", 8)) {
@@ -325,9 +327,9 @@
} else if(!strncmp(name, "backup:", 7)) {
ret = StartSubprocess(android::base::StringPrintf("/system/bin/bu backup %s",
(name + 7)).c_str(),
- SubprocessType::kRaw, SubprocessProtocol::kNone);
+ nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
} else if(!strncmp(name, "restore:", 8)) {
- ret = StartSubprocess("/system/bin/bu restore", SubprocessType::kRaw,
+ ret = StartSubprocess("/system/bin/bu restore", nullptr, SubprocessType::kRaw,
SubprocessProtocol::kNone);
} else if(!strncmp(name, "tcpip:", 6)) {
int port;
diff --git a/adb/shell_service.cpp b/adb/shell_service.cpp
index e3fde26..2e41fe6 100644
--- a/adb/shell_service.cpp
+++ b/adb/shell_service.cpp
@@ -175,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_; }
@@ -207,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;
@@ -220,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() {
@@ -290,6 +294,9 @@
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);
@@ -644,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 63c00da..6f8ea9b 100644
--- a/adb/shell_service.h
+++ b/adb/shell_service.h
@@ -141,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..a012f3e 100644
--- a/adb/shell_service_test.cpp
+++ b/adb/shell_service_test.cpp
@@ -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/sysdeps.h b/adb/sysdeps.h
index 9f4012a..cba66fc 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 <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
@@ -338,18 +345,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 {
@@ -425,6 +420,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
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
index e325889..81dcb41 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -35,6 +35,7 @@
#include <base/logging.h>
#include <base/stringprintf.h>
#include <base/strings.h>
+#include <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").
@@ -2546,15 +2565,14 @@
return _get_console_handle(fd) ? 1 : 0;
}
-// 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) {
+// 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;
}
@@ -2580,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;
}
@@ -2946,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(
@@ -3133,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,
@@ -3149,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
@@ -3296,46 +3296,15 @@
// 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
}
}
@@ -3454,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
@@ -3488,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
}
@@ -3637,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)
@@ -3663,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.
@@ -3683,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.
@@ -3716,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());
@@ -3732,29 +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);
+ std::wstring path_wide;
+ if (!android::base::UTF8ToWide(path, &path_wide)) {
+ return -1;
+ }
+
+ 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
@@ -3904,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
@@ -3936,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() {
@@ -3965,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});
}
}
@@ -3999,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/transport.cpp b/adb/transport.cpp
index 4066889..2f18f20 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -38,8 +38,8 @@
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 );
diff --git a/adb/usb_linux.cpp b/adb/usb_linux.cpp
index c633f7f..0358b62 100644
--- a/adb/usb_linux.cpp
+++ b/adb/usb_linux.cpp
@@ -81,8 +81,8 @@
pthread_t reaper_thread = 0;
};
-static std::mutex g_usb_handles_mutex;
-static std::list<usb_handle*> g_usb_handles;
+static auto& g_usb_handles_mutex = *new std::mutex();
+static auto& g_usb_handles = *new std::list<usb_handle*>();
static int is_known_device(const char* dev_name) {
std::lock_guard<std::mutex> lock(g_usb_handles_mutex);
diff --git a/adb/usb_windows.cpp b/adb/usb_windows.cpp
index d811b24..8d3501e 100644
--- a/adb/usb_windows.cpp
+++ b/adb/usb_windows.cpp
@@ -55,7 +55,7 @@
ADBAPIHANDLE adb_write_pipe;
/// Interface name
- char* interface_name;
+ wchar_t* interface_name;
/// Mask for determining when to use zero length packets
unsigned zero_mask;
@@ -74,11 +74,11 @@
ADB_MUTEX_DEFINE( usb_lock );
/// Checks if there is opened usb handle in handle_list for this device.
-int known_device(const char* dev_name);
+int known_device(const wchar_t* dev_name);
/// Checks if there is opened usb handle in handle_list for this device.
/// usb_lock mutex must be held before calling this routine.
-int known_device_locked(const char* dev_name);
+int known_device_locked(const wchar_t* dev_name);
/// Registers opened usb handle (adds it to handle_list).
int register_new_device(usb_handle* handle);
@@ -118,7 +118,7 @@
/// Closes opened usb handle
int usb_close(usb_handle* handle);
-int known_device_locked(const char* dev_name) {
+int known_device_locked(const wchar_t* dev_name) {
usb_handle* usb;
if (NULL != dev_name) {
@@ -126,7 +126,7 @@
for(usb = handle_list.next; usb != &handle_list; usb = usb->next) {
// In Windows names are not case sensetive!
if((NULL != usb->interface_name) &&
- (0 == stricmp(usb->interface_name, dev_name))) {
+ (0 == wcsicmp(usb->interface_name, dev_name))) {
return 1;
}
}
@@ -135,7 +135,7 @@
return 0;
}
-int known_device(const char* dev_name) {
+int known_device(const wchar_t* dev_name) {
int ret = 0;
if (NULL != dev_name) {
@@ -316,17 +316,16 @@
AdbGetInterfaceName(ret->adb_interface,
NULL,
&name_len,
- true);
+ false);
if (0 == name_len) {
D("AdbGetInterfaceName returned name length of zero: %s",
SystemErrorCodeToString(GetLastError()).c_str());
goto fail;
}
- ret->interface_name = (char*)malloc(name_len);
+ ret->interface_name = (wchar_t*)malloc(name_len * sizeof(ret->interface_name[0]));
if (NULL == ret->interface_name) {
- D("Could not allocate %lu bytes for interface_name: %s", name_len,
- strerror(errno));
+ D("Could not allocate %lu characters for interface_name: %s", name_len, strerror(errno));
goto fail;
}
@@ -334,7 +333,7 @@
if (!AdbGetInterfaceName(ret->adb_interface,
ret->interface_name,
&name_len,
- true)) {
+ false)) {
D("AdbGetInterfaceName failed: %s",
SystemErrorCodeToString(GetLastError()).c_str());
goto fail;
@@ -581,12 +580,10 @@
}
void find_devices() {
- usb_handle* handle = NULL;
+ usb_handle* handle = NULL;
char entry_buffer[2048];
- char interf_name[2048];
AdbInterfaceInfo* next_interface = (AdbInterfaceInfo*)(&entry_buffer[0]);
unsigned long entry_buffer_size = sizeof(entry_buffer);
- char* copy_name;
// Enumerate all present and active interfaces.
ADBAPIHANDLE enum_handle =
@@ -599,25 +596,21 @@
}
while (AdbNextInterface(enum_handle, next_interface, &entry_buffer_size)) {
- // TODO: FIXME - temp hack converting wchar_t into char.
- // It would be better to change AdbNextInterface so it will return
- // interface name as single char string.
- const wchar_t* wchar_name = next_interface->device_name;
- for(copy_name = interf_name;
- L'\0' != *wchar_name;
- wchar_name++, copy_name++) {
- *copy_name = (char)(*wchar_name);
- }
- *copy_name = '\0';
-
// Lets see if we already have this device in the list
- if (!known_device(interf_name)) {
+ if (!known_device(next_interface->device_name)) {
// This seems to be a new device. Open it!
- handle = do_usb_open(next_interface->device_name);
- if (NULL != handle) {
+ handle = do_usb_open(next_interface->device_name);
+ if (NULL != handle) {
// Lets see if this interface (device) belongs to us
if (recognized_device(handle)) {
- D("adding a new device %s", interf_name);
+ D("adding a new device %ls", next_interface->device_name);
+
+ // We don't request a wchar_t string from AdbGetSerialNumber() because of a bug in
+ // adb_winusb_interface.cpp:CopyMemory(buffer, ser_num->bString, bytes_written) where the
+ // last parameter should be (str_len * sizeof(wchar_t)). The bug reads 2 bytes past the
+ // end of a stack buffer in the best case, and in the unlikely case of a long serial
+ // number, it will read 2 bytes past the end of a heap allocation. This doesn't affect the
+ // resulting string, but we should avoid the bad reads in the first place.
char serial_number[512];
unsigned long serial_number_len = sizeof(serial_number);
if (AdbGetSerialNumber(handle->adb_interface,
@@ -628,7 +621,7 @@
if (register_new_device(handle)) {
register_usb_transport(handle, serial_number, NULL, 1);
} else {
- D("register_new_device failed for %s", interf_name);
+ D("register_new_device failed for %ls", next_interface->device_name);
usb_cleanup_handle(handle);
free(handle);
}
diff --git a/base/Android.mk b/base/Android.mk
index 613636b..cba70d4 100644
--- a/base/Android.mk
+++ b/base/Android.mk
@@ -23,6 +23,9 @@
strings.cpp \
test_utils.cpp \
+libbase_windows_src_files := \
+ utf8.cpp \
+
libbase_test_src_files := \
file_test.cpp \
logging_test.cpp \
@@ -31,19 +34,31 @@
strings_test.cpp \
test_main.cpp \
+libbase_test_windows_src_files := \
+ utf8_test.cpp \
+
libbase_cppflags := \
-Wall \
-Wextra \
-Werror \
+libbase_linux_cppflags := \
+ -Wexit-time-destructors \
+
+libbase_darwin_cppflags := \
+ -Wexit-time-destructors \
+
# Device
# ------------------------------------------------------------------------------
include $(CLEAR_VARS)
LOCAL_MODULE := libbase
LOCAL_CLANG := true
LOCAL_SRC_FILES := $(libbase_src_files)
+LOCAL_SRC_FILES_darwin := $(libbase_darwin_src_files)
+LOCAL_SRC_FILES_linux := $(libbase_linux_src_files)
+LOCAL_SRC_FILES_windows := $(libbase_windows_src_files)
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_CPPFLAGS := $(libbase_cppflags)
+LOCAL_CPPFLAGS := $(libbase_cppflags) $(libbase_linux_cppflags)
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
LOCAL_STATIC_LIBRARIES := libcutils
LOCAL_MULTILIB := both
@@ -64,8 +79,13 @@
include $(CLEAR_VARS)
LOCAL_MODULE := libbase
LOCAL_SRC_FILES := $(libbase_src_files)
+LOCAL_SRC_FILES_darwin := $(libbase_darwin_src_files)
+LOCAL_SRC_FILES_linux := $(libbase_linux_src_files)
+LOCAL_SRC_FILES_windows := $(libbase_windows_src_files)
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
LOCAL_CPPFLAGS := $(libbase_cppflags)
+LOCAL_CPPFLAGS_darwin := $(libbase_darwin_cppflags)
+LOCAL_CPPFLAGS_linux := $(libbase_linux_cppflags)
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
LOCAL_STATIC_LIBRARIES := libcutils
LOCAL_MULTILIB := both
@@ -88,6 +108,9 @@
LOCAL_MODULE := libbase_test
LOCAL_CLANG := true
LOCAL_SRC_FILES := $(libbase_test_src_files)
+LOCAL_SRC_FILES_darwin := $(libbase_test_darwin_src_files)
+LOCAL_SRC_FILES_linux := $(libbase_test_linux_src_files)
+LOCAL_SRC_FILES_windows := $(libbase_test_windows_src_files)
LOCAL_C_INCLUDES := $(LOCAL_PATH)
LOCAL_CPPFLAGS := $(libbase_cppflags)
LOCAL_SHARED_LIBRARIES := libbase
@@ -100,6 +123,9 @@
LOCAL_MODULE := libbase_test
LOCAL_MODULE_HOST_OS := darwin linux windows
LOCAL_SRC_FILES := $(libbase_test_src_files)
+LOCAL_SRC_FILES_darwin := $(libbase_test_darwin_src_files)
+LOCAL_SRC_FILES_linux := $(libbase_test_linux_src_files)
+LOCAL_SRC_FILES_windows := $(libbase_test_windows_src_files)
LOCAL_C_INCLUDES := $(LOCAL_PATH)
LOCAL_CPPFLAGS := $(libbase_cppflags)
LOCAL_SHARED_LIBRARIES := libbase
diff --git a/base/file.cpp b/base/file.cpp
index 3468dcf..7b5e7b1 100644
--- a/base/file.cpp
+++ b/base/file.cpp
@@ -24,6 +24,7 @@
#include <string>
#include "base/macros.h" // For TEMP_FAILURE_RETRY on Darwin.
+#include "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/include/base/unique_fd.h b/base/include/base/unique_fd.h
new file mode 100644
index 0000000..4117775
--- /dev/null
+++ b/base/include/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 <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/base/utf8.h b/base/include/base/utf8.h
new file mode 100755
index 0000000..3b0ed0a
--- /dev/null
+++ b/base/include/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 248cd06..85f8b3f 100644
--- a/base/logging.cpp
+++ b/base/logging.cpp
@@ -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);
}
diff --git a/base/utf8.cpp b/base/utf8.cpp
new file mode 100755
index 0000000..99f0f54
--- /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 "base/utf8.h"
+
+#include <fcntl.h>
+
+#include <string>
+
+#include "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..13f6431
--- /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 "base/utf8.h"
+
+#include <gtest/gtest.h>
+
+#include "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/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/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..884d4d5 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);
}
}
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 d4be63b..ac5a17a 100644
--- a/fastboot/engine.cpp
+++ b/fastboot/engine.cpp
@@ -75,13 +75,13 @@
-bool fb_getvar(usb_handle* usb, const std::string& key, std::string* value) {
+bool fb_getvar(Transport* transport, const std::string& key, std::string* value) {
std::string cmd = "getvar:";
cmd += key;
char buf[FB_RESPONSE_SZ + 1];
memset(buf, 0, sizeof(buf));
- if (fb_command_response(usb, cmd.c_str(), buf)) {
+ if (fb_command_response(transport, cmd.c_str(), buf)) {
return false;
}
*value = buf;
@@ -330,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];
@@ -350,25 +350,25 @@
fprintf(stderr,"%s...\n",a->msg);
}
if (a->op == OP_DOWNLOAD) {
- status = fb_download_data(usb, a->data, a->size);
+ status = fb_download_data(transport, a->data, a->size);
status = a->func(a, status, status ? fb_get_error() : "");
if (status) break;
} else if (a->op == OP_COMMAND) {
- status = fb_command(usb, a->cmd);
+ status = fb_command(transport, a->cmd);
status = a->func(a, status, status ? fb_get_error() : "");
if (status) break;
} else if (a->op == OP_QUERY) {
- status = fb_command_response(usb, a->cmd, resp);
+ status = fb_command_response(transport, a->cmd, resp);
status = a->func(a, status, status ? fb_get_error() : resp);
if (status) break;
} else if (a->op == OP_NOTICE) {
fprintf(stderr,"%s\n",(char*)a->data);
} else if (a->op == OP_DOWNLOAD_SPARSE) {
- status = fb_download_data_sparse(usb, reinterpret_cast<sparse_file*>(a->data));
+ status = fb_download_data_sparse(transport, reinterpret_cast<sparse_file*>(a->data));
status = a->func(a, status, status ? fb_get_error() : "");
if (status) break;
} else if (a->op == OP_WAIT_FOR_DISCONNECT) {
- usb_wait_for_disconnect(usb);
+ transport->WaitForDisconnect();
} else {
die("bogus action");
}
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index be30d81..a16d7dd 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -55,6 +55,8 @@
#include "bootimg_utils.h"
#include "fastboot.h"
#include "fs.h"
+#include "transport.h"
+#include "usb.h"
#ifndef O_BINARY
#define O_BINARY 0
@@ -222,16 +224,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");
}
@@ -277,8 +279,6 @@
" 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"
" boot <kernel> [ <ramdisk> [ <second> ] ] Download and boot kernel.\n"
" flash:raw boot <kernel> [ <ramdisk> [ <second> ] ]\n"
" Create bootimage and flash it.\n"
@@ -299,9 +299,15 @@
" -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"
@@ -312,9 +318,15 @@
" 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.\n"
+ " --unbuffered Do not buffer input or output.\n"
+ " --version Display version.\n"
+ " -h, --help show this message.\n"
);
}
-
static void* load_bootable_image(const char* kernel, const char* ramdisk,
const char* secondstage, int64_t* sz,
const char* cmdline) {
@@ -587,9 +599,10 @@
return out_s;
}
-static int64_t get_target_sparse_limit(usb_handle* usb) {
+static int64_t get_target_sparse_limit(Transport* transport) {
std::string max_download_size;
- if (!fb_getvar(usb, "max-download-size", &max_download_size) || max_download_size.empty()) {
+ 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;
}
@@ -608,7 +621,7 @@
return limit;
}
-static int64_t get_sparse_limit(usb_handle* usb, int64_t size) {
+static int64_t get_sparse_limit(Transport* transport, int64_t size) {
int64_t limit;
if (sparse_limit == 0) {
@@ -617,7 +630,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;
@@ -636,22 +649,22 @@
// Until we get lazy inode table init working in make_ext4fs, we need to
// erase partitions of type ext4 before flashing a filesystem so no stale
// inodes are left lying around. Otherwise, e2fsck gets very upset.
-static bool needs_erase(usb_handle* usb, const char* partition) {
+static bool needs_erase(Transport* transport, const char* partition) {
std::string partition_type;
- if (!fb_getvar(usb, std::string("partition-type:") + partition, &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) {
@@ -670,8 +683,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;
@@ -680,7 +692,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)
@@ -703,84 +715,88 @@
}
}
-static std::vector<std::string> get_suffixes(usb_handle* usb) {
+static std::vector<std::string> get_suffixes(Transport* transport) {
std::vector<std::string> suffixes;
std::string suffix_list;
- if (!fb_getvar(usb, "slot-suffixes", &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(usb_handle* usb, const char *slot) {
+static std::string verify_slot(Transport* transport, const char *slot) {
if (strcmp(slot, "all") == 0) {
return "all";
}
- std::vector<std::string> suffixes = get_suffixes(usb);
+ 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:", 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 void do_for_partition(usb_handle* usb, const char *part, const char *slot, std::function<void(const std::string&)> func, bool force_slot) {
- std::string partition_slot;
+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(usb, std::string("partition-slot:")+part, &partition_slot)) {
- /* If partition-slot is not supported, the answer is no. */
- partition_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 (partition_slot == "1") {
+ if (has_slot == "yes") {
if (!slot || slot[0] == 0) {
- if (!fb_getvar(usb, "current-slot", ¤t_slot)) {
+ if (!fb_getvar(transport, "current-slot", ¤t_slot)) {
die("Failed to identify current slot.\n");
}
- func(std::string(part) + '-' + current_slot);
+ func(std::string(part) + current_slot);
} else {
- func(std::string(part) + '-' + slot);
+ 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);
+ 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.
+/* 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(usb_handle* usb, const char *part, const char *slot, std::function<void(const std::string&)> func, bool force_slot) {
- std::string partition_slot;
+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(usb, std::string("partition-slot:") + part, &partition_slot)) {
+ if (!fb_getvar(transport, std::string("has-slot:") + part, &has_slot)) {
die("Could not check if partition %s has slot.", part);
}
- if (partition_slot == "1") {
- std::vector<std::string> suffixes = get_suffixes(usb);
+ if (has_slot == "yes") {
+ std::vector<std::string> suffixes = get_suffixes(transport);
for (std::string &suffix : suffixes) {
- do_for_partition(usb, part, suffix.c_str(), func, force_slot);
+ do_for_partition(transport, part, suffix.c_str(), func, force_slot);
}
} else {
- do_for_partition(usb, part, "", func, force_slot);
+ do_for_partition(transport, part, "", func, force_slot);
}
} else {
- do_for_partition(usb, part, slot, func, force_slot);
+ do_for_partition(transport, part, slot, func, force_slot);
}
}
-static void do_flash(usb_handle* usb, const char* pname, const char* fname) {
+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);
@@ -794,7 +810,7 @@
fb_queue_command("signature", "installing signature");
}
-static void do_update(usb_handle* usb, const char* filename, const char* slot_override, 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));
@@ -825,12 +841,12 @@
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);
auto update = [&](const std::string &partition) {
do_update_signature(zip, images[i].sig_name);
- if (erase_first && needs_erase(usb, partition.c_str())) {
+ if (erase_first && needs_erase(transport, partition.c_str())) {
fb_queue_erase(partition.c_str());
}
flash_buf(partition.c_str(), &buf);
@@ -839,7 +855,7 @@
* program exits.
*/
};
- do_for_partitions(usb, images[i].part_name, slot_override, update, false);
+ do_for_partitions(transport, images[i].part_name, slot_override, update, false);
}
CloseArchive(zip);
@@ -861,7 +877,7 @@
fb_queue_command("signature", "installing signature");
}
-static void do_flashall(usb_handle* usb, const char *slot_override, 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));
@@ -878,7 +894,7 @@
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);
@@ -886,12 +902,12 @@
auto flashall = [&](const std::string &partition) {
do_send_signature(fname);
- if (erase_first && needs_erase(usb, partition.c_str())) {
+ if (erase_first && needs_erase(transport, partition.c_str())) {
fb_queue_erase(partition.c_str());
}
flash_buf(partition.c_str(), &buf);
};
- do_for_partitions(usb, images[i].part_name, slot_override, flashall, false);
+ do_for_partitions(transport, images[i].part_name, slot_override, flashall, false);
}
}
@@ -976,7 +992,7 @@
return num;
}
-static void fb_perform_format(usb_handle* usb,
+static void fb_perform_format(Transport* transport,
const char* partition, int skip_if_not_supported,
const char* type_override, const char* size_override) {
std::string partition_type, partition_size;
@@ -994,7 +1010,7 @@
limit = sparse_limit;
}
- if (!fb_getvar(usb, std::string("partition-type:") + partition, &partition_type)) {
+ if (!fb_getvar(transport, std::string("partition-type:") + partition, &partition_type)) {
errMsg = "Can't determine partition type.\n";
goto failed;
}
@@ -1006,7 +1022,7 @@
partition_type = type_override;
}
- if (!fb_getvar(usb, std::string("partition-size:") + partition, &partition_size)) {
+ if (!fb_getvar(transport, std::string("partition-size:") + partition, &partition_size)) {
errMsg = "Unable to get partition size\n";
goto failed;
}
@@ -1017,6 +1033,11 @@
}
partition_size = size_override;
}
+ // Some bootloaders (angler, for example), send spurious leading whitespace.
+ partition_size = android::base::Trim(partition_size);
+ // Some bootloaders (hammerhead, for example) use implicit hex.
+ // This code used to use strtol with base 16.
+ if (!android::base::StartsWith(partition_size, "0x")) partition_size = "0x" + partition_size;
gen = fs_get_generator(partition_type);
if (!gen) {
@@ -1030,10 +1051,6 @@
return;
}
- // 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;
-
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());
@@ -1047,7 +1064,7 @@
return;
}
- if (load_buf_fd(usb, fd, &buf)) {
+ if (load_buf_fd(transport, fd, &buf)) {
fprintf(stderr, "Cannot read image: %s\n", strerror(errno));
close(fd);
return;
@@ -1065,37 +1082,50 @@
int main(int argc, char **argv)
{
- int wants_wipe = 0;
- int wants_reboot = 0;
- int wants_reboot_bootloader = 0;
+ bool wants_wipe = false;
+ bool wants_reboot = false;
+ bool wants_reboot_bootloader = false;
+ bool wants_set_active = false;
bool erase_first = true;
void *data;
int64_t sz;
int longindex;
std::string slot_override;
+ std::string next_active;
const struct option longopts[] = {
{"base", required_argument, 0, 'b'},
{"kernel_offset", required_argument, 0, 'k'},
+ {"kernel-offset", required_argument, 0, 'k'},
{"page_size", required_argument, 0, 'n'},
+ {"page-size", required_argument, 0, 'n'},
{"ramdisk_offset", required_argument, 0, 'r'},
+ {"ramdisk-offset", required_argument, 0, 'r'},
{"tags_offset", required_argument, 0, 't'},
+ {"tags-offset", required_argument, 0, 't'},
{"help", no_argument, 0, 'h'},
{"unbuffered", no_argument, 0, 0},
{"version", no_argument, 0, 0},
{"slot", required_argument, 0, 0},
+ {"set_active", optional_argument, 0, 'a'},
+ {"set-active", optional_argument, 0, 'a'},
{0, 0, 0, 0}
};
serial = getenv("ANDROID_SERIAL");
while (1) {
- int c = getopt_long(argc, argv, "wub:k:n:r:t:s:S:lp:c:i:m:h", longopts, &longindex);
+ int c = getopt_long(argc, argv, "wub:k:n:r:t:s:S:lp:c:i:m:ha::", longopts, &longindex);
if (c < 0) {
break;
}
/* Alphabetical cases */
switch (c) {
+ case 'a':
+ wants_set_active = true;
+ if (optarg)
+ next_active = optarg;
+ break;
case 'b':
base_addr = strtoul(optarg, 0, 16);
break;
@@ -1147,7 +1177,7 @@
erase_first = false;
break;
case 'w':
- wants_wipe = 1;
+ wants_wipe = true;
break;
case '?':
return 1;
@@ -1170,7 +1200,7 @@
argc -= optind;
argv += optind;
- if (argc == 0 && !wants_wipe) {
+ if (argc == 0 && !wants_wipe && !wants_set_active) {
usage();
return 1;
}
@@ -1186,9 +1216,21 @@
return 0;
}
- usb_handle* usb = open_device();
+ Transport* transport = open_device();
if (slot_override != "")
- slot_override = verify_slot(usb, slot_override.c_str());
+ slot_override = verify_slot(transport, slot_override.c_str());
+ if (next_active != "")
+ next_active = verify_slot(transport, next_active.c_str());
+
+ if (wants_set_active) {
+ if (next_active == "") {
+ if (slot_override == "") {
+ wants_set_active = false;
+ } else {
+ next_active = slot_override;
+ }
+ }
+ }
while (argc > 0) {
if (!strcmp(*argv, "getvar")) {
@@ -1199,8 +1241,8 @@
require(2);
auto erase = [&](const std::string &partition) {
- std::string partition_type;
- if (fb_getvar(usb, std::string("partition-type:") + argv[1], &partition_type) &&
+ 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());
@@ -1208,7 +1250,7 @@
fb_queue_erase(partition.c_str());
};
- do_for_partitions(usb, argv[1], slot_override.c_str(), erase, true);
+ do_for_partitions(transport, argv[1], slot_override.c_str(), erase, true);
skip(2);
} else if(!strncmp(*argv, "format", strlen("format"))) {
char *overrides;
@@ -1238,12 +1280,12 @@
if (size_override && !size_override[0]) size_override = nullptr;
auto format = [&](const std::string &partition) {
- if (erase_first && needs_erase(usb, partition.c_str())) {
+ if (erase_first && needs_erase(transport, partition.c_str())) {
fb_queue_erase(partition.c_str());
}
- fb_perform_format(usb, partition.c_str(), 0, type_override, size_override);
+ fb_perform_format(transport, partition.c_str(), 0, type_override, size_override);
};
- do_for_partitions(usb, argv[1], slot_override.c_str(), format, true);
+ do_for_partitions(transport, argv[1], slot_override.c_str(), format, true);
skip(2);
} else if(!strcmp(*argv, "signature")) {
require(2);
@@ -1254,18 +1296,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");
@@ -1305,12 +1347,12 @@
if (fname == 0) die("cannot determine image filename for '%s'", pname);
auto flash = [&](const std::string &partition) {
- if (erase_first && needs_erase(usb, partition.c_str())) {
+ if (erase_first && needs_erase(transport, partition.c_str())) {
fb_queue_erase(partition.c_str());
}
- do_flash(usb, partition.c_str(), fname);
+ do_flash(transport, partition.c_str(), fname);
};
- do_for_partitions(usb, pname, slot_override.c_str(), flash, true);
+ do_for_partitions(transport, pname, slot_override.c_str(), flash, true);
} else if(!strcmp(*argv, "flash:raw")) {
char *kname = argv[2];
char *rname = 0;
@@ -1330,24 +1372,20 @@
auto flashraw = [&](const std::string &partition) {
fb_queue_flash(partition.c_str(), data, sz);
};
- do_for_partitions(usb, argv[1], slot_override.c_str(), flashraw, true);
+ do_for_partitions(transport, argv[1], slot_override.c_str(), flashraw, true);
} else if(!strcmp(*argv, "flashall")) {
skip(1);
- do_flashall(usb, slot_override.c_str(), 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], slot_override.c_str(), erase_first);
+ do_update(transport, argv[1], slot_override.c_str(), erase_first);
skip(2);
} else {
- do_update(usb, "update.zip", slot_override.c_str(), 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);
- fb_set_active(argv[1]);
- skip(2);
+ wants_reboot = true;
} else if(!strcmp(*argv, "oem")) {
argc = do_oem_command(argc, argv);
} else if(!strcmp(*argv, "flashing")) {
@@ -1375,15 +1413,18 @@
if (wants_wipe) {
fprintf(stderr, "wiping userdata...\n");
fb_queue_erase("userdata");
- fb_perform_format(usb, "userdata", 1, nullptr, nullptr);
+ fb_perform_format(transport, "userdata", 1, nullptr, nullptr);
std::string cache_type;
- if (fb_getvar(usb, "partition-type:cache", &cache_type) && !cache_type.empty()) {
+ if (fb_getvar(transport, "partition-type:cache", &cache_type) && !cache_type.empty()) {
fprintf(stderr, "wiping cache...\n");
fb_queue_erase("cache");
- fb_perform_format(usb, "cache", 1, nullptr, nullptr);
+ fb_perform_format(transport, "cache", 1, nullptr, nullptr);
}
}
+ if (wants_set_active) {
+ fb_set_active(next_active.c_str());
+ }
if (wants_reboot) {
fb_queue_reboot();
fb_queue_wait_for_disconnect();
@@ -1392,5 +1433,5 @@
fb_queue_wait_for_disconnect();
}
- return fb_execute_queue(usb) ? EXIT_FAILURE : EXIT_SUCCESS;
+ return fb_execute_queue(transport) ? EXIT_FAILURE : EXIT_SUCCESS;
}
diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h
index 784ec5c..acfbc13 100644
--- a/fastboot/fastboot.h
+++ b/fastboot/fastboot.h
@@ -34,22 +34,22 @@
#include <string>
-#include "usb.h"
+#include "transport.h"
struct sparse_file;
/* protocol.c - fastboot protocol */
-int fb_command(usb_handle *usb, const char *cmd);
-int fb_command_response(usb_handle *usb, const char *cmd, char *response);
-int fb_download_data(usb_handle *usb, const void *data, uint32_t size);
-int fb_download_data_sparse(usb_handle *usb, struct sparse_file *s);
+int fb_command(Transport* transport, const char* cmd);
+int fb_command_response(Transport* transport, const char* cmd, char* response);
+int fb_download_data(Transport* transport, const void* data, uint32_t size);
+int fb_download_data_sparse(Transport* transport, struct sparse_file* s);
char *fb_get_error(void);
#define FB_COMMAND_SZ 64
#define FB_RESPONSE_SZ 64
/* engine.c - high level command queue engine */
-bool fb_getvar(usb_handle* usb, const std::string& key, std::string* value);
+bool fb_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);
@@ -63,7 +63,7 @@
void fb_queue_download(const char *name, void *data, uint32_t size);
void fb_queue_notice(const char *notice);
void fb_queue_wait_for_disconnect(void);
-int fb_execute_queue(usb_handle *usb);
+int fb_execute_queue(Transport* transport);
void fb_set_active(const char *slot);
/* util stuff */
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..55a5abb
--- /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 <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/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index cb77a8e..63904b6 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -136,6 +136,9 @@
{ "USB_DCP", ANDROID_POWER_SUPPLY_TYPE_AC },
{ "USB_CDP", ANDROID_POWER_SUPPLY_TYPE_AC },
{ "USB_ACA", ANDROID_POWER_SUPPLY_TYPE_AC },
+ { "USB_C", ANDROID_POWER_SUPPLY_TYPE_AC },
+ { "USB_PD", ANDROID_POWER_SUPPLY_TYPE_AC },
+ { "USB_PD_DRP", ANDROID_POWER_SUPPLY_TYPE_USB },
{ "Wireless", ANDROID_POWER_SUPPLY_TYPE_WIRELESS },
{ NULL, 0 },
};
diff --git a/include/log/log.h b/include/log/log.h
index 1cdf7bc..086d742 100644
--- a/include/log/log.h
+++ b/include/log/log.h
@@ -614,9 +614,11 @@
/*
* 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_error_write(int tag, const char *subTag, int32_t uid, const char *data,
uint32_t dataLen);
diff --git a/include/utils/LruCache.h b/include/utils/LruCache.h
index 1741fb2..ed96fe4 100644
--- a/include/utils/LruCache.h
+++ b/include/utils/LruCache.h
@@ -200,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/libcutils/fs_config.c b/libcutils/fs_config.c
index 2a178aa..f4454bb 100644
--- a/libcutils/fs_config.c
+++ b/libcutils/fs_config.c
@@ -88,6 +88,8 @@
{ 00771, AID_SHARED_RELRO, AID_SHARED_RELRO, 0, "data/misc/shared_relro" },
{ 00775, AID_MEDIA_RW, AID_MEDIA_RW, 0, "data/media" },
{ 00775, AID_MEDIA_RW, AID_MEDIA_RW, 0, "data/media/Music" },
+ { 00750, AID_ROOT, AID_SHELL, 0, "data/nativetest" },
+ { 00750, AID_ROOT, AID_SHELL, 0, "data/nativetest64" },
{ 00771, AID_SYSTEM, AID_SYSTEM, 0, "data" },
{ 00755, AID_ROOT, AID_SYSTEM, 0, "mnt" },
{ 00755, AID_ROOT, AID_ROOT, 0, "root" },
@@ -125,6 +127,8 @@
{ 00644, AID_MEDIA_RW, AID_MEDIA_RW, 0, "data/media/*" },
{ 00644, AID_SYSTEM, AID_SYSTEM, 0, "data/app-private/*" },
{ 00644, AID_APP, AID_APP, 0, "data/data/*" },
+ { 00640, AID_ROOT, AID_SHELL, 0, "data/nativetest/tests.txt" },
+ { 00640, AID_ROOT, AID_SHELL, 0, "data/nativetest64/tests.txt" },
{ 00750, AID_ROOT, AID_SHELL, 0, "data/nativetest/*" },
{ 00750, AID_ROOT, AID_SHELL, 0, "data/nativetest64/*" },
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..e128edb 100644
--- a/liblog/log_is_loggable.c
+++ b/liblog/log_is_loggable.c
@@ -23,6 +23,22 @@
#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;
@@ -49,9 +65,7 @@
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 +100,7 @@
strcpy(key, log_namespace);
- pthread_mutex_lock(&lock);
+ lock();
current_global_serial = __system_property_area_serial();
@@ -156,7 +170,7 @@
global_serial = current_global_serial;
- pthread_mutex_unlock(&lock);
+ unlock();
switch (toupper(c)) {
case 'V': return ANDROID_LOG_VERBOSE;
@@ -168,36 +182,47 @@
case 'A': return ANDROID_LOG_FATAL;
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;
}
+/*
+ * 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_timestamp = PTHREAD_MUTEX_INITIALIZER;
+
char android_log_timestamp()
{
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;
- pthread_mutex_lock(&lock);
+ if (pthread_mutex_trylock(&lock_timestamp)) {
+ /* We are willing to accept some race in this context */
+ if (!(retval = p_time_cache.c)) {
+ retval = 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 (!(retval = p_time_cache.c)) {
+ retval = 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;
+ pthread_mutex_unlock(&lock_timestamp);
}
- if (!(retval = p_time_cache.c)) {
- retval = r_time_cache.c;
- }
-
- pthread_mutex_unlock(&lock);
return tolower(retval ?: 'r');
}
diff --git a/liblog/logd_write.c b/liblog/logd_write.c
index a4310ae..83c6dc2 100644
--- a/liblog/logd_write.c
+++ b/liblog/logd_write.c
@@ -54,14 +54,35 @@
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 };
#else
@@ -277,15 +298,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;
@@ -329,18 +346,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);
@@ -352,9 +365,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);
}
diff --git a/liblog/logprint.c b/liblog/logprint.c
index 9f12c96..ebf9786 100644
--- a/liblog/logprint.c
+++ b/liblog/logprint.c
@@ -908,7 +908,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') {
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/logd/LogKlog.cpp b/logd/LogKlog.cpp
index da5e78d..2a3f52f 100644
--- a/logd/LogKlog.cpp
+++ b/logd/LogKlog.cpp
@@ -582,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
@@ -596,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;
@@ -620,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)) {
@@ -694,7 +696,7 @@
p = cp + 1;
}
}
- }
+ } /* else no tag */
size = etag - tag;
if ((size <= 1)
// register names like x9
@@ -721,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
@@ -735,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;
@@ -763,8 +779,8 @@
np[b] = '\0';
// 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/metricsd/Android.mk b/metricsd/Android.mk
index 291c983..97c0d28 100644
--- a/metricsd/Android.mk
+++ b/metricsd/Android.mk
@@ -25,11 +25,14 @@
metrics_client_sources := \
metrics_client.cc
-metrics_daemon_common := \
+metrics_collector_common := \
collectors/averaged_statistics_collector.cc \
collectors/cpu_usage_collector.cc \
collectors/disk_usage_collector.cc \
- metrics_daemon.cc \
+ metrics_collector.cc \
+ persistent_integer.cc \
+
+metricsd_common := \
persistent_integer.cc \
serialization/metric_sample.cc \
serialization/serialization_utils.cc \
@@ -40,14 +43,16 @@
uploader/system_profile_cache.cc \
uploader/upload_service.cc \
-metrics_tests_sources := \
+metrics_collector_tests_sources := \
collectors/averaged_statistics_collector_test.cc \
collectors/cpu_usage_collector_test.cc \
- metrics_daemon_test.cc \
+ metrics_collector_test.cc \
metrics_library_test.cc \
persistent_integer_test.cc \
serialization/serialization_utils_unittest.cc \
timer_test.cc \
+
+metricsd_tests_sources := \
uploader/metrics_hashes_unittest.cc \
uploader/metrics_log_base_unittest.cc \
uploader/mock/sender_mock.cc \
@@ -56,7 +61,6 @@
metrics_CFLAGS := -Wall \
-Wno-char-subscripts \
-Wno-missing-field-initializers \
- -Wno-unused-function \
-Wno-unused-parameter \
-Werror \
-fvisibility=default
@@ -67,17 +71,22 @@
metrics_includes := external/gtest/include \
$(LOCAL_PATH)/include
libmetrics_shared_libraries := libchrome libbrillo
-metrics_daemon_shared_libraries := $(libmetrics_shared_libraries) \
- libbrillo-http \
+metrics_collector_shared_libraries := $(libmetrics_shared_libraries) \
libbrillo-dbus \
+ libbrillo-http \
libchrome-dbus \
libdbus \
libmetrics \
- libprotobuf-cpp-lite \
librootdev \
- libupdate_engine_client \
libweaved \
+metricsd_shared_libraries := \
+ libbrillo \
+ libbrillo-http \
+ libchrome \
+ libprotobuf-cpp-lite \
+ libupdate_engine_client \
+
# Shared library for metrics.
# ========================================================
include $(CLEAR_VARS)
@@ -107,10 +116,10 @@
LOCAL_SRC_FILES := $(metrics_client_sources)
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 += \
@@ -118,40 +127,71 @@
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_INIT_RC := metrics_collector.rc
LOCAL_REQUIRED_MODULES := \
metrics.json \
- metrics.schema.json \
-
+ metrics.schema.json
LOCAL_RTTI_FLAG := -frtti
-LOCAL_SHARED_LIBRARIES := $(metrics_daemon_shared_libraries)
-LOCAL_CLANG := true
-LOCAL_SRC_FILES := $(metrics_daemon_common) \
- metrics_daemon_main.cc
-LOCAL_STATIC_LIBRARIES := metrics_daemon_protos
+LOCAL_SHARED_LIBRARIES := $(metrics_collector_shared_libraries)
+LOCAL_SRC_FILES := $(metrics_collector_common) \
+ metrics_collector_main.cc
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_RTTI_FLAG := -frtti
+LOCAL_SHARED_LIBRARIES := $(metricsd_shared_libraries)
+LOCAL_STATIC_LIBRARIES := metricsd_protos
+LOCAL_SRC_FILES := $(metricsd_common) \
+ metricsd_main.cc
+include $(BUILD_EXECUTABLE)
+
+# Unit tests for metricsd.
+# ========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := metricsd_tests
+LOCAL_CFLAGS := $(metrics_CFLAGS)
+LOCAL_CLANG := true
LOCAL_CPP_EXTENSION := $(metrics_cpp_extension)
LOCAL_CPPFLAGS := $(metrics_CPPFLAGS) -Wno-sign-compare
LOCAL_RTTI_FLAG := -frtti
-LOCAL_SHARED_LIBRARIES := $(metrics_daemon_shared_libraries)
-LOCAL_SRC_FILES := $(metrics_tests_sources) $(metrics_daemon_common)
-LOCAL_STATIC_LIBRARIES := libBionicGtestMain libgmock metrics_daemon_protos
+LOCAL_SHARED_LIBRARIES := $(metricsd_shared_libraries) libmetrics
+LOCAL_SRC_FILES := $(metricsd_tests_sources) $(metricsd_common)
+LOCAL_STATIC_LIBRARIES := libBionicGtestMain libgmock metricsd_protos
+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_collector_shared_libraries)
+LOCAL_SRC_FILES := $(metrics_collector_tests_sources) \
+ $(metrics_collector_common)
+LOCAL_STATIC_LIBRARIES := libBionicGtestMain libgmock
include $(BUILD_NATIVE_TEST)
# Weave schema files
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/metrics_daemon.cc b/metricsd/metrics_collector.cc
similarity index 75%
rename from metricsd/metrics_daemon.cc
rename to metricsd/metrics_collector.cc
index b606fd0..28194a1 100644
--- a/metricsd/metrics_daemon.cc
+++ b/metricsd/metrics_collector.cc
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "metrics_daemon.h"
+#include "metrics_collector.h"
#include <sysexits.h>
#include <time.h>
@@ -33,7 +33,6 @@
#include <dbus/message.h>
#include "constants.h"
-#include "uploader/upload_service.h"
using base::FilePath;
using base::StringPrintf;
@@ -74,18 +73,13 @@
const char kMeminfoFileName[] = "/proc/meminfo";
const char kVmStatFileName[] = "/proc/vmstat";
-// 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";
+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,
@@ -99,15 +93,15 @@
600 * kSecondsPerMinute, // 12.5 hour mark
};
-MetricsDaemon::MetricsDaemon()
+MetricsCollector::MetricsCollector()
: memuse_final_time_(0),
memuse_interval_index_(0) {}
-MetricsDaemon::~MetricsDaemon() {
+MetricsCollector::~MetricsCollector() {
}
// static
-double MetricsDaemon::GetActiveTime() {
+double MetricsCollector::GetActiveTime() {
struct timespec ts;
int r = clock_gettime(CLOCK_MONOTONIC, &ts);
if (r < 0) {
@@ -118,7 +112,7 @@
}
}
-int MetricsDaemon::Run() {
+int MetricsCollector::Run() {
if (CheckSystemCrash(kKernelCrashDetectedFile)) {
ProcessKernelCrash();
}
@@ -139,16 +133,7 @@
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() {
+uint32_t MetricsCollector::GetOsVersionHash() {
brillo::OsReleaseReader reader;
reader.Load();
string version;
@@ -164,26 +149,15 @@
return version_hash;
}
-void MetricsDaemon::Init(bool testing,
- bool uploader_active,
- bool dbus_enabled,
+void MetricsCollector::Init(bool testing,
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;
-
daily_active_use_.reset(
new PersistentInteger("Platform.UseTime.PerDay"));
version_cumulative_active_use_.reset(
@@ -221,8 +195,6 @@
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,
@@ -230,9 +202,8 @@
cpu_usage_collector_.reset(new CpuUsageCollector(metrics_lib_));
}
-int MetricsDaemon::OnInit() {
- int return_code = dbus_enabled_ ? brillo::DBusDaemon::OnInit() :
- brillo::Daemon::OnInit();
+int MetricsCollector::OnInit() {
+ int return_code = brillo::DBusDaemon::OnInit();
if (return_code != EX_OK)
return return_code;
@@ -246,66 +217,58 @@
if (testing_)
return EX_OK;
- if (dbus_enabled_) {
- bus_->AssertOnDBusThread();
- CHECK(bus_->SetUpAsyncOperations());
+ 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)));
- }
-
- latest_cpu_use_microseconds_ = cpu_usage_collector_->GetCumulativeCpuUse();
- 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()) {
+ if (bus_->is_connected()) {
const std::string match_rule =
base::StringPrintf(kCrashReporterMatchRule,
kCrashReporterInterface,
kCrashReporterUserCrashSignal);
- bus_->RemoveFilterFunction(&MetricsDaemon::MessageFilter, this);
+ bus_->AddFilterFunction(&MetricsCollector::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(&MetricsCollector::UpdateWeaveState, base::Unretained(this)));
+ device_->AddCommandHandler(
+ "_metrics._enableAnalyticsReporting",
+ base::Bind(&MetricsCollector::OnEnableMetrics, base::Unretained(this)));
+ device_->AddCommandHandler(
+ "_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) {
+ if (!testing_ && bus_->is_connected()) {
+ const std::string match_rule =
+ base::StringPrintf(kCrashReporterMatchRule,
+ kCrashReporterInterface,
+ kCrashReporterUserCrashSignal);
+
+ bus_->RemoveFilterFunction(&MetricsCollector::MessageFilter, this);
DBusError error;
dbus_error_init(&error);
@@ -319,7 +282,8 @@
brillo::DBusDaemon::OnShutdown(return_code);
}
-void MetricsDaemon::OnEnableMetrics(const std::weak_ptr<weaved::Command>& cmd) {
+void MetricsCollector::OnEnableMetrics(
+ const std::weak_ptr<weaved::Command>& cmd) {
auto command = cmd.lock();
if (!command)
return;
@@ -336,7 +300,7 @@
command->Complete({}, nullptr);
}
-void MetricsDaemon::OnDisableMetrics(
+void MetricsCollector::OnDisableMetrics(
const std::weak_ptr<weaved::Command>& cmd) {
auto command = cmd.lock();
if (!command)
@@ -354,7 +318,7 @@
command->Complete({}, nullptr);
}
-void MetricsDaemon::UpdateWeaveState() {
+void MetricsCollector::UpdateWeaveState() {
if (!device_)
return;
@@ -369,9 +333,9 @@
}
// static
-DBusHandlerResult MetricsDaemon::MessageFilter(DBusConnection* connection,
- DBusMessage* message,
- void* user_data) {
+DBusHandlerResult MetricsCollector::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;
@@ -383,7 +347,7 @@
const std::string member(dbus_message_get_member(message));
DLOG(INFO) << "Got " << interface << "." << member << " D-Bus signal";
- MetricsDaemon* daemon = static_cast<MetricsDaemon*>(user_data);
+ MetricsCollector* daemon = static_cast<MetricsCollector*>(user_data);
DBusMessageIter iter;
dbus_message_iter_init(message, &iter);
@@ -398,7 +362,7 @@
return DBUS_HANDLER_RESULT_HANDLED;
}
-void MetricsDaemon::ProcessUserCrash() {
+void MetricsCollector::ProcessUserCrash() {
// Counts the active time up to now.
UpdateStats(TimeTicks::Now(), Time::Now());
@@ -411,7 +375,7 @@
user_crashes_weekly_count_->Add(1);
}
-void MetricsDaemon::ProcessKernelCrash() {
+void MetricsCollector::ProcessKernelCrash() {
// Counts the active time up to now.
UpdateStats(TimeTicks::Now(), Time::Now());
@@ -426,7 +390,7 @@
kernel_crashes_version_count_->Add(1);
}
-void MetricsDaemon::ProcessUncleanShutdown() {
+void MetricsCollector::ProcessUncleanShutdown() {
// Counts the active time up to now.
UpdateStats(TimeTicks::Now(), Time::Now());
@@ -439,7 +403,7 @@
any_crashes_weekly_count_->Add(1);
}
-bool MetricsDaemon::CheckSystemCrash(const string& crash_file) {
+bool MetricsCollector::CheckSystemCrash(const string& crash_file) {
FilePath crash_detected(crash_file);
if (!base::PathExists(crash_detected))
return false;
@@ -450,7 +414,7 @@
return true;
}
-void MetricsDaemon::StatsReporterInit() {
+void MetricsCollector::StatsReporterInit() {
disk_usage_collector_->Schedule();
cpu_usage_collector_->Init();
@@ -461,75 +425,18 @@
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) {
+void MetricsCollector::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),
+ base::Bind(&MetricsCollector::MeminfoCallback, base::Unretained(this),
waitDelta),
waitDelta);
}
-void MetricsDaemon::MeminfoCallback(base::TimeDelta wait) {
+void MetricsCollector::MeminfoCallback(base::TimeDelta wait) {
string meminfo_raw;
const FilePath meminfo_path(kMeminfoFileName);
if (!base::ReadFileToString(meminfo_path, &meminfo_raw)) {
@@ -539,15 +446,15 @@
// 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),
+ base::Bind(&MetricsCollector::MeminfoCallback, base::Unretained(this),
wait),
wait);
}
}
// static
-bool MetricsDaemon::ReadFileToUint64(const base::FilePath& path,
- uint64_t* value) {
+bool MetricsCollector::ReadFileToUint64(const base::FilePath& path,
+ uint64_t* value) {
std::string content;
if (!base::ReadFileToString(path, &content)) {
PLOG(WARNING) << "cannot read " << path.MaybeAsASCII();
@@ -562,7 +469,7 @@
return true;
}
-bool MetricsDaemon::ReportZram(const base::FilePath& zram_dir) {
+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;
@@ -599,7 +506,7 @@
return true;
}
-bool MetricsDaemon::ProcessMeminfo(const string& meminfo_raw) {
+bool MetricsCollector::ProcessMeminfo(const string& meminfo_raw) {
static const MeminfoRecord fields_array[] = {
{ "MemTotal", "MemTotal" }, // SPECIAL CASE: total system memory
{ "MemFree", "MemFree" },
@@ -670,8 +577,8 @@
return true;
}
-bool MetricsDaemon::FillMeminfo(const string& meminfo_raw,
- vector<MeminfoRecord>* fields) {
+bool MetricsCollector::FillMeminfo(const string& meminfo_raw,
+ vector<MeminfoRecord>* fields) {
vector<string> lines;
unsigned int nlines = Tokenize(meminfo_raw, "\n", &lines);
@@ -702,16 +609,16 @@
return true;
}
-void MetricsDaemon::ScheduleMemuseCallback(double interval) {
+void MetricsCollector::ScheduleMemuseCallback(double interval) {
if (testing_) {
return;
}
base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
- base::Bind(&MetricsDaemon::MemuseCallback, base::Unretained(this)),
+ base::Bind(&MetricsCollector::MemuseCallback, base::Unretained(this)),
base::TimeDelta::FromSeconds(interval));
}
-void MetricsDaemon::MemuseCallback() {
+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.
@@ -732,7 +639,7 @@
}
}
-bool MetricsDaemon::MemuseCallbackWork() {
+bool MetricsCollector::MemuseCallbackWork() {
string meminfo_raw;
const FilePath meminfo_path(kMeminfoFileName);
if (!base::ReadFileToString(meminfo_path, &meminfo_raw)) {
@@ -742,7 +649,7 @@
return ProcessMemuse(meminfo_raw);
}
-bool MetricsDaemon::ProcessMemuse(const string& meminfo_raw) {
+bool MetricsCollector::ProcessMemuse(const string& meminfo_raw) {
static const MeminfoRecord fields_array[] = {
{ "MemTotal", "MemTotal" }, // SPECIAL CASE: total system memory
{ "ActiveAnon", "Active(anon)" },
@@ -768,12 +675,12 @@
return true;
}
-void MetricsDaemon::SendSample(const string& name, int sample,
- int min, int max, int nbuckets) {
+void MetricsCollector::SendSample(const string& name, int sample,
+ int min, int max, int nbuckets) {
metrics_lib_->SendToUMA(name, sample, min, max, nbuckets);
}
-void MetricsDaemon::SendKernelCrashesCumulativeCountStats() {
+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();
@@ -818,7 +725,7 @@
}
}
-void MetricsDaemon::SendAndResetDailyUseSample(
+void MetricsCollector::SendAndResetDailyUseSample(
const scoped_ptr<PersistentInteger>& use) {
SendSample(use->Name(),
use->GetAndClear(),
@@ -827,7 +734,7 @@
50); // number of buckets
}
-void MetricsDaemon::SendAndResetCrashIntervalSample(
+void MetricsCollector::SendAndResetCrashIntervalSample(
const scoped_ptr<PersistentInteger>& interval) {
SendSample(interval->Name(),
interval->GetAndClear(),
@@ -836,7 +743,7 @@
50); // number of buckets
}
-void MetricsDaemon::SendAndResetCrashFrequencySample(
+void MetricsCollector::SendAndResetCrashFrequencySample(
const scoped_ptr<PersistentInteger>& frequency) {
SendSample(frequency->Name(),
frequency->GetAndClear(),
@@ -845,16 +752,16 @@
50); // number of buckets
}
-void MetricsDaemon::SendLinearSample(const string& name, int sample,
- int max, int nbuckets) {
+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 MetricsDaemon::UpdateStats(TimeTicks now_ticks,
- Time now_wall_time) {
+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);
@@ -889,10 +796,10 @@
}
}
-void MetricsDaemon::HandleUpdateStatsTimeout() {
+void MetricsCollector::HandleUpdateStatsTimeout() {
UpdateStats(TimeTicks::Now(), Time::Now());
base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
- base::Bind(&MetricsDaemon::HandleUpdateStatsTimeout,
+ 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 80%
rename from metricsd/metrics_daemon.h
rename to metricsd/metrics_collector.h
index f12b02e..e080ac0 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>
@@ -36,25 +36,18 @@
#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);
// Initializes DBus and MessageLoop variables before running the MessageLoop.
@@ -66,9 +59,6 @@
// 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();
@@ -79,26 +69,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
@@ -215,12 +203,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();
@@ -243,13 +225,6 @@
// 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_;
@@ -301,14 +276,7 @@
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..117426e
--- /dev/null
+++ b/metricsd/metrics_collector_main.cc
@@ -0,0 +1,92 @@
+/*
+ * 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(metrics_directory,
+ metrics::kMetricsDirectory,
+ "Root of the configuration files (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_metrics_directory));
+
+ daemon.Run();
+}
diff --git a/metricsd/metrics_daemon_test.cc b/metricsd/metrics_collector_test.cc
similarity index 75%
rename from metricsd/metrics_daemon_test.cc
rename to metricsd/metrics_collector_test.cc
index d3c9a23..a0e7087 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,15 @@
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);
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());
+ daemon_.Init(true, &metrics_lib_, "", temp_dir_.path());
}
// Adds a metrics library mock expectation that the specified metric
@@ -114,28 +100,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) {
+TEST_F(MetricsCollectorTest, 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_);
+ MetricsCollector::MessageFilter(/* connection */ nullptr, msg, &daemon_);
EXPECT_EQ(DBUS_HANDLER_RESULT_NOT_YET_HANDLED, res);
DeleteDBusMessage(msg);
@@ -144,7 +126,7 @@
"org.chromium.CrashReporter",
"UserCrash",
signal_args);
- res = MetricsDaemon::MessageFilter(/* connection */ nullptr, msg, &daemon_);
+ res = MetricsCollector::MessageFilter(/* connection */ nullptr, msg, &daemon_);
EXPECT_EQ(DBUS_HANDLER_RESULT_HANDLED, res);
DeleteDBusMessage(msg);
@@ -155,18 +137,18 @@
"org.chromium.UnknownService.Manager",
"StateChanged",
signal_args);
- res = MetricsDaemon::MessageFilter(/* connection */ nullptr, msg, &daemon_);
+ res = MetricsCollector::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 +185,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 +203,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_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_daemon.rc b/metricsd/metricsd.rc
similarity index 64%
rename from metricsd/metrics_daemon.rc
rename to metricsd/metricsd.rc
index 0ee577e..b5e7b82 100644
--- a/metricsd/metrics_daemon.rc
+++ b/metricsd/metricsd.rc
@@ -1,7 +1,7 @@
on post-fs-data
mkdir /data/misc/metrics 0770 system system
-service metrics_daemon /system/bin/metrics_daemon --uploader -nodaemon
+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..ab71e6b
--- /dev/null
+++ b/metricsd/metricsd_main.cc
@@ -0,0 +1,80 @@
+/*
+ * 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/files/file_path.h>
+#include <base/logging.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/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(metrics_directory,
+ metrics::kMetricsDirectory,
+ "Root of the configuration files (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;
+ }
+
+ UploadService service(FLAGS_server,
+ base::TimeDelta::FromSeconds(FLAGS_upload_interval_secs),
+ base::FilePath(FLAGS_metrics_directory));
+
+ service.Run();
+}
diff --git a/metricsd/persistent_integer.cc b/metricsd/persistent_integer.cc
index e849f00..ddc4b50 100644
--- a/metricsd/persistent_integer.cc
+++ b/metricsd/persistent_integer.cc
@@ -22,7 +22,6 @@
#include <base/posix/eintr_wrapper.h>
#include "constants.h"
-#include "metrics/metrics_library.h"
namespace chromeos_metrics {
diff --git a/metricsd/uploader/system_profile_cache.cc b/metricsd/uploader/system_profile_cache.cc
index f7060a2..8928a0d 100644
--- a/metricsd/uploader/system_profile_cache.cc
+++ b/metricsd/uploader/system_profile_cache.cc
@@ -84,10 +84,12 @@
auto client = update_engine::UpdateEngineClient::CreateInstance();
if (!client->GetChannel(&channel)) {
LOG(ERROR) << "failed to read the current channel from update engine.";
+ return false;
}
}
- if (!reader.GetString(metrics::kProductId, &profile_.product_id)) {
+ if (!reader.GetString(metrics::kProductId, &profile_.product_id)
+ || profile_.product_id.empty()) {
LOG(ERROR) << "product_id is not set.";
return false;
}
@@ -179,13 +181,13 @@
metrics::SystemProfileProto_Channel SystemProfileCache::ProtoChannelFromString(
const std::string& channel) {
- if (channel == "stable") {
+ if (channel == "stable-channel") {
return metrics::SystemProfileProto::CHANNEL_STABLE;
- } else if (channel == "dev") {
+ } else if (channel == "dev-channel") {
return metrics::SystemProfileProto::CHANNEL_DEV;
- } else if (channel == "beta") {
+ } else if (channel == "beta-channel") {
return metrics::SystemProfileProto::CHANNEL_BETA;
- } else if (channel == "canary") {
+ } else if (channel == "canary-channel") {
return metrics::SystemProfileProto::CHANNEL_CANARY;
}
diff --git a/metricsd/uploader/system_profile_cache.h b/metricsd/uploader/system_profile_cache.h
index ae54a2a..0a21ad4 100644
--- a/metricsd/uploader/system_profile_cache.h
+++ b/metricsd/uploader/system_profile_cache.h
@@ -69,6 +69,7 @@
FRIEND_TEST(UploadServiceTest, ReadKeyValueFromFile);
FRIEND_TEST(UploadServiceTest, SessionIdIncrementedAtInitialization);
FRIEND_TEST(UploadServiceTest, ValuesInConfigFileAreSent);
+ FRIEND_TEST(UploadServiceTest, ProductIdMandatory);
// Fetches all informations and populates |profile_|
bool Initialize();
diff --git a/metricsd/uploader/upload_service.cc b/metricsd/uploader/upload_service.cc
index b630cec..ca5024e 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>
@@ -39,38 +41,34 @@
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& metrics_directory)
+ : histogram_snapshot_manager_(this),
sender_(new HttpSender(server)),
failed_upload_count_(metrics::kFailedUploadCountName),
- testing_(false) {
-}
-
-UploadService::UploadService(SystemProfileSetter* setter,
- MetricsLibraryInterface* metrics_lib,
- const std::string& server,
- bool testing)
- : UploadService(setter, metrics_lib, server) {
- testing_ = testing;
-}
-
-void UploadService::Init(const base::TimeDelta& upload_interval,
- const base::FilePath& metrics_directory) {
- base::StatisticsRecorder::Initialize();
+ upload_interval_(upload_interval) {
metrics_file_ = metrics_directory.Append(metrics::kMetricsEventsFileName);
staged_log_path_ = metrics_directory.Append(metrics::kStagedLogName);
+ consent_file_ = metrics_directory.Append(metrics::kConsentFileName);
+}
- if (!testing_) {
- base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
- base::Bind(&UploadService::UploadEventCallback,
- base::Unretained(this),
- upload_interval),
- upload_interval);
- }
+int UploadService::OnInit() {
+ base::StatisticsRecorder::Initialize();
+
+ 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::InitForTest(SystemProfileSetter* setter) {
+ base::StatisticsRecorder::Initialize();
+ system_profile_setter_.reset(setter);
}
void UploadService::StartNewLog() {
@@ -114,7 +112,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;
@@ -263,3 +261,8 @@
failed_upload_count_.Set(0);
}
}
+
+bool UploadService::AreMetricsEnabled() {
+ return base::PathExists(consent_file_);
+}
+
diff --git a/metricsd/uploader/upload_service.h b/metricsd/uploader/upload_service.h
index 77df74b..7faf357 100644
--- a/metricsd/uploader/upload_service.h
+++ b/metricsd/uploader/upload_service.h
@@ -19,11 +19,11 @@
#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/metrics_log.h"
#include "uploader/sender.h"
@@ -67,14 +67,14 @@
// - if the upload fails, we keep the staged log in memory to retry
// uploading 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& metrics_directory);
- 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.
@@ -114,11 +114,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.
@@ -136,6 +133,9 @@
// 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.
void GatherHistograms();
@@ -158,12 +158,14 @@
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_;
+
+ base::TimeDelta upload_interval_;
+ base::FilePath consent_file_;
base::FilePath metrics_file_;
base::FilePath staged_log_path_;
diff --git a/metricsd/uploader/upload_service_test.cc b/metricsd/uploader/upload_service_test.cc
index 236376a..24e3127 100644
--- a/metricsd/uploader/upload_service_test.cc
+++ b/metricsd/uploader/upload_service_test.cc
@@ -44,11 +44,11 @@
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));
+ upload_service_.reset(new UploadService("", base::TimeDelta(),
+ dir_.path()));
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();
}
@@ -58,7 +58,8 @@
}
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(),
@@ -159,8 +160,7 @@
}
TEST_F(UploadServiceTest, LogEmptyByDefault) {
- UploadService upload_service(new MockSystemProfileSetter(), &metrics_lib_,
- "");
+ UploadService upload_service("", base::TimeDelta(), dir_.path());
// current_log_ should be initialized later as it needs AtExitManager to exit
// in order to gather system information from SysInfo.
@@ -214,10 +214,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"));
@@ -291,3 +291,14 @@
EXPECT_EQ(1, sender->send_call_count());
}
+
+// The product id must be set for metrics to be uploaded.
+// If it is not set, the system profile cache should fail to initialize.
+TEST_F(UploadServiceTest, ProductIdMandatory) {
+ SystemProfileCache cache(true, dir_.path());
+ ASSERT_FALSE(cache.Initialize());
+ SetTestingProperty(metrics::kProductId, "");
+ ASSERT_FALSE(cache.Initialize());
+ SetTestingProperty(metrics::kProductId, "hello");
+ ASSERT_TRUE(cache.Initialize());
+}
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index b134f93..895a25d 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -48,6 +48,8 @@
ln -sf /storage/self/primary $(TARGET_ROOT_OUT)/sdcard
ifdef BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE
LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/vendor
+else
+ LOCAL_POST_INSTALL_CMD += ; ln -sf /system/vendor $(TARGET_ROOT_OUT)/vendor
endif
ifdef BOARD_ROOT_EXTRA_SYMLINKS
# BOARD_ROOT_EXTRA_SYMLINKS is a list of <target>:<link_name>.
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 7fa4b95..a0b1acf 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -342,7 +342,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
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);
}