Merge "metricsd: Rename build_target_id to product_id."
diff --git a/adb/adb_utils.cpp b/adb/adb_utils.cpp
index 7ff5c3d..f7b2e4e 100644
--- a/adb/adb_utils.cpp
+++ b/adb/adb_utils.cpp
@@ -229,3 +229,19 @@
 std::string perror_str(const char* msg) {
     return android::base::StringPrintf("%s: %s", msg, strerror(errno));
 }
+
+#if !defined(_WIN32)
+bool set_file_block_mode(int fd, bool block) {
+    int flags = fcntl(fd, F_GETFL, 0);
+    if (flags == -1) {
+        PLOG(ERROR) << "failed to fcntl(F_GETFL) for fd " << fd;
+        return false;
+    }
+    flags = block ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK);
+    if (fcntl(fd, F_SETFL, flags) != 0) {
+        PLOG(ERROR) << "failed to fcntl(F_SETFL) for fd " << fd << ", flags " << flags;
+        return false;
+    }
+    return true;
+}
+#endif
diff --git a/adb/adb_utils.h b/adb/adb_utils.h
index b9da980..537d0e4 100644
--- a/adb/adb_utils.h
+++ b/adb/adb_utils.h
@@ -46,4 +46,6 @@
 
 std::string perror_str(const char* msg);
 
+bool set_file_block_mode(int fd, bool block);
+
 #endif
diff --git a/adb/adb_utils_test.cpp b/adb/adb_utils_test.cpp
index 17c8d0a..4a2787a 100644
--- a/adb/adb_utils_test.cpp
+++ b/adb/adb_utils_test.cpp
@@ -202,3 +202,19 @@
   ASSERT_EQ(0, chdir(td.path)) << strerror(errno);
   test_mkdirs(std::string("relative/subrel/file"));
 }
+
+#if !defined(_WIN32)
+TEST(adb_utils, set_file_block_mode) {
+  int fd = adb_open("/dev/null", O_RDWR | O_APPEND);
+  ASSERT_GE(fd, 0);
+  int flags = fcntl(fd, F_GETFL, 0);
+  ASSERT_EQ(O_RDWR | O_APPEND, (flags & (O_RDWR | O_APPEND)));
+  ASSERT_TRUE(set_file_block_mode(fd, false));
+  int new_flags = fcntl(fd, F_GETFL, 0);
+  ASSERT_EQ(flags | O_NONBLOCK, new_flags);
+  ASSERT_TRUE(set_file_block_mode(fd, true));
+  new_flags = fcntl(fd, F_GETFL, 0);
+  ASSERT_EQ(flags, new_flags);
+  ASSERT_EQ(0, adb_close(fd));
+}
+#endif
diff --git a/adb/fdevent.cpp b/adb/fdevent.cpp
index 1ccee9b..18b64f6 100644
--- a/adb/fdevent.cpp
+++ b/adb/fdevent.cpp
@@ -36,6 +36,7 @@
 
 #include "adb_io.h"
 #include "adb_trace.h"
+#include "adb_utils.h"
 
 #if !ADB_HOST
 // This socket is used when a subproc shell service exists.
@@ -124,11 +125,11 @@
     fde->fd = fd;
     fde->func = func;
     fde->arg = arg;
-    if (fcntl(fd, F_SETFL, O_NONBLOCK) != 0) {
+    if (!set_file_block_mode(fd, false)) {
         // Here is not proper to handle the error. If it fails here, some error is
         // likely to be detected by poll(), then we can let the callback function
         // to handle it.
-        LOG(ERROR) << "failed to fcntl(" << fd << ") to be nonblock";
+        LOG(ERROR) << "failed to set non-blocking mode for fd " << fd;
     }
     auto pair = g_poll_node_map.emplace(fde->fd, PollNode(fde));
     CHECK(pair.second) << "install existing fd " << fd;
diff --git a/adb/fdevent_test.cpp b/adb/fdevent_test.cpp
index 7457712..7fe3d37 100644
--- a/adb/fdevent_test.cpp
+++ b/adb/fdevent_test.cpp
@@ -18,14 +18,14 @@
 
 #include <gtest/gtest.h>
 
+#include <pthread.h>
+#include <signal.h>
+
 #include <limits>
 #include <queue>
 #include <string>
 #include <vector>
 
-#include <pthread.h>
-#include <signal.h>
-
 #include "adb_io.h"
 
 class FdHandler {
diff --git a/adb/jdwp_service.cpp b/adb/jdwp_service.cpp
index 4f0f740..cc2d44e 100644
--- a/adb/jdwp_service.cpp
+++ b/adb/jdwp_service.cpp
@@ -27,6 +27,7 @@
 #include <unistd.h>
 
 #include "adb.h"
+#include "adb_utils.h"
 
 /* here's how these things work.
 
@@ -343,7 +344,6 @@
             struct iovec     iov;
             char             dummy = '!';
             char             buffer[sizeof(struct cmsghdr) + sizeof(int)];
-            int flags;
 
             iov.iov_base       = &dummy;
             iov.iov_len        = 1;
@@ -361,18 +361,8 @@
             cmsg->cmsg_type  = SCM_RIGHTS;
             ((int*)CMSG_DATA(cmsg))[0] = fd;
 
-            flags = fcntl(proc->socket,F_GETFL,0);
-
-            if (flags == -1) {
-                D("failed to get cntl flags for socket %d: %s",
-                  proc->pid, strerror(errno));
-                goto CloseProcess;
-
-            }
-
-            if (fcntl(proc->socket, F_SETFL, flags & ~O_NONBLOCK) == -1) {
-                D("failed to remove O_NONBLOCK flag for socket %d: %s",
-                  proc->pid, strerror(errno));
+            if (!set_file_block_mode(proc->socket, true)) {
+                VLOG(JDWP) << "failed to set blocking mode for fd " << proc->socket;
                 goto CloseProcess;
             }
 
@@ -395,9 +385,8 @@
             for (n = 1; n < proc->out_count; n++)
                 proc->out_fds[n-1] = proc->out_fds[n];
 
-            if (fcntl(proc->socket, F_SETFL, flags) == -1) {
-                D("failed to set O_NONBLOCK flag for socket %d: %s",
-                  proc->pid, strerror(errno));
+            if (!set_file_block_mode(proc->socket, false)) {
+                VLOG(JDWP) << "failed to set non-blocking mode for fd " << proc->socket;
                 goto CloseProcess;
             }
 
diff --git a/adb/shell_service.cpp b/adb/shell_service.cpp
index 714a2d8..8aeea81 100644
--- a/adb/shell_service.cpp
+++ b/adb/shell_service.cpp
@@ -77,9 +77,9 @@
 
 #define TRACE_TAG SHELL
 
-#include "shell_service.h"
+#include "sysdeps.h"
 
-#if !ADB_HOST
+#include "shell_service.h"
 
 #include <errno.h>
 #include <pty.h>
@@ -95,7 +95,7 @@
 #include "adb.h"
 #include "adb_io.h"
 #include "adb_trace.h"
-#include "sysdeps.h"
+#include "adb_utils.h"
 
 namespace {
 
@@ -327,9 +327,8 @@
         // the pipe fills up.
         for (int fd : {stdinout_sfd_.fd(), stderr_sfd_.fd()}) {
             if (fd >= 0) {
-                int flags = fcntl(fd, F_GETFL, 0);
-                if (flags < 0 || fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) {
-                    PLOG(ERROR) << "error making FD " << fd << " non-blocking";
+                if (!set_file_block_mode(fd, false)) {
+                    LOG(ERROR) << "failed to set non-blocking mode for fd " << fd;
                     return false;
                 }
             }
@@ -624,5 +623,3 @@
       subprocess->local_socket_fd(), subprocess->pid());
     return subprocess->local_socket_fd();
 }
-
-#endif  // !ADB_HOST
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 9440b68..54cbc20 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -656,7 +656,7 @@
             break;
 
             case 'f':
-                if ((tail_time == log_time::EPOCH) && (tail_lines != 0)) {
+                if ((tail_time == log_time::EPOCH) && (tail_lines == 0)) {
                     tail_time = lastLogTime(optarg);
                 }
                 // redirect output to a file
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index bcf8d82..610a6ec 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -15,10 +15,12 @@
  */
 
 #include <ctype.h>
+#include <dirent.h>
 #include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/types.h>
 
 #include <gtest/gtest.h>
 #include <log/log.h>
@@ -368,7 +370,7 @@
 
     while (fgets(buffer, sizeof(buffer), fp)) {
         int size, consumed, max, payload;
-        char size_mult[2], consumed_mult[2];
+        char size_mult[3], consumed_mult[3];
         long full_size, full_consumed;
 
         size = consumed = max = payload = 0;
@@ -573,12 +575,12 @@
     static const char comm[] = "logcat -b radio -b events -b system -b main"
                                      " -d -f %s/log.txt -n 7 -r 1";
     char command[sizeof(buf) + sizeof(comm)];
-    sprintf(command, comm, buf);
+    snprintf(command, sizeof(command), comm, buf);
 
     int ret;
     EXPECT_FALSE((ret = system(command)));
     if (!ret) {
-        sprintf(command, "ls -s %s 2>/dev/null", buf);
+        snprintf(command, sizeof(command), "ls -s %s 2>/dev/null", buf);
 
         FILE *fp;
         EXPECT_TRUE(NULL != (fp = popen(command, "r")));
@@ -587,16 +589,12 @@
             int count = 0;
 
             while (fgets(buffer, sizeof(buffer), fp)) {
-                static const char match_1[] = "4 log.txt";
-                static const char match_2[] = "8 log.txt";
-                static const char match_3[] = "12 log.txt";
-                static const char match_4[] = "16 log.txt";
                 static const char total[] = "total ";
+                int num;
+                char c;
 
-                if (!strncmp(buffer, match_1, sizeof(match_1) - 1)
-                 || !strncmp(buffer, match_2, sizeof(match_2) - 1)
-                 || !strncmp(buffer, match_3, sizeof(match_3) - 1)
-                 || !strncmp(buffer, match_4, sizeof(match_4) - 1)) {
+                if ((2 == sscanf(buffer, "%d log.tx%c", &num, &c)) &&
+                        (num <= 24)) {
                     ++count;
                 } else if (strncmp(buffer, total, sizeof(total) - 1)) {
                     fprintf(stderr, "WARNING: Parse error: %s", buffer);
@@ -606,7 +604,7 @@
             EXPECT_TRUE(count == 7 || count == 8);
         }
     }
-    sprintf(command, "rm -rf %s", buf);
+    snprintf(command, sizeof(command), "rm -rf %s", buf);
     EXPECT_FALSE(system(command));
 }
 
@@ -618,12 +616,12 @@
     static const char logcat_cmd[] = "logcat -b radio -b events -b system -b main"
                                      " -d -f %s/log.txt -n 10 -r 1";
     char command[sizeof(tmp_out_dir) + sizeof(logcat_cmd)];
-    sprintf(command, logcat_cmd, tmp_out_dir);
+    snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir);
 
     int ret;
     EXPECT_FALSE((ret = system(command)));
     if (!ret) {
-        sprintf(command, "ls %s 2>/dev/null", tmp_out_dir);
+        snprintf(command, sizeof(command), "ls %s 2>/dev/null", tmp_out_dir);
 
         FILE *fp;
         EXPECT_TRUE(NULL != (fp = popen(command, "r")));
@@ -659,7 +657,113 @@
         pclose(fp);
         EXPECT_EQ(11, log_file_count);
     }
-    sprintf(command, "rm -rf %s", tmp_out_dir);
+    snprintf(command, sizeof(command), "rm -rf %s", tmp_out_dir);
+    EXPECT_FALSE(system(command));
+}
+
+TEST(logcat, logrotate_continue) {
+    static const char tmp_out_dir_form[] = "/data/local/tmp/logcat.logrotate.XXXXXX";
+    char tmp_out_dir[sizeof(tmp_out_dir_form)];
+    ASSERT_TRUE(NULL != mkdtemp(strcpy(tmp_out_dir, tmp_out_dir_form)));
+
+    static const char log_filename[] = "log.txt";
+    static const char logcat_cmd[] = "logcat -b all -d -f %s/%s -n 256 -r 1024";
+    static const char cleanup_cmd[] = "rm -rf %s";
+    char command[sizeof(tmp_out_dir) + sizeof(logcat_cmd) + sizeof(log_filename)];
+    snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir, log_filename);
+
+    int ret;
+    EXPECT_FALSE((ret = system(command)));
+    if (ret) {
+        snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
+        EXPECT_FALSE(system(command));
+        return;
+    }
+    FILE *fp;
+    snprintf(command, sizeof(command), "%s/%s", tmp_out_dir, log_filename);
+    EXPECT_TRUE(NULL != ((fp = fopen(command, "r"))));
+    if (!fp) {
+        snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
+        EXPECT_FALSE(system(command));
+        return;
+    }
+    char *line = NULL;
+    char *last_line = NULL; // this line is allowed to stutter, one-line overlap
+    char *second_last_line = NULL;
+    size_t len = 0;
+    while (getline(&line, &len, fp) != -1) {
+        free(second_last_line);
+        second_last_line = last_line;
+        last_line = line;
+        line = NULL;
+    }
+    fclose(fp);
+    free(line);
+    if (second_last_line == NULL) {
+        fprintf(stderr, "No second to last line, using last, test may fail\n");
+        second_last_line = last_line;
+        last_line = NULL;
+    }
+    free(last_line);
+    EXPECT_TRUE(NULL != second_last_line);
+    if (!second_last_line) {
+        snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
+        EXPECT_FALSE(system(command));
+        return;
+    }
+    // re-run the command, it should only add a few lines more content if it
+    // continues where it left off.
+    snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir, log_filename);
+    EXPECT_FALSE((ret = system(command)));
+    if (ret) {
+        snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
+        EXPECT_FALSE(system(command));
+        return;
+    }
+    DIR *dir;
+    EXPECT_TRUE(NULL != (dir = opendir(tmp_out_dir)));
+    if (!dir) {
+        snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
+        EXPECT_FALSE(system(command));
+        return;
+    }
+    struct dirent *entry;
+    unsigned count = 0;
+    while ((entry = readdir(dir))) {
+        if (strncmp(entry->d_name, log_filename, sizeof(log_filename) - 1)) {
+            continue;
+        }
+        snprintf(command, sizeof(command), "%s/%s", tmp_out_dir, entry->d_name);
+        EXPECT_TRUE(NULL != ((fp = fopen(command, "r"))));
+        if (!fp) {
+            fprintf(stderr, "%s ?\n", command);
+            continue;
+        }
+        line = NULL;
+        size_t number = 0;
+        while (getline(&line, &len, fp) != -1) {
+            ++number;
+            if (!strcmp(line, second_last_line)) {
+                EXPECT_TRUE(++count <= 1);
+                fprintf(stderr, "%s(%zu):\n", entry->d_name, number);
+            }
+        }
+        fclose(fp);
+        free(line);
+        unlink(command);
+    }
+    closedir(dir);
+    if (count > 1) {
+        char *brk = strpbrk(second_last_line, "\r\n");
+        if (!brk) {
+            brk = second_last_line + strlen(second_last_line);
+        }
+        fprintf(stderr, "\"%.*s\" occured %u times\n",
+            (int)(brk - second_last_line), second_last_line, count);
+    }
+    free(second_last_line);
+
+    snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
     EXPECT_FALSE(system(command));
 }