Merge changes I975ba933,Ica9d211b

* changes:
  Add tests for multiton issue
  Add GetExecutableDirectory to libbase
diff --git a/init/init.cpp b/init/init.cpp
index 9b14467..bddf005 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -52,6 +52,8 @@
 
 #include <fstream>
 #include <memory>
+#include <set>
+#include <vector>
 
 #include "action.h"
 #include "bootchart.h"
@@ -700,6 +702,92 @@
     return true;
 }
 
+// Creates devices with uevent->partition_name matching one in the in/out
+// partition_names. Note that the partition_names MUST have A/B suffix
+// when A/B is used. Found partitions will then be removed from the
+// partition_names for caller to check which devices are NOT created.
+static void early_device_init(std::set<std::string>* partition_names) {
+    if (partition_names->empty()) {
+        return;
+    }
+    device_init(nullptr, [=](uevent* uevent) -> coldboot_action_t {
+        if (!strncmp(uevent->subsystem, "firmware", 8)) {
+            return COLDBOOT_CONTINUE;
+        }
+
+        // we need platform devices to create symlinks
+        if (!strncmp(uevent->subsystem, "platform", 8)) {
+            return COLDBOOT_CREATE;
+        }
+
+        // Ignore everything that is not a block device
+        if (strncmp(uevent->subsystem, "block", 5)) {
+            return COLDBOOT_CONTINUE;
+        }
+
+        if (uevent->partition_name) {
+            // match partition names to create device nodes for partitions
+            // both partition_names and uevent->partition_name have A/B suffix when A/B is used
+            auto iter = partition_names->find(uevent->partition_name);
+            if (iter != partition_names->end()) {
+                LOG(VERBOSE) << "early_mount: found partition: " << *iter;
+                partition_names->erase(iter);
+                if (partition_names->empty()) {
+                    return COLDBOOT_STOP;  // found all partitions, stop coldboot
+                } else {
+                    return COLDBOOT_CREATE;  // create this device and continue to find others
+                }
+            }
+        }
+        // Not found a partition or find an unneeded partition, continue to find others
+        return COLDBOOT_CONTINUE;
+    });
+}
+
+static bool get_early_partitions(const std::vector<fstab_rec*>& early_fstab_recs,
+                                 std::set<std::string>* out_partitions, bool* out_need_verity) {
+    std::string meta_partition;
+    out_partitions->clear();
+    *out_need_verity = false;
+
+    for (auto fstab_rec : early_fstab_recs) {
+        // don't allow verifyatboot for early mounted partitions
+        if (fs_mgr_is_verifyatboot(fstab_rec)) {
+            LOG(ERROR) << "early_mount: partitions can't be verified at boot";
+            return false;
+        }
+        // check for verified partitions
+        if (fs_mgr_is_verified(fstab_rec)) {
+            *out_need_verity = true;
+        }
+        // check if verity metadata is on a separate partition and get partition
+        // name from the end of the ->verity_loc path. verity state is not partition
+        // specific, so there must be only 1 additional partition that carries
+        // verity state.
+        if (fstab_rec->verity_loc) {
+            if (!meta_partition.empty()) {
+                LOG(ERROR) << "early_mount: more than one meta partition found: " << meta_partition
+                           << ", " << basename(fstab_rec->verity_loc);
+                return false;
+            } else {
+                meta_partition = basename(fstab_rec->verity_loc);
+            }
+        }
+    }
+
+    // includes those early mount partitions and meta_partition (if any)
+    // note that fstab_rec->blk_device has A/B suffix updated by fs_mgr when A/B is used
+    for (auto fstab_rec : early_fstab_recs) {
+        out_partitions->emplace(basename(fstab_rec->blk_device));
+    }
+
+    if (!meta_partition.empty()) {
+        out_partitions->emplace(std::move(meta_partition));
+    }
+
+    return true;
+}
+
 /* Early mount vendor and ODM partitions. The fstab is read from device-tree. */
 static bool early_mount() {
     // first check if device tree fstab entries are compatible
@@ -716,122 +804,36 @@
     }
 
     // find out fstab records for odm, system and vendor
-    // TODO: add std::map<std::string, fstab_rec*> so all required information about
-    // them can be gathered at once in a single loop
-    fstab_rec* odm_rec = fs_mgr_get_entry_for_mount_point(tab.get(), "/odm");
-    fstab_rec* system_rec = fs_mgr_get_entry_for_mount_point(tab.get(), "/system");
-    fstab_rec* vendor_rec = fs_mgr_get_entry_for_mount_point(tab.get(), "/vendor");
-    if (!odm_rec && !system_rec && !vendor_rec) {
-        // nothing to early mount
-        return true;
+    std::vector<fstab_rec*> early_fstab_recs;
+    for (auto mount_point : {"/odm", "/system", "/vendor"}) {
+        fstab_rec* fstab_rec = fs_mgr_get_entry_for_mount_point(tab.get(), mount_point);
+        if (fstab_rec != nullptr) {
+            early_fstab_recs.push_back(fstab_rec);
+        }
     }
 
-    // don't allow verifyatboot for early mounted partitions
-    if ((odm_rec && fs_mgr_is_verifyatboot(odm_rec)) ||
-        (system_rec && fs_mgr_is_verifyatboot(system_rec)) ||
-        (vendor_rec && fs_mgr_is_verifyatboot(vendor_rec))) {
-        LOG(ERROR) << "Early mount partitions can't be verified at boot";
+    // nothing to early mount
+    if (early_fstab_recs.empty()) return true;
+
+    bool need_verity;
+    std::set<std::string> partition_names;
+    // partition_names MUST have A/B suffix when A/B is used
+    if (!get_early_partitions(early_fstab_recs, &partition_names, &need_verity)) {
         return false;
     }
 
-    // assume A/B device if we find 'slotselect' in any fstab entry
-    bool is_ab = ((odm_rec && fs_mgr_is_slotselect(odm_rec)) ||
-                  (system_rec && fs_mgr_is_slotselect(system_rec)) ||
-                  (vendor_rec && fs_mgr_is_slotselect(vendor_rec)));
-
-    // check for verified partitions
-    bool need_verity = ((odm_rec && fs_mgr_is_verified(odm_rec)) ||
-                        (system_rec && fs_mgr_is_verified(system_rec)) ||
-                        (vendor_rec && fs_mgr_is_verified(vendor_rec)));
-
-    // check if verity metadata is on a separate partition and get partition
-    // name from the end of the ->verity_loc path. verity state is not partition
-    // specific, so there must be only 1 additional partition that carries
-    // verity state.
-    std::string meta_partition;
-    if (odm_rec && odm_rec->verity_loc) {
-        meta_partition = basename(odm_rec->verity_loc);
-    } else if (system_rec && system_rec->verity_loc) {
-        meta_partition = basename(system_rec->verity_loc);
-    } else if (vendor_rec && vendor_rec->verity_loc) {
-        meta_partition = basename(vendor_rec->verity_loc);
-    }
-
-    bool found_odm = !odm_rec;
-    bool found_system = !system_rec;
-    bool found_vendor = !vendor_rec;
-    bool found_meta = meta_partition.empty();
-    int count_odm = 0, count_vendor = 0, count_system = 0;
-
+    bool success = false;
     // create the devices we need..
-    device_init(nullptr, [&](uevent* uevent) -> coldboot_action_t {
-        if (!strncmp(uevent->subsystem, "firmware", 8)) {
-            return COLDBOOT_CONTINUE;
-        }
+    early_device_init(&partition_names);
 
-        // we need platform devices to create symlinks
-        if (!strncmp(uevent->subsystem, "platform", 8)) {
-            return COLDBOOT_CREATE;
-        }
-
-        // Ignore everything that is not a block device
-        if (strncmp(uevent->subsystem, "block", 5)) {
-            return COLDBOOT_CONTINUE;
-        }
-
-        coldboot_action_t ret;
-        bool create_this_node = false;
-        if (uevent->partition_name) {
-            // prefix match partition names so we create device nodes for
-            // A/B-ed partitions
-            if (!found_odm && !strncmp(uevent->partition_name, "odm", 3)) {
-                LOG(VERBOSE) << "early_mount: found (" << uevent->partition_name << ") partition";
-
-                // wait twice for A/B-ed partitions
-                count_odm++;
-                if (!is_ab || count_odm == 2) {
-                    found_odm = true;
-                }
-
-                create_this_node = true;
-            } else if (!found_system && !strncmp(uevent->partition_name, "system", 6)) {
-                LOG(VERBOSE) << "early_mount: found (" << uevent->partition_name << ") partition";
-
-                count_system++;
-                if (!is_ab || count_system == 2) {
-                    found_system = true;
-                }
-
-                create_this_node = true;
-            } else if (!found_vendor && !strncmp(uevent->partition_name, "vendor", 6)) {
-                LOG(VERBOSE) << "early_mount: found (" << uevent->partition_name << ") partition";
-                count_vendor++;
-                if (!is_ab || count_vendor == 2) {
-                    found_vendor = true;
-                }
-
-                create_this_node = true;
-            } else if (!found_meta && (meta_partition == uevent->partition_name)) {
-                LOG(VERBOSE) <<  "early_mount: found (" << uevent->partition_name << ") partition";
-                found_meta = true;
-                create_this_node = true;
-            }
-        }
-
-        // if we found all other partitions already, create this
-        // node and stop coldboot. If this is a prefix matched
-        // partition, create device node and continue. For everything
-        // else skip the device node
-        if (found_meta && found_odm && found_system && found_vendor) {
-            ret = COLDBOOT_STOP;
-        } else if (create_this_node) {
-            ret = COLDBOOT_CREATE;
-        } else {
-            ret = COLDBOOT_CONTINUE;
-        }
-
-        return ret;
-    });
+    // early_device_init will remove found partitions from partition_names
+    // So if the partition_names is not empty here, means some partitions
+    // are not found
+    if (!partition_names.empty()) {
+        LOG(ERROR) << "early_mount: partition(s) not found: "
+                   << android::base::Join(partition_names, ", ");
+        goto done;
+    }
 
     if (need_verity) {
         // create /dev/device mapper
@@ -839,10 +841,10 @@
                     [&](uevent* uevent) -> coldboot_action_t { return COLDBOOT_STOP; });
     }
 
-    bool success = true;
-    if (odm_rec && !(success = early_mount_one(odm_rec))) goto done;
-    if (system_rec && !(success = early_mount_one(system_rec))) goto done;
-    if (vendor_rec && !(success = early_mount_one(vendor_rec))) goto done;
+    for (auto fstab_rec : early_fstab_recs) {
+        if (!early_mount_one(fstab_rec)) goto done;
+    }
+    success = true;
 
 done:
     device_close();
diff --git a/libcutils/fs_config.c b/libcutils/fs_config.c
index 75e2492..1915ced 100644
--- a/libcutils/fs_config.c
+++ b/libcutils/fs_config.c
@@ -165,7 +165,7 @@
 
     /* Support Bluetooth legacy hal accessing /sys/class/rfkill */
     { 00700, AID_BLUETOOTH, AID_BLUETOOTH, CAP_MASK_LONG(CAP_NET_ADMIN),
-                                              "system/bin/hw/android.hardware.bluetooth@1.0-service" },
+                                              "vendor/bin/hw/android.hardware.bluetooth@1.0-service" },
 
     /* A non-privileged zygote that spawns isolated processes for web rendering. */
     { 0750,  AID_ROOT,      AID_ROOT,      CAP_MASK_LONG(CAP_SETUID) |
diff --git a/libcutils/tests/Android.bp b/libcutils/tests/Android.bp
index 0b0dc09..718d76b 100644
--- a/libcutils/tests/Android.bp
+++ b/libcutils/tests/Android.bp
@@ -46,6 +46,12 @@
             suffix: "64",
         },
     },
+
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
 }
 
 test_libraries = [
diff --git a/libcutils/tests/multiuser_test.cpp b/libcutils/tests/multiuser_test.cpp
index c5f58b4..ae5c416 100644
--- a/libcutils/tests/multiuser_test.cpp
+++ b/libcutils/tests/multiuser_test.cpp
@@ -17,59 +17,61 @@
 #include <cutils/multiuser.h>
 #include <gtest/gtest.h>
 
+static constexpr auto ERR_GID = static_cast<gid_t>(-1);
+
 TEST(MultiuserTest, TestMerge) {
-    EXPECT_EQ(0, multiuser_get_uid(0, 0));
-    EXPECT_EQ(1000, multiuser_get_uid(0, 1000));
-    EXPECT_EQ(10000, multiuser_get_uid(0, 10000));
-    EXPECT_EQ(50000, multiuser_get_uid(0, 50000));
-    EXPECT_EQ(1000000, multiuser_get_uid(10, 0));
-    EXPECT_EQ(1001000, multiuser_get_uid(10, 1000));
-    EXPECT_EQ(1010000, multiuser_get_uid(10, 10000));
-    EXPECT_EQ(1050000, multiuser_get_uid(10, 50000));
+    EXPECT_EQ(0U, multiuser_get_uid(0, 0));
+    EXPECT_EQ(1000U, multiuser_get_uid(0, 1000));
+    EXPECT_EQ(10000U, multiuser_get_uid(0, 10000));
+    EXPECT_EQ(50000U, multiuser_get_uid(0, 50000));
+    EXPECT_EQ(1000000U, multiuser_get_uid(10, 0));
+    EXPECT_EQ(1001000U, multiuser_get_uid(10, 1000));
+    EXPECT_EQ(1010000U, multiuser_get_uid(10, 10000));
+    EXPECT_EQ(1050000U, multiuser_get_uid(10, 50000));
 }
 
 TEST(MultiuserTest, TestSplitUser) {
-    EXPECT_EQ(0, multiuser_get_user_id(0));
-    EXPECT_EQ(0, multiuser_get_user_id(1000));
-    EXPECT_EQ(0, multiuser_get_user_id(10000));
-    EXPECT_EQ(0, multiuser_get_user_id(50000));
-    EXPECT_EQ(10, multiuser_get_user_id(1000000));
-    EXPECT_EQ(10, multiuser_get_user_id(1001000));
-    EXPECT_EQ(10, multiuser_get_user_id(1010000));
-    EXPECT_EQ(10, multiuser_get_user_id(1050000));
+    EXPECT_EQ(0U, multiuser_get_user_id(0));
+    EXPECT_EQ(0U, multiuser_get_user_id(1000));
+    EXPECT_EQ(0U, multiuser_get_user_id(10000));
+    EXPECT_EQ(0U, multiuser_get_user_id(50000));
+    EXPECT_EQ(10U, multiuser_get_user_id(1000000));
+    EXPECT_EQ(10U, multiuser_get_user_id(1001000));
+    EXPECT_EQ(10U, multiuser_get_user_id(1010000));
+    EXPECT_EQ(10U, multiuser_get_user_id(1050000));
 }
 
 TEST(MultiuserTest, TestSplitApp) {
-    EXPECT_EQ(0, multiuser_get_app_id(0));
-    EXPECT_EQ(1000, multiuser_get_app_id(1000));
-    EXPECT_EQ(10000, multiuser_get_app_id(10000));
-    EXPECT_EQ(50000, multiuser_get_app_id(50000));
-    EXPECT_EQ(0, multiuser_get_app_id(1000000));
-    EXPECT_EQ(1000, multiuser_get_app_id(1001000));
-    EXPECT_EQ(10000, multiuser_get_app_id(1010000));
-    EXPECT_EQ(50000, multiuser_get_app_id(1050000));
+    EXPECT_EQ(0U, multiuser_get_app_id(0));
+    EXPECT_EQ(1000U, multiuser_get_app_id(1000));
+    EXPECT_EQ(10000U, multiuser_get_app_id(10000));
+    EXPECT_EQ(50000U, multiuser_get_app_id(50000));
+    EXPECT_EQ(0U, multiuser_get_app_id(1000000));
+    EXPECT_EQ(1000U, multiuser_get_app_id(1001000));
+    EXPECT_EQ(10000U, multiuser_get_app_id(1010000));
+    EXPECT_EQ(50000U, multiuser_get_app_id(1050000));
 }
 
 TEST(MultiuserTest, TestCache) {
-    EXPECT_EQ(-1, multiuser_get_cache_gid(0, 0));
-    EXPECT_EQ(-1, multiuser_get_cache_gid(0, 1000));
-    EXPECT_EQ(20000, multiuser_get_cache_gid(0, 10000));
-    EXPECT_EQ(-1, multiuser_get_cache_gid(0, 50000));
-    EXPECT_EQ(1020000, multiuser_get_cache_gid(10, 10000));
+    EXPECT_EQ(ERR_GID, multiuser_get_cache_gid(0, 0));
+    EXPECT_EQ(ERR_GID, multiuser_get_cache_gid(0, 1000));
+    EXPECT_EQ(20000U, multiuser_get_cache_gid(0, 10000));
+    EXPECT_EQ(ERR_GID, multiuser_get_cache_gid(0, 50000));
+    EXPECT_EQ(1020000U, multiuser_get_cache_gid(10, 10000));
 }
 
 TEST(MultiuserTest, TestExt) {
-    EXPECT_EQ(-1, multiuser_get_ext_gid(0, 0));
-    EXPECT_EQ(-1, multiuser_get_ext_gid(0, 1000));
-    EXPECT_EQ(30000, multiuser_get_ext_gid(0, 10000));
-    EXPECT_EQ(-1, multiuser_get_ext_gid(0, 50000));
-    EXPECT_EQ(1030000, multiuser_get_ext_gid(10, 10000));
+    EXPECT_EQ(ERR_GID, multiuser_get_ext_gid(0, 0));
+    EXPECT_EQ(ERR_GID, multiuser_get_ext_gid(0, 1000));
+    EXPECT_EQ(30000U, multiuser_get_ext_gid(0, 10000));
+    EXPECT_EQ(ERR_GID, multiuser_get_ext_gid(0, 50000));
+    EXPECT_EQ(1030000U, multiuser_get_ext_gid(10, 10000));
 }
 
 TEST(MultiuserTest, TestShared) {
-    EXPECT_EQ(-1, multiuser_get_shared_gid(0, 0));
-    EXPECT_EQ(-1, multiuser_get_shared_gid(0, 1000));
-    EXPECT_EQ(50000, multiuser_get_shared_gid(0, 10000));
-    EXPECT_EQ(-1, multiuser_get_shared_gid(0, 50000));
-    EXPECT_EQ(1050000, multiuser_get_shared_gid(10, 10000));
+    EXPECT_EQ(ERR_GID, multiuser_get_shared_gid(0, 0));
+    EXPECT_EQ(ERR_GID, multiuser_get_shared_gid(0, 1000));
+    EXPECT_EQ(50000U, multiuser_get_shared_gid(0, 10000));
+    EXPECT_EQ(ERR_GID, multiuser_get_shared_gid(0, 50000));
+    EXPECT_EQ(1050000U, multiuser_get_shared_gid(10, 10000));
 }
diff --git a/libcutils/tests/sockets_test.cpp b/libcutils/tests/sockets_test.cpp
index 0441fb6..b762ac1 100644
--- a/libcutils/tests/sockets_test.cpp
+++ b/libcutils/tests/sockets_test.cpp
@@ -101,7 +101,7 @@
     // should always be able to read its port.
     for (int port : {10000, 12345, 15999, 20202, 25000}) {
         for (int type : {SOCK_DGRAM, SOCK_STREAM}) {
-            server = socket_inaddr_any_server(port, SOCK_DGRAM);
+            server = socket_inaddr_any_server(port, type);
             if (server != INVALID_SOCKET) {
                 EXPECT_EQ(port, socket_get_local_port(server));
             }
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 7f852d4..d67dee5 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -67,8 +67,8 @@
     std::vector<const char*> argv_hold;
     std::vector<std::string> envs;
     std::vector<const char*> envp_hold;
-    int output_fd;
-    int error_fd;
+    int output_fd; // duplication of fileno(output) (below)
+    int error_fd;  // duplication of fileno(error) (below)
 
     // library
     int fds[2];    // From popen call
@@ -284,6 +284,15 @@
         return;
     }
     context->output = fdopen(context->output_fd, "web");
+    if (context->output == NULL) {
+        logcat_panic(context, HELP_FALSE, "couldn't fdopen output file");
+        return;
+    }
+    if (context->stderr_stdout) {
+        close_error(context);
+        context->error = context->output;
+        context->error_fd = context->output_fd;
+    }
 
     context->outByteCount = 0;
 }
@@ -788,6 +797,7 @@
         // Simulate shell stderr redirect parsing
         if ((argv[i][0] != '2') || (argv[i][1] != '>')) continue;
 
+        // Append to file not implemented, just open file
         size_t skip = (argv[i][2] == '>') + 2;
         if (!strcmp(&argv[i][skip], "/dev/null")) {
             context->stderr_null = true;
@@ -799,16 +809,29 @@
                     "stderr redirection to file %s unsupported, skipping\n",
                     &argv[i][skip]);
         }
+        // Only the first one
+        break;
+    }
+
+    const char* filename = NULL;
+    for (int i = 0; i < argc; ++i) {
+        // Simulate shell stdout redirect parsing
+        if (argv[i][0] != '>') continue;
+
+        // Append to file not implemented, just open file
+        filename = &argv[i][(argv[i][1] == '>') + 1];
+        // Only the first one
+        break;
     }
 
     // Deal with setting up file descriptors and FILE pointers
-    if (context->error_fd >= 0) {
+    if (context->error_fd >= 0) { // Is an error file descriptor supplied?
         if (context->error_fd == context->output_fd) {
             context->stderr_stdout = true;
-        } else if (context->stderr_null) {
+        } else if (context->stderr_null) { // redirection told us to close it
             close(context->error_fd);
             context->error_fd = -1;
-        } else {
+        } else { // All Ok, convert error to a FILE pointer
             context->error = fdopen(context->error_fd, "web");
             if (!context->error) {
                 context->retval = -errno;
@@ -819,22 +842,32 @@
             }
         }
     }
-    if (context->output_fd >= 0) {
-        context->output = fdopen(context->output_fd, "web");
-        if (!context->output) {
-            context->retval = -errno;
-            fprintf(context->stderr_stdout ? stdout : context->error,
-                    "Failed to fdopen(output_fd=%d) %s\n", context->output_fd,
-                    strerror(errno));
-            goto exit;
+    if (context->output_fd >= 0) { // Is an output file descriptor supplied?
+        if (filename) { // redirect to file, close the supplied file descriptor.
+            close(context->output_fd);
+            context->output_fd = -1;
+        } else { // All Ok, convert output to a FILE pointer
+            context->output = fdopen(context->output_fd, "web");
+            if (!context->output) {
+                context->retval = -errno;
+                fprintf(context->stderr_stdout ? stdout : context->error,
+                        "Failed to fdopen(output_fd=%d) %s\n",
+                        context->output_fd, strerror(errno));
+                goto exit;
+            }
         }
     }
+    if (filename) { // We supplied an output file redirected in command line
+        context->output = fopen(filename, "web");
+    }
+    // Deal with 2>&1
     if (context->stderr_stdout) context->error = context->output;
+    // Deal with 2>/dev/null
     if (context->stderr_null) {
         context->error_fd = -1;
         context->error = NULL;
     }
-    // Only happens if output=stdout
+    // Only happens if output=stdout or output=filename
     if ((context->output_fd < 0) && context->output) {
         context->output_fd = fileno(context->output);
     }
@@ -1351,6 +1384,8 @@
         for (int i = optind ; i < argc ; i++) {
             // skip stderr redirections of _all_ kinds
             if ((argv[i][0] == '2') && (argv[i][1] == '>')) continue;
+            // skip stdout redirections of _all_ kinds
+            if (argv[i][0] == '>') continue;
 
             err = android_log_addFilterString(context->logformat, argv[i]);
             if (err < 0) {
diff --git a/logcat/tests/Android.mk b/logcat/tests/Android.mk
index 1c3feba..22aca17 100644
--- a/logcat/tests/Android.mk
+++ b/logcat/tests/Android.mk
@@ -27,11 +27,12 @@
     -fno-builtin \
 
 # -----------------------------------------------------------------------------
-# Benchmarks (actually a gTest where the result code does not matter)
+# Benchmarks
 # ----------------------------------------------------------------------------
 
 benchmark_src_files := \
-    logcat_benchmark.cpp
+    logcat_benchmark.cpp \
+    exec_benchmark.cpp \
 
 # Build benchmarks for the device. Run with:
 #   adb shell /data/nativetest/logcat-benchmarks/logcat-benchmarks
@@ -40,7 +41,8 @@
 LOCAL_MODULE_TAGS := $(test_tags)
 LOCAL_CFLAGS += $(test_c_flags)
 LOCAL_SRC_FILES := $(benchmark_src_files)
-include $(BUILD_NATIVE_TEST)
+LOCAL_SHARED_LIBRARIES := libbase liblogcat
+include $(BUILD_NATIVE_BENCHMARK)
 
 # -----------------------------------------------------------------------------
 # Unit tests.
diff --git a/logcat/tests/exec_benchmark.cpp b/logcat/tests/exec_benchmark.cpp
new file mode 100644
index 0000000..c30a5f5
--- /dev/null
+++ b/logcat/tests/exec_benchmark.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2017 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 <stdio.h>
+
+#include <android-base/file.h>
+#include <benchmark/benchmark.h>
+#include <log/logcat.h>
+
+// Dump the statistics and report results
+
+static void logcat_popen_libc(benchmark::State& state, const char* cmd) {
+    while (state.KeepRunning()) {
+        FILE* fp = popen(cmd, "r");
+        std::string ret;
+        android::base::ReadFdToString(fileno(fp), &ret);
+        pclose(fp);
+    }
+}
+
+static void BM_logcat_stat_popen_libc(benchmark::State& state) {
+    logcat_popen_libc(state, "logcat -b all -S");
+}
+BENCHMARK(BM_logcat_stat_popen_libc);
+
+static void logcat_popen_liblogcat(benchmark::State& state, const char* cmd) {
+    while (state.KeepRunning()) {
+        android_logcat_context ctx;
+        FILE* fp = android_logcat_popen(&ctx, cmd);
+        std::string ret;
+        android::base::ReadFdToString(fileno(fp), &ret);
+        android_logcat_pclose(&ctx, fp);
+    }
+}
+
+static void BM_logcat_stat_popen_liblogcat(benchmark::State& state) {
+    logcat_popen_liblogcat(state, "logcat -b all -S");
+}
+BENCHMARK(BM_logcat_stat_popen_liblogcat);
+
+static void logcat_system_libc(benchmark::State& state, const char* cmd) {
+    while (state.KeepRunning()) {
+        system(cmd);
+    }
+}
+
+static void BM_logcat_stat_system_libc(benchmark::State& state) {
+    logcat_system_libc(state, "logcat -b all -S >/dev/null 2>/dev/null");
+}
+BENCHMARK(BM_logcat_stat_system_libc);
+
+static void logcat_system_liblogcat(benchmark::State& state, const char* cmd) {
+    while (state.KeepRunning()) {
+        android_logcat_system(cmd);
+    }
+}
+
+static void BM_logcat_stat_system_liblogcat(benchmark::State& state) {
+    logcat_system_liblogcat(state, "logcat -b all -S >/dev/null 2>/dev/null");
+}
+BENCHMARK(BM_logcat_stat_system_liblogcat);
+
+// Dump the logs and report results
+
+static void BM_logcat_dump_popen_libc(benchmark::State& state) {
+    logcat_popen_libc(state, "logcat -b all -d");
+}
+BENCHMARK(BM_logcat_dump_popen_libc);
+
+static void BM_logcat_dump_popen_liblogcat(benchmark::State& state) {
+    logcat_popen_liblogcat(state, "logcat -b all -d");
+}
+BENCHMARK(BM_logcat_dump_popen_liblogcat);
+
+static void BM_logcat_dump_system_libc(benchmark::State& state) {
+    logcat_system_libc(state, "logcat -b all -d >/dev/null 2>/dev/null");
+}
+BENCHMARK(BM_logcat_dump_system_libc);
+
+static void BM_logcat_dump_system_liblogcat(benchmark::State& state) {
+    logcat_system_liblogcat(state, "logcat -b all -d >/dev/null 2>/dev/null");
+}
+BENCHMARK(BM_logcat_dump_system_liblogcat);
diff --git a/logcat/tests/logcat_benchmark.cpp b/logcat/tests/logcat_benchmark.cpp
index dd85164..8d88628 100644
--- a/logcat/tests/logcat_benchmark.cpp
+++ b/logcat/tests/logcat_benchmark.cpp
@@ -18,19 +18,22 @@
 #include <stdlib.h>
 #include <string.h>
 
-#include <gtest/gtest.h>
+#include <benchmark/benchmark.h>
 
 static const char begin[] = "--------- beginning of ";
 
-TEST(logcat, sorted_order) {
-    FILE *fp;
+static void BM_logcat_sorted_order(benchmark::State& state) {
+    FILE* fp;
 
-    ASSERT_TRUE(NULL != (fp = popen(
-      "logcat -v time -b radio -b events -b system -b main -d 2>/dev/null",
-      "r")));
+    if (!state.KeepRunning()) return;
+
+    fp = popen(
+        "logcat -v time -b radio -b events -b system -b main -d 2>/dev/null",
+        "r");
+    if (!fp) return;
 
     class timestamp {
-    private:
+       private:
         int month;
         int day;
         int hour;
@@ -39,44 +42,39 @@
         int millisecond;
         bool ok;
 
-    public:
-        void init(const char *buffer)
-        {
+       public:
+        void init(const char* buffer) {
             ok = false;
             if (buffer != NULL) {
-                ok = sscanf(buffer, "%d-%d %d:%d:%d.%d ",
-                    &month, &day, &hour, &minute, &second, &millisecond) == 6;
+                ok = sscanf(buffer, "%d-%d %d:%d:%d.%d ", &month, &day, &hour,
+                            &minute, &second, &millisecond) == 6;
             }
         }
 
-        explicit timestamp(const char *buffer)
-        {
+        explicit timestamp(const char* buffer) {
             init(buffer);
         }
 
-        bool operator< (timestamp &T)
-        {
-            return !ok || !T.ok
-             || (month < T.month)
-             || ((month == T.month)
-              && ((day < T.day)
-               || ((day == T.day)
-                && ((hour < T.hour)
-                 || ((hour == T.hour)
-                  && ((minute < T.minute)
-                   || ((minute == T.minute)
-                    && ((second < T.second)
-                     || ((second == T.second)
-                      && (millisecond < T.millisecond))))))))));
+        bool operator<(timestamp& T) {
+            return !ok || !T.ok || (month < T.month) ||
+                   ((month == T.month) &&
+                    ((day < T.day) ||
+                     ((day == T.day) &&
+                      ((hour < T.hour) ||
+                       ((hour == T.hour) &&
+                        ((minute < T.minute) ||
+                         ((minute == T.minute) &&
+                          ((second < T.second) ||
+                           ((second == T.second) &&
+                            (millisecond < T.millisecond))))))))));
         }
 
-        bool valid(void)
-        {
+        bool valid(void) {
             return ok;
         }
     } last(NULL);
 
-    char *last_buffer = NULL;
+    char* last_buffer = NULL;
     char buffer[5120];
 
     int count = 0;
@@ -114,15 +112,22 @@
 
     // Allow few fails, happens with readers active
     fprintf(stderr, "%s: %d/%d out of order entries\n",
-            (next_lt_last)
-                ? ((next_lt_last <= max_ok)
-                    ? "WARNING"
-                    : "ERROR")
-                : "INFO",
+            (next_lt_last) ? ((next_lt_last <= max_ok) ? "WARNING" : "ERROR")
+                           : "INFO",
             next_lt_last, count);
 
-    EXPECT_GE(max_ok, next_lt_last);
+    if (next_lt_last > max_ok) {
+        fprintf(stderr, "EXPECT_GE(max_ok=%d, next_lt_last=%d)\n", max_ok,
+                next_lt_last);
+    }
 
     // sample statistically too small
-    EXPECT_LT(100, count);
+    if (count < 100) {
+        fprintf(stderr, "EXPECT_LT(100, count=%d)\n", count);
+    }
+
+    state.KeepRunning();
 }
+BENCHMARK(BM_logcat_sorted_order);
+
+BENCHMARK_MAIN();