Merge "Add dataspace to match upstream EGL extension"
diff --git a/adf/libadfhwc/adfhwc.cpp b/adf/libadfhwc/adfhwc.cpp
index a97862a..63c0f75 100644
--- a/adf/libadfhwc/adfhwc.cpp
+++ b/adf/libadfhwc/adfhwc.cpp
@@ -166,6 +166,71 @@
     return 0;
 }
 
+static int32_t adf_display_attribute_hwc2(const adf_interface_data &data,
+        const drm_mode_modeinfo &mode, const uint32_t attribute)
+{
+    switch (attribute) {
+    case HWC2_ATTRIBUTE_VSYNC_PERIOD:
+        if (mode.vrefresh)
+            return 1000000000 / mode.vrefresh;
+        return 0;
+
+    case HWC2_ATTRIBUTE_WIDTH:
+        return mode.hdisplay;
+
+    case HWC2_ATTRIBUTE_HEIGHT:
+        return mode.vdisplay;
+
+    case HWC2_ATTRIBUTE_DPI_X:
+        return dpi(mode.hdisplay, data.width_mm);
+
+    case HWC2_ATTRIBUTE_DPI_Y:
+        return dpi(mode.vdisplay, data.height_mm);
+
+    default:
+        ALOGE("unknown display attribute %u", attribute);
+        return -EINVAL;
+    }
+}
+
+int adf_getDisplayAttributes_hwc2(struct adf_hwc_helper *dev, int disp,
+        uint32_t config, const uint32_t *attributes, int32_t *values)
+{
+    if ((size_t)disp >= dev->intf_fds.size())
+        return -EINVAL;
+
+    if (config >= dev->display_configs.size())
+        return -EINVAL;
+
+    adf_interface_data data;
+    int err = adf_get_interface_data(dev->intf_fds[disp], &data);
+    if (err < 0) {
+        ALOGE("failed to get ADF interface data: %s", strerror(err));
+        return err;
+    }
+
+    for (int i = 0; attributes[i] != HWC2_ATTRIBUTE_INVALID; i++)
+        values[i] = adf_display_attribute_hwc2(data,
+                dev->display_configs[config], attributes[i]);
+
+    adf_free_interface_data(&data);
+    return 0;
+}
+
+int adf_set_active_config_hwc2(struct adf_hwc_helper *dev, int disp,
+        uint32_t config)
+{
+    if ((size_t)disp >= dev->intf_fds.size())
+        return -EINVAL;
+
+    if (config >= dev->display_configs.size())
+        return -EINVAL;
+
+    struct drm_mode_modeinfo mode = dev->display_configs[config];
+
+    return adf_interface_set_mode(dev->intf_fds[disp], &mode);
+}
+
 static void handle_adf_event(struct adf_hwc_helper *dev, int disp)
 {
     adf_event *event;
@@ -208,28 +273,39 @@
 
     setpriority(PRIO_PROCESS, 0, HAL_PRIORITY_URGENT_DISPLAY);
 
-    pollfd *fds = new pollfd[dev->intf_fds.size()];
+    struct sigaction action = { };
+    sigemptyset(&action.sa_mask);
+    action.sa_flags = 0;
+    action.sa_handler = [](int) { pthread_exit(0); };
+
+    if (sigaction(SIGUSR2, &action, NULL) < 0) {
+        ALOGE("failed to set thread exit action %s", strerror(errno));
+        return NULL;
+    }
+
+    sigset_t signal_set;
+    sigemptyset(&signal_set);
+    sigaddset(&signal_set, SIGUSR2);
+
+    pthread_sigmask(SIG_UNBLOCK, &signal_set, NULL);
+
+    pollfd fds[dev->intf_fds.size()];
     for (size_t i = 0; i < dev->intf_fds.size(); i++) {
         fds[i].fd = dev->intf_fds[i];
         fds[i].events = POLLIN | POLLPRI;
     }
 
     while (true) {
-        int err = poll(fds, dev->intf_fds.size(), -1);
-
-        if (err > 0) {
-            for (size_t i = 0; i < dev->intf_fds.size(); i++)
-                if (fds[i].revents & (POLLIN | POLLPRI))
-                    handle_adf_event(dev, i);
-        }
-        else if (err == -1) {
-            if (errno == EINTR)
-                break;
+        if (TEMP_FAILURE_RETRY(poll(fds, dev->intf_fds.size(), -1)) < 0) {
             ALOGE("error in event thread: %s", strerror(errno));
+            break;
         }
+
+        for (size_t i = 0; i < dev->intf_fds.size(); i++)
+            if (fds[i].revents & (POLLIN | POLLPRI))
+                handle_adf_event(dev, i);
     }
 
-    delete [] fds;
     return NULL;
 }
 
@@ -264,6 +340,12 @@
         }
     }
 
+    sigset_t signal_set;
+    sigemptyset(&signal_set);
+    sigaddset(&signal_set, SIGUSR2);
+
+    pthread_sigmask(SIG_BLOCK, &signal_set, NULL);
+
     ret = pthread_create(&dev_ret->event_thread, NULL, adf_event_thread,
             dev_ret);
     if (ret) {
@@ -284,7 +366,7 @@
 
 void adf_hwc_close(struct adf_hwc_helper *dev)
 {
-    pthread_kill(dev->event_thread, SIGTERM);
+    pthread_kill(dev->event_thread, SIGUSR2);
     pthread_join(dev->event_thread, NULL);
 
     for (size_t i = 0; i < dev->intf_fds.size(); i++)
diff --git a/adf/libadfhwc/include/adfhwc/adfhwc.h b/adf/libadfhwc/include/adfhwc/adfhwc.h
index 71e7624..4f70925 100644
--- a/adf/libadfhwc/include/adfhwc/adfhwc.h
+++ b/adf/libadfhwc/include/adfhwc/adfhwc.h
@@ -23,6 +23,7 @@
 #include <video/adf.h>
 
 #include <hardware/hwcomposer.h>
+#include <hardware/hwcomposer2.h>
 
 struct adf_hwc_helper;
 
@@ -123,6 +124,16 @@
         uint32_t *configs, size_t *numConfigs);
 int adf_getDisplayAttributes(struct adf_hwc_helper *dev, int disp,
         uint32_t config, const uint32_t *attributes, int32_t *values);
+/**
+ * Generic implementation of common HWC2 functions.
+ *
+ * The HWC2 should not return these functions directly through getFunction.
+ * Instead, the HWC2 should return stub functions which call these helpers.
+ */
+int adf_getDisplayAttributes_hwc2(struct adf_hwc_helper *dev, int disp,
+        uint32_t config, const uint32_t *attributes, int32_t *values);
+int adf_set_active_config_hwc2(struct adf_hwc_helper *dev, int disp,
+        uint32_t config);
 
 __END_DECLS
 
diff --git a/debuggerd/client/debuggerd_client.cpp b/debuggerd/client/debuggerd_client.cpp
index 81d70df..f2975d1 100644
--- a/debuggerd/client/debuggerd_client.cpp
+++ b/debuggerd/client/debuggerd_client.cpp
@@ -45,42 +45,6 @@
   return true;
 }
 
-static bool check_dumpable(pid_t pid) {
-  // /proc/<pid> is owned by the effective UID of the process.
-  // Ownership of most of the other files in /proc/<pid> varies based on PR_SET_DUMPABLE.
-  // If PR_GET_DUMPABLE would return 0, they're owned by root, instead.
-  std::string proc_pid_path = android::base::StringPrintf("/proc/%d/", pid);
-  std::string proc_pid_status_path = proc_pid_path + "/status";
-
-  unique_fd proc_pid_fd(open(proc_pid_path.c_str(), O_DIRECTORY | O_RDONLY | O_CLOEXEC));
-  if (proc_pid_fd == -1) {
-    return false;
-  }
-  unique_fd proc_pid_status_fd(openat(proc_pid_fd, "status", O_RDONLY | O_CLOEXEC));
-  if (proc_pid_status_fd == -1) {
-    return false;
-  }
-
-  struct stat proc_pid_st;
-  struct stat proc_pid_status_st;
-  if (fstat(proc_pid_fd.get(), &proc_pid_st) != 0 ||
-      fstat(proc_pid_status_fd.get(), &proc_pid_status_st) != 0) {
-    return false;
-  }
-
-  // We can't figure out if a process is dumpable if its effective UID is root, but that's fine
-  // because being root bypasses the PR_SET_DUMPABLE check for ptrace.
-  if (proc_pid_st.st_uid == 0) {
-    return true;
-  }
-
-  if (proc_pid_status_st.st_uid == 0) {
-    return false;
-  }
-
-  return true;
-}
-
 bool debuggerd_trigger_dump(pid_t pid, unique_fd output_fd, DebuggerdDumpType dump_type,
                             int timeout_ms) {
   LOG(INFO) << "libdebuggerd_client: started dumping process " << pid;
@@ -114,11 +78,6 @@
     return true;
   };
 
-  if (!check_dumpable(pid)) {
-    dprintf(output_fd.get(), "target pid %d is not dumpable\n", pid);
-    return true;
-  }
-
   sockfd.reset(socket(AF_LOCAL, SOCK_SEQPACKET, 0));
   if (sockfd == -1) {
     PLOG(ERROR) << "libdebugger_client: failed to create socket";
diff --git a/init/Android.mk b/init/Android.mk
index 18cbedc..2fc6f19 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -107,6 +107,24 @@
     libnl \
     libavb
 
+# Include SELinux policy. We do this here because different modules
+# need to be included based on the value of PRODUCT_FULL_TREBLE. This
+# type of conditional inclusion cannot be done in top-level files such
+# as build/target/product/embedded.mk.
+# This conditional inclusion closely mimics the conditional logic
+# inside init/init.cpp for loading SELinux policy from files.
+ifeq ($(PRODUCT_FULL_TREBLE),true)
+# Use split SELinux policy
+LOCAL_REQUIRED_MODULES += \
+    mapping_sepolicy.cil \
+    nonplat_sepolicy.cil \
+    plat_sepolicy.cil \
+    secilc
+else
+# Use monolithic SELinux policy
+LOCAL_REQUIRED_MODULES += sepolicy
+endif
+
 # Create symlinks.
 LOCAL_POST_INSTALL_CMD := $(hide) mkdir -p $(TARGET_ROOT_OUT)/sbin; \
     ln -sf ../init $(TARGET_ROOT_OUT)/sbin/ueventd; \
diff --git a/init/init.cpp b/init/init.cpp
index ad40426..53e7482 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -43,6 +43,7 @@
 #include <android-base/file.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
+#include <android-base/unique_fd.h>
 #include <cutils/fs.h>
 #include <cutils/iosched_policy.h>
 #include <cutils/list.h>
@@ -618,6 +619,133 @@
     return 0;
 }
 
+/*
+ * Forks, executes the provided program in the child, and waits for the completion in the parent.
+ *
+ * Returns true if the child exited with status code 0, returns false otherwise.
+ */
+static bool fork_execve_and_wait_for_completion(const char* filename, char* const argv[],
+                                                char* const envp[]) {
+    pid_t child_pid = fork();
+    if (child_pid == -1) {
+        PLOG(ERROR) << "Failed to fork for " << filename;
+        return false;
+    }
+
+    if (child_pid == 0) {
+        // fork succeeded -- this is executing in the child process
+        if (execve(filename, argv, envp) == -1) {
+            PLOG(ERROR) << "Failed to execve " << filename;
+            return false;
+        }
+        // Unreachable because execve will have succeeded and replaced this code
+        // with child process's code.
+        _exit(127);
+        return false;
+    } else {
+        // fork succeeded -- this is executing in the original/parent process
+        int status;
+        if (TEMP_FAILURE_RETRY(waitpid(child_pid, &status, 0)) != child_pid) {
+            PLOG(ERROR) << "Failed to wait for " << filename;
+            return false;
+        }
+
+        if (WIFEXITED(status)) {
+            int status_code = WEXITSTATUS(status);
+            if (status_code == 0) {
+                return true;
+            } else {
+                LOG(ERROR) << filename << " exited with status " << status_code;
+            }
+        } else if (WIFSIGNALED(status)) {
+            LOG(ERROR) << filename << " killed by signal " << WTERMSIG(status);
+        } else if (WIFSTOPPED(status)) {
+            LOG(ERROR) << filename << " stopped by signal " << WSTOPSIG(status);
+        } else {
+            LOG(ERROR) << "waitpid for " << filename << " returned unexpected status: " << status;
+        }
+
+        return false;
+    }
+}
+
+static constexpr const char plat_policy_cil_file[] = "/plat_sepolicy.cil";
+
+static bool selinux_is_split_policy_device() { return access(plat_policy_cil_file, R_OK) != -1; }
+
+/*
+ * Loads SELinux policy split across platform/system and non-platform/vendor files.
+ *
+ * Returns true upon success, false otherwise (failure cause is logged).
+ */
+static bool selinux_load_split_policy() {
+    // IMPLEMENTATION NOTE: Split policy consists of three CIL files:
+    // * platform -- policy needed due to logic contained in the system image,
+    // * non-platform -- policy needed due to logic contained in the vendor image,
+    // * mapping -- mapping policy which helps preserve forward-compatibility of non-platform policy
+    //   with newer versions of platform policy.
+    //
+    // secilc is invoked to compile the above three policy files into a single monolithic policy
+    // file. This file is then loaded into the kernel.
+
+    LOG(INFO) << "Compiling SELinux policy";
+
+    // We store the output of the compilation on /dev because this is the most convenient tmpfs
+    // storage mount available this early in the boot sequence.
+    char compiled_sepolicy[] = "/dev/sepolicy.XXXXXX";
+    android::base::unique_fd compiled_sepolicy_fd(mkostemp(compiled_sepolicy, O_CLOEXEC));
+    if (compiled_sepolicy_fd < 0) {
+        PLOG(ERROR) << "Failed to create temporary file " << compiled_sepolicy;
+        return false;
+    }
+
+    const char* compile_args[] = {"/system/bin/secilc", plat_policy_cil_file, "-M", "true", "-c",
+                                  "30",  // TODO: pass in SELinux policy version from build system
+                                  "/mapping_sepolicy.cil", "/nonplat_sepolicy.cil", "-o",
+                                  compiled_sepolicy,
+                                  // We don't care about file_contexts output by the compiler
+                                  "-f", "/sys/fs/selinux/null",  // /dev/null is not yet available
+                                  nullptr};
+
+    if (!fork_execve_and_wait_for_completion(compile_args[0], (char**)compile_args, (char**)ENV)) {
+        unlink(compiled_sepolicy);
+        return false;
+    }
+    unlink(compiled_sepolicy);
+
+    LOG(INFO) << "Loading compiled SELinux policy";
+    if (selinux_android_load_policy_from_fd(compiled_sepolicy_fd, compiled_sepolicy) < 0) {
+        LOG(ERROR) << "Failed to load SELinux policy from " << compiled_sepolicy;
+        return false;
+    }
+
+    return true;
+}
+
+/*
+ * Loads SELinux policy from a monolithic file.
+ *
+ * Returns true upon success, false otherwise (failure cause is logged).
+ */
+static bool selinux_load_monolithic_policy() {
+    LOG(VERBOSE) << "Loading SELinux policy from monolithic file";
+    if (selinux_android_load_policy() < 0) {
+        PLOG(ERROR) << "Failed to load monolithic SELinux policy";
+        return false;
+    }
+    return true;
+}
+
+/*
+ * Loads SELinux policy into the kernel.
+ *
+ * Returns true upon success, false otherwise (failure cause is logged).
+ */
+static bool selinux_load_policy() {
+    return selinux_is_split_policy_device() ? selinux_load_split_policy()
+                                            : selinux_load_monolithic_policy();
+}
+
 static void selinux_initialize(bool in_kernel_domain) {
     Timer t;
 
@@ -628,10 +756,9 @@
     selinux_set_callback(SELINUX_CB_AUDIT, cb);
 
     if (in_kernel_domain) {
-        LOG(INFO) << "Loading SELinux policy...";
-        if (selinux_android_load_policy() < 0) {
-            PLOG(ERROR) << "failed to load policy";
-            security_failure();
+        LOG(INFO) << "Loading SELinux policy";
+        if (!selinux_load_policy()) {
+            panic();
         }
 
         bool kernel_enforcing = (security_getenforce() == 1);
diff --git a/init/property_service.cpp b/init/property_service.cpp
index decd644..d88b72e 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -609,8 +609,7 @@
     load_override_properties();
     /* Read persistent properties after all default values have been loaded. */
     load_persistent_properties();
-    uint64_t start_ns = boot_clock::now().time_since_epoch().count();
-    property_set("ro.boottime.persistent_properties", StringPrintf("%" PRIu64, start_ns).c_str());
+    property_set("ro.persistent_properties.ready", "true");
 }
 
 void load_recovery_id_prop() {
diff --git a/libmemtrack/memtrack.cpp b/libmemtrack/memtrack.cpp
index e83f181..c5e74c1 100644
--- a/libmemtrack/memtrack.cpp
+++ b/libmemtrack/memtrack.cpp
@@ -31,6 +31,7 @@
 using android::hardware::memtrack::V1_0::MemtrackFlag;
 using android::hardware::memtrack::V1_0::MemtrackStatus;
 using android::hardware::hidl_vec;
+using android::hardware::Return;
 
 struct memtrack_proc_type {
     MemtrackType type;
@@ -69,7 +70,7 @@
     if (memtrack == nullptr)
         return -1;
 
-    memtrack->getMemory(pid, type,
+    Return<void> ret = memtrack->getMemory(pid, type,
         [&t, &err](MemtrackStatus status, hidl_vec<MemtrackRecord> records) {
             if (status != MemtrackStatus::SUCCESS) {
                 err = -1;
@@ -81,7 +82,7 @@
                 t->records[i].flags = records[i].flags;
             }
     });
-    return err;
+    return ret.isOk() ? err : -1;
 }
 
 /* TODO: sanity checks on return values from HALs:
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index d67dee5..077332a 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; // duplication of fileno(output) (below)
-    int error_fd;  // duplication of fileno(error) (below)
+    int output_fd;  // duplication of fileno(output) (below)
+    int error_fd;   // duplication of fileno(error) (below)
 
     // library
     int fds[2];    // From popen call
@@ -108,7 +108,7 @@
 
     context = (android_logcat_context_internal*)calloc(
         1, sizeof(android_logcat_context_internal));
-    if (!context) return NULL;
+    if (!context) return nullptr;
 
     context->fds[0] = -1;
     context->fds[1] = -1;
@@ -139,10 +139,10 @@
     log_device_t(const char* d, bool b) {
         device = d;
         binary = b;
-        next = NULL;
+        next = nullptr;
         printed = false;
-        logger = NULL;
-        logger_list = NULL;
+        logger = nullptr;
+        logger_list = nullptr;
     }
 };
 
@@ -162,7 +162,7 @@
 static void close_output(android_logcat_context_internal* context) {
     // split output_from_error
     if (context->error == context->output) {
-        context->output = NULL;
+        context->output = nullptr;
         context->output_fd = -1;
     }
     if (context->error && (context->output_fd == fileno(context->error))) {
@@ -182,7 +182,7 @@
             }
             fclose(context->output);
         }
-        context->output = NULL;
+        context->output = nullptr;
     }
     if (context->output_fd >= 0) {
         if (context->output_fd != fileno(stdout)) {
@@ -198,7 +198,7 @@
 static void close_error(android_logcat_context_internal* context) {
     // split error_from_output
     if (context->output == context->error) {
-        context->error = NULL;
+        context->error = nullptr;
         context->error_fd = -1;
     }
     if (context->output && (context->error_fd == fileno(context->output))) {
@@ -218,14 +218,12 @@
             }
             fclose(context->error);
         }
-        context->error = NULL;
+        context->error = nullptr;
     }
     if (context->error_fd >= 0) {
         if ((context->error_fd != fileno(stdout)) &&
             (context->error_fd != fileno(stderr))) {
-            if (context->fds[1] == context->error_fd) {
-                context->fds[1] = -1;
-            }
+            if (context->fds[1] == context->error_fd) context->fds[1] = -1;
             close(context->error_fd);
         }
         context->error_fd = -1;
@@ -236,9 +234,7 @@
     int err;
 
     // Can't rotate logs if we're not outputting to a file
-    if (context->outputFileName == NULL) {
-        return;
-    }
+    if (!context->outputFileName) return;
 
     close_output(context);
 
@@ -257,7 +253,7 @@
             "%s.%.*d", context->outputFileName, maxRotationCountDigits, i);
 
         std::string file0;
-        if (i - 1 == 0) {
+        if (!(i - 1)) {
             file0 = android::base::StringPrintf("%s", context->outputFileName);
         } else {
             file0 =
@@ -265,7 +261,7 @@
                                             maxRotationCountDigits, i - 1);
         }
 
-        if ((file0.length() == 0) || (file1.length() == 0)) {
+        if (!file0.length() || !file1.length()) {
             perror("while rotating log files");
             break;
         }
@@ -284,7 +280,7 @@
         return;
     }
     context->output = fdopen(context->output_fd, "web");
-    if (context->output == NULL) {
+    if (!context->output) {
         logcat_panic(context, HELP_FALSE, "couldn't fdopen output file");
         return;
     }
@@ -305,9 +301,7 @@
 
 static bool regexOk(android_logcat_context_internal* context,
                     const AndroidLogEntry& entry) {
-    if (!context->regex) {
-        return true;
-    }
+    if (!context->regex) return true;
 
     std::string messageString(entry.message, entry.messageLen);
 
@@ -323,7 +317,7 @@
 
     if (dev->binary) {
         if (!context->eventTagMap && !context->hasOpenedEventTagMap) {
-            context->eventTagMap = android_openEventTagMap(NULL);
+            context->eventTagMap = android_openEventTagMap(nullptr);
             context->hasOpenedEventTagMap = true;
         }
         err = android_log_processBinaryLogBuffer(
@@ -334,9 +328,7 @@
     } else {
         err = android_log_processLogBuffer(&buf->entry_v1, &entry);
     }
-    if ((err < 0) && !context->debug) {
-        return;
-    }
+    if ((err < 0) && !context->debug) return;
 
     if (android_log_shouldPrintLine(
             context->logformat, std::string(entry.tag, entry.tagLen).c_str(),
@@ -381,7 +373,7 @@
 
 static void setupOutputAndSchedulingPolicy(
     android_logcat_context_internal* context, bool blocking) {
-    if (context->outputFileName == NULL) return;
+    if (!context->outputFileName) return;
 
     if (blocking) {
         // Lower priority and set to batch scheduling if we are saving
@@ -454,6 +446,8 @@
                     "                  and individually flagged modifying adverbs can be added:\n"
                     "                    color descriptive epoch monotonic printable uid\n"
                     "                    usec UTC year zone\n"
+                    "                  Multiple -v parameters or comma separated list of format and\n"
+                    "                  format modifiers are allowed.\n"
                     // private and undocumented nsec, no signal, too much noise
                     // useful for -T or -t <timestamp> accurate testing though.
                     "  -D, --dividers  Print dividers between each log buffer\n"
@@ -562,10 +556,8 @@
 
     format = android_log_formatFromString(formatString);
 
-    if (format == FORMAT_OFF) {
-        // FORMAT_OFF means invalid string
-        return -1;
-    }
+    // invalid string?
+    if (format == FORMAT_OFF) return -1;
 
     return android_log_setPrintFormat(context->logformat, format);
 }
@@ -592,21 +584,15 @@
 // String to unsigned int, returns -1 if it fails
 static bool getSizeTArg(const char* ptr, size_t* val, size_t min = 0,
                         size_t max = SIZE_MAX) {
-    if (!ptr) {
-        return false;
-    }
+    if (!ptr) return false;
 
     char* endp;
     errno = 0;
     size_t ret = (size_t)strtoll(ptr, &endp, 0);
 
-    if (endp[0] || errno) {
-        return false;
-    }
+    if (endp[0] || errno) return false;
 
-    if ((ret > max) || (ret < min)) {
-        return false;
-    }
+    if ((ret > max) || (ret < min)) return false;
 
     *val = ret;
     return true;
@@ -642,22 +628,16 @@
 
 static char* parseTime(log_time& t, const char* cp) {
     char* ep = t.strptime(cp, "%m-%d %H:%M:%S.%q");
-    if (ep) {
-        return ep;
-    }
+    if (ep) return ep;
     ep = t.strptime(cp, "%Y-%m-%d %H:%M:%S.%q");
-    if (ep) {
-        return ep;
-    }
+    if (ep) return ep;
     return t.strptime(cp, "%s.%q");
 }
 
 // Find last logged line in <outputFileName>, or <outputFileName>.1
 static log_time lastLogTime(char* outputFileName) {
     log_time retval(log_time::EPOCH);
-    if (!outputFileName) {
-        return retval;
-    }
+    if (!outputFileName) return retval;
 
     std::string directory;
     char* file = strrchr(outputFileName, '/');
@@ -673,9 +653,7 @@
 
     std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(directory.c_str()),
                                             closedir);
-    if (!dir.get()) {
-        return retval;
-    }
+    if (!dir.get()) return retval;
 
     log_time now(android_log_clockid());
 
@@ -683,10 +661,10 @@
     log_time modulo(0, NS_PER_SEC);
     struct dirent* dp;
 
-    while ((dp = readdir(dir.get())) != NULL) {
-        if ((dp->d_type != DT_REG) || (strncmp(dp->d_name, file, len) != 0) ||
+    while (!!(dp = readdir(dir.get()))) {
+        if ((dp->d_type != DT_REG) || !!strncmp(dp->d_name, file, len) ||
             (dp->d_name[len] && ((dp->d_name[len] != '.') ||
-                                 (strtoll(dp->d_name + 1, NULL, 10) != 1)))) {
+                                 (strtoll(dp->d_name + 1, nullptr, 10) != 1)))) {
             continue;
         }
 
@@ -694,17 +672,13 @@
         file_name += "/";
         file_name += dp->d_name;
         std::string file;
-        if (!android::base::ReadFileToString(file_name, &file)) {
-            continue;
-        }
+        if (!android::base::ReadFileToString(file_name, &file)) continue;
 
         bool found = false;
         for (const auto& line : android::base::Split(file, "\n")) {
             log_time t(log_time::EPOCH);
             char* ep = parseTime(t, line.c_str());
-            if (!ep || (*ep != ' ')) {
-                continue;
-            }
+            if (!ep || (*ep != ' ')) continue;
             // determine the time precision of the logs (eg: msec or usec)
             for (unsigned long mod = 1UL; mod < modulo.tv_nsec; mod *= 10) {
                 if (t.tv_nsec % (mod * 10)) {
@@ -722,13 +696,9 @@
             }
         }
         // We count on the basename file to be the definitive end, so stop here.
-        if (!dp->d_name[len] && found) {
-            break;
-        }
+        if (!dp->d_name[len] && found) break;
     }
-    if (retval == log_time::EPOCH) {
-        return retval;
-    }
+    if (retval == log_time::EPOCH) return retval;
     // tail_time prints matching or higher, round up by the modulo to prevent
     // a replay of the last entry we have just checked.
     retval += modulo;
@@ -736,32 +706,29 @@
 }
 
 const char* getenv(android_logcat_context_internal* context, const char* name) {
-    if (!context->envp || !name || !*name) return NULL;
+    if (!context->envp || !name || !*name) return nullptr;
 
     for (size_t len = strlen(name), i = 0; context->envp[i]; ++i) {
         if (strncmp(context->envp[i], name, len)) continue;
         if (context->envp[i][len] == '=') return &context->envp[i][len + 1];
     }
-    return NULL;
+    return nullptr;
 }
 
 }  // namespace android
 
 void reportErrorName(const char** current, const char* name,
                      bool blockSecurity) {
-    if (*current) {
-        return;
+    if (*current) return;
+    if (!blockSecurity || (android_name_to_log_id(name) != LOG_ID_SECURITY)) {
+        *current = name;
     }
-    if (blockSecurity && (android_name_to_log_id(name) == LOG_ID_SECURITY)) {
-        return;
-    }
-    *current = name;
 }
 
 static int __logcat(android_logcat_context_internal* context) {
     using namespace android;
     int err;
-    int hasSetLogFormat = 0;
+    bool hasSetLogFormat = false;
     bool clearLog = false;
     bool allSelected = false;
     bool getLogSize = false;
@@ -769,11 +736,11 @@
     bool printStatistics = false;
     bool printDividers = false;
     unsigned long setLogSize = 0;
-    char* setPruneList = NULL;
-    char* setId = NULL;
+    char* setPruneList = nullptr;
+    char* setId = nullptr;
     int mode = ANDROID_LOG_RDONLY;
     std::string forceFilters;
-    log_device_t* devices = NULL;
+    log_device_t* devices = nullptr;
     log_device_t* dev;
     struct logger_list* logger_list;
     size_t tail_lines = 0;
@@ -783,10 +750,10 @@
 
     // object instantiations before goto's can happen
     log_device_t unexpected("unexpected", false);
-    const char* openDeviceFail = NULL;
-    const char* clearFail = NULL;
-    const char* setSizeFail = NULL;
-    const char* getSizeFail = NULL;
+    const char* openDeviceFail = nullptr;
+    const char* clearFail = nullptr;
+    const char* setSizeFail = nullptr;
+    const char* getSizeFail = nullptr;
     int argc = context->argc;
     char* const* argv = context->argv;
 
@@ -813,7 +780,7 @@
         break;
     }
 
-    const char* filename = NULL;
+    const char* filename = nullptr;
     for (int i = 0; i < argc; ++i) {
         // Simulate shell stdout redirect parsing
         if (argv[i][0] != '>') continue;
@@ -825,13 +792,13 @@
     }
 
     // Deal with setting up file descriptors and FILE pointers
-    if (context->error_fd >= 0) { // Is an error file descriptor supplied?
+    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) { // redirection told us to close it
+        } else if (context->stderr_null) {  // redirection told us to close it
             close(context->error_fd);
             context->error_fd = -1;
-        } else { // All Ok, convert error to a FILE pointer
+        } else {  // All Ok, convert error to a FILE pointer
             context->error = fdopen(context->error_fd, "web");
             if (!context->error) {
                 context->retval = -errno;
@@ -842,11 +809,11 @@
             }
         }
     }
-    if (context->output_fd >= 0) { // Is an output file descriptor supplied?
-        if (filename) { // redirect to file, close the supplied file descriptor.
+    if (context->output_fd >= 0) {  // Is an output file descriptor supplied?
+        if (filename) {  // redirect to file, close supplied file descriptor.
             close(context->output_fd);
             context->output_fd = -1;
-        } else { // All Ok, convert output to a FILE pointer
+        } else {  // All Ok, convert output to a FILE pointer
             context->output = fdopen(context->output_fd, "web");
             if (!context->output) {
                 context->retval = -errno;
@@ -857,7 +824,7 @@
             }
         }
     }
-    if (filename) { // We supplied an output file redirected in command line
+    if (filename) {  // We supplied an output file redirected in command line
         context->output = fopen(filename, "web");
     }
     // Deal with 2>&1
@@ -865,7 +832,7 @@
     // Deal with 2>/dev/null
     if (context->stderr_null) {
         context->error_fd = -1;
-        context->error = NULL;
+        context->error = nullptr;
     }
     // Only happens if output=stdout or output=filename
     if ((context->output_fd < 0) && context->output) {
@@ -878,12 +845,16 @@
 
     context->logformat = android_log_format_new();
 
-    if (argc == 2 && 0 == strcmp(argv[1], "--help")) {
+    if (argc == 2 && !strcmp(argv[1], "--help")) {
         show_help(context);
         context->retval = EXIT_SUCCESS;
         goto exit;
     }
 
+    // meant to catch comma-delimited values, but cast a wider
+    // net for stability dealing with possible mistaken inputs.
+    static const char delimiters[] = ",:; \t\n\r\f";
+
     // danger: getopt is _not_ reentrant
     optind = 1;
     for (;;) {
@@ -898,43 +869,40 @@
         static const char print_str[] = "print";
         // clang-format off
         static const struct option long_options[] = {
-          { "binary",        no_argument,       NULL,   'B' },
-          { "buffer",        required_argument, NULL,   'b' },
-          { "buffer-size",   optional_argument, NULL,   'g' },
-          { "clear",         no_argument,       NULL,   'c' },
-          { debug_str,       no_argument,       NULL,   0 },
-          { "dividers",      no_argument,       NULL,   'D' },
-          { "file",          required_argument, NULL,   'f' },
-          { "format",        required_argument, NULL,   'v' },
+          { "binary",        no_argument,       nullptr, 'B' },
+          { "buffer",        required_argument, nullptr, 'b' },
+          { "buffer-size",   optional_argument, nullptr, 'g' },
+          { "clear",         no_argument,       nullptr, 'c' },
+          { debug_str,       no_argument,       nullptr, 0 },
+          { "dividers",      no_argument,       nullptr, 'D' },
+          { "file",          required_argument, nullptr, 'f' },
+          { "format",        required_argument, nullptr, 'v' },
           // hidden and undocumented reserved alias for --regex
-          { "grep",          required_argument, NULL,   'e' },
+          { "grep",          required_argument, nullptr, 'e' },
           // hidden and undocumented reserved alias for --max-count
-          { "head",          required_argument, NULL,   'm' },
-          { id_str,          required_argument, NULL,   0 },
-          { "last",          no_argument,       NULL,   'L' },
-          { "max-count",     required_argument, NULL,   'm' },
-          { pid_str,         required_argument, NULL,   0 },
-          { print_str,       no_argument,       NULL,   0 },
-          { "prune",         optional_argument, NULL,   'p' },
-          { "regex",         required_argument, NULL,   'e' },
-          { "rotate-count",  required_argument, NULL,   'n' },
-          { "rotate-kbytes", required_argument, NULL,   'r' },
-          { "statistics",    no_argument,       NULL,   'S' },
+          { "head",          required_argument, nullptr, 'm' },
+          { id_str,          required_argument, nullptr, 0 },
+          { "last",          no_argument,       nullptr, 'L' },
+          { "max-count",     required_argument, nullptr, 'm' },
+          { pid_str,         required_argument, nullptr, 0 },
+          { print_str,       no_argument,       nullptr, 0 },
+          { "prune",         optional_argument, nullptr, 'p' },
+          { "regex",         required_argument, nullptr, 'e' },
+          { "rotate-count",  required_argument, nullptr, 'n' },
+          { "rotate-kbytes", required_argument, nullptr, 'r' },
+          { "statistics",    no_argument,       nullptr, 'S' },
           // hidden and undocumented reserved alias for -t
-          { "tail",          required_argument, NULL,   't' },
+          { "tail",          required_argument, nullptr, 't' },
           // support, but ignore and do not document, the optional argument
-          { wrap_str,        optional_argument, NULL,   0 },
-          { NULL,            0,                 NULL,   0 }
+          { wrap_str,        optional_argument, nullptr, 0 },
+          { nullptr,         0,                 nullptr, 0 }
         };
         // clang-format on
 
         ret = getopt_long(argc, argv,
                           ":cdDLt:T:gG:sQf:r:n:v:b:BSpP:m:e:", long_options,
                           &option_index);
-
-        if (ret < 0) {
-            break;
-        }
+        if (ret < 0) break;
 
         switch (ret) {
             case 0:
@@ -976,8 +944,7 @@
                     break;
                 }
                 if (long_options[option_index].name == id_str) {
-                    setId = optarg && optarg[0] ? optarg : NULL;
-                    break;
+                    setId = (optarg && optarg[0]) ? optarg : nullptr;
                 }
                 break;
 
@@ -1045,7 +1012,7 @@
                 break;
 
             case 'm': {
-                char* end = NULL;
+                char* end = nullptr;
                 if (!getSizeTArg(optarg, &context->maxCount)) {
                     logcat_panic(context, HELP_FALSE,
                                  "-%c \"%s\" isn't an "
@@ -1109,19 +1076,23 @@
                 break;
 
             case 'b': {
+                std::unique_ptr<char, void (*)(void*)> buffers(strdup(optarg),
+                                                               free);
+                optarg = buffers.get();
                 unsigned idMask = 0;
-                while ((optarg = strtok(optarg, ",:; \t\n\r\f")) != NULL) {
-                    if (strcmp(optarg, "default") == 0) {
+                char* sv = nullptr;  // protect against -ENOMEM above
+                while (!!(optarg = strtok_r(optarg, delimiters, &sv))) {
+                    if (!strcmp(optarg, "default")) {
                         idMask |= (1 << LOG_ID_MAIN) | (1 << LOG_ID_SYSTEM) |
                                   (1 << LOG_ID_CRASH);
-                    } else if (strcmp(optarg, "all") == 0) {
+                    } else if (!strcmp(optarg, "all")) {
                         allSelected = true;
                         idMask = (unsigned)-1;
                     } else {
                         log_id_t log_id = android_name_to_log_id(optarg);
                         const char* name = android_log_id_to_name(log_id);
 
-                        if (strcmp(name, optarg) != 0) {
+                        if (!!strcmp(name, optarg)) {
                             logcat_panic(context, HELP_TRUE,
                                          "unknown buffer %s\n", optarg);
                             goto exit;
@@ -1129,19 +1100,15 @@
                         if (log_id == LOG_ID_SECURITY) allSelected = false;
                         idMask |= (1 << log_id);
                     }
-                    optarg = NULL;
+                    optarg = nullptr;
                 }
 
                 for (int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
                     const char* name = android_log_id_to_name((log_id_t)i);
                     log_id_t log_id = android_name_to_log_id(name);
 
-                    if (log_id != (log_id_t)i) {
-                        continue;
-                    }
-                    if ((idMask & (1 << i)) == 0) {
-                        continue;
-                    }
+                    if (log_id != (log_id_t)i) continue;
+                    if (!(idMask & (1 << i))) continue;
 
                     bool found = false;
                     for (dev = devices; dev; dev = dev->next) {
@@ -1149,13 +1116,9 @@
                             found = true;
                             break;
                         }
-                        if (!dev->next) {
-                            break;
-                        }
+                        if (!dev->next) break;
                     }
-                    if (found) {
-                        continue;
-                    }
+                    if (found) continue;
 
                     bool binary =
                         !strcmp(name, "events") || !strcmp(name, "security");
@@ -1176,7 +1139,7 @@
                 break;
 
             case 'f':
-                if ((tail_time == log_time::EPOCH) && (tail_lines == 0)) {
+                if ((tail_time == log_time::EPOCH) && !tail_lines) {
                     tail_time = lastLogTime(optarg);
                 }
                 // redirect output to a file
@@ -1199,20 +1162,28 @@
                 }
                 break;
 
-            case 'v':
+            case 'v': {
                 if (!strcmp(optarg, "help") || !strcmp(optarg, "--help")) {
                     show_format_help(context);
                     context->retval = EXIT_SUCCESS;
                     goto exit;
                 }
-                err = setLogFormat(context, optarg);
-                if (err < 0) {
-                    logcat_panic(context, HELP_FORMAT,
-                                 "Invalid parameter \"%s\" to -v\n", optarg);
-                    goto exit;
+                std::unique_ptr<char, void (*)(void*)> formats(strdup(optarg),
+                                                               free);
+                optarg = formats.get();
+                unsigned idMask = 0;
+                char* sv = nullptr;  // protect against -ENOMEM above
+                while (!!(optarg = strtok_r(optarg, delimiters, &sv))) {
+                    err = setLogFormat(context, optarg);
+                    if (err < 0) {
+                        logcat_panic(context, HELP_FORMAT,
+                                     "Invalid parameter \"%s\" to -v\n", optarg);
+                        goto exit;
+                    }
+                    optarg = nullptr;
+                    if (err) hasSetLogFormat = true;
                 }
-                hasSetLogFormat |= err;
-                break;
+            } break;
 
             case 'Q':
 #define KERNEL_OPTION "androidboot.logcat="
@@ -1319,13 +1290,13 @@
         }
     }
 
-    if (context->logRotateSizeKBytes != 0 && context->outputFileName == NULL) {
+    if (!!context->logRotateSizeKBytes && !context->outputFileName) {
         logcat_panic(context, HELP_TRUE, "-r requires -f as well\n");
         goto exit;
     }
 
-    if (setId != NULL) {
-        if (context->outputFileName == NULL) {
+    if (!!setId) {
+        if (!context->outputFileName) {
             logcat_panic(context, HELP_TRUE,
                          "--id='%s' requires -f as well\n", setId);
             goto exit;
@@ -1337,22 +1308,29 @@
         bool file_ok = android::base::ReadFileToString(file_name, &file);
         android::base::WriteStringToFile(setId, file_name, S_IRUSR | S_IWUSR,
                                          getuid(), getgid());
-        if (!file_ok || (file.compare(setId) == 0)) {
-            setId = NULL;
-        }
+        if (!file_ok || !file.compare(setId)) setId = nullptr;
     }
 
-    if (hasSetLogFormat == 0) {
+    if (!hasSetLogFormat) {
         const char* logFormat = android::getenv(context, "ANDROID_PRINTF_LOG");
 
-        if (logFormat != NULL) {
-            err = setLogFormat(context, logFormat);
-            if ((err < 0) && context->error) {
-                fprintf(context->error,
-                        "invalid format in ANDROID_PRINTF_LOG '%s'\n",
-                        logFormat);
+        if (!!logFormat) {
+            std::unique_ptr<char, void (*)(void*)> formats(strdup(logFormat),
+                                                           free);
+            char* sv = nullptr;  // protect against -ENOMEM above
+            char* arg = formats.get();
+            while (!!(arg = strtok_r(arg, delimiters, &sv))) {
+                err = setLogFormat(context, arg);
+                // environment should not cause crash of logcat
+                if ((err < 0) && context->error) {
+                    fprintf(context->error,
+                            "invalid format in ANDROID_PRINTF_LOG '%s'\n", arg);
+                }
+                arg = nullptr;
+                if (err > 0) hasSetLogFormat = true;
             }
-        } else {
+        }
+        if (!hasSetLogFormat) {
             setLogFormat(context, "threadtime");
         }
     }
@@ -1369,7 +1347,7 @@
         // Add from environment variable
         const char* env_tags_orig = android::getenv(context, "ANDROID_LOG_TAGS");
 
-        if (env_tags_orig != NULL) {
+        if (!!env_tags_orig) {
             err = android_log_addFilterString(context->logformat,
                                               env_tags_orig);
 
@@ -1424,7 +1402,7 @@
                 for (int i = context->maxRotatedLogs ; i >= 0 ; --i) {
                     std::string file;
 
-                    if (i == 0) {
+                    if (!i) {
                         file = android::base::StringPrintf(
                             "%s", context->outputFileName);
                     } else {
@@ -1432,7 +1410,7 @@
                             context->outputFileName, maxRotationCountDigits, i);
                     }
 
-                    if (file.length() == 0) {
+                    if (!file.length()) {
                         perror("while clearing log files");
                         reportErrorName(&clearFail, dev->device, allSelected);
                         break;
@@ -1440,7 +1418,7 @@
 
                     err = unlink(file.c_str());
 
-                    if (err < 0 && errno != ENOENT && clearFail == NULL) {
+                    if (err < 0 && errno != ENOENT && !clearFail) {
                         perror("while clearing log files");
                         reportErrorName(&clearFail, dev->device, allSelected);
                     }
@@ -1507,7 +1485,7 @@
         size_t len = strlen(setPruneList);
         // extra 32 bytes are needed by android_logger_set_prune_list
         size_t bLen = len + 32;
-        char* buf = NULL;
+        char* buf = nullptr;
         if (asprintf(&buf, "%-*s", (int)(bLen - 1), setPruneList) > 0) {
             buf[len] = '\0';
             if (android_logger_set_prune_list(logger_list, buf, bLen)) {
@@ -1527,7 +1505,7 @@
         char* buf;
 
         for (int retry = 32; (retry >= 0) && ((buf = new char[len]));
-             delete[] buf, buf = NULL, --retry) {
+             delete[] buf, buf = nullptr, --retry) {
             if (getPruneList) {
                 android_logger_get_prune_list(logger_list, buf, len);
             } else {
@@ -1536,7 +1514,7 @@
             buf[len - 1] = '\0';
             if (atol(buf) < 3) {
                 delete[] buf;
-                buf = NULL;
+                buf = nullptr;
                 break;
             }
             size_t ret = atol(buf) + 1;
@@ -1556,19 +1534,13 @@
         char* cp = buf + len - 1;
         *cp = '\0';
         bool truncated = *--cp != '\f';
-        if (!truncated) {
-            *cp = '\0';
-        }
+        if (!truncated) *cp = '\0';
 
         // squash out the byte count
         cp = buf;
         if (!truncated) {
-            while (isdigit(*cp)) {
-                ++cp;
-            }
-            if (*cp == '\n') {
-                ++cp;
-            }
+            while (isdigit(*cp)) ++cp;
+            if (*cp == '\n') ++cp;
         }
 
         len = strlen(cp);
@@ -1577,32 +1549,28 @@
         goto close;
     }
 
-    if (getLogSize || setLogSize || clearLog) {
-        goto close;
-    }
+    if (getLogSize || setLogSize || clearLog) goto close;
 
-    setupOutputAndSchedulingPolicy(context, (mode & ANDROID_LOG_NONBLOCK) == 0);
+    setupOutputAndSchedulingPolicy(context, !(mode & ANDROID_LOG_NONBLOCK));
     if (context->stop) goto close;
 
     // LOG_EVENT_INT(10, 12345);
     // LOG_EVENT_LONG(11, 0x1122334455667788LL);
     // LOG_EVENT_STRING(0, "whassup, doc?");
 
-    dev = NULL;
+    dev = nullptr;
 
     while (!context->stop &&
            (!context->maxCount || (context->printCount < context->maxCount))) {
         struct log_msg log_msg;
         int ret = android_logger_list_read(logger_list, &log_msg);
-        if (ret == 0) {
+        if (!ret) {
             logcat_panic(context, HELP_FALSE, "read: unexpected EOF!\n");
             break;
         }
 
         if (ret < 0) {
-            if (ret == -EAGAIN) {
-                break;
-            }
+            if (ret == -EAGAIN) break;
 
             if (ret == -EIO) {
                 logcat_panic(context, HELP_FALSE, "read: unexpected EOF!\n");
@@ -1618,9 +1586,7 @@
 
         log_device_t* d;
         for (d = devices; d; d = d->next) {
-            if (android_name_to_log_id(d->device) == log_msg.id()) {
-                break;
-            }
+            if (android_name_to_log_id(d->device) == log_msg.id()) break;
         }
         if (!d) {
             context->devCount = 2; // set to Multiple
@@ -1686,9 +1652,7 @@
     android_logcat_context_internal* context = ctx;
 
     int save_errno = EBUSY;
-    if ((context->fds[0] >= 0) || (context->fds[1] >= 0)) {
-        goto exit;
-    }
+    if ((context->fds[0] >= 0) || (context->fds[1] >= 0)) goto exit;
 
     if (pipe(context->fds) < 0) {
         save_errno = errno;
@@ -1725,11 +1689,11 @@
     for (auto& str : context->args) {
         context->argv_hold.push_back(str.c_str());
     }
-    context->argv_hold.push_back(NULL);
+    context->argv_hold.push_back(nullptr);
     for (auto& str : context->envs) {
         context->envp_hold.push_back(str.c_str());
     }
-    context->envp_hold.push_back(NULL);
+    context->envp_hold.push_back(nullptr);
 
     context->argc = context->argv_hold.size() - 1;
     context->argv = (char* const*)&context->argv_hold[0];
@@ -1738,7 +1702,7 @@
 #ifdef DEBUG
     fprintf(stderr, "argv[%d] = {", context->argc);
     for (auto str : context->argv_hold) {
-        fprintf(stderr, " \"%s\"", str ?: "NULL");
+        fprintf(stderr, " \"%s\"", str ?: "nullptr");
     }
     fprintf(stderr, " }\n");
     fflush(stderr);
@@ -1784,11 +1748,14 @@
 int android_logcat_destroy(android_logcat_context* ctx) {
     android_logcat_context_internal* context = *ctx;
 
-    *ctx = NULL;
+    if (!context) return -EBADF;
+
+    *ctx = nullptr;
 
     context->stop = true;
 
     while (context->thread_stopped == false) {
+        // Makes me sad, replace thread_stopped with semaphore.  Short lived.
         sched_yield();
     }