Merge commit 'f85d6cd8045ce933dfdc19bc888228cd9382e48c' into HEAD
diff --git a/adb/Android.mk b/adb/Android.mk
index c507905..903d1e1 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -213,12 +213,13 @@
LOCAL_REQUIRED_MODULES_windows := AdbWinApi AdbWinUsbApi
LOCAL_SRC_FILES := \
+ adb_client.cpp \
client/main.cpp \
console.cpp \
commandline.cpp \
- adb_client.cpp \
- services.cpp \
file_sync_client.cpp \
+ line_printer.cpp \
+ services.cpp \
shell_service_protocol.cpp \
LOCAL_CFLAGS += \
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 1eb3a3c..c39c178 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -963,9 +963,11 @@
fflush(stdout);
SendOkay(reply_fd);
- // At least on Windows, if we exit() without shutdown(SD_SEND) or
- // closesocket(), the client's next recv() will error-out with
- // WSAECONNRESET and they'll never read the OKAY.
+ // On Windows, if the process exits with open sockets that
+ // shutdown(SD_SEND) has not been called on, TCP RST segments will be
+ // sent to the peers which will cause their next recv() to error-out
+ // with WSAECONNRESET. In the case of this code, that means the client
+ // may not read the OKAY sent above.
adb_shutdown(reply_fd);
exit(0);
diff --git a/adb/adb_client.cpp b/adb/adb_client.cpp
index fa8ce9e..ddeb5f1 100644
--- a/adb/adb_client.cpp
+++ b/adb/adb_client.cpp
@@ -206,6 +206,7 @@
return -1;
}
+ ReadOrderlyShutdown(fd);
adb_close(fd);
if (sscanf(&version_string[0], "%04x", &version) != 1) {
@@ -227,6 +228,7 @@
version, ADB_SERVER_VERSION);
fd = _adb_connect("host:kill", error);
if (fd >= 0) {
+ ReadOrderlyShutdown(fd);
adb_close(fd);
} else {
// If we couldn't connect to the server or had some other error,
@@ -271,6 +273,8 @@
return false;
}
+ ReadOrderlyShutdown(fd);
+ adb_close(fd);
return true;
}
@@ -286,5 +290,8 @@
adb_close(fd);
return false;
}
+
+ ReadOrderlyShutdown(fd);
+ adb_close(fd);
return true;
}
diff --git a/adb/adb_io.cpp b/adb/adb_io.cpp
index 7788996..a37fbc0 100644
--- a/adb/adb_io.cpp
+++ b/adb/adb_io.cpp
@@ -137,3 +137,43 @@
return WriteFdExactly(fd, str);
}
+
+bool ReadOrderlyShutdown(int fd) {
+ char buf[16];
+
+ // Only call this function if you're sure that the peer does
+ // orderly/graceful shutdown of the socket, closing the socket so that
+ // adb_read() will return 0. If the peer keeps the socket open, adb_read()
+ // will never return.
+ int result = adb_read(fd, buf, sizeof(buf));
+ if (result == -1) {
+ // If errno is EAGAIN, that means this function was called on a
+ // nonblocking socket and it would have blocked (which would be bad
+ // because we'd probably block the main thread where nonblocking IO is
+ // done). Don't do that. If you have a nonblocking socket, use the
+ // fdevent APIs to get called on FDE_READ, and then call this function
+ // if you really need to, but it shouldn't be needed for server sockets.
+ CHECK_NE(errno, EAGAIN);
+
+ // Note that on Windows, orderly shutdown sometimes causes
+ // recv() == SOCKET_ERROR && WSAGetLastError() == WSAECONNRESET. That
+ // can be ignored.
+ return false;
+ } else if (result == 0) {
+ // Peer has performed an orderly/graceful shutdown.
+ return true;
+ } else {
+ // Unexpectedly received data. This is essentially a protocol error
+ // because you should not call this function unless you expect no more
+ // data. We don't repeatedly call adb_read() until we get zero because
+ // we don't know how long that would take, but we do know that the
+ // caller wants to close the socket soon.
+ VLOG(RWX) << "ReadOrderlyShutdown(" << fd << ") unexpectedly read "
+ << dump_hex(buf, result);
+ // Shutdown the socket to prevent the caller from reading or writing to
+ // it which doesn't make sense if we just read and discarded some data.
+ adb_shutdown(fd);
+ errno = EINVAL;
+ return false;
+ }
+}
diff --git a/adb/adb_io.h b/adb/adb_io.h
index 9c3b2a5..aa550af 100644
--- a/adb/adb_io.h
+++ b/adb/adb_io.h
@@ -41,6 +41,24 @@
// If this function fails, the contents of buf are undefined.
bool ReadFdExactly(int fd, void* buf, size_t len);
+// Given a client socket, wait for orderly/graceful shutdown. Call this:
+//
+// * Before closing a client socket.
+// * Only when no more data is expected to come in.
+// * Only when the server is not waiting for data from the client (because then
+// the client and server will deadlock waiting for each other).
+// * Only when the server is expected to close its socket right now.
+// * Don't call shutdown(SHUT_WR) before calling this because that will shutdown
+// the client socket early, defeating the purpose of calling this.
+//
+// Waiting for orderly/graceful shutdown of the server socket will cause the
+// server socket to close before the client socket. That prevents the client
+// socket from staying in TIME_WAIT which eventually causes subsequent
+// connect()s from the client to fail with WSAEADDRINUSE on Windows.
+// Returns true if it is sure that orderly/graceful shutdown has occurred with
+// no additional data read from the server.
+bool ReadOrderlyShutdown(int fd);
+
// Writes exactly len bytes from buf to fd.
//
// Returns false if there is an error or if the fd was closed before the write
diff --git a/adb/adb_trace.cpp b/adb/adb_trace.cpp
index 04b82f6..9586f7c 100644
--- a/adb/adb_trace.cpp
+++ b/adb/adb_trace.cpp
@@ -157,7 +157,7 @@
// Don't open log file if no tracing, since this will block
// the crypto unmount of /data
if (!get_trace_setting().empty()) {
- if (isatty(STDOUT_FILENO) == 0) {
+ if (unix_isatty(STDOUT_FILENO) == 0) {
start_device_log();
}
}
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index 354273c..8f0a7cc 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -33,6 +33,7 @@
#include <memory>
#include <string>
+#include <vector>
#include <base/logging.h>
#include <base/stringprintf.h>
@@ -101,13 +102,11 @@
" will disconnect from all connected TCP/IP devices.\n"
"\n"
"device commands:\n"
- " adb push [-p] <local> <remote>\n"
- " - copy file/dir to device\n"
- " ('-p' to display the transfer progress)\n"
- " adb pull [-p] [-a] <remote> [<local>]\n"
- " - copy file/dir from device\n"
- " ('-p' to display the transfer progress)\n"
- " ('-a' means copy timestamp and mode)\n"
+ " adb push <local>... <remote>\n"
+ " - copy files/dirs to device\n"
+ " adb pull [-a] <remote>... <local>\n"
+ " - copy files/dirs from device\n"
+ " (-a preserves file timestamp and mode)\n"
" adb sync [ <directory> ] - copy host->device only if changed\n"
" (-l means list but don't copy)\n"
" adb shell [-Ttx] - run remote shell interactively\n"
@@ -140,7 +139,7 @@
" localabstract:<unix domain socket name>\n"
" localreserved:<unix domain socket name>\n"
" localfilesystem:<unix domain socket name>\n"
- " adb reverse --norebind <remote> <local>\n"
+ " adb reverse --no-rebind <remote> <local>\n"
" - same as 'adb reverse <remote> <local>' but fails\n"
" if <remote> is already reversed.\n"
" adb reverse --remove <remote>\n"
@@ -441,7 +440,7 @@
adb_thread_setname("stdin reader");
-#ifndef __WIN32
+#ifndef _WIN32
// Mask SIGTTIN in case we're in a backgrounded process
sigset_t sigset;
sigemptyset(&sigset);
@@ -651,6 +650,7 @@
std::string error;
adb_status(fd, &error);
fprintf(stderr,"* failed to write data '%s' *\n", error.c_str());
+ adb_close(fd);
return -1;
}
sz -= xfer;
@@ -666,6 +666,7 @@
if (!adb_status(fd, &error)) {
fprintf(stderr,"* error response '%s' *\n", error.c_str());
+ adb_close(fd);
return -1;
}
@@ -1065,32 +1066,35 @@
return path;
}
-static void parse_push_pull_args(const char **arg, int narg, char const **path1,
- char const **path2, bool* show_progress,
- int *copy_attrs) {
- *show_progress = false;
- *copy_attrs = 0;
+static void parse_push_pull_args(const char** arg, int narg,
+ std::vector<const char*>* srcs,
+ const char** dst, bool* copy_attrs) {
+ *copy_attrs = false;
+ srcs->clear();
+ bool ignore_flags = false;
while (narg > 0) {
- if (!strcmp(*arg, "-p")) {
- *show_progress = true;
- } else if (!strcmp(*arg, "-a")) {
- *copy_attrs = 1;
+ if (ignore_flags || *arg[0] != '-') {
+ srcs->push_back(*arg);
} else {
- break;
+ if (!strcmp(*arg, "-p")) {
+ // Silently ignore for backwards compatibility.
+ } else if (!strcmp(*arg, "-a")) {
+ *copy_attrs = true;
+ } else if (!strcmp(*arg, "--")) {
+ ignore_flags = true;
+ } else {
+ fprintf(stderr, "adb: unrecognized option '%s'\n", *arg);
+ exit(1);
+ }
}
++arg;
--narg;
}
- if (narg > 0) {
- *path1 = *arg;
- ++arg;
- --narg;
- }
-
- if (narg > 0) {
- *path2 = *arg;
+ if (srcs->size() > 1) {
+ *dst = srcs->back();
+ srcs->pop_back();
}
}
@@ -1389,12 +1393,12 @@
// things like `adb shell < my_script.sh` work as expected.
// Otherwise leave |shell_type_arg| blank which uses PTY for
// interactive shells and raw for non-interactive.
- if (!isatty(STDIN_FILENO)) {
+ if (!unix_isatty(STDIN_FILENO)) {
shell_type_arg = kShellServiceArgRaw;
}
} else if (t_arg_count == 1) {
// A single -t arg isn't enough to override implicit -T.
- if (!isatty(STDIN_FILENO)) {
+ if (!unix_isatty(STDIN_FILENO)) {
fprintf(stderr,
"Remote PTY will not be allocated because stdin is not a terminal.\n"
"Use multiple -t options to force remote PTY allocation.\n");
@@ -1471,6 +1475,7 @@
} else {
// Successfully connected, kill command sent, okay status came back.
// Server should exit() in a moment, if not already.
+ ReadOrderlyShutdown(fd);
adb_close(fd);
return 0;
}
@@ -1561,22 +1566,22 @@
return do_sync_ls(argv[1]) ? 0 : 1;
}
else if (!strcmp(argv[0], "push")) {
- bool show_progress = false;
- int copy_attrs = 0;
- const char* lpath = NULL, *rpath = NULL;
+ bool copy_attrs = false;
+ std::vector<const char*> srcs;
+ const char* dst = nullptr;
- parse_push_pull_args(&argv[1], argc - 1, &lpath, &rpath, &show_progress, ©_attrs);
- if (!lpath || !rpath || copy_attrs != 0) return usage();
- return do_sync_push(lpath, rpath, show_progress) ? 0 : 1;
+ parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, ©_attrs);
+ if (srcs.empty() || !dst) return usage();
+ return do_sync_push(srcs, dst) ? 0 : 1;
}
else if (!strcmp(argv[0], "pull")) {
- bool show_progress = false;
- int copy_attrs = 0;
- const char* rpath = NULL, *lpath = ".";
+ bool copy_attrs = false;
+ std::vector<const char*> srcs;
+ const char* dst = ".";
- parse_push_pull_args(&argv[1], argc - 1, &rpath, &lpath, &show_progress, ©_attrs);
- if (!rpath) return usage();
- return do_sync_pull(rpath, lpath, show_progress, copy_attrs) ? 0 : 1;
+ parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, ©_attrs);
+ if (srcs.empty()) return usage();
+ return do_sync_pull(srcs, dst, copy_attrs) ? 0 : 1;
}
else if (!strcmp(argv[0], "install")) {
if (argc < 2) return usage();
@@ -1766,9 +1771,10 @@
}
int result = -1;
- const char* apk_file = argv[last_apk];
- std::string apk_dest = android::base::StringPrintf(where, adb_basename(apk_file).c_str());
- if (!do_sync_push(apk_file, apk_dest.c_str(), false)) goto cleanup_apk;
+ std::vector<const char*> apk_file = {argv[last_apk]};
+ std::string apk_dest = android::base::StringPrintf(
+ where, adb_basename(argv[last_apk]).c_str());
+ if (!do_sync_push(apk_file, apk_dest.c_str())) goto cleanup_apk;
argv[last_apk] = apk_dest.c_str(); /* destination name, not source location */
result = pm_command(transport, serial, argc, argv);
diff --git a/adb/console.cpp b/adb/console.cpp
index ba5a72b..5a9c6ab 100644
--- a/adb/console.cpp
+++ b/adb/console.cpp
@@ -25,6 +25,7 @@
#include "adb.h"
#include "adb_client.h"
+#include "adb_io.h"
// Return the console port of the currently connected emulator (if any) or -1 if
// there is no emulator, and -2 if there is more than one.
@@ -87,14 +88,20 @@
return 1;
}
+ std::string commands;
+
for (int i = 1; i < argc; i++) {
- adb_write(fd, argv[i], strlen(argv[i]));
- adb_write(fd, i == argc - 1 ? "\n" : " ", 1);
+ commands.append(argv[i]);
+ commands.append(i == argc - 1 ? "\n" : " ");
}
- const char disconnect_command[] = "quit\n";
- if (adb_write(fd, disconnect_command, sizeof(disconnect_command) - 1) == -1) {
- LOG(FATAL) << "Could not finalize emulator command";
+ commands.append("quit\n");
+
+ if (!WriteFdExactly(fd, commands)) {
+ fprintf(stderr, "error: cannot write to emulator: %s\n",
+ strerror(errno));
+ adb_close(fd);
+ return 1;
}
// Drain output that the emulator console has sent us to prevent a problem
@@ -106,11 +113,14 @@
do {
char buf[BUFSIZ];
result = adb_read(fd, buf, sizeof(buf));
- // Keep reading until zero bytes (EOF) or an error. If 'adb emu kill'
- // is executed, the emulator calls exit() which causes adb to get
- // ECONNRESET. Any other emu command is followed by the quit command
- // that we sent above, and that causes the emulator to close the socket
- // which should cause zero bytes (EOF) to be returned.
+ // Keep reading until zero bytes (orderly/graceful shutdown) or an
+ // error. If 'adb emu kill' is executed, the emulator calls exit() with
+ // the socket open (and shutdown(SD_SEND) was not called), which causes
+ // Windows to send a TCP RST segment which causes adb to get ECONNRESET.
+ // Any other emu command is followed by the quit command that we
+ // appended above, and that causes the emulator to close the socket
+ // which should cause zero bytes (orderly/graceful shutdown) to be
+ // returned.
} while (result > 0);
adb_close(fd);
diff --git a/adb/fdevent.cpp b/adb/fdevent.cpp
index ddd15a2..4458c85 100644
--- a/adb/fdevent.cpp
+++ b/adb/fdevent.cpp
@@ -72,6 +72,19 @@
// 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 bool main_thread_valid;
+static pthread_t main_thread;
+
+static void check_main_thread() {
+ if (main_thread_valid) {
+ CHECK_NE(0, pthread_equal(main_thread, pthread_self()));
+ }
+}
+
+static void set_main_thread() {
+ main_thread_valid = true;
+ main_thread = pthread_self();
+}
static std::string dump_fde(const fdevent* fde) {
std::string state;
@@ -101,6 +114,7 @@
fdevent *fdevent_create(int fd, fd_func func, void *arg)
{
+ check_main_thread();
fdevent *fde = (fdevent*) malloc(sizeof(fdevent));
if(fde == 0) return 0;
fdevent_install(fde, fd, func, arg);
@@ -110,6 +124,7 @@
void fdevent_destroy(fdevent *fde)
{
+ check_main_thread();
if(fde == 0) return;
if(!(fde->state & FDE_CREATED)) {
LOG(FATAL) << "destroying fde not created by fdevent_create(): " << dump_fde(fde);
@@ -119,6 +134,7 @@
}
void fdevent_install(fdevent* fde, int fd, fd_func func, void* arg) {
+ check_main_thread();
CHECK_GE(fd, 0);
memset(fde, 0, sizeof(fdevent));
fde->state = FDE_ACTIVE;
@@ -137,6 +153,7 @@
}
void fdevent_remove(fdevent* fde) {
+ check_main_thread();
D("fdevent_remove %s", dump_fde(fde).c_str());
if (fde->state & FDE_ACTIVE) {
g_poll_node_map.erase(fde->fd);
@@ -171,6 +188,7 @@
}
void fdevent_set(fdevent* fde, unsigned events) {
+ check_main_thread();
events &= FDE_EVENTMASK;
if ((fde->state & FDE_EVENTMASK) == events) {
return;
@@ -190,10 +208,12 @@
}
void fdevent_add(fdevent* fde, unsigned events) {
+ check_main_thread();
fdevent_set(fde, (fde->state & FDE_EVENTMASK) | events);
}
void fdevent_del(fdevent* fde, unsigned events) {
+ check_main_thread();
fdevent_set(fde, (fde->state & FDE_EVENTMASK) & ~events);
}
@@ -335,6 +355,7 @@
void fdevent_loop()
{
+ set_main_thread();
#if !ADB_HOST
fdevent_subproc_setup();
#endif // !ADB_HOST
@@ -359,4 +380,5 @@
void fdevent_reset() {
g_poll_node_map.clear();
g_pending_list.clear();
+ main_thread_valid = false;
}
diff --git a/adb/file_sync_client.cpp b/adb/file_sync_client.cpp
index 1d668bc..56859b5 100644
--- a/adb/file_sync_client.cpp
+++ b/adb/file_sync_client.cpp
@@ -28,6 +28,7 @@
#include <unistd.h>
#include <utime.h>
+#include <functional>
#include <memory>
#include <vector>
@@ -38,6 +39,7 @@
#include "adb_io.h"
#include "adb_utils.h"
#include "file_sync_service.h"
+#include "line_printer.h"
#include <base/file.h>
#include <base/strings.h>
@@ -49,44 +51,30 @@
char data[SYNC_DATA_MAX];
};
-static long long NOW() {
- struct timeval tv;
- gettimeofday(&tv, 0);
- return ((long long) tv.tv_usec) + 1000000LL * ((long long) tv.tv_sec);
-}
-
-static void print_transfer_progress(uint64_t bytes_current,
- uint64_t bytes_total) {
- if (bytes_total == 0) return;
-
- fprintf(stderr, "\rTransferring: %" PRIu64 "/%" PRIu64 " (%d%%)",
- bytes_current, bytes_total,
- (int) (bytes_current * 100 / bytes_total));
-
- if (bytes_current == bytes_total) {
- fputc('\n', stderr);
- }
-
- fflush(stderr);
-}
-
class SyncConnection {
public:
- SyncConnection() : total_bytes(0), start_time_(NOW()) {
+ SyncConnection() : total_bytes(0), start_time_ms_(CurrentTimeMs()) {
max = SYNC_DATA_MAX; // TODO: decide at runtime.
std::string error;
fd = adb_connect("sync:", &error);
if (fd < 0) {
- fprintf(stderr, "adb: error: %s\n", error.c_str());
+ Error("connect failed: %s", error.c_str());
}
}
~SyncConnection() {
if (!IsValid()) return;
- SendQuit();
- ShowTransferRate();
+ if (SendQuit()) {
+ // We sent a quit command, so the server should be doing orderly
+ // shutdown soon. But if we encountered an error while we were using
+ // the connection, the server might still be sending data (before
+ // doing orderly shutdown), in which case we won't wait for all of
+ // the data nor the coming orderly shutdown. In the common success
+ // case, this will wait for the server to do orderly shutdown.
+ ReadOrderlyShutdown(fd);
+ }
adb_close(fd);
}
@@ -95,7 +83,7 @@
bool SendRequest(int id, const char* path_and_mode) {
size_t path_length = strlen(path_and_mode);
if (path_length > 1024) {
- fprintf(stderr, "adb: SendRequest failed: path too long: %zu\n", path_length);
+ Error("SendRequest failed: path too long: %zu", path_length);
errno = ENAMETOOLONG;
return false;
}
@@ -115,11 +103,14 @@
// Sending header, payload, and footer in a single write makes a huge
// difference to "adb sync" performance.
bool SendSmallFile(const char* path_and_mode,
+ const char* rpath,
const char* data, size_t data_length,
unsigned mtime) {
+ Print(rpath);
+
size_t path_length = strlen(path_and_mode);
if (path_length > 1024) {
- fprintf(stderr, "adb: SendSmallFile failed: path too long: %zu\n", path_length);
+ Error("SendSmallFile failed: path too long: %zu", path_length);
errno = ENAMETOOLONG;
return false;
}
@@ -157,16 +148,14 @@
bool CopyDone(const char* from, const char* to) {
syncmsg msg;
if (!ReadFdExactly(fd, &msg.status, sizeof(msg.status))) {
- fprintf(stderr, "adb: failed to copy '%s' to '%s': no ID_DONE: %s\n",
- from, to, strerror(errno));
+ Error("failed to copy '%s' to '%s': no ID_DONE: %s", from, to, strerror(errno));
return false;
}
if (msg.status.id == ID_OKAY) {
return true;
}
if (msg.status.id != ID_FAIL) {
- fprintf(stderr, "adb: failed to copy '%s' to '%s': unknown reason %d\n",
- from, to, msg.status.id);
+ Error("failed to copy '%s' to '%s': unknown reason %d", from, to, msg.status.id);
return false;
}
return ReportCopyFailure(from, to, msg);
@@ -175,15 +164,51 @@
bool ReportCopyFailure(const char* from, const char* to, const syncmsg& msg) {
std::vector<char> buf(msg.status.msglen + 1);
if (!ReadFdExactly(fd, &buf[0], msg.status.msglen)) {
- fprintf(stderr, "adb: failed to copy '%s' to '%s'; failed to read reason (!): %s\n",
- from, to, strerror(errno));
+ Error("failed to copy '%s' to '%s'; failed to read reason (!): %s",
+ from, to, strerror(errno));
return false;
}
buf[msg.status.msglen] = 0;
- fprintf(stderr, "adb: failed to copy '%s' to '%s': %s\n", from, to, &buf[0]);
+ Error("failed to copy '%s' to '%s': %s", from, to, &buf[0]);
return false;
}
+ std::string TransferRate() {
+ uint64_t ms = CurrentTimeMs() - start_time_ms_;
+ if (total_bytes == 0 || ms == 0) return "";
+
+ double s = static_cast<double>(ms) / 1000LL;
+ double rate = (static_cast<double>(total_bytes) / s) / (1024*1024);
+ return android::base::StringPrintf(" %.1f MB/s (%" PRId64 " bytes in %.3fs)",
+ rate, total_bytes, s);
+ }
+
+ void Print(const std::string& s) {
+ // TODO: we actually don't want ELIDE; we want "ELIDE if smart, FULL if dumb".
+ line_printer_.Print(s, LinePrinter::ELIDE);
+ }
+
+ void Printf(const char* fmt, ...) __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 3))) {
+ std::string s;
+ va_list ap;
+ va_start(ap, fmt);
+ android::base::StringAppendV(&s, fmt, ap);
+ va_end(ap);
+
+ Print(s);
+ }
+
+ void Error(const char* fmt, ...) __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 3))) {
+ std::string s = "adb: error: ";
+
+ va_list ap;
+ va_start(ap, fmt);
+ android::base::StringAppendV(&s, fmt, ap);
+ va_end(ap);
+
+ line_printer_.Print(s, LinePrinter::FULL);
+ }
+
uint64_t total_bytes;
// TODO: add a char[max] buffer here, to replace syncsendbuf...
@@ -191,25 +216,25 @@
size_t max;
private:
- uint64_t start_time_;
+ uint64_t start_time_ms_;
- void SendQuit() {
- SendRequest(ID_QUIT, ""); // TODO: add a SendResponse?
+ LinePrinter line_printer_;
+
+ bool SendQuit() {
+ return SendRequest(ID_QUIT, ""); // TODO: add a SendResponse?
}
- void ShowTransferRate() {
- uint64_t t = NOW() - start_time_;
- if (total_bytes == 0 || t == 0) return;
-
- fprintf(stderr, "%lld KB/s (%" PRId64 " bytes in %lld.%03llds)\n",
- ((total_bytes * 1000000LL) / t) / 1024LL,
- total_bytes, (t / 1000000LL), (t % 1000000LL) / 1000LL);
+ static uint64_t CurrentTimeMs() {
+ struct timeval tv;
+ gettimeofday(&tv, 0); // (Not clock_gettime because of Mac/Windows.)
+ return static_cast<uint64_t>(tv.tv_sec) * 1000 + tv.tv_usec / 1000;
}
};
-typedef void (*sync_ls_cb)(unsigned mode, unsigned size, unsigned time, const char* name, void* cookie);
+typedef void (sync_ls_cb)(unsigned mode, unsigned size, unsigned time, const char* name);
-static bool sync_ls(SyncConnection& sc, const char* path, sync_ls_cb func, void* cookie) {
+static bool sync_ls(SyncConnection& sc, const char* path,
+ std::function<sync_ls_cb> func) {
if (!sc.SendRequest(ID_LIST, path)) return false;
while (true) {
@@ -226,7 +251,7 @@
if (!ReadFdExactly(sc.fd, buf, len)) return false;
buf[len] = 0;
- func(msg.dent.mode, msg.dent.size, msg.dent.time, buf, cookie);
+ func(msg.dent.mode, msg.dent.size, msg.dent.time, buf);
}
}
@@ -249,29 +274,26 @@
return sc.SendRequest(ID_STAT, path) && sync_finish_stat(sc, timestamp, mode, size);
}
-static bool SendLargeFile(SyncConnection& sc, const char* path_and_mode, const char* path,
- unsigned mtime, bool show_progress) {
+static bool SendLargeFile(SyncConnection& sc, const char* path_and_mode,
+ const char* lpath, const char* rpath,
+ unsigned mtime) {
if (!sc.SendRequest(ID_SEND, path_and_mode)) {
- fprintf(stderr, "adb: failed to send ID_SEND message '%s': %s\n",
- path_and_mode, strerror(errno));
+ sc.Error("failed to send ID_SEND message '%s': %s", path_and_mode, strerror(errno));
return false;
}
- unsigned long long size = 0;
- if (show_progress) {
- // Determine local file size.
- struct stat st;
- if (stat(path, &st) == -1) {
- fprintf(stderr, "adb: cannot stat '%s': %s\n", path, strerror(errno));
- return false;
- }
-
- size = st.st_size;
+ struct stat st;
+ if (stat(lpath, &st) == -1) {
+ sc.Error("cannot stat '%s': %s", lpath, strerror(errno));
+ return false;
}
- int lfd = adb_open(path, O_RDONLY);
+ uint64_t total_size = st.st_size;
+ uint64_t bytes_copied = 0;
+
+ int lfd = adb_open(lpath, O_RDONLY);
if (lfd < 0) {
- fprintf(stderr, "adb: cannot open '%s': %s\n", path, strerror(errno));
+ sc.Error("cannot open '%s': %s", lpath, strerror(errno));
return false;
}
@@ -281,7 +303,7 @@
int ret = adb_read(lfd, sbuf.data, sc.max);
if (ret <= 0) {
if (ret < 0) {
- fprintf(stderr, "adb: cannot read '%s': %s\n", path, strerror(errno));
+ sc.Error("cannot read '%s': %s", lpath, strerror(errno));
adb_close(lfd);
return false;
}
@@ -295,9 +317,10 @@
}
sc.total_bytes += ret;
- if (show_progress) {
- print_transfer_progress(sc.total_bytes, size);
- }
+ bytes_copied += ret;
+
+ int percentage = static_cast<int>(bytes_copied * 100 / total_size);
+ sc.Printf("%s: %d%%", rpath, percentage);
}
adb_close(lfd);
@@ -306,8 +329,7 @@
msg.data.id = ID_DONE;
msg.data.size = mtime;
if (!WriteFdExactly(sc.fd, &msg.data, sizeof(msg.data))) {
- fprintf(stderr, "adb: failed to send ID_DONE message for '%s': %s\n",
- path, strerror(errno));
+ sc.Error("failed to send ID_DONE message for '%s': %s", rpath, strerror(errno));
return false;
}
@@ -315,7 +337,7 @@
}
static bool sync_send(SyncConnection& sc, const char* lpath, const char* rpath,
- unsigned mtime, mode_t mode, bool show_progress)
+ unsigned mtime, mode_t mode)
{
std::string path_and_mode = android::base::StringPrintf("%s,%d", rpath, mode);
@@ -324,44 +346,48 @@
char buf[PATH_MAX];
ssize_t data_length = readlink(lpath, buf, PATH_MAX - 1);
if (data_length == -1) {
- fprintf(stderr, "adb: readlink '%s' failed: %s\n", lpath, strerror(errno));
+ sc.Error("readlink '%s' failed: %s", lpath, strerror(errno));
return false;
}
buf[data_length++] = '\0';
- if (!sc.SendSmallFile(path_and_mode.c_str(), buf, data_length, mtime)) return false;
+ if (!sc.SendSmallFile(path_and_mode.c_str(), rpath, buf, data_length, mtime)) return false;
return sc.CopyDone(lpath, rpath);
#endif
}
if (!S_ISREG(mode)) {
- fprintf(stderr, "adb: local file '%s' has unsupported mode: 0o%o\n", lpath, mode);
+ sc.Error("local file '%s' has unsupported mode: 0o%o", lpath, mode);
return false;
}
struct stat st;
if (stat(lpath, &st) == -1) {
- fprintf(stderr, "adb: failed to stat local file '%s': %s\n", lpath, strerror(errno));
+ sc.Error("failed to stat local file '%s': %s", lpath, strerror(errno));
return false;
}
if (st.st_size < SYNC_DATA_MAX) {
std::string data;
if (!android::base::ReadFileToString(lpath, &data)) {
- fprintf(stderr, "adb: failed to read all of '%s': %s\n", lpath, strerror(errno));
+ sc.Error("failed to read all of '%s': %s", lpath, strerror(errno));
return false;
}
- if (!sc.SendSmallFile(path_and_mode.c_str(), data.data(), data.size(), mtime)) return false;
+ if (!sc.SendSmallFile(path_and_mode.c_str(), rpath, data.data(), data.size(), mtime)) {
+ return false;
+ }
} else {
- if (!SendLargeFile(sc, path_and_mode.c_str(), lpath, mtime, show_progress)) return false;
+ if (!SendLargeFile(sc, path_and_mode.c_str(), lpath, rpath, mtime)) {
+ return false;
+ }
}
return sc.CopyDone(lpath, rpath);
}
-static bool sync_recv(SyncConnection& sc, const char* rpath, const char* lpath, bool show_progress) {
+static bool sync_recv(SyncConnection& sc, const char* rpath, const char* lpath) {
+ sc.Print(rpath);
+
unsigned size = 0;
- if (show_progress) {
- if (!sync_stat(sc, rpath, nullptr, nullptr, &size)) return false;
- }
+ if (!sync_stat(sc, rpath, nullptr, nullptr, &size)) return false;
if (!sc.SendRequest(ID_RECV, rpath)) return false;
@@ -369,10 +395,11 @@
mkdirs(lpath);
int lfd = adb_creat(lpath, 0644);
if (lfd < 0) {
- fprintf(stderr, "adb: cannot create '%s': %s\n", lpath, strerror(errno));
+ sc.Error("cannot create '%s': %s", lpath, strerror(errno));
return false;
}
+ uint64_t bytes_copied = 0;
while (true) {
syncmsg msg;
if (!ReadFdExactly(sc.fd, &msg.data, sizeof(msg.data))) {
@@ -391,7 +418,7 @@
}
if (msg.data.size > sc.max) {
- fprintf(stderr, "adb: msg.data.size too large: %u (max %zu)\n", msg.data.size, sc.max);
+ sc.Error("msg.data.size too large: %u (max %zu)", msg.data.size, sc.max);
adb_close(lfd);
adb_unlink(lpath);
return false;
@@ -405,7 +432,7 @@
}
if (!WriteFdExactly(lfd, buffer, msg.data.size)) {
- fprintf(stderr, "adb: cannot write '%s': %s\n", lpath, strerror(errno));
+ sc.Error("cannot write '%s': %s", lpath, strerror(errno));
adb_close(lfd);
adb_unlink(lpath);
return false;
@@ -413,85 +440,67 @@
sc.total_bytes += msg.data.size;
- if (show_progress) {
- print_transfer_progress(sc.total_bytes, size);
- }
+ bytes_copied += msg.data.size;
+
+ int percentage = static_cast<int>(bytes_copied * 100 / size);
+ sc.Printf("%s: %d%%", rpath, percentage);
}
adb_close(lfd);
return true;
}
-static void do_sync_ls_cb(unsigned mode, unsigned size, unsigned time,
- const char* name, void* /*cookie*/) {
- printf("%08x %08x %08x %s\n", mode, size, time, name);
-}
-
bool do_sync_ls(const char* path) {
SyncConnection sc;
if (!sc.IsValid()) return false;
- return sync_ls(sc, path, do_sync_ls_cb, 0);
+ return sync_ls(sc, path, [](unsigned mode, unsigned size, unsigned time,
+ const char* name) {
+ printf("%08x %08x %08x %s\n", mode, size, time, name);
+ });
}
struct copyinfo
{
- copyinfo *next;
- const char *src;
- const char *dst;
+ std::string src;
+ std::string dst;
unsigned int time;
unsigned int mode;
uint64_t size;
int flag;
};
-static copyinfo* mkcopyinfo(const char* spath, const char* dpath, const char* name, int isdir) {
- int slen = strlen(spath);
- int dlen = strlen(dpath);
- int nlen = strlen(name);
- int ssize = slen + nlen + 2;
- int dsize = dlen + nlen + 2;
-
- copyinfo *ci = reinterpret_cast<copyinfo*>(malloc(sizeof(copyinfo) + ssize + dsize));
- if(ci == 0) {
- fprintf(stderr, "out of memory\n");
- abort();
- }
-
- ci->next = 0;
- ci->time = 0;
- ci->mode = 0;
- ci->size = 0;
- ci->flag = 0;
- ci->src = (const char*)(ci + 1);
- ci->dst = ci->src + ssize;
- snprintf((char*) ci->src, ssize, isdir ? "%s%s/" : "%s%s", spath, name);
- snprintf((char*) ci->dst, dsize, isdir ? "%s%s/" : "%s%s", dpath, name);
-
- return ci;
+static copyinfo mkcopyinfo(const char* spath, const char* dpath, const char* name, bool isdir) {
+ copyinfo result;
+ result.src = android::base::StringPrintf(isdir ? "%s%s/" : "%s%s", spath, name);
+ result.dst = android::base::StringPrintf(isdir ? "%s%s/" : "%s%s", dpath, name);
+ result.time = 0;
+ result.mode = 0;
+ result.size = 0;
+ result.flag = 0;
+ return result;
}
static bool IsDotOrDotDot(const char* name) {
return name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'));
}
-static int local_build_list(copyinfo** filelist, const char* lpath, const char* rpath) {
- copyinfo *dirlist = 0;
- copyinfo *ci, *next;
-
- std::unique_ptr<DIR, int(*)(DIR*)> dir(opendir(lpath), closedir);
+static int local_build_list(SyncConnection& sc, std::vector<copyinfo>* filelist,
+ const char* lpath, const char* rpath) {
+ std::vector<copyinfo> dirlist;
+ std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(lpath), closedir);
if (!dir) {
- fprintf(stderr, "adb: cannot open '%s': %s\n", lpath, strerror(errno));
+ sc.Error("cannot open '%s': %s", lpath, strerror(errno));
return -1;
}
- dirent *de;
+ dirent* de;
while ((de = readdir(dir.get()))) {
if (IsDotOrDotDot(de->d_name)) continue;
char stat_path[PATH_MAX];
if (strlen(lpath) + strlen(de->d_name) + 1 > sizeof(stat_path)) {
- fprintf(stderr, "adb: skipping long path '%s%s'\n", lpath, de->d_name);
+ sc.Error("skipping long path '%s%s'", lpath, de->d_name);
continue;
}
strcpy(stat_path, lpath);
@@ -500,33 +509,27 @@
struct stat st;
if (!lstat(stat_path, &st)) {
if (S_ISDIR(st.st_mode)) {
- ci = mkcopyinfo(lpath, rpath, de->d_name, 1);
- ci->next = dirlist;
- dirlist = ci;
+ dirlist.push_back(mkcopyinfo(lpath, rpath, de->d_name, 1));
} else {
- ci = mkcopyinfo(lpath, rpath, de->d_name, 0);
if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
- fprintf(stderr, "adb: skipping special file '%s'\n", ci->src);
- free(ci);
+ sc.Error("skipping special file '%s'", lpath);
} else {
- ci->time = st.st_mtime;
- ci->mode = st.st_mode;
- ci->size = st.st_size;
- ci->next = *filelist;
- *filelist = ci;
+ copyinfo ci = mkcopyinfo(lpath, rpath, de->d_name, 0);
+ ci.time = st.st_mtime;
+ ci.mode = st.st_mode;
+ ci.size = st.st_size;
+ filelist->push_back(ci);
}
}
} else {
- fprintf(stderr, "adb: cannot lstat '%s': %s\n",stat_path , strerror(errno));
+ sc.Error("cannot lstat '%s': %s", stat_path, strerror(errno));
}
}
// Close this directory and recurse.
dir.reset();
- for (ci = dirlist; ci != 0; ci = next) {
- next = ci->next;
- local_build_list(filelist, ci->src, ci->dst);
- free(ci);
+ for (const copyinfo& ci : dirlist) {
+ local_build_list(sc, filelist, ci.src.c_str(), ci.dst.c_str());
}
return 0;
@@ -534,8 +537,7 @@
static bool copy_local_dir_remote(SyncConnection& sc, const char* lpath, const char* rpath,
bool check_timestamps, bool list_only) {
- copyinfo *filelist = 0;
- copyinfo *ci, *next;
+ std::vector<copyinfo> filelist;
int pushed = 0;
int skipped = 0;
@@ -555,133 +557,137 @@
rpath = tmp;
}
- if (local_build_list(&filelist, lpath, rpath)) {
+ if (local_build_list(sc, &filelist, lpath, rpath)) {
return false;
}
if (check_timestamps) {
- for (ci = filelist; ci != 0; ci = ci->next) {
- if (!sc.SendRequest(ID_STAT, ci->dst)) return false;
+ for (const copyinfo& ci : filelist) {
+ if (!sc.SendRequest(ID_STAT, ci.dst.c_str())) return false;
}
- for(ci = filelist; ci != 0; ci = ci->next) {
+ for (copyinfo& ci : filelist) {
unsigned int timestamp, mode, size;
if (!sync_finish_stat(sc, ×tamp, &mode, &size)) return false;
- if (size == ci->size) {
+ if (size == ci.size) {
/* for links, we cannot update the atime/mtime */
- if ((S_ISREG(ci->mode & mode) && timestamp == ci->time) ||
- (S_ISLNK(ci->mode & mode) && timestamp >= ci->time)) {
- ci->flag = 1;
+ if ((S_ISREG(ci.mode & mode) && timestamp == ci.time) ||
+ (S_ISLNK(ci.mode & mode) && timestamp >= ci.time)) {
+ ci.flag = 1;
}
}
}
}
- for (ci = filelist; ci != 0; ci = next) {
- next = ci->next;
- if (ci->flag == 0) {
- fprintf(stderr, "%spush: %s -> %s\n", list_only ? "would " : "", ci->src, ci->dst);
- if (!list_only && !sync_send(sc, ci->src, ci->dst, ci->time, ci->mode, false)) {
- return false;
+
+ for (const copyinfo& ci : filelist) {
+ if (ci.flag == 0) {
+ if (list_only) {
+ fprintf(stderr, "would push: %s -> %s\n", ci.src.c_str(),
+ ci.dst.c_str());
+ } else {
+ if (!sync_send(sc, ci.src.c_str(), ci.dst.c_str(), ci.time, ci.mode)) {
+ return false;
+ }
}
pushed++;
} else {
skipped++;
}
- free(ci);
}
- fprintf(stderr, "%d file%s pushed. %d file%s skipped.\n",
- pushed, (pushed == 1) ? "" : "s",
- skipped, (skipped == 1) ? "" : "s");
-
+ sc.Printf("%s: %d file%s pushed. %d file%s skipped.%s\n", rpath, pushed,
+ (pushed == 1) ? "" : "s", skipped, (skipped == 1) ? "" : "s",
+ sc.TransferRate().c_str());
return true;
}
-bool do_sync_push(const char* lpath, const char* rpath, bool show_progress) {
+bool do_sync_push(const std::vector<const char*>& srcs, const char* dst) {
SyncConnection sc;
if (!sc.IsValid()) return false;
- struct stat st;
- if (stat(lpath, &st)) {
- fprintf(stderr, "adb: cannot stat '%s': %s\n", lpath, strerror(errno));
- return false;
- }
-
- if (S_ISDIR(st.st_mode)) {
- return copy_local_dir_remote(sc, lpath, rpath, false, false);
- }
-
+ bool success = true;
unsigned mode;
- if (!sync_stat(sc, rpath, nullptr, &mode, nullptr)) return false;
- std::string path_holder;
- if (mode != 0 && S_ISDIR(mode)) {
- // If we're copying a local file to a remote directory,
- // we really want to copy to remote_dir + "/" + local_filename.
- path_holder = android::base::StringPrintf("%s/%s", rpath, adb_basename(lpath).c_str());
- rpath = path_holder.c_str();
+ if (!sync_stat(sc, dst, nullptr, &mode, nullptr)) return false;
+ bool dst_isdir = mode != 0 && S_ISDIR(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] == '/') {
+ sc.Error("failed to access '%s': Not a directory", dst);
+ return false;
+ }
+ }
}
- return sync_send(sc, lpath, rpath, st.st_mtime, st.st_mode, show_progress);
+
+ for (const char* src_path : srcs) {
+ const char* dst_path = dst;
+ struct stat st;
+ if (stat(src_path, &st)) {
+ 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);
+ continue;
+ }
+
+ std::string path_holder;
+ if (mode != 0 && S_ISDIR(mode)) {
+ // If we're copying a local file to a remote directory,
+ // we really want to copy to remote_dir + "/" + local_filename.
+ path_holder = android::base::StringPrintf(
+ "%s/%s", dst_path, adb_basename(src_path).c_str());
+ dst_path = path_holder.c_str();
+ }
+ success &= sync_send(sc, src_path, dst_path, st.st_mtime, st.st_mode);
+ }
+
+ sc.Print("\n");
+ return success;
}
-
-struct sync_ls_build_list_cb_args {
- copyinfo **filelist;
- copyinfo **dirlist;
- const char *rpath;
- const char *lpath;
-};
-
-static void sync_ls_build_list_cb(unsigned mode, unsigned size, unsigned time,
- const char* name, void* cookie)
-{
- sync_ls_build_list_cb_args *args = (sync_ls_build_list_cb_args *)cookie;
- copyinfo *ci;
-
- if (S_ISDIR(mode)) {
- copyinfo **dirlist = args->dirlist;
-
- // Don't try recursing down "." or "..".
- if (IsDotOrDotDot(name)) return;
-
- ci = mkcopyinfo(args->rpath, args->lpath, name, 1);
- ci->next = *dirlist;
- *dirlist = ci;
- } else if (S_ISREG(mode) || S_ISLNK(mode)) {
- copyinfo **filelist = args->filelist;
-
- ci = mkcopyinfo(args->rpath, args->lpath, name, 0);
- ci->time = time;
- ci->mode = mode;
- ci->size = size;
- ci->next = *filelist;
- *filelist = ci;
- } else {
- fprintf(stderr, "adb: skipping special file '%s'\n", name);
- }
-}
-
-static bool remote_build_list(SyncConnection& sc, copyinfo **filelist,
- const char *rpath, const char *lpath) {
- copyinfo *dirlist = NULL;
- sync_ls_build_list_cb_args args;
-
- args.filelist = filelist;
- args.dirlist = &dirlist;
- args.rpath = rpath;
- args.lpath = lpath;
+static bool remote_build_list(SyncConnection& sc,
+ std::vector<copyinfo>* filelist,
+ const char* rpath, const char* lpath) {
+ std::vector<copyinfo> dirlist;
// Put the files/dirs in rpath on the lists.
- if (!sync_ls(sc, rpath, sync_ls_build_list_cb, (void *)&args)) {
+ auto callback = [&](unsigned mode, unsigned size, unsigned time,
+ const char* name) {
+ if (S_ISDIR(mode)) {
+ // Don't try recursing down "." or "..".
+ if (IsDotOrDotDot(name)) return;
+
+ dirlist.push_back(mkcopyinfo(rpath, lpath, name, 1));
+ } else if (S_ISREG(mode) || S_ISLNK(mode)) {
+ copyinfo ci = mkcopyinfo(rpath, lpath, name, 0);
+ ci.time = time;
+ ci.mode = mode;
+ ci.size = size;
+ filelist->push_back(ci);
+ } else {
+ sc.Print(android::base::StringPrintf("skipping special file '%s'\n",
+ name));
+ }
+ };
+
+ if (!sync_ls(sc, rpath, callback)) {
return false;
}
// Recurse into each directory we found.
- while (dirlist != NULL) {
- copyinfo *next = dirlist->next;
- if (!remote_build_list(sc, filelist, dirlist->src, dirlist->dst)) {
+ while (!dirlist.empty()) {
+ copyinfo current = dirlist.back();
+ dirlist.pop_back();
+ if (!remote_build_list(sc, filelist, current.src.c_str(),
+ current.dst.c_str())) {
return false;
}
- free(dirlist);
- dirlist = next;
}
return true;
@@ -693,7 +699,7 @@
int r1 = utime(lpath, ×);
/* use umask for permissions */
- mode_t mask=umask(0000);
+ mode_t mask = umask(0000);
umask(mask);
int r2 = chmod(lpath, mode & ~mask);
@@ -701,7 +707,7 @@
}
static bool copy_remote_dir_local(SyncConnection& sc, const char* rpath, const char* lpath,
- int copy_attrs) {
+ bool copy_attrs) {
// Make sure that both directory paths end in a slash.
std::string rpath_clean(rpath);
std::string lpath_clean(lpath);
@@ -710,79 +716,115 @@
if (lpath_clean.back() != '/') lpath_clean.push_back('/');
// Recursively build the list of files to copy.
- fprintf(stderr, "pull: building file list...\n");
- copyinfo* filelist = nullptr;
- if (!remote_build_list(sc, &filelist, rpath_clean.c_str(), lpath_clean.c_str())) return false;
+ sc.Print("pull: building file list...");
+ std::vector<copyinfo> filelist;
+ if (!remote_build_list(sc, &filelist, rpath_clean.c_str(),
+ lpath_clean.c_str())) {
+ return false;
+ }
int pulled = 0;
int skipped = 0;
- copyinfo* ci = filelist;
- while (ci) {
- copyinfo* next = ci->next;
- if (ci->flag == 0) {
- fprintf(stderr, "pull: %s -> %s\n", ci->src, ci->dst);
- if (!sync_recv(sc, ci->src, ci->dst, false)) {
+ for (const copyinfo &ci : filelist) {
+ if (ci.flag == 0) {
+ sc.Printf("pull: %s -> %s", ci.src.c_str(), ci.dst.c_str());
+ if (!sync_recv(sc, ci.src.c_str(), ci.dst.c_str())) {
return false;
}
- if (copy_attrs && set_time_and_mode(ci->dst, ci->time, ci->mode)) {
+ if (copy_attrs &&
+ set_time_and_mode(ci.dst.c_str(), ci.time, ci.mode)) {
return false;
}
pulled++;
} else {
skipped++;
}
- free(ci);
- ci = next;
}
- fprintf(stderr, "%d file%s pulled. %d file%s skipped.\n",
- pulled, (pulled == 1) ? "" : "s",
- skipped, (skipped == 1) ? "" : "s");
+ sc.Printf("%s: %d file%s pulled. %d file%s skipped.%s\n", rpath, pulled,
+ (pulled == 1) ? "" : "s", skipped, (skipped == 1) ? "" : "s",
+ sc.TransferRate().c_str());
return true;
}
-bool do_sync_pull(const char* rpath, const char* lpath, bool show_progress, int copy_attrs) {
+bool do_sync_pull(const std::vector<const char*>& srcs, const char* dst,
+ bool copy_attrs) {
SyncConnection sc;
if (!sc.IsValid()) return false;
+ bool success = true;
unsigned mode, time;
- if (!sync_stat(sc, rpath, &time, &mode, nullptr)) return false;
- if (mode == 0) {
- fprintf(stderr, "adb: remote object '%s' does not exist\n", rpath);
- return false;
+ struct stat st;
+ if (stat(dst, &st)) {
+ // If we're only pulling one file, 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));
+ return false;
+ }
}
- if (S_ISREG(mode) || S_ISLNK(mode) || S_ISCHR(mode) || S_ISBLK(mode)) {
- std::string path_holder;
- struct stat st;
- if (stat(lpath, &st) == 0) {
- if (S_ISDIR(st.st_mode)) {
- // If we're copying a remote file to a local directory,
- // we really want to copy to local_dir + "/" + basename(remote).
- path_holder = android::base::StringPrintf("%s/%s", lpath, adb_basename(rpath).c_str());
- lpath = path_holder.c_str();
- }
- }
- if (!sync_recv(sc, rpath, lpath, show_progress)) {
+ bool dst_isdir = S_ISDIR(st.st_mode);
+ if (!dst_isdir) {
+ if (srcs.size() > 1) {
+ sc.Error("target '%s' is not a directory", dst);
return false;
} else {
- if (copy_attrs && set_time_and_mode(lpath, time, mode)) {
+ size_t dst_len = strlen(dst);
+ if (dst[dst_len - 1] == '/') {
+ sc.Error("failed to access '%s': Not a directory", dst);
return false;
}
}
- return true;
- } else if (S_ISDIR(mode)) {
- return copy_remote_dir_local(sc, rpath, lpath, copy_attrs);
}
- fprintf(stderr, "adb: remote object '%s' not a file or directory\n", rpath);
- return false;
+ for (const char* src_path : srcs) {
+ const char* dst_path = dst;
+ if (!sync_stat(sc, src_path, &time, &mode, nullptr)) return false;
+ if (mode == 0) {
+ sc.Error("remote object '%s' does not exist", src_path);
+ success = false;
+ continue;
+ }
+
+ if (S_ISREG(mode) || S_ISLNK(mode) || S_ISCHR(mode) || S_ISBLK(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 (!sync_recv(sc, src_path, dst_path)) {
+ success = false;
+ continue;
+ } else {
+ if (copy_attrs && set_time_and_mode(dst_path, time, mode)) {
+ success = false;
+ continue;
+ }
+ }
+ } else if (S_ISDIR(mode)) {
+ success &= copy_remote_dir_local(sc, src_path, dst_path, copy_attrs);
+ continue;
+ } else {
+ sc.Error("remote object '%s' not a file or directory", src_path);
+ success = false;
+ continue;
+ }
+ }
+
+ sc.Print("\n");
+ return success;
}
bool do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only) {
- fprintf(stderr, "syncing %s...\n", rpath.c_str());
-
SyncConnection sc;
if (!sc.IsValid()) return false;
diff --git a/adb/file_sync_service.h b/adb/file_sync_service.h
index 67ed3fc..38382c1 100644
--- a/adb/file_sync_service.h
+++ b/adb/file_sync_service.h
@@ -18,6 +18,7 @@
#define _FILE_SYNC_SERVICE_H_
#include <string>
+#include <vector>
#define MKID(a,b,c,d) ((a) | ((b) << 8) | ((c) << 16) | ((d) << 24))
@@ -64,9 +65,11 @@
void file_sync_service(int fd, void* cookie);
bool do_sync_ls(const char* path);
-bool do_sync_push(const char* lpath, const char* rpath, bool show_progress);
+bool do_sync_push(const std::vector<const char*>& srcs, const char* dst);
+bool do_sync_pull(const std::vector<const char*>& srcs, const char* dst,
+ bool copy_attrs);
+
bool do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only);
-bool do_sync_pull(const char* rpath, const char* lpath, bool show_progress, int copy_attrs);
#define SYNC_DATA_MAX (64*1024)
diff --git a/adb/line_printer.cpp b/adb/line_printer.cpp
new file mode 100644
index 0000000..aa332f7
--- /dev/null
+++ b/adb/line_printer.cpp
@@ -0,0 +1,161 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "line_printer.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <termios.h>
+#include <sys/time.h>
+#endif
+
+// Make sure printf is really adb_printf which works for UTF-8 on Windows.
+#include <sysdeps.h>
+
+// Stuff from ninja's util.h that's needed below.
+#include <vector>
+using namespace std;
+string ElideMiddle(const string& str, size_t width) {
+ const int kMargin = 3; // Space for "...".
+ string result = str;
+ if (result.size() + kMargin > width) {
+ size_t elide_size = (width - kMargin) / 2;
+ result = result.substr(0, elide_size)
+ + "..."
+ + result.substr(result.size() - elide_size, elide_size);
+ }
+ return result;
+}
+
+LinePrinter::LinePrinter() : have_blank_line_(true), console_locked_(false) {
+#ifndef _WIN32
+ const char* term = getenv("TERM");
+ smart_terminal_ = unix_isatty(1) && term && string(term) != "dumb";
+#else
+ // Disable output buffer. It'd be nice to use line buffering but
+ // MSDN says: "For some systems, [_IOLBF] provides line
+ // buffering. However, for Win32, the behavior is the same as _IOFBF
+ // - Full Buffering."
+ setvbuf(stdout, NULL, _IONBF, 0);
+ console_ = GetStdHandle(STD_OUTPUT_HANDLE);
+ CONSOLE_SCREEN_BUFFER_INFO csbi;
+ smart_terminal_ = GetConsoleScreenBufferInfo(console_, &csbi);
+#endif
+}
+
+void LinePrinter::Print(string to_print, LineType type) {
+ if (console_locked_) {
+ line_buffer_ = to_print;
+ line_type_ = type;
+ return;
+ }
+
+ if (smart_terminal_) {
+ printf("\r"); // Print over previous line, if any.
+ // On Windows, calling a C library function writing to stdout also handles
+ // pausing the executable when the "Pause" key or Ctrl-S is pressed.
+ }
+
+ if (smart_terminal_ && type == ELIDE) {
+#ifdef _WIN32
+ CONSOLE_SCREEN_BUFFER_INFO csbi;
+ GetConsoleScreenBufferInfo(console_, &csbi);
+
+ // TODO: const std::wstring to_print_wide = widen(to_print);
+ // TODO: wstring ElideMiddle.
+ to_print = ElideMiddle(to_print, static_cast<size_t>(csbi.dwSize.X));
+ // We don't want to have the cursor spamming back and forth, so instead of
+ // printf use WriteConsoleOutput which updates the contents of the buffer,
+ // but doesn't move the cursor position.
+ COORD buf_size = { csbi.dwSize.X, 1 };
+ COORD zero_zero = { 0, 0 };
+ SMALL_RECT target = {
+ csbi.dwCursorPosition.X, csbi.dwCursorPosition.Y,
+ static_cast<SHORT>(csbi.dwCursorPosition.X + csbi.dwSize.X - 1),
+ csbi.dwCursorPosition.Y
+ };
+ vector<CHAR_INFO> char_data(csbi.dwSize.X);
+ for (size_t i = 0; i < static_cast<size_t>(csbi.dwSize.X); ++i) {
+ // TODO: UnicodeChar instead of AsciiChar, to_print_wide[i].
+ char_data[i].Char.AsciiChar = i < to_print.size() ? to_print[i] : ' ';
+ char_data[i].Attributes = csbi.wAttributes;
+ }
+ // TODO: WriteConsoleOutputW.
+ WriteConsoleOutput(console_, &char_data[0], buf_size, zero_zero, &target);
+#else
+ // Limit output to width of the terminal if provided so we don't cause
+ // line-wrapping.
+ winsize size;
+ if ((ioctl(0, TIOCGWINSZ, &size) == 0) && size.ws_col) {
+ to_print = ElideMiddle(to_print, size.ws_col);
+ }
+ printf("%s", to_print.c_str());
+ printf("\x1B[K"); // Clear to end of line.
+ fflush(stdout);
+#endif
+
+ have_blank_line_ = false;
+ } else {
+ printf("%s\n", to_print.c_str());
+ }
+}
+
+void LinePrinter::PrintOrBuffer(const char* data, size_t size) {
+ if (console_locked_) {
+ output_buffer_.append(data, size);
+ } else {
+ // Avoid printf and C strings, since the actual output might contain null
+ // bytes like UTF-16 does (yuck).
+ fwrite(data, 1, size, stdout);
+ }
+}
+
+void LinePrinter::PrintOnNewLine(const string& to_print) {
+ if (console_locked_ && !line_buffer_.empty()) {
+ output_buffer_.append(line_buffer_);
+ output_buffer_.append(1, '\n');
+ line_buffer_.clear();
+ }
+ if (!have_blank_line_) {
+ PrintOrBuffer("\n", 1);
+ }
+ if (!to_print.empty()) {
+ PrintOrBuffer(&to_print[0], to_print.size());
+ }
+ have_blank_line_ = to_print.empty() || *to_print.rbegin() == '\n';
+}
+
+void LinePrinter::SetConsoleLocked(bool locked) {
+ if (locked == console_locked_)
+ return;
+
+ if (locked)
+ PrintOnNewLine("");
+
+ console_locked_ = locked;
+
+ if (!locked) {
+ PrintOnNewLine(output_buffer_);
+ if (!line_buffer_.empty()) {
+ Print(line_buffer_, line_type_);
+ }
+ output_buffer_.clear();
+ line_buffer_.clear();
+ }
+}
diff --git a/adb/line_printer.h b/adb/line_printer.h
new file mode 100644
index 0000000..3d0a5bd
--- /dev/null
+++ b/adb/line_printer.h
@@ -0,0 +1,71 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef NINJA_LINE_PRINTER_H_
+#define NINJA_LINE_PRINTER_H_
+
+#include <stddef.h>
+#include <string>
+
+/// Prints lines of text, possibly overprinting previously printed lines
+/// if the terminal supports it.
+struct LinePrinter {
+ LinePrinter();
+
+ bool is_smart_terminal() const { return smart_terminal_; }
+ void set_smart_terminal(bool smart) { smart_terminal_ = smart; }
+
+ enum LineType {
+ FULL,
+ ELIDE
+ };
+ /// Overprints the current line. If type is ELIDE, elides to_print to fit on
+ /// one line.
+ void Print(std::string to_print, LineType type);
+
+ /// Prints a string on a new line, not overprinting previous output.
+ void PrintOnNewLine(const std::string& to_print);
+
+ /// Lock or unlock the console. Any output sent to the LinePrinter while the
+ /// console is locked will not be printed until it is unlocked.
+ void SetConsoleLocked(bool locked);
+
+ private:
+ /// Whether we can do fancy terminal control codes.
+ bool smart_terminal_;
+
+ /// Whether the caret is at the beginning of a blank line.
+ bool have_blank_line_;
+
+ /// Whether console is locked.
+ bool console_locked_;
+
+ /// Buffered current line while console is locked.
+ std::string line_buffer_;
+
+ /// Buffered line type while console is locked.
+ LineType line_type_;
+
+ /// Buffered console output while console is locked.
+ std::string output_buffer_;
+
+#ifdef _WIN32
+ void* console_;
+#endif
+
+ /// Print the given data to the console, or buffer it if it is locked.
+ void PrintOrBuffer(const char *data, size_t size);
+};
+
+#endif // NINJA_LINE_PRINTER_H_
diff --git a/adb/services.cpp b/adb/services.cpp
index e24b470..19a6726 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -184,15 +184,18 @@
adb_close(fd);
}
-void reverse_service(int fd, void* arg)
-{
- const char* command = reinterpret_cast<const char*>(arg);
-
- if (handle_forward_request(command, kTransportAny, NULL, fd) < 0) {
- SendFail(fd, "not a reverse forwarding command");
+int reverse_service(const char* command) {
+ int s[2];
+ if (adb_socketpair(s)) {
+ PLOG(ERROR) << "cannot create service socket pair.";
+ return -1;
}
- free(arg);
- adb_close(fd);
+ VLOG(SERVICES) << "service socketpair: " << s[0] << ", " << s[1];
+ if (handle_forward_request(command, kTransportAny, nullptr, s[1]) < 0) {
+ SendFail(s[1], "not a reverse forwarding command");
+ }
+ adb_close(s[1]);
+ return s[0];
}
// Shell service string can look like:
@@ -335,15 +338,7 @@
} else if(!strncmp(name, "usb:", 4)) {
ret = create_service_thread(restart_usb_service, NULL);
} else if (!strncmp(name, "reverse:", 8)) {
- char* cookie = strdup(name + 8);
- if (cookie == NULL) {
- ret = -1;
- } else {
- ret = create_service_thread(reverse_service, cookie);
- if (ret < 0) {
- free(cookie);
- }
- }
+ ret = reverse_service(name + 8);
} else if(!strncmp(name, "disable-verity:", 15)) {
ret = create_service_thread(set_verity_enabled_state_service, (void*)0);
} else if(!strncmp(name, "enable-verity:", 15)) {
diff --git a/adb/shell_service.cpp b/adb/shell_service.cpp
index 544afce..be5921d 100644
--- a/adb/shell_service.cpp
+++ b/adb/shell_service.cpp
@@ -83,6 +83,7 @@
#include <errno.h>
#include <pty.h>
+#include <pwd.h>
#include <sys/select.h>
#include <termios.h>
@@ -281,6 +282,15 @@
parent_error_sfd.Reset();
close_on_exec(child_error_sfd.fd());
+ // TODO: $HOSTNAME? Normally bash automatically sets that, but mksh doesn't.
+ passwd* pw = getpwuid(getuid());
+ if (pw != nullptr) {
+ setenv("HOME", pw->pw_dir, 1);
+ setenv("LOGNAME", pw->pw_name, 1);
+ setenv("SHELL", pw->pw_shell, 1);
+ setenv("USER", pw->pw_name, 1);
+ }
+
if (is_interactive()) {
execl(_PATH_BSHELL, _PATH_BSHELL, "-", nullptr);
} else {
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index 51d09a6..1735627 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -181,6 +181,17 @@
extern int unix_open(const char* path, int options, ...);
#define open ___xxx_unix_open
+// Checks if |fd| corresponds to a console.
+// Standard Windows isatty() returns 1 for both console FDs and character
+// devices like NUL. unix_isatty() performs some extra checking to only match
+// console FDs.
+// |fd| must be a real file descriptor, meaning STDxx_FILENO or unix_open() FDs
+// will work but adb_open() FDs will not. Additionally the OS handle associated
+// with |fd| must have GENERIC_READ access (which console FDs have by default).
+// Returns 1 if |fd| is a console FD, 0 otherwise. The value of errno after
+// calling this function is unreliable and should not be used.
+int unix_isatty(int fd);
+#define isatty ___xxx_isatty
/* normally provided by <cutils/misc.h> */
extern void* load_file(const char* pathname, unsigned* psize);
@@ -551,6 +562,11 @@
#undef creat
#define creat ___xxx_creat
+static __inline__ int unix_isatty(int fd) {
+ return isatty(fd);
+}
+#define isatty ___xxx_isatty
+
// Helper for network_* functions.
inline int _fd_set_error_str(int fd, std::string* error) {
if (fd == -1) {
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
index 14d1375..d2e6cdb 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -2499,10 +2499,52 @@
//
// Code organization:
//
+// * _get_console_handle() and unix_isatty() provide console information.
// * stdin_raw_init() and stdin_raw_restore() reconfigure the console.
// * unix_read() detects console windows (as opposed to pipes, files, etc.).
// * _console_read() is the main code of the emulation.
+// Returns a console HANDLE if |fd| is a console, otherwise returns nullptr.
+// If a valid HANDLE is returned and |mode| is not null, |mode| is also filled
+// with the console mode. Requires GENERIC_READ access to the underlying HANDLE.
+static HANDLE _get_console_handle(int fd, DWORD* mode=nullptr) {
+ // First check isatty(); this is very fast and eliminates most non-console
+ // FDs, but returns 1 for both consoles and character devices like NUL.
+#pragma push_macro("isatty")
+#undef isatty
+ if (!isatty(fd)) {
+ return nullptr;
+ }
+#pragma pop_macro("isatty")
+
+ // To differentiate between character devices and consoles we need to get
+ // the underlying HANDLE and use GetConsoleMode(), which is what requires
+ // GENERIC_READ permissions.
+ const intptr_t intptr_handle = _get_osfhandle(fd);
+ if (intptr_handle == -1) {
+ return nullptr;
+ }
+ const HANDLE handle = reinterpret_cast<const HANDLE>(intptr_handle);
+ DWORD temp_mode = 0;
+ if (!GetConsoleMode(handle, mode ? mode : &temp_mode)) {
+ return nullptr;
+ }
+
+ return handle;
+}
+
+// Returns a console handle if |stream| is a console, otherwise returns nullptr.
+static HANDLE _get_console_handle(FILE* const stream) {
+ const int fd = fileno(stream);
+ if (fd < 0) {
+ return nullptr;
+ }
+ return _get_console_handle(fd);
+}
+
+int unix_isatty(int fd) {
+ return _get_console_handle(fd) ? 1 : 0;
+}
// Read an input record from the console; one that should be processed.
static bool _get_interesting_input_record_uncached(const HANDLE console,
@@ -3302,20 +3344,7 @@
void stdin_raw_init(const int fd) {
if (STDIN_FILENO == fd) {
- const HANDLE in = GetStdHandle(STD_INPUT_HANDLE);
- if ((in == INVALID_HANDLE_VALUE) || (in == NULL)) {
- return;
- }
-
- if (GetFileType(in) != FILE_TYPE_CHAR) {
- // stdin might be a file or pipe.
- return;
- }
-
- if (!GetConsoleMode(in, &_old_console_mode)) {
- // If GetConsoleMode() fails, stdin is probably is not a console.
- return;
- }
+ const HANDLE in = _get_console_handle(fd, &_old_console_mode);
// Disable ENABLE_PROCESSED_INPUT so that Ctrl-C is read instead of
// calling the process Ctrl-C routine (configured by
@@ -3366,11 +3395,8 @@
} else {
// On older versions of Windows (definitely 7, definitely not 10),
// ReadConsole() with a size >= 31367 fails, so if |fd| is a console
- // we need to limit the read size. This may also catch devices like NUL,
- // but that is OK as we just want to avoid capping pipes and files which
- // don't need size limiting. This isatty() test is very simple and quick
- // and doesn't call the OS.
- if (isatty(fd) && len > 4096) {
+ // we need to limit the read size.
+ if (len > 4096 && unix_isatty(fd)) {
len = 4096;
}
// Just call into C Runtime which can read from pipes/files and which
@@ -3725,40 +3751,6 @@
return _wchmod(widen(path).c_str(), mode);
}
-// Internal function to get a Win32 console HANDLE from a C Runtime FILE*.
-static HANDLE _get_console_handle(FILE* const stream) {
- // Get a C Runtime file descriptor number from the FILE* structure.
- const int fd = fileno(stream);
- if (fd < 0) {
- return NULL;
- }
-
- // If it is not a "character device", it is probably a file and not a
- // console. Do this check early because it is probably cheap. Still do more
- // checks after this since there are devices that pass this test, but are
- // not a console, such as NUL, the Windows /dev/null equivalent (I think).
- if (!isatty(fd)) {
- return NULL;
- }
-
- // Given a C Runtime file descriptor number, get the underlying OS
- // file handle.
- const intptr_t osfh = _get_osfhandle(fd);
- if (osfh == -1) {
- return NULL;
- }
-
- const HANDLE h = reinterpret_cast<const HANDLE>(osfh);
-
- DWORD old_mode = 0;
- if (!GetConsoleMode(h, &old_mode)) {
- return NULL;
- }
-
- // If GetConsoleMode() was successful, assume this is a console.
- return h;
-}
-
// 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,
diff --git a/adb/sysdeps_win32_test.cpp b/adb/sysdeps_win32_test.cpp
index 66d1ba8..55b5eb4 100755
--- a/adb/sysdeps_win32_test.cpp
+++ b/adb/sysdeps_win32_test.cpp
@@ -18,6 +18,8 @@
#include "sysdeps.h"
+#include "base/test_utils.h"
+
TEST(sysdeps_win32, adb_getenv) {
// Insert all test env vars before first call to adb_getenv() which will
// read the env var block only once.
@@ -93,3 +95,45 @@
// adb_strerror() returns.
TestAdbStrError(ECONNRESET, "Connection reset by peer");
}
+
+TEST(sysdeps_win32, unix_isatty) {
+ // stdin and stdout should be consoles. Use CONIN$ and CONOUT$ special files
+ // so that we can test this even if stdin/stdout have been redirected. Read
+ // permissions are required for unix_isatty().
+ int conin_fd = unix_open("CONIN$", O_RDONLY);
+ int conout_fd = unix_open("CONOUT$", O_RDWR);
+ for (const int fd : {conin_fd, conout_fd}) {
+ EXPECT_TRUE(fd >= 0);
+ EXPECT_EQ(1, unix_isatty(fd));
+ EXPECT_EQ(0, unix_close(fd));
+ }
+
+ // nul returns 1 from isatty(), make sure unix_isatty() corrects that.
+ for (auto flags : {O_RDONLY, O_RDWR}) {
+ int nul_fd = unix_open("nul", flags);
+ EXPECT_TRUE(nul_fd >= 0);
+ EXPECT_EQ(0, unix_isatty(nul_fd));
+ EXPECT_EQ(0, unix_close(nul_fd));
+ }
+
+ // Check a real file, both read-write and read-only.
+ TemporaryFile temp_file;
+ EXPECT_TRUE(temp_file.fd >= 0);
+ EXPECT_EQ(0, unix_isatty(temp_file.fd));
+
+ int temp_file_ro_fd = unix_open(temp_file.path, O_RDONLY);
+ EXPECT_TRUE(temp_file_ro_fd >= 0);
+ EXPECT_EQ(0, unix_isatty(temp_file_ro_fd));
+ EXPECT_EQ(0, unix_close(temp_file_ro_fd));
+
+ // Check a real OS pipe.
+ int pipe_fds[2];
+ EXPECT_EQ(0, _pipe(pipe_fds, 64, _O_BINARY));
+ EXPECT_EQ(0, unix_isatty(pipe_fds[0]));
+ EXPECT_EQ(0, unix_isatty(pipe_fds[1]));
+ EXPECT_EQ(0, _close(pipe_fds[0]));
+ EXPECT_EQ(0, _close(pipe_fds[1]));
+
+ // Make sure an invalid FD is handled correctly.
+ EXPECT_EQ(0, unix_isatty(-1));
+}
diff --git a/adb/test_adb.py b/adb/test_adb.py
index 5bda22f..0f1b034 100644
--- a/adb/test_adb.py
+++ b/adb/test_adb.py
@@ -21,8 +21,11 @@
"""
from __future__ import print_function
+import contextlib
import os
import random
+import socket
+import struct
import subprocess
import threading
import unittest
@@ -140,6 +143,71 @@
subprocess.check_output(['adb', '-P', str(port), 'kill-server'],
stderr=subprocess.STDOUT)
+ # Use SO_LINGER to cause TCP RST segment to be sent on socket close.
+ def _reset_socket_on_close(self, sock):
+ # The linger structure is two shorts on Windows, but two ints on Unix.
+ linger_format = 'hh' if os.name == 'nt' else 'ii'
+ l_onoff = 1
+ l_linger = 0
+
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER,
+ struct.pack(linger_format, l_onoff, l_linger))
+ # Verify that we set the linger structure properly by retrieving it.
+ linger = sock.getsockopt(socket.SOL_SOCKET, socket.SO_LINGER, 16)
+ self.assertEqual((l_onoff, l_linger),
+ struct.unpack_from(linger_format, linger))
+
+ def test_emu_kill(self):
+ """Ensure that adb emu kill works.
+
+ Bug: https://code.google.com/p/android/issues/detail?id=21021
+ """
+ port = 12345
+
+ with contextlib.closing(
+ socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as listener:
+ # Use SO_REUSEADDR so subsequent runs of the test can grab the port
+ # even if it is in TIME_WAIT.
+ listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ listener.bind(('127.0.0.1', port))
+ listener.listen(4)
+
+ # Now that listening has started, start adb emu kill, telling it to
+ # connect to our mock emulator.
+ p = subprocess.Popen(
+ ['adb', '-s', 'emulator-' + str(port), 'emu', 'kill'],
+ stderr=subprocess.STDOUT)
+
+ accepted_connection, addr = listener.accept()
+ with contextlib.closing(accepted_connection) as conn:
+ # If WSAECONNABORTED (10053) is raised by any socket calls,
+ # then adb probably isn't reading the data that we sent it.
+ conn.sendall('Android Console: type \'help\' for a list ' +
+ 'of commands\r\n')
+ conn.sendall('OK\r\n')
+
+ with contextlib.closing(conn.makefile()) as f:
+ self.assertEqual('kill\n', f.readline())
+ self.assertEqual('quit\n', f.readline())
+
+ conn.sendall('OK: killing emulator, bye bye\r\n')
+
+ # Use SO_LINGER to send TCP RST segment to test whether adb
+ # ignores WSAECONNRESET on Windows. This happens with the
+ # real emulator because it just calls exit() without closing
+ # the socket or calling shutdown(SD_SEND). At process
+ # termination, Windows sends a TCP RST segment for every
+ # open socket that shutdown(SD_SEND) wasn't used on.
+ self._reset_socket_on_close(conn)
+
+ # Wait for adb to finish, so we can check return code.
+ p.communicate()
+
+ # If this fails, adb probably isn't ignoring WSAECONNRESET when
+ # reading the response from the adb emu kill command (on Windows).
+ self.assertEqual(0, p.returncode)
+
+
def main():
random.seed(0)
if len(adb.get_devices()) > 0:
diff --git a/base/include/base/parseint.h b/base/include/base/parseint.h
index 9ecbfbc..0543795 100644
--- a/base/include/base/parseint.h
+++ b/base/include/base/parseint.h
@@ -31,9 +31,10 @@
template <typename T>
bool ParseUint(const char* s, T* out,
T max = std::numeric_limits<T>::max()) {
+ int base = (s[0] == '0' && s[1] == 'x') ? 16 : 10;
errno = 0;
char* end;
- unsigned long long int result = strtoull(s, &end, 10);
+ unsigned long long int result = strtoull(s, &end, base);
if (errno != 0 || s == end || *end != '\0') {
return false;
}
@@ -52,9 +53,10 @@
bool ParseInt(const char* s, T* out,
T min = std::numeric_limits<T>::min(),
T max = std::numeric_limits<T>::max()) {
+ int base = (s[0] == '0' && s[1] == 'x') ? 16 : 10;
errno = 0;
char* end;
- long long int result = strtoll(s, &end, 10);
+ long long int result = strtoll(s, &end, base);
if (errno != 0 || s == end || *end != '\0') {
return false;
}
diff --git a/base/include/base/strings.h b/base/include/base/strings.h
index 638f845..20da144 100644
--- a/base/include/base/strings.h
+++ b/base/include/base/strings.h
@@ -36,8 +36,8 @@
std::string Trim(const std::string& s);
// Joins a container of things into a single string, using the given separator.
-template <typename ContainerT>
-std::string Join(const ContainerT& things, char separator) {
+template <typename ContainerT, typename SeparatorT>
+std::string Join(const ContainerT& things, SeparatorT separator) {
if (things.empty()) {
return "";
}
@@ -53,6 +53,8 @@
// We instantiate the common cases in strings.cpp.
extern template std::string Join(const std::vector<std::string>&, char);
extern template std::string Join(const std::vector<const char*>&, char);
+extern template std::string Join(const std::vector<std::string>&, const std::string&);
+extern template std::string Join(const std::vector<const char*>&, const std::string&);
// Tests whether 's' starts with 'prefix'.
bool StartsWith(const std::string& s, const char* prefix);
diff --git a/base/parseint_test.cpp b/base/parseint_test.cpp
index e19c6e3..8a11d29 100644
--- a/base/parseint_test.cpp
+++ b/base/parseint_test.cpp
@@ -66,3 +66,13 @@
ASSERT_TRUE(android::base::ParseUint("0123", &u));
ASSERT_EQ(123u, u);
}
+
+TEST(parseint, explicit_hex) {
+ int i;
+ ASSERT_TRUE(android::base::ParseInt("0x123", &i));
+ ASSERT_EQ(0x123, i);
+
+ unsigned int u;
+ ASSERT_TRUE(android::base::ParseUint("0x123", &u));
+ ASSERT_EQ(0x123u, u);
+}
diff --git a/base/strings.cpp b/base/strings.cpp
index bac983b..d687e3c 100644
--- a/base/strings.cpp
+++ b/base/strings.cpp
@@ -83,6 +83,8 @@
// aid compile time and binary size.
template std::string Join(const std::vector<std::string>&, char);
template std::string Join(const std::vector<const char*>&, char);
+template std::string Join(const std::vector<std::string>&, const std::string&);
+template std::string Join(const std::vector<const char*>&, const std::string&);
bool StartsWith(const std::string& s, const char* prefix) {
return s.compare(0, strlen(prefix), prefix) == 0;
diff --git a/fastboot/engine.cpp b/fastboot/engine.cpp
index a0e990a..44796d7 100644
--- a/fastboot/engine.cpp
+++ b/fastboot/engine.cpp
@@ -75,41 +75,17 @@
-int fb_getvar(struct usb_handle *usb, char *response, const char *fmt, ...)
-{
- char cmd[CMD_SIZE] = "getvar:";
- int getvar_len = strlen(cmd);
- va_list args;
+bool fb_getvar(usb_handle* usb, const std::string& key, std::string* value) {
+ std::string cmd = "getvar:";
+ cmd += key;
- response[FB_RESPONSE_SZ] = '\0';
- va_start(args, fmt);
- vsnprintf(cmd + getvar_len, sizeof(cmd) - getvar_len, fmt, args);
- va_end(args);
- cmd[CMD_SIZE - 1] = '\0';
- return fb_command_response(usb, cmd, response);
-}
-
-
-/* Return true if this partition is supported by the fastboot format command.
- * It is also used to determine if we should first erase a partition before
- * flashing it with an ext4 filesystem. See needs_erase()
- *
- * Not all devices report the filesystem type, so don't report any errors,
- * just return false.
- */
-int fb_format_supported(usb_handle *usb, const char *partition, const char *type_override)
-{
- char fs_type[FB_RESPONSE_SZ + 1] = {0,};
- int status;
-
- if (type_override) {
- return !!fs_get_generator(type_override);
+ char buf[FB_RESPONSE_SZ + 1];
+ memset(buf, 0, sizeof(buf));
+ if (fb_command_response(usb, cmd.c_str(), buf)) {
+ return false;
}
- status = fb_getvar(usb, fs_type, "partition-type:%s", partition);
- if (status) {
- return 0;
- }
- return !!fs_get_generator(fs_type);
+ *value = buf;
+ return true;
}
static int cb_default(Action* a, int status, const char* resp) {
@@ -394,8 +370,3 @@
fprintf(stderr,"finished. total time: %.3fs\n", (now() - start));
return status;
}
-
-int fb_queue_is_empty(void)
-{
- return (action_list == nullptr);
-}
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 0901310..15dd67d 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -43,6 +43,8 @@
#include <sys/types.h>
#include <unistd.h>
+#include <base/parseint.h>
+#include <base/strings.h>
#include <sparse/sparse.h>
#include <ziparchive/zip_archive.h>
@@ -572,25 +574,28 @@
return out_s;
}
-static int64_t get_target_sparse_limit(struct usb_handle *usb)
-{
- int64_t limit = 0;
- char response[FB_RESPONSE_SZ + 1];
- int status = fb_getvar(usb, response, "max-download-size");
-
- if (!status) {
- limit = strtoul(response, nullptr, 0);
- if (limit > 0) {
- fprintf(stderr, "target reported max download size of %" PRId64 " bytes\n",
- limit);
- }
+static int64_t get_target_sparse_limit(usb_handle* usb) {
+ std::string max_download_size;
+ if (!fb_getvar(usb, "max-download-size", &max_download_size) || max_download_size.empty()) {
+ fprintf(stderr, "target didn't report max-download-size\n");
+ return 0;
}
+ // Some bootloaders (angler, for example) send spurious whitespace too.
+ max_download_size = android::base::Trim(max_download_size);
+
+ uint64_t limit;
+ if (!android::base::ParseUint(max_download_size.c_str(), &limit)) {
+ fprintf(stderr, "couldn't parse max-download-size '%s'\n", max_download_size.c_str());
+ return 0;
+ }
+ if (limit > 0) {
+ fprintf(stderr, "target reported max download size of %" PRId64 " bytes\n", limit);
+ }
return limit;
}
-static int64_t get_sparse_limit(struct usb_handle *usb, int64_t size)
-{
+static int64_t get_sparse_limit(usb_handle* usb, int64_t size) {
int64_t limit;
if (sparse_limit == 0) {
@@ -615,16 +620,15 @@
return 0;
}
-/* Until we get lazy inode table init working in make_ext4fs, we need to
- * erase partitions of type ext4 before flashing a filesystem so no stale
- * inodes are left lying around. Otherwise, e2fsck gets very upset.
- */
-static int needs_erase(usb_handle* usb, const char *part)
-{
- /* The function fb_format_supported() currently returns the value
- * we want, so just call it.
- */
- return fb_format_supported(usb, part, nullptr);
+// Until we get lazy inode table init working in make_ext4fs, we need to
+// erase partitions of type ext4 before flashing a filesystem so no stale
+// inodes are left lying around. Otherwise, e2fsck gets very upset.
+static bool needs_erase(usb_handle* usb, const char* partition) {
+ std::string partition_type;
+ if (!fb_getvar(usb, 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) {
@@ -878,87 +882,88 @@
}
static void fb_perform_format(usb_handle* usb,
- const char *partition, int skip_if_not_supported,
- const char *type_override, const char *size_override) {
- char pTypeBuff[FB_RESPONSE_SZ + 1], pSizeBuff[FB_RESPONSE_SZ + 1];
- char *pType = pTypeBuff;
- char *pSize = pSizeBuff;
- unsigned int limit = INT_MAX;
+ const char* partition, int skip_if_not_supported,
+ const char* type_override, const char* size_override) {
+ std::string partition_type, partition_size;
+
struct fastboot_buffer buf;
- const char *errMsg = nullptr;
- const struct fs_generator *gen;
- uint64_t pSz;
- int status;
+ const char* errMsg = nullptr;
+ const struct fs_generator* gen = nullptr;
int fd;
- if (target_sparse_limit > 0 && target_sparse_limit < limit)
+ unsigned int limit = INT_MAX;
+ if (target_sparse_limit > 0 && target_sparse_limit < limit) {
limit = target_sparse_limit;
- if (sparse_limit > 0 && sparse_limit < limit)
+ }
+ if (sparse_limit > 0 && sparse_limit < limit) {
limit = sparse_limit;
+ }
- status = fb_getvar(usb, pType, "partition-type:%s", partition);
- if (status) {
+ if (!fb_getvar(usb, std::string("partition-type:") + partition, &partition_type)) {
errMsg = "Can't determine partition type.\n";
goto failed;
}
if (type_override) {
- if (strcmp(type_override, pType)) {
- fprintf(stderr,
- "Warning: %s type is %s, but %s was requested for formating.\n",
- partition, pType, type_override);
+ if (partition_type != type_override) {
+ fprintf(stderr, "Warning: %s type is %s, but %s was requested for formatting.\n",
+ partition, partition_type.c_str(), type_override);
}
- pType = (char *)type_override;
+ partition_type = type_override;
}
- status = fb_getvar(usb, pSize, "partition-size:%s", partition);
- if (status) {
+ if (!fb_getvar(usb, std::string("partition-size:") + partition, &partition_size)) {
errMsg = "Unable to get partition size\n";
goto failed;
}
if (size_override) {
- if (strcmp(size_override, pSize)) {
- fprintf(stderr,
- "Warning: %s size is %s, but %s was requested for formating.\n",
- partition, pSize, size_override);
+ if (partition_size != size_override) {
+ fprintf(stderr, "Warning: %s size is %s, but %s was requested for formatting.\n",
+ partition, partition_size.c_str(), size_override);
}
- pSize = (char *)size_override;
+ partition_size = size_override;
}
- gen = fs_get_generator(pType);
+ gen = fs_get_generator(partition_type);
if (!gen) {
if (skip_if_not_supported) {
fprintf(stderr, "Erase successful, but not automatically formatting.\n");
- fprintf(stderr, "File system type %s not supported.\n", pType);
+ fprintf(stderr, "File system type %s not supported.\n", partition_type.c_str());
return;
}
- fprintf(stderr, "Formatting is not supported for filesystem with type '%s'.\n", pType);
+ fprintf(stderr, "Formatting is not supported for file system with type '%s'.\n",
+ partition_type.c_str());
return;
}
- pSz = strtoll(pSize, (char **)nullptr, 16);
+ // 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());
+ return;
+ }
fd = fileno(tmpfile());
- if (fs_generator_generate(gen, fd, pSz)) {
+ if (fs_generator_generate(gen, fd, size)) {
+ fprintf(stderr, "Cannot generate image: %s\n", strerror(errno));
close(fd);
- fprintf(stderr, "Cannot generate image.\n");
return;
}
if (load_buf_fd(usb, fd, &buf)) {
- fprintf(stderr, "Cannot read image.\n");
+ fprintf(stderr, "Cannot read image: %s\n", strerror(errno));
close(fd);
return;
}
flash_buf(partition, &buf);
-
return;
-
failed:
if (skip_if_not_supported) {
fprintf(stderr, "Erase successful, but not automatically formatting.\n");
- if (errMsg)
- fprintf(stderr, "%s", errMsg);
+ if (errMsg) fprintf(stderr, "%s", errMsg);
}
fprintf(stderr,"FAILED (%s)\n", fb_get_error());
}
@@ -971,8 +976,6 @@
bool erase_first = true;
void *data;
int64_t sz;
- int status;
- int c;
int longindex;
const struct option longopts[] = {
@@ -990,7 +993,7 @@
serial = getenv("ANDROID_SERIAL");
while (1) {
- c = getopt_long(argc, argv, "wub:k:n:r:t:s:S:lp:c:i:m:h", longopts, &longindex);
+ int c = getopt_long(argc, argv, "wub:k:n:r:t:s:S:lp:c:i:m:h", longopts, &longindex);
if (c < 0) {
break;
}
@@ -1087,15 +1090,18 @@
usb_handle* usb = open_device();
while (argc > 0) {
- if(!strcmp(*argv, "getvar")) {
+ if (!strcmp(*argv, "getvar")) {
require(2);
fb_queue_display(argv[1], argv[1]);
skip(2);
} else if(!strcmp(*argv, "erase")) {
require(2);
- if (fb_format_supported(usb, argv[1], nullptr)) {
- fprintf(stderr, "******** Did you mean to fastboot format this partition?\n");
+ std::string partition_type;
+ if (fb_getvar(usb, std::string("partition-type:") + argv[1], &partition_type) &&
+ fs_get_generator(partition_type) != nullptr) {
+ fprintf(stderr, "******** Did you mean to fastboot format this %s partition?\n",
+ partition_type.c_str());
}
fb_queue_erase(argv[1]);
@@ -1249,10 +1255,16 @@
}
if (wants_wipe) {
+ fprintf(stderr, "wiping userdata...\n");
fb_queue_erase("userdata");
fb_perform_format(usb, "userdata", 1, nullptr, nullptr);
- fb_queue_erase("cache");
- fb_perform_format(usb, "cache", 1, nullptr, nullptr);
+
+ std::string cache_type;
+ if (fb_getvar(usb, "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);
+ }
}
if (wants_reboot) {
fb_queue_reboot();
@@ -1262,9 +1274,5 @@
fb_queue_wait_for_disconnect();
}
- if (fb_queue_is_empty())
- return 0;
-
- status = fb_execute_queue(usb);
- return (status) ? 1 : 0;
+ return fb_execute_queue(usb) ? EXIT_FAILURE : EXIT_SUCCESS;
}
diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h
index 091a70f..9e33531 100644
--- a/fastboot/fastboot.h
+++ b/fastboot/fastboot.h
@@ -32,6 +32,8 @@
#include <inttypes.h>
#include <stdlib.h>
+#include <string>
+
#include "usb.h"
struct sparse_file;
@@ -47,8 +49,7 @@
#define FB_RESPONSE_SZ 64
/* engine.c - high level command queue engine */
-int fb_getvar(struct usb_handle *usb, char *response, const char *fmt, ...);
-int fb_format_supported(usb_handle *usb, const char *partition, const char *type_override);
+bool fb_getvar(usb_handle* usb, 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 +64,6 @@
void fb_queue_notice(const char *notice);
void fb_queue_wait_for_disconnect(void);
int fb_execute_queue(usb_handle *usb);
-int fb_queue_is_empty(void);
/* util stuff */
double now();
diff --git a/fastboot/fs.cpp b/fastboot/fs.cpp
index c58a505..90d8474 100644
--- a/fastboot/fs.cpp
+++ b/fastboot/fs.cpp
@@ -39,9 +39,9 @@
#endif
};
-const struct fs_generator* fs_get_generator(const char* fs_type) {
+const struct fs_generator* fs_get_generator(const std::string& fs_type) {
for (size_t i = 0; i < sizeof(generators) / sizeof(*generators); i++) {
- if (strcmp(generators[i].fs_type, fs_type) == 0) {
+ if (fs_type == generators[i].fs_type) {
return generators + i;
}
}
diff --git a/fastboot/fs.h b/fastboot/fs.h
index 8444081..289488b 100644
--- a/fastboot/fs.h
+++ b/fastboot/fs.h
@@ -5,8 +5,7 @@
struct fs_generator;
-const struct fs_generator* fs_get_generator(const char *fs_type);
+const struct fs_generator* fs_get_generator(const std::string& fs_type);
int fs_generator_generate(const struct fs_generator* gen, int tmpFileNo, long long partSize);
#endif
-
diff --git a/fs_mgr/fs_mgr_verity.cpp b/fs_mgr/fs_mgr_verity.cpp
index c358982..6983b72 100644
--- a/fs_mgr/fs_mgr_verity.cpp
+++ b/fs_mgr/fs_mgr_verity.cpp
@@ -765,8 +765,9 @@
continue;
}
- if (current == VERITY_MODE_LOGGING) {
+ if (current != VERITY_MODE_DEFAULT) {
*mode = current;
+ break;
}
}
@@ -784,7 +785,6 @@
{
alignas(dm_ioctl) char buffer[DM_BUF_SIZE];
bool use_state = true;
- bool use_state_for_device = true;
char fstab_filename[PROPERTY_VALUE_MAX + sizeof(FSTAB_PREFIX)];
char *mount_point;
char propbuf[PROPERTY_VALUE_MAX];
@@ -801,12 +801,13 @@
property_get("ro.boot.veritymode", propbuf, "");
if (*propbuf != '\0') {
- if (fs_mgr_load_verity_state(&mode) == -1) {
- return -1;
- }
use_state = false; /* state is kept by the bootloader */
}
+ if (fs_mgr_load_verity_state(&mode) == -1) {
+ return -1;
+ }
+
fd = TEMP_FAILURE_RETRY(open("/dev/device-mapper", O_RDWR | O_CLOEXEC));
if (fd == -1) {
@@ -829,15 +830,6 @@
continue;
}
- use_state_for_device = use_state;
-
- if (use_state) {
- if (get_verity_state_offset(&fstab->recs[i], &offset) < 0 ||
- read_verity_state(fstab->recs[i].verity_loc, offset, &mode) < 0) {
- use_state_for_device = false;
- }
- }
-
mount_point = basename(fstab->recs[i].mount_point);
verity_ioctl_init(io, mount_point, 0);
@@ -849,7 +841,7 @@
status = &buffer[io->data_start + sizeof(struct dm_target_spec)];
- if (use_state_for_device && *status == 'C') {
+ if (use_state && *status == 'C') {
if (write_verity_state(fstab->recs[i].verity_loc, offset,
VERITY_MODE_LOGGING) < 0) {
continue;
diff --git a/include/log/logger.h b/include/log/logger.h
index f030dab..c795253 100644
--- a/include/log/logger.h
+++ b/include/log/logger.h
@@ -183,6 +183,8 @@
pid_t pid);
#define android_logger_list_close android_logger_list_free
+char android_log_timestamp();
+
/*
* log_id_t helpers
*/
diff --git a/include/log/logprint.h b/include/log/logprint.h
index 26b1ee5..204b3f2 100644
--- a/include/log/logprint.h
+++ b/include/log/logprint.h
@@ -42,6 +42,8 @@
FORMAT_MODIFIER_PRINTABLE, /* converts non-printable to printable escapes */
FORMAT_MODIFIER_YEAR, /* Adds year to date */
FORMAT_MODIFIER_ZONE, /* Adds zone to date */
+ FORMAT_MODIFIER_EPOCH, /* Print time as seconds since Jan 1 1970 */
+ FORMAT_MODIFIER_MONOTONIC, /* Print cpu time as seconds since start */
} AndroidLogPrintFormat;
typedef struct AndroidLogFormat_t AndroidLogFormat;
diff --git a/include/utils/StrongPointer.h b/include/utils/StrongPointer.h
index aba9577..50fde35 100644
--- a/include/utils/StrongPointer.h
+++ b/include/utils/StrongPointer.h
@@ -62,8 +62,10 @@
sp(T* other);
sp(const sp<T>& other);
+ sp(sp<T>&& other);
template<typename U> sp(U* other);
template<typename U> sp(const sp<U>& other);
+ template<typename U> sp(sp<U>&& other);
~sp();
@@ -71,8 +73,10 @@
sp& operator = (T* other);
sp& operator = (const sp<T>& other);
+ sp& operator = (sp<T>&& other);
template<typename U> sp& operator = (const sp<U>& other);
+ template<typename U> sp& operator = (sp<U>&& other);
template<typename U> sp& operator = (U* other);
//! Special optimization for use by ProcessState (and nobody else).
@@ -123,6 +127,12 @@
m_ptr->incStrong(this);
}
+template<typename T>
+sp<T>::sp(sp<T>&& other)
+ : m_ptr(other.m_ptr) {
+ other.m_ptr = nullptr;
+}
+
template<typename T> template<typename U>
sp<T>::sp(U* other)
: m_ptr(other) {
@@ -137,6 +147,12 @@
m_ptr->incStrong(this);
}
+template<typename T> template<typename U>
+sp<T>::sp(sp<U>&& other)
+ : m_ptr(other.m_ptr) {
+ other.m_ptr = nullptr;
+}
+
template<typename T>
sp<T>::~sp() {
if (m_ptr)
@@ -155,6 +171,15 @@
}
template<typename T>
+sp<T>& sp<T>::operator =(sp<T>&& other) {
+ if (m_ptr)
+ m_ptr->decStrong(this);
+ m_ptr = other.m_ptr;
+ other.m_ptr = nullptr;
+ return *this;
+}
+
+template<typename T>
sp<T>& sp<T>::operator =(T* other) {
if (other)
other->incStrong(this);
@@ -176,6 +201,15 @@
}
template<typename T> template<typename U>
+sp<T>& sp<T>::operator =(sp<U>&& other) {
+ if (m_ptr)
+ m_ptr->decStrong(this);
+ m_ptr = other.m_ptr;
+ other.m_ptr = nullptr;
+ return *this;
+}
+
+template<typename T> template<typename U>
sp<T>& sp<T>::operator =(U* other) {
if (other)
((T*) other)->incStrong(this);
diff --git a/include/ziparchive/zip_writer.h b/include/ziparchive/zip_writer.h
index 9ee4abe..d996c4a 100644
--- a/include/ziparchive/zip_writer.h
+++ b/include/ziparchive/zip_writer.h
@@ -21,9 +21,11 @@
#include <utils/Compat.h>
#include <cstdio>
-#include <string>
#include <ctime>
+#include <memory>
+#include <string>
#include <vector>
+#include <zlib.h>
/**
* Writes a Zip file via a stateful interface.
@@ -112,8 +114,6 @@
private:
DISALLOW_COPY_AND_ASSIGN(ZipWriter);
- int32_t HandleError(int32_t error_code);
-
struct FileInfo {
std::string path;
uint16_t compression_method;
@@ -125,6 +125,12 @@
uint32_t local_file_header_offset;
};
+ int32_t HandleError(int32_t error_code);
+ int32_t PrepareDeflate();
+ int32_t StoreBytes(FileInfo* file, const void* data, size_t len);
+ int32_t CompressBytes(FileInfo* file, const void* data, size_t len);
+ int32_t FlushCompressedBytes(FileInfo* file);
+
enum class State {
kWritingZip,
kWritingEntry,
@@ -136,6 +142,9 @@
off64_t current_offset_;
State state_;
std::vector<FileInfo> files_;
+
+ std::unique_ptr<z_stream, void(*)(z_stream*)> z_stream_;
+ std::vector<uint8_t> buffer_;
};
#endif /* LIBZIPARCHIVE_ZIPWRITER_H_ */
diff --git a/libcutils/fs_config.c b/libcutils/fs_config.c
index 970b386..2a178aa 100644
--- a/libcutils/fs_config.c
+++ b/libcutils/fs_config.c
@@ -125,6 +125,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/*" },
+ { 00750, AID_ROOT, AID_SHELL, 0, "data/nativetest/*" },
+ { 00750, AID_ROOT, AID_SHELL, 0, "data/nativetest64/*" },
/* the following four files are INTENTIONALLY set-uid, but they
* are NOT included on user builds. */
diff --git a/liblog/log_is_loggable.c b/liblog/log_is_loggable.c
index cd85ff6..814d96d 100644
--- a/liblog/log_is_loggable.c
+++ b/liblog/log_is_loggable.c
@@ -176,3 +176,28 @@
int logLevel = __android_log_level(tag, def);
return logLevel >= 0 && prio >= logLevel;
}
+
+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);
+
+ current_serial = __system_property_area_serial();
+ if (current_serial != serial) {
+ refresh_cache(&r_time_cache, "ro.logd.timestamp");
+ refresh_cache(&p_time_cache, "persist.logd.timestamp");
+ serial = current_serial;
+ }
+ if (!(retval = p_time_cache.c)) {
+ retval = r_time_cache.c;
+ }
+
+ pthread_mutex_unlock(&lock);
+
+ return tolower(retval ?: 'r');
+}
diff --git a/liblog/logprint.c b/liblog/logprint.c
index b6dba2e..0ea2269 100644
--- a/liblog/logprint.c
+++ b/liblog/logprint.c
@@ -29,9 +29,13 @@
#include <inttypes.h>
#include <sys/param.h>
+#include <cutils/list.h>
#include <log/logd.h>
#include <log/logprint.h>
+#define MS_PER_NSEC 1000000
+#define US_PER_NSEC 1000
+
/* open coded fragment, prevent circular dependencies */
#define WEAK static
@@ -50,6 +54,8 @@
bool printable_output;
bool year_output;
bool zone_output;
+ bool epoch_output;
+ bool monotonic_output;
};
/*
@@ -196,10 +202,14 @@
p_ret->printable_output = false;
p_ret->year_output = false;
p_ret->zone_output = false;
+ p_ret->epoch_output = false;
+ p_ret->monotonic_output = false;
return p_ret;
}
+static list_declare(convertHead);
+
void android_log_format_free(AndroidLogFormat *p_format)
{
FilterInfo *p_info, *p_info_old;
@@ -214,10 +224,15 @@
}
free(p_format);
+
+ /* Free conversion resource, can always be reconstructed */
+ while (!list_empty(&convertHead)) {
+ struct listnode *node = list_head(&convertHead);
+ list_remove(node);
+ free(node);
+ }
}
-
-
int android_log_setPrintFormat(AndroidLogFormat *p_format,
AndroidLogPrintFormat format)
{
@@ -237,6 +252,12 @@
case FORMAT_MODIFIER_ZONE:
p_format->zone_output = !p_format->zone_output;
return 0;
+ case FORMAT_MODIFIER_EPOCH:
+ p_format->epoch_output = true;
+ return 0;
+ case FORMAT_MODIFIER_MONOTONIC:
+ p_format->monotonic_output = true;
+ return 0;
default:
break;
}
@@ -267,6 +288,8 @@
else if (strcmp(formatString, "printable") == 0) format = FORMAT_MODIFIER_PRINTABLE;
else if (strcmp(formatString, "year") == 0) format = FORMAT_MODIFIER_YEAR;
else if (strcmp(formatString, "zone") == 0) format = FORMAT_MODIFIER_ZONE;
+ else if (strcmp(formatString, "epoch") == 0) format = FORMAT_MODIFIER_EPOCH;
+ else if (strcmp(formatString, "monotonic") == 0) format = FORMAT_MODIFIER_MONOTONIC;
else {
extern char *tzname[2];
static const char gmt[] = "GMT";
@@ -913,6 +936,285 @@
return p - begin;
}
+char *readSeconds(char *e, struct timespec *t)
+{
+ unsigned long multiplier;
+ char *p;
+ t->tv_sec = strtoul(e, &p, 10);
+ if (*p != '.') {
+ return NULL;
+ }
+ t->tv_nsec = 0;
+ multiplier = NS_PER_SEC;
+ while (isdigit(*++p) && (multiplier /= 10)) {
+ t->tv_nsec += (*p - '0') * multiplier;
+ }
+ return p;
+}
+
+static struct timespec *sumTimespec(struct timespec *left,
+ struct timespec *right)
+{
+ left->tv_nsec += right->tv_nsec;
+ left->tv_sec += right->tv_sec;
+ if (left->tv_nsec >= (long)NS_PER_SEC) {
+ left->tv_nsec -= NS_PER_SEC;
+ left->tv_sec += 1;
+ }
+ return left;
+}
+
+static struct timespec *subTimespec(struct timespec *result,
+ struct timespec *left,
+ struct timespec *right)
+{
+ result->tv_nsec = left->tv_nsec - right->tv_nsec;
+ result->tv_sec = left->tv_sec - right->tv_sec;
+ if (result->tv_nsec < 0) {
+ result->tv_nsec += NS_PER_SEC;
+ result->tv_sec -= 1;
+ }
+ return result;
+}
+
+static long long nsecTimespec(struct timespec *now)
+{
+ return (long long)now->tv_sec * NS_PER_SEC + now->tv_nsec;
+}
+
+static void convertMonotonic(struct timespec *result,
+ const AndroidLogEntry *entry)
+{
+ struct listnode *node;
+ struct conversionList {
+ struct listnode node; /* first */
+ struct timespec time;
+ struct timespec convert;
+ } *list, *next;
+ struct timespec time, convert;
+
+ /* If we do not have a conversion list, build one up */
+ if (list_empty(&convertHead)) {
+ bool suspended_pending = false;
+ struct timespec suspended_monotonic = { 0, 0 };
+ struct timespec suspended_diff = { 0, 0 };
+
+ /*
+ * Read dmesg for _some_ synchronization markers and insert
+ * Anything in the Android Logger before the dmesg logging span will
+ * be highly suspect regarding the monotonic time calculations.
+ */
+ FILE *p = popen("/system/bin/dmesg", "r");
+ if (p) {
+ char *line = NULL;
+ size_t len = 0;
+ while (getline(&line, &len, p) > 0) {
+ static const char suspend[] = "PM: suspend entry ";
+ static const char resume[] = "PM: suspend exit ";
+ static const char healthd[] = "healthd";
+ static const char battery[] = ": battery ";
+ static const char suspended[] = "Suspended for ";
+ struct timespec monotonic;
+ struct tm tm;
+ char *cp, *e = line;
+ bool add_entry = true;
+
+ if (*e == '<') {
+ while (*e && (*e != '>')) {
+ ++e;
+ }
+ if (*e != '>') {
+ continue;
+ }
+ }
+ if (*e != '[') {
+ continue;
+ }
+ while (*++e == ' ') {
+ ;
+ }
+ e = readSeconds(e, &monotonic);
+ if (!e || (*e != ']')) {
+ continue;
+ }
+
+ if ((e = strstr(e, suspend))) {
+ e += sizeof(suspend) - 1;
+ } else if ((e = strstr(line, resume))) {
+ e += sizeof(resume) - 1;
+ } else if (((e = strstr(line, healthd)))
+ && ((e = strstr(e + sizeof(healthd) - 1, battery)))) {
+ /* NB: healthd is roughly 150us late, worth the price to
+ * deal with ntp-induced or hardware clock drift. */
+ e += sizeof(battery) - 1;
+ } else if ((e = strstr(line, suspended))) {
+ e += sizeof(suspended) - 1;
+ e = readSeconds(e, &time);
+ if (!e) {
+ continue;
+ }
+ add_entry = false;
+ suspended_pending = true;
+ suspended_monotonic = monotonic;
+ suspended_diff = time;
+ } else {
+ continue;
+ }
+ if (add_entry) {
+ /* look for "????-??-?? ??:??:??.????????? UTC" */
+ cp = strstr(e, " UTC");
+ if (!cp || ((cp - e) < 29) || (cp[-10] != '.')) {
+ continue;
+ }
+ e = cp - 29;
+ cp = readSeconds(cp - 10, &time);
+ if (!cp) {
+ continue;
+ }
+ cp = strptime(e, "%Y-%m-%d %H:%M:%S.", &tm);
+ if (!cp) {
+ continue;
+ }
+ cp = getenv(tz);
+ if (cp) {
+ cp = strdup(cp);
+ }
+ setenv(tz, utc, 1);
+ time.tv_sec = mktime(&tm);
+ if (cp) {
+ setenv(tz, cp, 1);
+ free(cp);
+ } else {
+ unsetenv(tz);
+ }
+ list = calloc(1, sizeof(struct conversionList));
+ list_init(&list->node);
+ list->time = time;
+ subTimespec(&list->convert, &time, &monotonic);
+ list_add_tail(&convertHead, &list->node);
+ }
+ if (suspended_pending && !list_empty(&convertHead)) {
+ list = node_to_item(list_tail(&convertHead),
+ struct conversionList, node);
+ if (subTimespec(&time,
+ subTimespec(&time,
+ &list->time,
+ &list->convert),
+ &suspended_monotonic)->tv_sec > 0) {
+ /* resume, what is convert factor before? */
+ subTimespec(&convert, &list->convert, &suspended_diff);
+ } else {
+ /* suspend */
+ convert = list->convert;
+ }
+ time = suspended_monotonic;
+ sumTimespec(&time, &convert);
+ /* breakpoint just before sleep */
+ list = calloc(1, sizeof(struct conversionList));
+ list_init(&list->node);
+ list->time = time;
+ list->convert = convert;
+ list_add_tail(&convertHead, &list->node);
+ /* breakpoint just after sleep */
+ list = calloc(1, sizeof(struct conversionList));
+ list_init(&list->node);
+ list->time = time;
+ sumTimespec(&list->time, &suspended_diff);
+ list->convert = convert;
+ sumTimespec(&list->convert, &suspended_diff);
+ list_add_tail(&convertHead, &list->node);
+ suspended_pending = false;
+ }
+ }
+ pclose(p);
+ }
+ /* last entry is our current time conversion */
+ list = calloc(1, sizeof(struct conversionList));
+ list_init(&list->node);
+ clock_gettime(CLOCK_REALTIME, &list->time);
+ clock_gettime(CLOCK_MONOTONIC, &convert);
+ clock_gettime(CLOCK_MONOTONIC, &time);
+ /* Correct for instant clock_gettime latency (syscall or ~30ns) */
+ subTimespec(&time, &convert, subTimespec(&time, &time, &convert));
+ /* Calculate conversion factor */
+ subTimespec(&list->convert, &list->time, &time);
+ list_add_tail(&convertHead, &list->node);
+ if (suspended_pending) {
+ /* manufacture a suspend @ point before */
+ subTimespec(&convert, &list->convert, &suspended_diff);
+ time = suspended_monotonic;
+ sumTimespec(&time, &convert);
+ /* breakpoint just after sleep */
+ list = calloc(1, sizeof(struct conversionList));
+ list_init(&list->node);
+ list->time = time;
+ sumTimespec(&list->time, &suspended_diff);
+ list->convert = convert;
+ sumTimespec(&list->convert, &suspended_diff);
+ list_add_head(&convertHead, &list->node);
+ /* breakpoint just before sleep */
+ list = calloc(1, sizeof(struct conversionList));
+ list_init(&list->node);
+ list->time = time;
+ list->convert = convert;
+ list_add_head(&convertHead, &list->node);
+ }
+ }
+
+ /* Find the breakpoint in the conversion list */
+ list = node_to_item(list_head(&convertHead), struct conversionList, node);
+ next = NULL;
+ list_for_each(node, &convertHead) {
+ next = node_to_item(node, struct conversionList, node);
+ if (entry->tv_sec < next->time.tv_sec) {
+ break;
+ } else if (entry->tv_sec == next->time.tv_sec) {
+ if (entry->tv_nsec < next->time.tv_nsec) {
+ break;
+ }
+ }
+ list = next;
+ }
+
+ /* blend time from one breakpoint to the next */
+ convert = list->convert;
+ if (next) {
+ unsigned long long total, run;
+
+ total = nsecTimespec(subTimespec(&time, &next->time, &list->time));
+ time.tv_sec = entry->tv_sec;
+ time.tv_nsec = entry->tv_nsec;
+ run = nsecTimespec(subTimespec(&time, &time, &list->time));
+ if (run < total) {
+ long long crun;
+
+ float f = nsecTimespec(subTimespec(&time, &next->convert, &convert));
+ f *= run;
+ f /= total;
+ crun = f;
+ convert.tv_sec += crun / (long long)NS_PER_SEC;
+ if (crun < 0) {
+ convert.tv_nsec -= (-crun) % NS_PER_SEC;
+ if (convert.tv_nsec < 0) {
+ convert.tv_nsec += NS_PER_SEC;
+ convert.tv_sec -= 1;
+ }
+ } else {
+ convert.tv_nsec += crun % NS_PER_SEC;
+ if (convert.tv_nsec >= (long)NS_PER_SEC) {
+ convert.tv_nsec -= NS_PER_SEC;
+ convert.tv_sec += 1;
+ }
+ }
+ }
+ }
+
+ /* Apply the correction factor */
+ result->tv_sec = entry->tv_sec;
+ result->tv_nsec = entry->tv_nsec;
+ subTimespec(result, result, &convert);
+}
+
/**
* Formats a log message into a buffer
*
@@ -936,7 +1238,9 @@
char prefixBuf[128], suffixBuf[128];
char priChar;
int prefixSuffixIsHeaderFooter = 0;
- char * ret = NULL;
+ char *ret = NULL;
+ time_t now;
+ unsigned long nsec;
priChar = filterPriToChar(entry->priority);
size_t prefixLen = 0, suffixLen = 0;
@@ -954,23 +1258,41 @@
* The caller may have affected the timezone environment, this is
* expected to be sensitive to that.
*/
+ now = entry->tv_sec;
+ nsec = entry->tv_nsec;
+ if (p_format->monotonic_output) {
+ struct timespec time;
+ convertMonotonic(&time, entry);
+ now = time.tv_sec;
+ nsec = time.tv_nsec;
+ }
+ if (now < 0) {
+ nsec = NS_PER_SEC - nsec;
+ }
+ if (p_format->epoch_output || p_format->monotonic_output) {
+ ptm = NULL;
+ snprintf(timeBuf, sizeof(timeBuf),
+ p_format->monotonic_output ? "%6lld" : "%19lld",
+ (long long)now);
+ } else {
#if !defined(_WIN32)
- ptm = localtime_r(&(entry->tv_sec), &tmBuf);
+ ptm = localtime_r(&now, &tmBuf);
#else
- ptm = localtime(&(entry->tv_sec));
+ ptm = localtime(&now);
#endif
- strftime(timeBuf, sizeof(timeBuf),
- &"%Y-%m-%d %H:%M:%S"[p_format->year_output ? 0 : 3],
- ptm);
+ strftime(timeBuf, sizeof(timeBuf),
+ &"%Y-%m-%d %H:%M:%S"[p_format->year_output ? 0 : 3],
+ ptm);
+ }
len = strlen(timeBuf);
if (p_format->usec_time_output) {
len += snprintf(timeBuf + len, sizeof(timeBuf) - len,
- ".%06ld", entry->tv_nsec / 1000);
+ ".%06ld", nsec / US_PER_NSEC);
} else {
len += snprintf(timeBuf + len, sizeof(timeBuf) - len,
- ".%03ld", entry->tv_nsec / 1000000);
+ ".%03ld", nsec / MS_PER_NSEC);
}
- if (p_format->zone_output) {
+ if (p_format->zone_output && ptm) {
strftime(timeBuf + len, sizeof(timeBuf) - len, " %z", ptm);
}
diff --git a/libutils/tests/Android.mk b/libutils/tests/Android.mk
index 47415ab..8f07f1a 100644
--- a/libutils/tests/Android.mk
+++ b/libutils/tests/Android.mk
@@ -27,6 +27,7 @@
Looper_test.cpp \
LruCache_test.cpp \
String8_test.cpp \
+ StrongPointer_test.cpp \
Unicode_test.cpp \
Vector_test.cpp \
diff --git a/libutils/tests/StrongPointer_test.cpp b/libutils/tests/StrongPointer_test.cpp
new file mode 100644
index 0000000..f46d6d1
--- /dev/null
+++ b/libutils/tests/StrongPointer_test.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <utils/StrongPointer.h>
+#include <utils/RefBase.h>
+
+using namespace android;
+
+class Foo : public LightRefBase<Foo> {
+public:
+ Foo(bool* deleted_check) : mDeleted(deleted_check) {
+ *mDeleted = false;
+ }
+
+ ~Foo() {
+ *mDeleted = true;
+ }
+private:
+ bool* mDeleted;
+};
+
+TEST(StrongPointer, move) {
+ bool isDeleted;
+ Foo* foo = new Foo(&isDeleted);
+ ASSERT_EQ(0, foo->getStrongCount());
+ ASSERT_FALSE(isDeleted) << "Already deleted...?";
+ sp<Foo> sp1(foo);
+ ASSERT_EQ(1, foo->getStrongCount());
+ {
+ sp<Foo> sp2 = std::move(sp1);
+ ASSERT_EQ(1, foo->getStrongCount()) << "std::move failed, incremented refcnt";
+ ASSERT_EQ(nullptr, sp1.get()) << "std::move failed, sp1 is still valid";
+ // The strong count isn't increasing, let's double check the old object
+ // is properly reset and doesn't early delete
+ sp1 = std::move(sp2);
+ }
+ ASSERT_FALSE(isDeleted) << "deleted too early! still has a reference!";
+ {
+ // Now let's double check it deletes on time
+ sp<Foo> sp2 = std::move(sp1);
+ }
+ ASSERT_TRUE(isDeleted) << "foo was leaked!";
+}
diff --git a/libziparchive/Android.mk b/libziparchive/Android.mk
index 8ff94d4..8a4921f 100644
--- a/libziparchive/Android.mk
+++ b/libziparchive/Android.mk
@@ -18,9 +18,12 @@
source_files := zip_archive.cc zip_writer.cc
test_files := zip_archive_test.cc zip_writer_test.cc entry_name_utils_test.cc
+# ZLIB_CONST turns on const for input buffers, which is pretty standard.
+common_c_flags := -Werror -Wall -DZLIB_CONST
+
# Incorrectly warns when C++11 empty brace {} initializer is used.
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61489
-common_cpp_flags := -Wno-missing-field-initializers
+common_cpp_flags := -Wold-style-cast -Wno-missing-field-initializers
include $(CLEAR_VARS)
LOCAL_CPP_EXTENSION := .cc
@@ -28,8 +31,8 @@
LOCAL_STATIC_LIBRARIES := libz
LOCAL_SHARED_LIBRARIES := libutils libbase
LOCAL_MODULE:= libziparchive
-LOCAL_CFLAGS := -Werror -Wall
-LOCAL_CPPFLAGS := -Wold-style-cast $(common_cpp_flags)
+LOCAL_CFLAGS := $(common_c_flags)
+LOCAL_CPPFLAGS := $(common_cpp_flags)
include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
@@ -37,7 +40,7 @@
LOCAL_SRC_FILES := ${source_files}
LOCAL_STATIC_LIBRARIES := libz libutils libbase
LOCAL_MODULE:= libziparchive-host
-LOCAL_CFLAGS := -Werror
+LOCAL_CFLAGS := $(common_c_flags)
LOCAL_CFLAGS_windows := -mno-ms-bitfields
LOCAL_CPPFLAGS := $(common_cpp_flags)
@@ -51,7 +54,7 @@
LOCAL_STATIC_LIBRARIES := libutils
LOCAL_SHARED_LIBRARIES := libz-host liblog libbase
LOCAL_MODULE:= libziparchive-host
-LOCAL_CFLAGS := -Werror
+LOCAL_CFLAGS := $(common_c_flags)
LOCAL_CPPFLAGS := $(common_cpp_flags)
LOCAL_MULTILIB := both
include $(BUILD_HOST_SHARED_LIBRARY)
@@ -60,7 +63,7 @@
include $(CLEAR_VARS)
LOCAL_MODULE := ziparchive-tests
LOCAL_CPP_EXTENSION := .cc
-LOCAL_CFLAGS := -Werror
+LOCAL_CFLAGS := $(common_c_flags)
LOCAL_CPPFLAGS := $(common_cpp_flags)
LOCAL_SRC_FILES := $(test_files)
LOCAL_SHARED_LIBRARIES := liblog libbase
@@ -70,7 +73,7 @@
include $(CLEAR_VARS)
LOCAL_MODULE := ziparchive-tests-host
LOCAL_CPP_EXTENSION := .cc
-LOCAL_CFLAGS := -Werror
+LOCAL_CFLAGS := $(common_c_flags)
LOCAL_CPPFLAGS := -Wno-unnamed-type-template-args $(common_cpp_flags)
LOCAL_SRC_FILES := $(test_files)
LOCAL_SHARED_LIBRARIES := libziparchive-host liblog libbase
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index f1e13a7..3d18f7c 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -588,7 +588,7 @@
// and other interesting attributes from the central directory. These
// will later be compared against values from the local file header.
data->method = cdr->compression_method;
- data->mod_time = cdr->last_mod_time;
+ data->mod_time = cdr->last_mod_date << 16 | cdr->last_mod_time;
data->crc32 = cdr->crc32;
data->compressed_length = cdr->compressed_size;
data->uncompressed_length = cdr->uncompressed_size;
diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc
index 9a3cdb4..32b1a38 100644
--- a/libziparchive/zip_archive_test.cc
+++ b/libziparchive/zip_archive_test.cc
@@ -272,6 +272,7 @@
ASSERT_EQ(static_cast<uint32_t>(17), data.uncompressed_length);
ASSERT_EQ(static_cast<uint32_t>(13), data.compressed_length);
ASSERT_EQ(0x950821c5, data.crc32);
+ ASSERT_EQ(static_cast<uint32_t>(0x438a8005), data.mod_time);
// An entry that doesn't exist. Should be a negative return code.
ZipString absent_name;
diff --git a/libziparchive/zip_writer.cc b/libziparchive/zip_writer.cc
index de75d1e..22a7c53 100644
--- a/libziparchive/zip_writer.cc
+++ b/libziparchive/zip_writer.cc
@@ -18,10 +18,13 @@
#include "zip_archive_common.h"
#include "ziparchive/zip_writer.h"
+#include <utils/Log.h>
+
#include <cassert>
#include <cstdio>
#include <memory>
#include <zlib.h>
+#define DEF_MEM_LEVEL 8 // normally in zutil.h?
/* Zip compression methods we support */
enum {
@@ -29,6 +32,9 @@
kCompressDeflated = 8, // standard deflate
};
+// Size of the output buffer used for compression.
+static const size_t kBufSize = 32768u;
+
// No error, operation completed successfully.
static const int32_t kNoError = 0;
@@ -41,10 +47,14 @@
// The zip entry name was invalid.
static const int32_t kInvalidEntryName = -3;
+// An error occurred in zlib.
+static const int32_t kZlibError = -4;
+
static const char* sErrorCodes[] = {
"Invalid state",
"IO error",
"Invalid entry name",
+ "Zlib error",
};
const char* ZipWriter::ErrorCodeString(int32_t error_code) {
@@ -54,13 +64,21 @@
return nullptr;
}
-ZipWriter::ZipWriter(FILE* f) : file_(f), current_offset_(0), state_(State::kWritingZip) {
+static void DeleteZStream(z_stream* stream) {
+ deflateEnd(stream);
+ delete stream;
+}
+
+ZipWriter::ZipWriter(FILE* f) : file_(f), current_offset_(0), state_(State::kWritingZip),
+ z_stream_(nullptr, DeleteZStream), buffer_(kBufSize) {
}
ZipWriter::ZipWriter(ZipWriter&& writer) : file_(writer.file_),
current_offset_(writer.current_offset_),
state_(writer.state_),
- files_(std::move(writer.files_)) {
+ files_(std::move(writer.files_)),
+ z_stream_(std::move(writer.z_stream_)),
+ buffer_(std::move(writer.buffer_)){
writer.file_ = nullptr;
writer.state_ = State::kError;
}
@@ -70,6 +88,8 @@
current_offset_ = writer.current_offset_;
state_ = writer.state_;
files_ = std::move(writer.files_);
+ z_stream_ = std::move(writer.z_stream_);
+ buffer_ = std::move(writer.buffer_);
writer.file_ = nullptr;
writer.state_ = State::kError;
return *this;
@@ -77,6 +97,7 @@
int32_t ZipWriter::HandleError(int32_t error_code) {
state_ = State::kError;
+ z_stream_.reset();
return error_code;
}
@@ -126,8 +147,16 @@
// containing the crc and size fields.
header.gpb_flags |= kGPBDDFlagMask;
- // For now, ignore the ZipWriter::kCompress flag.
- fileInfo.compression_method = kCompressStored;
+ if (flags & ZipWriter::kCompress) {
+ fileInfo.compression_method = kCompressDeflated;
+
+ int32_t result = PrepareDeflate();
+ if (result != kNoError) {
+ return result;
+ }
+ } else {
+ fileInfo.compression_method = kCompressStored;
+ }
header.compression_method = fileInfo.compression_method;
ExtractTimeAndDate(time, &fileInfo.last_mod_time, &fileInfo.last_mod_date);
@@ -163,40 +192,138 @@
return kNoError;
}
+int32_t ZipWriter::PrepareDeflate() {
+ assert(state_ == State::kWritingZip);
+
+ // Initialize the z_stream for compression.
+ z_stream_ = std::unique_ptr<z_stream, void(*)(z_stream*)>(new z_stream(), DeleteZStream);
+
+ int zerr = deflateInit2(z_stream_.get(), Z_BEST_COMPRESSION, Z_DEFLATED, -MAX_WBITS,
+ DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
+ if (zerr != Z_OK) {
+ if (zerr == Z_VERSION_ERROR) {
+ ALOGE("Installed zlib is not compatible with linked version (%s)", ZLIB_VERSION);
+ return HandleError(kZlibError);
+ } else {
+ ALOGE("deflateInit2 failed (zerr=%d)", zerr);
+ return HandleError(kZlibError);
+ }
+ }
+
+ z_stream_->next_out = buffer_.data();
+ z_stream_->avail_out = buffer_.size();
+ return kNoError;
+}
+
int32_t ZipWriter::WriteBytes(const void* data, size_t len) {
if (state_ != State::kWritingEntry) {
return HandleError(kInvalidState);
}
FileInfo& currentFile = files_.back();
+ int32_t result = kNoError;
if (currentFile.compression_method & kCompressDeflated) {
- // TODO(adamlesinski): Implement compression using zlib deflate.
- assert(false);
+ result = CompressBytes(¤tFile, data, len);
} else {
- if (fwrite(data, 1, len, file_) != len) {
- return HandleError(kIoError);
- }
- currentFile.crc32 = crc32(currentFile.crc32, reinterpret_cast<const Bytef*>(data), len);
- currentFile.compressed_size += len;
- current_offset_ += len;
+ result = StoreBytes(¤tFile, data, len);
}
+ if (result != kNoError) {
+ return result;
+ }
+
+ currentFile.crc32 = crc32(currentFile.crc32, reinterpret_cast<const Bytef*>(data), len);
currentFile.uncompressed_size += len;
return kNoError;
}
+int32_t ZipWriter::StoreBytes(FileInfo* file, const void* data, size_t len) {
+ assert(state_ == State::kWritingEntry);
+
+ if (fwrite(data, 1, len, file_) != len) {
+ return HandleError(kIoError);
+ }
+ file->compressed_size += len;
+ current_offset_ += len;
+ return kNoError;
+}
+
+int32_t ZipWriter::CompressBytes(FileInfo* file, const void* data, size_t len) {
+ assert(state_ == State::kWritingEntry);
+ assert(z_stream_);
+ assert(z_stream_->next_out != nullptr);
+ assert(z_stream_->avail_out != 0);
+
+ // Prepare the input.
+ z_stream_->next_in = reinterpret_cast<const uint8_t*>(data);
+ z_stream_->avail_in = len;
+
+ while (z_stream_->avail_in > 0) {
+ // We have more data to compress.
+ int zerr = deflate(z_stream_.get(), Z_NO_FLUSH);
+ if (zerr != Z_OK) {
+ return HandleError(kZlibError);
+ }
+
+ if (z_stream_->avail_out == 0) {
+ // The output is full, let's write it to disk.
+ size_t dataToWrite = z_stream_->next_out - buffer_.data();
+ if (fwrite(buffer_.data(), 1, dataToWrite, file_) != dataToWrite) {
+ return HandleError(kIoError);
+ }
+ file->compressed_size += dataToWrite;
+ current_offset_ += dataToWrite;
+
+ // Reset the output buffer for the next input.
+ z_stream_->next_out = buffer_.data();
+ z_stream_->avail_out = buffer_.size();
+ }
+ }
+ return kNoError;
+}
+
+int32_t ZipWriter::FlushCompressedBytes(FileInfo* file) {
+ assert(state_ == State::kWritingEntry);
+ assert(z_stream_);
+ assert(z_stream_->next_out != nullptr);
+ assert(z_stream_->avail_out != 0);
+
+ int zerr = deflate(z_stream_.get(), Z_FINISH);
+ if (zerr != Z_STREAM_END) {
+ return HandleError(kZlibError);
+ }
+
+ size_t dataToWrite = z_stream_->next_out - buffer_.data();
+ if (dataToWrite != 0) {
+ if (fwrite(buffer_.data(), 1, dataToWrite, file_) != dataToWrite) {
+ return HandleError(kIoError);
+ }
+ file->compressed_size += dataToWrite;
+ current_offset_ += dataToWrite;
+ }
+ z_stream_.reset();
+ return kNoError;
+}
+
int32_t ZipWriter::FinishEntry() {
if (state_ != State::kWritingEntry) {
return kInvalidState;
}
+ FileInfo& currentFile = files_.back();
+ if (currentFile.compression_method & kCompressDeflated) {
+ int32_t result = FlushCompressedBytes(¤tFile);
+ if (result != kNoError) {
+ return result;
+ }
+ }
+
const uint32_t sig = DataDescriptor::kOptSignature;
if (fwrite(&sig, sizeof(sig), 1, file_) != 1) {
state_ = State::kError;
return kIoError;
}
- FileInfo& currentFile = files_.back();
DataDescriptor dd = {};
dd.crc32 = currentFile.crc32;
dd.compressed_size = currentFile.compressed_size;
@@ -241,8 +368,8 @@
EocdRecord er = {};
er.eocd_signature = EocdRecord::kSignature;
- er.disk_num = 1;
- er.cd_start_disk = 1;
+ er.disk_num = 0;
+ er.cd_start_disk = 0;
er.num_records_on_disk = files_.size();
er.num_records = files_.size();
er.cd_size = current_offset_ - startOfCdr;
diff --git a/libziparchive/zip_writer_test.cc b/libziparchive/zip_writer_test.cc
index 5269730..046f195 100644
--- a/libziparchive/zip_writer_test.cc
+++ b/libziparchive/zip_writer_test.cc
@@ -44,26 +44,26 @@
const char* expected = "hello";
- ASSERT_EQ(writer.StartEntry("file.txt", 0), 0);
- ASSERT_EQ(writer.WriteBytes("he", 2), 0);
- ASSERT_EQ(writer.WriteBytes("llo", 3), 0);
- ASSERT_EQ(writer.FinishEntry(), 0);
- ASSERT_EQ(writer.Finish(), 0);
+ ASSERT_EQ(0, writer.StartEntry("file.txt", 0));
+ ASSERT_EQ(0, writer.WriteBytes("he", 2));
+ ASSERT_EQ(0, writer.WriteBytes("llo", 3));
+ ASSERT_EQ(0, writer.FinishEntry());
+ ASSERT_EQ(0, writer.Finish());
- ASSERT_GE(lseek(fd_, 0, SEEK_SET), 0);
+ ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
ZipArchiveHandle handle;
- ASSERT_EQ(OpenArchiveFd(fd_, "temp", &handle, false), 0);
+ ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
ZipEntry data;
- ASSERT_EQ(FindEntry(handle, ZipString("file.txt"), &data), 0);
- EXPECT_EQ(data.compressed_length, strlen(expected));
- EXPECT_EQ(data.uncompressed_length, strlen(expected));
- EXPECT_EQ(data.method, kCompressStored);
+ ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
+ EXPECT_EQ(strlen(expected), data.compressed_length);
+ EXPECT_EQ(strlen(expected), data.uncompressed_length);
+ EXPECT_EQ(kCompressStored, data.method);
char buffer[6];
- EXPECT_EQ(ExtractToMemory(handle, &data, reinterpret_cast<uint8_t*>(&buffer), sizeof(buffer)),
- 0);
+ EXPECT_EQ(0,
+ ExtractToMemory(handle, &data, reinterpret_cast<uint8_t*>(&buffer), sizeof(buffer)));
buffer[5] = 0;
EXPECT_STREQ(expected, buffer);
@@ -74,49 +74,49 @@
TEST_F(zipwriter, WriteUncompressedZipWithMultipleFiles) {
ZipWriter writer(file_);
- ASSERT_EQ(writer.StartEntry("file.txt", 0), 0);
- ASSERT_EQ(writer.WriteBytes("he", 2), 0);
- ASSERT_EQ(writer.FinishEntry(), 0);
+ ASSERT_EQ(0, writer.StartEntry("file.txt", 0));
+ ASSERT_EQ(0, writer.WriteBytes("he", 2));
+ ASSERT_EQ(0, writer.FinishEntry());
- ASSERT_EQ(writer.StartEntry("file/file.txt", 0), 0);
- ASSERT_EQ(writer.WriteBytes("llo", 3), 0);
- ASSERT_EQ(writer.FinishEntry(), 0);
+ ASSERT_EQ(0, writer.StartEntry("file/file.txt", 0));
+ ASSERT_EQ(0, writer.WriteBytes("llo", 3));
+ ASSERT_EQ(0, writer.FinishEntry());
- ASSERT_EQ(writer.StartEntry("file/file2.txt", 0), 0);
- ASSERT_EQ(writer.FinishEntry(), 0);
+ ASSERT_EQ(0, writer.StartEntry("file/file2.txt", 0));
+ ASSERT_EQ(0, writer.FinishEntry());
- ASSERT_EQ(writer.Finish(), 0);
+ ASSERT_EQ(0, writer.Finish());
- ASSERT_GE(lseek(fd_, 0, SEEK_SET), 0);
+ ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
ZipArchiveHandle handle;
- ASSERT_EQ(OpenArchiveFd(fd_, "temp", &handle, false), 0);
+ ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
char buffer[4];
ZipEntry data;
- ASSERT_EQ(FindEntry(handle, ZipString("file.txt"), &data), 0);
- EXPECT_EQ(data.method, kCompressStored);
- EXPECT_EQ(data.compressed_length, 2u);
- EXPECT_EQ(data.uncompressed_length, 2u);
- ASSERT_EQ(ExtractToMemory(handle, &data, reinterpret_cast<uint8_t*>(buffer), arraysize(buffer)),
- 0);
+ ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
+ EXPECT_EQ(kCompressStored, data.method);
+ EXPECT_EQ(2u, data.compressed_length);
+ EXPECT_EQ(2u, data.uncompressed_length);
+ ASSERT_EQ(0,
+ ExtractToMemory(handle, &data, reinterpret_cast<uint8_t*>(buffer), arraysize(buffer)));
buffer[2] = 0;
EXPECT_STREQ("he", buffer);
- ASSERT_EQ(FindEntry(handle, ZipString("file/file.txt"), &data), 0);
- EXPECT_EQ(data.method, kCompressStored);
- EXPECT_EQ(data.compressed_length, 3u);
- EXPECT_EQ(data.uncompressed_length, 3u);
- ASSERT_EQ(ExtractToMemory(handle, &data, reinterpret_cast<uint8_t*>(buffer), arraysize(buffer)),
- 0);
+ ASSERT_EQ(0, FindEntry(handle, ZipString("file/file.txt"), &data));
+ EXPECT_EQ(kCompressStored, data.method);
+ EXPECT_EQ(3u, data.compressed_length);
+ EXPECT_EQ(3u, data.uncompressed_length);
+ ASSERT_EQ(0,
+ ExtractToMemory(handle, &data, reinterpret_cast<uint8_t*>(buffer), arraysize(buffer)));
buffer[3] = 0;
EXPECT_STREQ("llo", buffer);
- ASSERT_EQ(FindEntry(handle, ZipString("file/file2.txt"), &data), 0);
- EXPECT_EQ(data.method, kCompressStored);
- EXPECT_EQ(data.compressed_length, 0u);
- EXPECT_EQ(data.uncompressed_length, 0u);
+ ASSERT_EQ(0, FindEntry(handle, ZipString("file/file2.txt"), &data));
+ EXPECT_EQ(kCompressStored, data.method);
+ EXPECT_EQ(0u, data.compressed_length);
+ EXPECT_EQ(0u, data.uncompressed_length);
CloseArchive(handle);
}
@@ -124,17 +124,47 @@
TEST_F(zipwriter, WriteUncompressedZipWithAlignedFile) {
ZipWriter writer(file_);
- ASSERT_EQ(writer.StartEntry("align.txt", ZipWriter::kAlign32), 0);
- ASSERT_EQ(writer.WriteBytes("he", 2), 0);
- ASSERT_EQ(writer.FinishEntry(), 0);
- ASSERT_EQ(writer.Finish(), 0);
+ ASSERT_EQ(0, writer.StartEntry("align.txt", ZipWriter::kAlign32));
+ ASSERT_EQ(0, writer.WriteBytes("he", 2));
+ ASSERT_EQ(0, writer.FinishEntry());
+ ASSERT_EQ(0, writer.Finish());
- ASSERT_GE(lseek(fd_, 0, SEEK_SET), 0);
+ ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
ZipArchiveHandle handle;
- ASSERT_EQ(OpenArchiveFd(fd_, "temp", &handle, false), 0);
+ ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
ZipEntry data;
- ASSERT_EQ(FindEntry(handle, ZipString("align.txt"), &data), 0);
- EXPECT_EQ(data.offset & 0x03, 0);
+ ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
+ EXPECT_EQ(0, data.offset & 0x03);
+
+ CloseArchive(handle);
+}
+
+TEST_F(zipwriter, WriteCompressedZipWithOneFile) {
+ ZipWriter writer(file_);
+
+ ASSERT_EQ(0, writer.StartEntry("file.txt", ZipWriter::kCompress));
+ ASSERT_EQ(0, writer.WriteBytes("helo", 4));
+ ASSERT_EQ(0, writer.FinishEntry());
+ ASSERT_EQ(0, writer.Finish());
+
+ ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
+
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
+
+ ZipEntry data;
+ ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
+ EXPECT_EQ(kCompressDeflated, data.method);
+ EXPECT_EQ(4u, data.uncompressed_length);
+
+ char buffer[5];
+ ASSERT_EQ(0,
+ ExtractToMemory(handle, &data, reinterpret_cast<uint8_t*>(buffer), arraysize(buffer)));
+ buffer[4] = 0;
+
+ EXPECT_STREQ("helo", buffer);
+
+ CloseArchive(handle);
}
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index ddbfb3e..cb9a85b 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -259,8 +259,8 @@
" -r <kbytes> Rotate log every kbytes. Requires -f\n"
" -n <count> Sets max number of rotated logs to <count>, default 4\n"
" -v <format> Sets the log print format, where <format> is:\n\n"
- " brief color long printable process raw tag thread\n"
- " threadtime time usec UTC year zone\n\n"
+ " brief color epoch long monotonic printable process raw\n"
+ " tag thread threadtime time usec UTC year zone\n\n"
" -D print dividers between each log buffer\n"
" -c clear (flush) the entire log and exit\n"
" -d dump the log and then exit (don't block)\n"
@@ -269,7 +269,7 @@
" -T <count> print only the most recent <count> lines (does not imply -d)\n"
" -T '<time>' print most recent lines since specified time (not imply -d)\n"
" count is pure numerical, time is 'MM-DD hh:mm:ss.mmm...'\n"
- " or 'YYYY-MM-DD hh:mm:ss.mmm...' format\n"
+ " 'YYYY-MM-DD hh:mm:ss.mmm...' or 'sssss.mmm...' format\n"
" -g get the size of the log's ring buffer and exit\n"
" -L dump logs from prior to last reboot\n"
" -b <buffer> Request alternate ring buffer, 'main', 'system', 'radio',\n"
@@ -384,7 +384,11 @@
if (ep) {
return ep;
}
- return t.strptime(cp, "%Y-%m-%d %H:%M:%S.%q");
+ ep = t.strptime(cp, "%Y-%m-%d %H:%M:%S.%q");
+ if (ep) {
+ return ep;
+ }
+ return t.strptime(cp, "%s.%q");
}
// Find last logged line in gestalt of all matching existing output files
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp
index fdb2576..416edd8 100644
--- a/logd/LogStatistics.cpp
+++ b/logd/LogStatistics.cpp
@@ -190,13 +190,13 @@
return formatLine(android::base::StringPrintf(
name.c_str(), android_log_id_to_name(id)),
std::string("Size"),
- std::string(isprune ? "Pruned" : ""))
+ std::string(isprune ? "+/- Pruned" : ""))
+ formatLine(std::string("UID PACKAGE"),
std::string("BYTES"),
std::string(isprune ? "NUM" : ""));
}
-std::string UidEntry::format(const LogStatistics &stat, log_id_t /* id */) const {
+std::string UidEntry::format(const LogStatistics &stat, log_id_t id) const {
uid_t uid = getKey();
std::string name = android::base::StringPrintf("%u", uid);
const char *nameTmp = stat.uidToName(uid);
@@ -210,9 +210,59 @@
std::string size = android::base::StringPrintf("%zu", getSizes());
std::string pruned = "";
- size_t dropped = getDropped();
- if (dropped) {
- pruned = android::base::StringPrintf("%zu", dropped);
+ if (worstUidEnabledForLogid(id)) {
+ size_t totalDropped = 0;
+ for (LogStatistics::uidTable_t::const_iterator it = stat.uidTable[id].begin();
+ it != stat.uidTable[id].end(); ++it) {
+ totalDropped += it->second.getDropped();
+ }
+ size_t sizes = stat.sizes(id);
+ size_t totalSize = stat.sizesTotal(id);
+ size_t totalElements = stat.elementsTotal(id);
+ float totalVirtualSize = (float)sizes + (float)totalDropped * totalSize
+ / totalElements;
+ size_t entrySize = getSizes();
+ float virtualEntrySize = entrySize;
+ int realPermille = virtualEntrySize * 1000.0 / sizes;
+ size_t dropped = getDropped();
+ if (dropped) {
+ pruned = android::base::StringPrintf("%zu", dropped);
+ virtualEntrySize += (float)dropped * totalSize / totalElements;
+ }
+ int virtualPermille = virtualEntrySize * 1000.0 / totalVirtualSize;
+ int permille = (realPermille - virtualPermille) * 1000L
+ / (virtualPermille ?: 1);
+ if ((permille < -1) || (1 < permille)) {
+ std::string change;
+ const char *units = "%";
+ const char *prefix = (permille > 0) ? "+" : "";
+
+ if (permille > 999) {
+ permille = (permille + 1000) / 100; // Now tenths fold
+ units = "X";
+ prefix = "";
+ }
+ if ((-99 < permille) && (permille < 99)) {
+ change = android::base::StringPrintf("%s%d.%u%s",
+ prefix,
+ permille / 10,
+ ((permille < 0) ? (-permille % 10) : (permille % 10)),
+ units);
+ } else {
+ change = android::base::StringPrintf("%s%d%s",
+ prefix,
+ (permille + 5) / 10, units);
+ }
+ ssize_t spaces = EntryBaseConstants::pruned_len
+ - 2 - pruned.length() - change.length();
+ if ((spaces <= 0) && pruned.length()) {
+ spaces = 1;
+ }
+ if (spaces > 0) {
+ change += android::base::StringPrintf("%*s", (int)spaces, "");
+ }
+ pruned = change + pruned;
+ }
}
return formatLine(name, size, pruned);
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index 6943820..41f8b95 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -372,6 +372,8 @@
// Log Statistics
class LogStatistics {
+ friend UidEntry;
+
size_t mSizes[LOG_ID_MAX];
size_t mElements[LOG_ID_MAX];
size_t mDroppedElements[LOG_ID_MAX];
diff --git a/logd/LogWhiteBlackList.cpp b/logd/LogWhiteBlackList.cpp
index ad005ec..c71beb5 100644
--- a/logd/LogWhiteBlackList.cpp
+++ b/logd/LogWhiteBlackList.cpp
@@ -17,6 +17,7 @@
#include <ctype.h>
#include <base/stringprintf.h>
+#include <cutils/properties.h>
#include "LogWhiteBlackList.h"
@@ -49,7 +50,8 @@
return std::string("/");
}
-PruneList::PruneList() : mWorstUidEnabled(true) {
+PruneList::PruneList() {
+ init(NULL);
}
PruneList::~PruneList() {
@@ -72,13 +74,44 @@
it = mNaughty.erase(it);
}
- if (!str) {
- return 0;
+ static const char _default[] = "default";
+ // default here means take ro.logd.filter, persist.logd.filter then
+ // internal default in that order.
+ if (str && !strcmp(str, _default)) {
+ str = NULL;
+ }
+ static const char _disable[] = "disable";
+ if (str && !strcmp(str, _disable)) {
+ str = "";
+ }
+
+ std::string filter;
+
+ if (str) {
+ filter = str;
+ } else {
+ char property[PROPERTY_VALUE_MAX];
+ property_get("ro.logd.filter", property, _default);
+ filter = property;
+ property_get("persist.logd.filter", property, filter.c_str());
+ // default here means take ro.logd.filter
+ if (strcmp(property, _default)) {
+ filter = property;
+ }
+ }
+
+ // default here means take internal default.
+ if (filter == _default) {
+ // See README.property for description of filter format
+ filter = "~!";
+ }
+ if (filter == _disable) {
+ filter = "";
}
mWorstUidEnabled = false;
- for(; *str; ++str) {
+ for(str = filter.c_str(); *str; ++str) {
if (isspace(*str)) {
continue;
}
diff --git a/logd/README.property b/logd/README.property
index a472efd..05ef528 100644
--- a/logd/README.property
+++ b/logd/README.property
@@ -12,16 +12,24 @@
default false
persist.logd.logpersistd string Enable logpersist daemon, "logcatd"
turns on logcat -f in logd context
-persist.logd.size number 256K default size of the buffer for all
- log ids at initial startup, at runtime
- use: logcat -b all -G <value>
+persist.logd.size number 256K Global default size of the buffer for
+ all log ids at initial startup, at
+ runtime use: logcat -b all -G <value>
persist.logd.size.main number 256K Size of the buffer for the main log
persist.logd.size.system number 256K Size of the buffer for the system log
persist.logd.size.radio number 256K Size of the buffer for the radio log
persist.logd.size.event number 256K Size of the buffer for the event log
persist.logd.size.crash number 256K Size of the buffer for the crash log
+persist.logd.filter string Pruning filter to optimize content,
+ default is ro.logd.filter or
+ "~!" which means to prune the oldest
+ entries of chattiest UID. At runtime
+ use: logcat -P "<string>"
NB:
-- number support multipliers (K or M) for convenience. Range is limited
- to between 64K and 256M for log buffer sizes. Individual logs override the
- global default.
+- Number support multipliers (K or M) for convenience. Range is limited
+ to between 64K and 256M for log buffer sizes. Individual log buffer ids
+ such as main, system, ... override global default.
+- Pruning filter is of form of a space-separated list of [~][UID][/PID]
+ references, where '~' prefix means to blacklist otherwise whitelist. For
+ blacklisting, UID may be a '!' to instead reference the chattiest client.
diff --git a/logd/main.cpp b/logd/main.cpp
index 13dda78..cf8cb8f 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -212,6 +212,7 @@
// Anything that reads persist.<property>
if (logBuf) {
logBuf->init();
+ logBuf->initPrune(NULL);
}
}
diff --git a/metricsd/constants.h b/metricsd/constants.h
index 7e1e116..3a7569b 100644
--- a/metricsd/constants.h
+++ b/metricsd/constants.h
@@ -30,6 +30,10 @@
// Build time properties name.
static const char kProductId[] = "product_id";
static const char kProductVersion[] = "product_version";
+
+// Weave configuration.
+static const char kWeaveConfigurationFile[] = "/system/etc/weaved/weaved.conf";
+static const char kModelManifestId[] = "model_id";
} // namespace metrics
#endif // METRICS_CONSTANTS_H_
diff --git a/metricsd/uploader/metrics_log_base.cc b/metricsd/uploader/metrics_log_base.cc
index 311e43a..1a60b4f 100644
--- a/metricsd/uploader/metrics_log_base.cc
+++ b/metricsd/uploader/metrics_log_base.cc
@@ -16,6 +16,7 @@
#include "uploader/metrics_log_base.h"
+#include "base/build_time.h"
#include "base/metrics/histogram_base.h"
#include "base/metrics/histogram_samples.h"
#include "uploader/metrics_hashes.h"
@@ -82,10 +83,7 @@
int64_t MetricsLogBase::GetBuildTime() {
static int64_t integral_build_time = 0;
if (!integral_build_time) {
- Time time;
- const char* kDateTime = __DATE__ " " __TIME__ " GMT";
- bool result = Time::FromString(kDateTime, &time);
- DCHECK(result);
+ Time time = base::GetBuildTime();
integral_build_time = static_cast<int64_t>(time.ToTimeT());
}
return integral_build_time;
diff --git a/metricsd/uploader/system_profile_cache.cc b/metricsd/uploader/system_profile_cache.cc
index 1995510..f7060a2 100644
--- a/metricsd/uploader/system_profile_cache.cc
+++ b/metricsd/uploader/system_profile_cache.cc
@@ -108,7 +108,18 @@
profile_.client_id = testing_ ?
"client_id_test" :
GetPersistentGUID(guid_path);
- profile_.hardware_class = "unknown";
+ profile_.model_manifest_id = "unknown";
+ if (!testing_) {
+ brillo::KeyValueStore weave_config;
+ if (!weave_config.Load(base::FilePath(metrics::kWeaveConfigurationFile))) {
+ LOG(ERROR) << "Failed to load the weave configuration file.";
+ } else if (!weave_config.GetString(metrics::kModelManifestId,
+ &profile_.model_manifest_id)) {
+ LOG(ERROR) << "The model manifest id (model_id) is undefined in "
+ << metrics::kWeaveConfigurationFile;
+ }
+ }
+
profile_.channel = ProtoChannelFromString(channel);
// Increment the session_id everytime we initialize this. If metrics_daemon
@@ -143,7 +154,7 @@
metrics::SystemProfileProto* profile_proto =
metrics_proto->mutable_system_profile();
profile_proto->mutable_hardware()->set_hardware_class(
- profile_.hardware_class);
+ profile_.model_manifest_id);
profile_proto->set_app_version(profile_.version);
profile_proto->set_channel(profile_.channel);
metrics::SystemProfileProto_BrilloDeviceData* device_data =
diff --git a/metricsd/uploader/system_profile_cache.h b/metricsd/uploader/system_profile_cache.h
index 97fb33a..ae54a2a 100644
--- a/metricsd/uploader/system_profile_cache.h
+++ b/metricsd/uploader/system_profile_cache.h
@@ -35,7 +35,7 @@
struct SystemProfile {
std::string version;
- std::string hardware_class;
+ std::string model_manifest_id;
std::string client_id;
int session_id;
metrics::SystemProfileProto::Channel channel;
diff --git a/metricsd/uploader/upload_service.h b/metricsd/uploader/upload_service.h
index a4d0a1e..77df74b 100644
--- a/metricsd/uploader/upload_service.h
+++ b/metricsd/uploader/upload_service.h
@@ -46,8 +46,8 @@
//
// The two states are the presence or not of a staged log.
// A staged log is a compressed protobuffer containing both the aggregated
-// metrics and event and information about the client. (product, hardware id,
-// etc...).
+// metrics and event and information about the client. (product,
+// model_manifest_id, etc...).
//
// At regular intervals, the upload event will be triggered and the following
// will happen:
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 8b16996..b134f93 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -41,17 +41,21 @@
# because init.rc is conditionally included.
#
# create some directories (some are mount points) and symlinks
-local_post_install_cmd_base := mkdir -p $(addprefix $(TARGET_ROOT_OUT)/, \
- sbin dev proc sys system data oem acct cache config storage mnt root); \
+LOCAL_POST_INSTALL_CMD := mkdir -p $(addprefix $(TARGET_ROOT_OUT)/, \
+ sbin dev proc sys system data oem acct cache config storage mnt root $(BOARD_ROOT_EXTRA_FOLDERS)); \
ln -sf /system/etc $(TARGET_ROOT_OUT)/etc; \
ln -sf /sys/kernel/debug $(TARGET_ROOT_OUT)/d; \
ln -sf /storage/self/primary $(TARGET_ROOT_OUT)/sdcard
ifdef BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE
- LOCAL_POST_INSTALL_CMD := $(local_post_install_cmd_base); mkdir -p $(TARGET_ROOT_OUT)/vendor
-else
- LOCAL_POST_INSTALL_CMD := $(local_post_install_cmd_base)
+ LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/vendor
endif
-local_post_install_cmd_base :=
+ifdef BOARD_ROOT_EXTRA_SYMLINKS
+# BOARD_ROOT_EXTRA_SYMLINKS is a list of <target>:<link_name>.
+ LOCAL_POST_INSTALL_CMD += $(foreach s, $(BOARD_ROOT_EXTRA_SYMLINKS),\
+ $(eval p := $(subst :,$(space),$(s)))\
+ ; mkdir -p $(dir $(TARGET_ROOT_OUT)/$(word 2,$(p))) \
+ ; ln -sf $(word 1,$(p)) $(TARGET_ROOT_OUT)/$(word 2,$(p)))
+endif
include $(BUILD_SYSTEM)/base_rules.mk
diff --git a/toolbox/ps.c b/toolbox/ps.c
index cf3f05a..3bc540d 100644
--- a/toolbox/ps.c
+++ b/toolbox/ps.c
@@ -41,14 +41,14 @@
static void print_exe_abi(int pid);
-static int ps_line(int pid, int tid, char *namefilter)
+static int ps_line(int pid, int tid)
{
char statline[1024];
char cmdline[1024];
char macline[1024];
char user[32];
struct stat stats;
- int fd, r;
+ int r;
char *ptr, *name, *state;
int ppid;
unsigned rss, vss;
@@ -68,7 +68,7 @@
sprintf(statline, "/proc/%d/stat", pid);
sprintf(cmdline, "/proc/%d/cmdline", pid);
snprintf(macline, sizeof(macline), "/proc/%d/attr/current", pid);
- fd = open(cmdline, O_RDONLY);
+ int fd = open(cmdline, O_RDONLY);
if(fd == 0) {
r = 0;
} else {
@@ -79,7 +79,7 @@
cmdline[r] = 0;
}
- fd = open(statline, O_RDONLY);
+ int fd = open(statline, O_RDONLY);
if(fd == 0) return -1;
r = read(fd, statline, 1023);
close(fd);
@@ -158,51 +158,48 @@
return 0;
}
- if(!namefilter || !strncmp(cmdline[0] ? cmdline : name, namefilter, strlen(namefilter))) {
- if (display_flags & SHOW_MACLABEL) {
- fd = open(macline, O_RDONLY);
- strcpy(macline, "-");
- if (fd >= 0) {
- r = read(fd, macline, sizeof(macline)-1);
- close(fd);
- if (r > 0)
- macline[r] = 0;
- }
- printf("%-30s %-9s %-5d %-5d %s\n", macline, user, pid, ppid, cmdline[0] ? cmdline : name);
- return 0;
+ if (display_flags & SHOW_MACLABEL) {
+ fd = open(macline, O_RDONLY);
+ strcpy(macline, "-");
+ if (fd >= 0) {
+ r = read(fd, macline, sizeof(macline)-1);
+ close(fd);
+ if (r > 0)
+ macline[r] = 0;
}
-
- printf("%-9s %-5d %-5d %-6d %-5d", user, pid, ppid, vss / 1024, rss * 4);
- if (display_flags & SHOW_CPU)
- printf(" %-2d", psr);
- if (display_flags & SHOW_PRIO)
- printf(" %-5d %-5d %-5d %-5d", prio, nice, rtprio, sched);
- if (display_flags & SHOW_POLICY) {
- SchedPolicy p;
- if (get_sched_policy(pid, &p) < 0)
- printf(" un ");
- else
- printf(" %.2s ", get_sched_policy_name(p));
- }
- char path[PATH_MAX];
- snprintf(path, sizeof(path), "/proc/%d/wchan", pid);
- char wchan[10];
- int fd = open(path, O_RDONLY);
- ssize_t wchan_len = read(fd, wchan, sizeof(wchan));
- if (wchan_len == -1) {
- wchan[wchan_len = 0] = '\0';
- }
- close(fd);
- printf(" %10.*s %0*" PRIxPTR " %s ", (int) wchan_len, wchan, (int) PC_WIDTH, eip, state);
- if (display_flags & SHOW_ABI) {
- print_exe_abi(pid);
- }
- printf("%s", cmdline[0] ? cmdline : name);
- if(display_flags&SHOW_TIME)
- printf(" (u:%d, s:%d)", utime, stime);
-
- printf("\n");
+ printf("%-30s ", macline);
}
+
+ printf("%-9s %-5d %-5d %-6d %-5d", user, pid, ppid, vss / 1024, rss * 4);
+ if (display_flags & SHOW_CPU)
+ printf(" %-2d", psr);
+ if (display_flags & SHOW_PRIO)
+ printf(" %-5d %-5d %-5d %-5d", prio, nice, rtprio, sched);
+ if (display_flags & SHOW_POLICY) {
+ SchedPolicy p;
+ if (get_sched_policy(pid, &p) < 0)
+ printf(" un ");
+ else
+ printf(" %.2s ", get_sched_policy_name(p));
+ }
+ char path[PATH_MAX];
+ snprintf(path, sizeof(path), "/proc/%d/wchan", pid);
+ char wchan[10];
+ fd = open(path, O_RDONLY);
+ ssize_t wchan_len = read(fd, wchan, sizeof(wchan));
+ if (wchan_len == -1) {
+ wchan[wchan_len = 0] = '\0';
+ }
+ close(fd);
+ printf(" %10.*s %0*" PRIxPTR " %s ", (int) wchan_len, wchan, (int) PC_WIDTH, eip, state);
+ if (display_flags & SHOW_ABI) {
+ print_exe_abi(pid);
+ }
+ printf("%s", cmdline[0] ? cmdline : name);
+ if(display_flags&SHOW_TIME)
+ printf(" (u:%d, s:%d)", utime, stime);
+
+ printf("\n");
return 0;
}
@@ -240,7 +237,7 @@
}
}
-void ps_threads(int pid, char *namefilter)
+void ps_threads(int pid)
{
char tmp[128];
DIR *d;
@@ -254,7 +251,7 @@
if(isdigit(de->d_name[0])){
int tid = atoi(de->d_name);
if(tid == pid) continue;
- ps_line(pid, tid, namefilter);
+ ps_line(pid, tid);
}
}
closedir(d);
@@ -264,7 +261,6 @@
{
DIR *d;
struct dirent *de;
- char *namefilter = 0;
int pidfilter = 0;
int threads = 0;
@@ -290,33 +286,39 @@
display_flags |= SHOW_ABI;
} else if(!strcmp(argv[1],"--ppid")) {
ppid_filter = atoi(argv[2]);
+ if (ppid_filter == 0) {
+ fprintf(stderr, "bad ppid '%s'\n", argv[2]);
+ return 1;
+ }
argc--;
argv++;
- } else if(isdigit(argv[1][0])){
- pidfilter = atoi(argv[1]);
} else {
- namefilter = argv[1];
+ pidfilter = atoi(argv[1]);
+ if (pidfilter == 0) {
+ fprintf(stderr, "bad pid '%s'\n", argv[1]);
+ return 1;
+ }
}
argc--;
argv++;
}
if (display_flags & SHOW_MACLABEL) {
- printf("LABEL USER PID PPID NAME\n");
- } else {
- printf("USER PID PPID VSIZE RSS %s%s %sWCHAN %*s %sNAME\n",
- (display_flags&SHOW_CPU)?"CPU ":"",
- (display_flags&SHOW_PRIO)?"PRIO NICE RTPRI SCHED ":"",
- (display_flags&SHOW_POLICY)?"PCY " : "",
- (int) PC_WIDTH, "PC",
- (display_flags&SHOW_ABI)?"ABI " : "");
+ printf("LABEL ");
}
+ printf("USER PID PPID VSIZE RSS %s%s %sWCHAN %*s %sNAME\n",
+ (display_flags&SHOW_CPU)?"CPU ":"",
+ (display_flags&SHOW_PRIO)?"PRIO NICE RTPRI SCHED ":"",
+ (display_flags&SHOW_POLICY)?"PCY " : "",
+ (int) PC_WIDTH, "PC",
+ (display_flags&SHOW_ABI)?"ABI " : "");
+
while((de = readdir(d)) != 0){
if(isdigit(de->d_name[0])){
int pid = atoi(de->d_name);
if(!pidfilter || (pidfilter == pid)) {
- ps_line(pid, 0, namefilter);
- if(threads) ps_threads(pid, namefilter);
+ ps_line(pid, 0);
+ if(threads) ps_threads(pid);
}
}
}