liblogcat: add android_logcat_popen and android_logcat_system

Supply a wrapper to the logcat API that provides some analogous
functionality to popen and system libc calls with some bits of
KISS shell-like parsing for environment, quotes and error
redirection handling.

Test: gTest logcat-unit-tests
Bug: 35326290
Change-Id: I9494ce71267ad2b2bec7fcccfc7d4beddae9aea6
diff --git a/logcat/Android.mk b/logcat/Android.mk
index 4b8746c..5ed0938 100644
--- a/logcat/Android.mk
+++ b/logcat/Android.mk
@@ -17,7 +17,7 @@
 include $(CLEAR_VARS)
 
 LOCAL_MODULE := liblogcat
-LOCAL_SRC_FILES := logcat.cpp
+LOCAL_SRC_FILES := logcat.cpp logcat_system.cpp
 LOCAL_SHARED_LIBRARIES := $(logcatLibs)
 LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
 LOCAL_EXPORT_C_INCLUDES_DIR := $(LOCAL_PATH)/include
diff --git a/logcat/include/log/logcat.h b/logcat/include/log/logcat.h
index c41a6b7..009672c 100644
--- a/logcat/include/log/logcat.h
+++ b/logcat/include/log/logcat.h
@@ -17,6 +17,8 @@
 #ifndef _LIBS_LOGCAT_H /* header boilerplate */
 #define _LIBS_LOGCAT_H
 
+#include <stdio.h>
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -95,6 +97,22 @@
  */
 int android_logcat_destroy(android_logcat_context* ctx);
 
+/* derived helpers */
+
+/*
+ * In-process thread that acts like somewhat like libc-like system and popen
+ * respectively.  Can not handle shell scripting, only pure calls to the
+ * logcat operations. The android_logcat_system is a wrapper for the
+ * create_android_logcat, android_logcat_run_command and android_logcat_destroy
+ * API above.  The android_logcat_popen is a wrapper for the
+ * android_logcat_run_command_thread API above.  The android_logcat_pclose is
+ * a wrapper for a reasonable wait until output has subsided for command
+ * completion, fclose on the FILE pointer and the android_logcat_destroy API.
+ */
+int android_logcat_system(const char* command);
+FILE* android_logcat_popen(android_logcat_context* ctx, const char* command);
+int android_logcat_pclose(android_logcat_context* ctx, FILE* output);
+
 #endif /* __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE */
 
 #ifdef __cplusplus
diff --git a/logcat/logcat_system.cpp b/logcat/logcat_system.cpp
new file mode 100644
index 0000000..ea393bd
--- /dev/null
+++ b/logcat/logcat_system.cpp
@@ -0,0 +1,148 @@
+/*
+ * 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 <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+#include <log/logcat.h>
+
+static std::string unquote(const char*& cp, const char*& delim) {
+    if ((*cp == '\'') || (*cp == '"')) {
+        // KISS: Simple quotes. Do not handle the case
+        //       of concatenation like "blah"foo'bar'
+        char quote = *cp++;
+        delim = strchr(cp, quote);
+        if (!delim) delim = cp + strlen(cp);
+        std::string str(cp, delim);
+        if (*delim) ++delim;
+        return str;
+    }
+    delim = strpbrk(cp, " \t\f\r\n");
+    if (!delim) delim = cp + strlen(cp);
+    return std::string(cp, delim);
+}
+
+static bool __android_logcat_parse(const char* command,
+                                   std::vector<std::string>& args,
+                                   std::vector<std::string>& envs) {
+    for (const char *delim, *cp = command; cp && *cp; cp = delim) {
+        while (isspace(*cp)) ++cp;
+        if ((args.size() == 0) && (*cp != '=') && !isdigit(*cp)) {
+            const char* env = cp;
+            while (isalnum(*cp) || (*cp == '_')) ++cp;
+            if (cp && (*cp == '=')) {
+                std::string str(env, ++cp);
+                str += unquote(cp, delim);
+                envs.push_back(str);
+                continue;
+            }
+            cp = env;
+        }
+        args.push_back(unquote(cp, delim));
+        if ((args.size() == 1) && (args[0] != "logcat") &&
+            (args[0] != "/system/bin/logcat")) {
+            return false;
+        }
+    }
+    return args.size() != 0;
+}
+
+FILE* android_logcat_popen(android_logcat_context* ctx, const char* command) {
+    *ctx = NULL;
+
+    std::vector<std::string> args;
+    std::vector<std::string> envs;
+    if (!__android_logcat_parse(command, args, envs)) return NULL;
+
+    std::vector<const char*> argv;
+    for (auto& str : args) {
+        argv.push_back(str.c_str());
+    }
+    argv.push_back(NULL);
+
+    std::vector<const char*> envp;
+    for (auto& str : envs) {
+        envp.push_back(str.c_str());
+    }
+    envp.push_back(NULL);
+
+    *ctx = create_android_logcat();
+    if (!*ctx) return NULL;
+
+    int fd = android_logcat_run_command_thread(
+        *ctx, argv.size() - 1, (char* const*)&argv[0], (char* const*)&envp[0]);
+    argv.clear();
+    args.clear();
+    envp.clear();
+    envs.clear();
+    if (fd < 0) {
+        android_logcat_destroy(ctx);
+        return NULL;
+    }
+
+    FILE* retval = fdopen(fd, "reb");
+    if (!retval) android_logcat_destroy(ctx);
+    return retval;
+}
+
+int android_logcat_pclose(android_logcat_context* ctx, FILE* output) {
+    if (*ctx) {
+        static const useconds_t wait_sample = 20000;
+        // Wait two seconds maximum
+        for (size_t retry = ((2 * 1000000) + wait_sample - 1) / wait_sample;
+             android_logcat_run_command_thread_running(*ctx) && retry; --retry) {
+            usleep(wait_sample);
+        }
+    }
+
+    if (output) fclose(output);
+    return android_logcat_destroy(ctx);
+}
+
+int android_logcat_system(const char* command) {
+    std::vector<std::string> args;
+    std::vector<std::string> envs;
+    if (!__android_logcat_parse(command, args, envs)) return -1;
+
+    std::vector<const char*> argv;
+    for (auto& str : args) {
+        argv.push_back(str.c_str());
+    }
+    argv.push_back(NULL);
+
+    std::vector<const char*> envp;
+    for (auto& str : envs) {
+        envp.push_back(str.c_str());
+    }
+    envp.push_back(NULL);
+
+    android_logcat_context ctx = create_android_logcat();
+    if (!ctx) return -1;
+    /* Command return value */
+    int retval = android_logcat_run_command(ctx, -1, -1, argv.size() - 1,
+                                            (char* const*)&argv[0],
+                                            (char* const*)&envp[0]);
+    /* destroy return value */
+    int ret = android_logcat_destroy(&ctx);
+    /* Paranoia merging any discrepancies between the two return values */
+    if (!ret) ret = retval;
+    return ret;
+}
diff --git a/logcat/tests/Android.mk b/logcat/tests/Android.mk
index cb8b061..bfb20f0 100644
--- a/logcat/tests/Android.mk
+++ b/logcat/tests/Android.mk
@@ -48,6 +48,7 @@
 
 test_src_files := \
     logcat_test.cpp \
+    liblogcat_test.cpp \
 
 # Build tests for the device (with .so). Run with:
 #   adb shell /data/nativetest/logcat-unit-tests/logcat-unit-tests
@@ -55,6 +56,7 @@
 LOCAL_MODULE := $(test_module_prefix)unit-tests
 LOCAL_MODULE_TAGS := $(test_tags)
 LOCAL_CFLAGS += $(test_c_flags)
-LOCAL_SHARED_LIBRARIES := liblog libbase
+LOCAL_SHARED_LIBRARIES := liblog libbase liblogcat
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include
 LOCAL_SRC_FILES := $(test_src_files)
 include $(BUILD_NATIVE_TEST)
diff --git a/logcat/tests/liblogcat_test.cpp b/logcat/tests/liblogcat_test.cpp
new file mode 100644
index 0000000..9e9a2c2
--- /dev/null
+++ b/logcat/tests/liblogcat_test.cpp
@@ -0,0 +1,25 @@
+/*
+ * 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 <log/logcat.h>
+
+#define logcat_define(context) android_logcat_context context
+#define logcat_popen(context, command) android_logcat_popen(&context, command)
+#define logcat_pclose(context, fp) android_logcat_pclose(&context, fp)
+#define logcat_system(command) android_logcat_system(command)
+#define logcat liblogcat
+
+#include "logcat_test.cpp"
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index 8f2da21..9c777b3 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -33,6 +33,13 @@
 #include <log/log.h>
 #include <log/log_event_list.h>
 
+#ifndef logcat_popen
+#define logcat_define(context)
+#define logcat_popen(context, command) popen((command), "r")
+#define logcat_pclose(context, fp) pclose(fp)
+#define logcat_system(command) system(command)
+#endif
+
 #define BIG_BUFFER (5 * 1024)
 
 // enhanced version of LOG_FAILURE_RETRY to add support for EAGAIN and
@@ -53,6 +60,7 @@
 
 TEST(logcat, buckets) {
     FILE* fp;
+    logcat_define(ctx);
 
 #undef LOG_TAG
 #define LOG_TAG "inject"
@@ -61,9 +69,9 @@
 
     ASSERT_TRUE(
         NULL !=
-        (fp =
-             popen("logcat -b radio -b events -b system -b main -d 2>/dev/null",
-                   "r")));
+        (fp = logcat_popen(
+             ctx,
+             "logcat -b radio -b events -b system -b main -d 2>/dev/null")));
 
     char buffer[BIG_BUFFER];
 
@@ -81,7 +89,7 @@
         }
     }
 
-    pclose(fp);
+    logcat_pclose(ctx, fp);
 
     EXPECT_EQ(15, ids);
 
@@ -90,11 +98,13 @@
 
 TEST(logcat, event_tag_filter) {
     FILE* fp;
+    logcat_define(ctx);
 
-    ASSERT_TRUE(NULL != (fp = popen("logcat -b events -d -s auditd "
-                                    "am_proc_start am_pss am_proc_bound "
-                                    "dvm_lock_sample am_wtf 2>/dev/null",
-                                    "r")));
+    ASSERT_TRUE(NULL !=
+                (fp = logcat_popen(ctx,
+                                   "logcat -b events -d -s auditd "
+                                   "am_proc_start am_pss am_proc_bound "
+                                   "dvm_lock_sample am_wtf 2>/dev/null")));
 
     char buffer[BIG_BUFFER];
 
@@ -104,7 +114,7 @@
         ++count;
     }
 
-    pclose(fp);
+    logcat_pclose(ctx, fp);
 
     EXPECT_LT(4, count);
 }
@@ -146,6 +156,7 @@
 
     do {
         FILE* fp;
+        logcat_define(ctx);
 
         char needle[32];
         time_t now;
@@ -161,7 +172,8 @@
 
         ASSERT_TRUE(
             NULL !=
-            (fp = popen("logcat -v long -v year -b all -t 3 2>/dev/null", "r")));
+            (fp = logcat_popen(
+                 ctx, "logcat -v long -v year -b all -t 3 2>/dev/null")));
 
         char buffer[BIG_BUFFER];
 
@@ -172,7 +184,7 @@
                 ++count;
             }
         }
-        pclose(fp);
+        logcat_pclose(ctx, fp);
 
     } while ((count < 3) && --tries && inject(3 - count));
 
@@ -222,10 +234,12 @@
 
     do {
         FILE* fp;
+        logcat_define(ctx);
 
-        ASSERT_TRUE(NULL != (fp = popen("logcat -v long -v America/Los_Angeles "
-                                        "-b all -t 3 2>/dev/null",
-                                        "r")));
+        ASSERT_TRUE(NULL !=
+                    (fp = logcat_popen(ctx,
+                                       "logcat -v long -v America/Los_Angeles "
+                                       "-b all -t 3 2>/dev/null")));
 
         char buffer[BIG_BUFFER];
 
@@ -239,7 +253,7 @@
             }
         }
 
-        pclose(fp);
+        logcat_pclose(ctx, fp);
 
     } while ((count < 3) && --tries && inject(3 - count));
 
@@ -248,10 +262,12 @@
 
 TEST(logcat, ntz) {
     FILE* fp;
+    logcat_define(ctx);
 
-    ASSERT_TRUE(NULL != (fp = popen("logcat -v long -v America/Los_Angeles -v "
-                                    "zone -b all -t 3 2>/dev/null",
-                                    "r")));
+    ASSERT_TRUE(NULL !=
+                (fp = logcat_popen(ctx,
+                                   "logcat -v long -v America/Los_Angeles -v "
+                                   "zone -b all -t 3 2>/dev/null")));
 
     char buffer[BIG_BUFFER];
 
@@ -263,7 +279,7 @@
         }
     }
 
-    pclose(fp);
+    logcat_pclose(ctx, fp);
 
     ASSERT_EQ(0, count);
 }
@@ -281,7 +297,8 @@
                  "ANDROID_PRINTF_LOG=long logcat -b all -t %d 2>/dev/null", num);
 
         FILE* fp;
-        ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
+        logcat_define(ctx);
+        ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, buffer)));
 
         count = 0;
 
@@ -289,7 +306,7 @@
             ++count;
         }
 
-        pclose(fp);
+        logcat_pclose(ctx, fp);
 
     } while ((count < num) && --tries && inject(num - count));
 
@@ -326,13 +343,14 @@
     int tries = 4;  // in case run too soon after system start or buffer clear
 
     do {
-        ASSERT_TRUE(NULL != (fp = popen("logcat"
-                                        " -v long"
-                                        " -v nsec"
-                                        " -b all"
-                                        " -t 10"
-                                        " 2>&1",
-                                        "r")));
+        logcat_define(ctx);
+        ASSERT_TRUE(NULL != (fp = logcat_popen(ctx,
+                                               "logcat"
+                                               " -v long"
+                                               " -v nsec"
+                                               " -b all"
+                                               " -t 10"
+                                               " 2>&1")));
         count = 0;
 
         while ((input = fgetLongTime(buffer, sizeof(buffer), fp))) {
@@ -345,7 +363,7 @@
             free(last_timestamp);
             last_timestamp = strdup(input);
         }
-        pclose(fp);
+        logcat_pclose(ctx, fp);
 
     } while ((count < 10) && --tries && inject(10 - count));
 
@@ -362,7 +380,8 @@
              " -t '%s'"
              " 2>&1",
              first_timestamp);
-    ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
+    logcat_define(ctx);
+    ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, buffer)));
 
     int second_count = 0;
     int last_timestamp_count = -1;
@@ -402,7 +421,7 @@
             last_timestamp_count = second_count;
         }
     }
-    pclose(fp);
+    logcat_pclose(ctx, fp);
 
     EXPECT_TRUE(found);
     if (!found) {
@@ -435,9 +454,10 @@
     ASSERT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)));
 
     FILE* fp;
-    ASSERT_TRUE(
-        NULL !=
-        (fp = popen("logcat -v brief -b events -t 100 2>/dev/null", "r")));
+    logcat_define(ctx);
+    ASSERT_TRUE(NULL !=
+                (fp = logcat_popen(
+                     ctx, "logcat -v brief -b events -t 100 2>/dev/null")));
 
     char buffer[BIG_BUFFER];
 
@@ -458,16 +478,17 @@
         }
     }
 
-    pclose(fp);
+    logcat_pclose(ctx, fp);
 
     ASSERT_EQ(1, count);
 }
 
 static int get_groups(const char* cmd) {
     FILE* fp;
+    logcat_define(ctx);
 
     // NB: crash log only available in user space
-    EXPECT_TRUE(NULL != (fp = popen(cmd, "r")));
+    EXPECT_TRUE(NULL != (fp = logcat_popen(ctx, cmd)));
 
     if (fp == NULL) {
         return 0;
@@ -531,7 +552,7 @@
         }
     }
 
-    pclose(fp);
+    logcat_pclose(ctx, fp);
 
     return count;
 }
@@ -555,6 +576,7 @@
             "logcat -v brief -b radio,events,bogo,system,main -g 2>/dev/null"));
 }
 
+#ifndef logcat
 static void caught_blocking(int signum) {
     unsigned long long v = 0xDEADBEEFA55A0000ULL;
 
@@ -692,6 +714,7 @@
 
     EXPECT_EQ(1, signals);
 }
+#endif
 
 // meant to be handed to ASSERT_FALSE / EXPECT_FALSE to expand the message
 static testing::AssertionResult IsFalse(int ret, const char* command) {
@@ -712,7 +735,7 @@
     snprintf(command, sizeof(command), comm, buf);
 
     int ret;
-    EXPECT_FALSE(IsFalse(ret = system(command), command));
+    EXPECT_FALSE(IsFalse(ret = logcat_system(command), command));
     if (!ret) {
         snprintf(command, sizeof(command), "ls -s %s 2>/dev/null", buf);
 
@@ -758,7 +781,7 @@
     snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir);
 
     int ret;
-    EXPECT_FALSE(IsFalse(ret = system(command), command));
+    EXPECT_FALSE(IsFalse(ret = logcat_system(command), command));
     if (!ret) {
         snprintf(command, sizeof(command), "ls %s 2>/dev/null", tmp_out_dir);
 
@@ -817,7 +840,7 @@
     snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir, log_filename);
 
     int ret;
-    EXPECT_FALSE(IsFalse(ret = system(command), command));
+    EXPECT_FALSE(IsFalse(ret = logcat_system(command), command));
     if (ret) {
         snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
         EXPECT_FALSE(IsFalse(system(command), command));
@@ -866,7 +889,7 @@
     // 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(IsFalse(ret = system(command), command));
+    EXPECT_FALSE(IsFalse(ret = logcat_system(command), command));
     if (ret) {
         snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
         EXPECT_FALSE(IsFalse(system(command), command));
@@ -948,7 +971,7 @@
                  tmp_out_dir, log_filename, num_val);
 
         int ret;
-        EXPECT_FALSE(IsFalse(ret = system(command), command));
+        EXPECT_FALSE(IsFalse(ret = logcat_system(command), command));
         if (ret) {
             snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
             EXPECT_FALSE(IsFalse(system(command), command));
@@ -978,7 +1001,7 @@
         strcat(command, clear_cmd);
 
         int ret;
-        EXPECT_FALSE(IsFalse(ret = system(command), command));
+        EXPECT_FALSE(IsFalse(ret = logcat_system(command), command));
         if (ret) {
             snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
             EXPECT_FALSE(system(command));
@@ -1016,7 +1039,7 @@
 
     snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir, log_filename);
 
-    int ret = system(command);
+    int ret = logcat_system(command);
     if (ret) {
         fprintf(stderr, "system(\"%s\")=%d", command, ret);
         return -1;
@@ -1090,9 +1113,10 @@
         "logcat -b all -d"
         " -f /das/nein/gerfingerpoken/logcat/log.txt"
         " -n 256 -r 1024";
-    EXPECT_FALSE(IsFalse(0 == system(command), command));
+    EXPECT_FALSE(IsFalse(0 == logcat_system(command), command));
 }
 
+#ifndef logcat
 static void caught_blocking_clear(int signum) {
     unsigned long long v = 0xDEADBEEFA55C0000ULL;
 
@@ -1216,11 +1240,13 @@
 
     EXPECT_EQ(1, signals);
 }
+#endif
 
 static bool get_white_black(char** list) {
     FILE* fp;
+    logcat_define(ctx);
 
-    fp = popen("logcat -p 2>/dev/null", "r");
+    fp = logcat_popen(ctx, "logcat -p 2>/dev/null");
     if (fp == NULL) {
         fprintf(stderr, "ERROR: logcat -p 2>/dev/null\n");
         return false;
@@ -1248,17 +1274,18 @@
             asprintf(list, "%s", buf);
         }
     }
-    pclose(fp);
+    logcat_pclose(ctx, fp);
     return *list != NULL;
 }
 
 static bool set_white_black(const char* list) {
     FILE* fp;
+    logcat_define(ctx);
 
     char buffer[BIG_BUFFER];
 
     snprintf(buffer, sizeof(buffer), "logcat -P '%s' 2>&1", list ? list : "");
-    fp = popen(buffer, "r");
+    fp = logcat_popen(ctx, buffer);
     if (fp == NULL) {
         fprintf(stderr, "ERROR: %s\n", buffer);
         return false;
@@ -1277,10 +1304,10 @@
             continue;
         }
         fprintf(stderr, "%s\n", buf);
-        pclose(fp);
+        logcat_pclose(ctx, fp);
         return false;
     }
-    return pclose(fp) == 0;
+    return logcat_pclose(ctx, fp) == 0;
 }
 
 TEST(logcat, white_black_adjust) {
@@ -1315,44 +1342,51 @@
 
 TEST(logcat, regex) {
     FILE* fp;
+    logcat_define(ctx);
     int count = 0;
 
     char buffer[BIG_BUFFER];
+// Have to make liblogcat data unique from logcat data injection
+#ifdef logcat
+#define logcat_regex_prefix "lolcat_test"
+#else
+#define logcat_regex_prefix "logcat_test"
+#endif
 
-    snprintf(buffer, sizeof(buffer), "logcat --pid %d -d -e logcat_test_a+b",
-             getpid());
+    snprintf(buffer, sizeof(buffer),
+             "logcat --pid %d -d -e " logcat_regex_prefix "_a+b", getpid());
 
-    LOG_FAILURE_RETRY(
-        __android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test_ab"));
-    LOG_FAILURE_RETRY(
-        __android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test_b"));
-    LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test",
-                                          "logcat_test_aaaab"));
-    LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test",
-                                          "logcat_test_aaaa"));
-
+    LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, logcat_regex_prefix,
+                                          logcat_regex_prefix "_ab"));
+    LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, logcat_regex_prefix,
+                                          logcat_regex_prefix "_b"));
+    LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, logcat_regex_prefix,
+                                          logcat_regex_prefix "_aaaab"));
+    LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, logcat_regex_prefix,
+                                          logcat_regex_prefix "_aaaa"));
     // Let the logs settle
     sleep(1);
 
-    ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
+    ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, buffer)));
 
     while (fgets(buffer, sizeof(buffer), fp)) {
         if (!strncmp(begin, buffer, sizeof(begin) - 1)) {
             continue;
         }
 
-        EXPECT_TRUE(strstr(buffer, "logcat_test_") != NULL);
+        EXPECT_TRUE(strstr(buffer, logcat_regex_prefix "_") != NULL);
 
         count++;
     }
 
-    pclose(fp);
+    logcat_pclose(ctx, fp);
 
     ASSERT_EQ(2, count);
 }
 
 TEST(logcat, maxcount) {
     FILE* fp;
+    logcat_define(ctx);
     int count = 0;
 
     char buffer[BIG_BUFFER];
@@ -1372,7 +1406,7 @@
     // Let the logs settle
     sleep(1);
 
-    ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
+    ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, buffer)));
 
     while (fgets(buffer, sizeof(buffer), fp)) {
         if (!strncmp(begin, buffer, sizeof(begin) - 1)) {
@@ -1382,7 +1416,7 @@
         count++;
     }
 
-    pclose(fp);
+    logcat_pclose(ctx, fp);
 
     ASSERT_EQ(3, count);
 }
@@ -1394,14 +1428,14 @@
     ;
 
 static bool End_to_End(const char* tag, const char* fmt, ...) {
-    FILE* fp = popen(
-        "logcat"
-        " -v brief"
-        " -b events"
-        " -v descriptive"
-        " -t 100"
-        " 2>/dev/null",
-        "r");
+    logcat_define(ctx);
+    FILE* fp = logcat_popen(ctx,
+                            "logcat"
+                            " -v brief"
+                            " -b events"
+                            " -v descriptive"
+                            " -t 100"
+                            " 2>/dev/null");
     if (!fp) {
         fprintf(stderr, "End_to_End: popen failed");
         return false;
@@ -1436,17 +1470,19 @@
         }
     }
 
-    pclose(fp);
+    logcat_pclose(ctx, fp);
 
     if ((count == 0) && (lastMatch.length() > 0)) {
         // Help us pinpoint where things went wrong ...
         fprintf(stderr, "Closest match for\n    %s\n  is\n    %s",
                 expect.c_str(), lastMatch.c_str());
-    } else if (count > 1) {
+    } else if (count > 2) {
         fprintf(stderr, "Too many matches (%d) for %s\n", count, expect.c_str());
     }
 
-    return count == 1;
+    // Expect one the first time around as either liblogcat.descriptive or
+    // logcat.descriptive.  Expect two the second time as the other.
+    return count == 1 || count == 2;
 }
 
 TEST(logcat, descriptive) {
@@ -1568,12 +1604,13 @@
 }
 
 static bool reportedSecurity(const char* command) {
-    FILE* fp = popen(command, "r");
+    logcat_define(ctx);
+    FILE* fp = logcat_popen(ctx, command);
     if (!fp) return true;
 
     std::string ret;
     bool val = android::base::ReadFdToString(fileno(fp), &ret);
-    pclose(fp);
+    logcat_pclose(ctx, fp);
 
     if (!val) return true;
     return std::string::npos != ret.find("'security'");