Merge "Move early_hal start to late-fs trigger action" into oc-dev
diff --git a/include/ziparchive/zip_writer.h b/include/ziparchive/zip_writer.h
index 41ca2e1..08ead48 100644
--- a/include/ziparchive/zip_writer.h
+++ b/include/ziparchive/zip_writer.h
@@ -75,7 +75,8 @@
     uint32_t uncompressed_size;
     uint16_t last_mod_time;
     uint16_t last_mod_date;
-    uint32_t local_file_header_offset;
+    uint32_t padding_length;
+    off64_t local_file_header_offset;
   };
 
   static const char* ErrorCodeString(int32_t error_code);
@@ -172,6 +173,7 @@
   };
 
   FILE* file_;
+  bool seekable_;
   off64_t current_offset_;
   State state_;
   std::vector<FileEntry> files_;
diff --git a/init/init.cpp b/init/init.cpp
index 7e61767..94bf37a 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -714,14 +714,18 @@
         return false;
     }
     std::string actual_plat_id;
-    if (!read_first_line("/system/etc/selinux/plat_sepolicy.cil.sha256", &actual_plat_id)) {
-        PLOG(INFO) << "Failed to read /system/etc/selinux/plat_sepolicy.cil.sha256";
+    if (!read_first_line("/system/etc/selinux/plat_and_mapping_sepolicy.cil.sha256",
+                         &actual_plat_id)) {
+        PLOG(INFO) << "Failed to read "
+                      "/system/etc/selinux/plat_and_mapping_sepolicy.cil.sha256";
         return false;
     }
     std::string precompiled_plat_id;
-    if (!read_first_line("/vendor/etc/selinux/precompiled_sepolicy.plat.sha256",
+    if (!read_first_line("/vendor/etc/selinux/precompiled_sepolicy.plat_and_mapping.sha256",
                          &precompiled_plat_id)) {
-        PLOG(INFO) << "Failed to read /vendor/etc/selinux/precompiled_sepolicy.plat.sha256";
+        PLOG(INFO) << "Failed to read "
+                      "/vendor/etc/selinux/"
+                      "precompiled_sepolicy.plat_and_mapping.sha256";
         return false;
     }
     if ((actual_plat_id.empty()) || (actual_plat_id != precompiled_plat_id)) {
@@ -793,7 +797,7 @@
         "-M", "true",
         // Target the highest policy language version supported by the kernel
         "-c", std::to_string(max_policy_version).c_str(),
-        "/vendor/etc/selinux/mapping_sepolicy.cil",
+        "/system/etc/selinux/mapping_sepolicy.cil",
         "/vendor/etc/selinux/nonplat_sepolicy.cil",
         "-o", compiled_sepolicy,
         // We don't care about file_contexts output by the compiler
diff --git a/libziparchive/zip_writer.cc b/libziparchive/zip_writer.cc
index 7600528..2edf224 100644
--- a/libziparchive/zip_writer.cc
+++ b/libziparchive/zip_writer.cc
@@ -18,6 +18,7 @@
 
 #include <cstdio>
 #include <sys/param.h>
+#include <sys/stat.h>
 #include <zlib.h>
 #define DEF_MEM_LEVEL 8                // normally in zutil.h?
 
@@ -84,11 +85,19 @@
   delete stream;
 }
 
-ZipWriter::ZipWriter(FILE* f) : file_(f), current_offset_(0), state_(State::kWritingZip),
-                                z_stream_(nullptr, DeleteZStream), buffer_(kBufSize) {
+ZipWriter::ZipWriter(FILE* f) : file_(f), seekable_(false), current_offset_(0),
+                                state_(State::kWritingZip), z_stream_(nullptr, DeleteZStream),
+                                buffer_(kBufSize) {
+  // Check if the file is seekable (regular file). If fstat fails, that's fine, subsequent calls
+  // will fail as well.
+  struct stat file_stats;
+  if (fstat(fileno(f), &file_stats) == 0) {
+    seekable_ = S_ISREG(file_stats.st_mode);
+  }
 }
 
 ZipWriter::ZipWriter(ZipWriter&& writer) : file_(writer.file_),
+                                           seekable_(writer.seekable_),
                                            current_offset_(writer.current_offset_),
                                            state_(writer.state_),
                                            files_(std::move(writer.files_)),
@@ -100,6 +109,7 @@
 
 ZipWriter& ZipWriter::operator=(ZipWriter&& writer) {
   file_ = writer.file_;
+  seekable_ = writer.seekable_;
   current_offset_ = writer.current_offset_;
   state_ = writer.state_;
   files_ = std::move(writer.files_);
@@ -159,6 +169,30 @@
   *out_time = ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1;
 }
 
+static void CopyFromFileEntry(const ZipWriter::FileEntry& src, bool use_data_descriptor,
+                              LocalFileHeader* dst) {
+  dst->lfh_signature = LocalFileHeader::kSignature;
+  if (use_data_descriptor) {
+    // Set this flag to denote that a DataDescriptor struct will appear after the data,
+    // containing the crc and size fields.
+    dst->gpb_flags |= kGPBDDFlagMask;
+
+    // The size and crc fields must be 0.
+    dst->compressed_size = 0u;
+    dst->uncompressed_size = 0u;
+    dst->crc32 = 0u;
+  } else {
+    dst->compressed_size = src.compressed_size;
+    dst->uncompressed_size = src.uncompressed_size;
+    dst->crc32 = src.crc32;
+  }
+  dst->compression_method = src.compression_method;
+  dst->last_mod_time = src.last_mod_time;
+  dst->last_mod_date = src.last_mod_date;
+  dst->file_name_length = src.path.size();
+  dst->extra_field_length = src.padding_length;
+}
+
 int32_t ZipWriter::StartAlignedEntryWithTime(const char* path, size_t flags,
                                              time_t time, uint32_t alignment) {
   if (state_ != State::kWritingZip) {
@@ -173,66 +207,58 @@
     return kInvalidAlignment;
   }
 
-  current_file_entry_ = {};
-  current_file_entry_.path = path;
-  current_file_entry_.local_file_header_offset = current_offset_;
+  FileEntry file_entry = {};
+  file_entry.local_file_header_offset = current_offset_;
+  file_entry.path = path;
 
-  if (!IsValidEntryName(reinterpret_cast<const uint8_t*>(current_file_entry_.path.data()),
-                        current_file_entry_.path.size())) {
+  if (!IsValidEntryName(reinterpret_cast<const uint8_t*>(file_entry.path.data()),
+                        file_entry.path.size())) {
     return kInvalidEntryName;
   }
 
-  LocalFileHeader header = {};
-  header.lfh_signature = LocalFileHeader::kSignature;
-
-  // Set this flag to denote that a DataDescriptor struct will appear after the data,
-  // containing the crc and size fields.
-  header.gpb_flags |= kGPBDDFlagMask;
-
   if (flags & ZipWriter::kCompress) {
-    current_file_entry_.compression_method = kCompressDeflated;
+    file_entry.compression_method = kCompressDeflated;
 
     int32_t result = PrepareDeflate();
     if (result != kNoError) {
       return result;
     }
   } else {
-    current_file_entry_.compression_method = kCompressStored;
+    file_entry.compression_method = kCompressStored;
   }
-  header.compression_method = current_file_entry_.compression_method;
 
-  ExtractTimeAndDate(time, &current_file_entry_.last_mod_time, &current_file_entry_.last_mod_date);
-  header.last_mod_time = current_file_entry_.last_mod_time;
-  header.last_mod_date = current_file_entry_.last_mod_date;
+  ExtractTimeAndDate(time, &file_entry.last_mod_time, &file_entry.last_mod_date);
 
-  header.file_name_length = current_file_entry_.path.size();
-
-  off64_t offset = current_offset_ + sizeof(header) + current_file_entry_.path.size();
+  off_t offset = current_offset_ + sizeof(LocalFileHeader) + file_entry.path.size();
   std::vector<char> zero_padding;
   if (alignment != 0 && (offset & (alignment - 1))) {
     // Pad the extra field so the data will be aligned.
     uint16_t padding = alignment - (offset % alignment);
-    header.extra_field_length = padding;
+    file_entry.padding_length = padding;
     offset += padding;
-    zero_padding.resize(padding);
-    memset(zero_padding.data(), 0, zero_padding.size());
+    zero_padding.resize(padding, 0);
   }
 
+  LocalFileHeader header = {};
+  // Always start expecting a data descriptor. When the data has finished being written,
+  // if it is possible to seek back, the GPB flag will reset and the sizes written.
+  CopyFromFileEntry(file_entry, true /*use_data_descriptor*/, &header);
+
   if (fwrite(&header, sizeof(header), 1, file_) != 1) {
     return HandleError(kIoError);
   }
 
-  if (fwrite(path, sizeof(*path), current_file_entry_.path.size(), file_)
-      != current_file_entry_.path.size()) {
+  if (fwrite(path, sizeof(*path), file_entry.path.size(), file_) != file_entry.path.size()) {
     return HandleError(kIoError);
   }
 
-  if (header.extra_field_length != 0 &&
-      fwrite(zero_padding.data(), 1, header.extra_field_length, file_)
-      != header.extra_field_length) {
+  if (file_entry.padding_length != 0 &&
+      fwrite(zero_padding.data(), 1, file_entry.padding_length, file_)
+      != file_entry.padding_length) {
     return HandleError(kIoError);
   }
 
+  current_file_entry_ = std::move(file_entry);
   current_offset_ = offset;
   state_ = State::kWritingEntry;
   return kNoError;
@@ -405,23 +431,41 @@
     }
   }
 
-  const uint32_t sig = DataDescriptor::kOptSignature;
-  if (fwrite(&sig, sizeof(sig), 1, file_) != 1) {
-    state_ = State::kError;
-    return kIoError;
-  }
+  if ((current_file_entry_.compression_method & kCompressDeflated) || !seekable_) {
+    // Some versions of ZIP don't allow STORED data to have a trailing DataDescriptor.
+    // If this file is not seekable, or if the data is compressed, write a DataDescriptor.
+    const uint32_t sig = DataDescriptor::kOptSignature;
+    if (fwrite(&sig, sizeof(sig), 1, file_) != 1) {
+      return HandleError(kIoError);
+    }
 
-  DataDescriptor dd = {};
-  dd.crc32 = current_file_entry_.crc32;
-  dd.compressed_size = current_file_entry_.compressed_size;
-  dd.uncompressed_size = current_file_entry_.uncompressed_size;
-  if (fwrite(&dd, sizeof(dd), 1, file_) != 1) {
-    return HandleError(kIoError);
+    DataDescriptor dd = {};
+    dd.crc32 = current_file_entry_.crc32;
+    dd.compressed_size = current_file_entry_.compressed_size;
+    dd.uncompressed_size = current_file_entry_.uncompressed_size;
+    if (fwrite(&dd, sizeof(dd), 1, file_) != 1) {
+      return HandleError(kIoError);
+    }
+    current_offset_ += sizeof(DataDescriptor::kOptSignature) + sizeof(dd);
+  } else {
+    // Seek back to the header and rewrite to include the size.
+    if (fseeko(file_, current_file_entry_.local_file_header_offset, SEEK_SET) != 0) {
+      return HandleError(kIoError);
+    }
+
+    LocalFileHeader header = {};
+    CopyFromFileEntry(current_file_entry_, false /*use_data_descriptor*/, &header);
+
+    if (fwrite(&header, sizeof(header), 1, file_) != 1) {
+      return HandleError(kIoError);
+    }
+
+    if (fseeko(file_, current_offset_, SEEK_SET) != 0) {
+      return HandleError(kIoError);
+    }
   }
 
   files_.emplace_back(std::move(current_file_entry_));
-
-  current_offset_ += sizeof(DataDescriptor::kOptSignature) + sizeof(dd);
   state_ = State::kWritingZip;
   return kNoError;
 }
@@ -431,7 +475,7 @@
     return kInvalidState;
   }
 
-  off64_t startOfCdr = current_offset_;
+  off_t startOfCdr = current_offset_;
   for (FileEntry& file : files_) {
     CentralDirectoryRecord cdr = {};
     cdr.record_signature = CentralDirectoryRecord::kSignature;
@@ -443,7 +487,7 @@
     cdr.compressed_size = file.compressed_size;
     cdr.uncompressed_size = file.uncompressed_size;
     cdr.file_name_length = file.path.size();
-    cdr.local_file_header_offset = file.local_file_header_offset;
+    cdr.local_file_header_offset = static_cast<uint32_t>(file.local_file_header_offset);
     if (fwrite(&cdr, sizeof(cdr), 1, file_) != 1) {
       return HandleError(kIoError);
     }
@@ -473,7 +517,7 @@
   // Since we can BackUp() and potentially finish writing at an offset less than one we had
   // already written at, we must truncate the file.
 
-  if (ftruncate64(fileno(file_), current_offset_) != 0) {
+  if (ftruncate(fileno(file_), current_offset_) != 0) {
     return HandleError(kIoError);
   }
 
diff --git a/libziparchive/zip_writer_test.cc b/libziparchive/zip_writer_test.cc
index 30f4950..5b526a4 100644
--- a/libziparchive/zip_writer_test.cc
+++ b/libziparchive/zip_writer_test.cc
@@ -64,6 +64,7 @@
   ZipEntry data;
   ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
   EXPECT_EQ(kCompressStored, data.method);
+  EXPECT_EQ(0u, data.has_data_descriptor);
   EXPECT_EQ(strlen(expected), data.compressed_length);
   ASSERT_EQ(strlen(expected), data.uncompressed_length);
   ASSERT_TRUE(AssertFileEntryContentsEq(expected, handle, &data));
diff --git a/logcat/Android.mk b/logcat/Android.mk
index f564f0f..4e11ca9 100644
--- a/logcat/Android.mk
+++ b/logcat/Android.mk
@@ -15,6 +15,16 @@
 
 include $(CLEAR_VARS)
 
+LOCAL_MODULE := logcatd
+LOCAL_MODULE_TAGS := debug
+LOCAL_SRC_FILES := logcatd_main.cpp event.logtags
+LOCAL_SHARED_LIBRARIES := liblogcat $(logcatLibs)
+LOCAL_CFLAGS := -Werror
+
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+
 LOCAL_MODULE := liblogcat
 LOCAL_SRC_FILES := logcat.cpp getopt_long.cpp logcat_system.cpp
 LOCAL_SHARED_LIBRARIES := $(logcatLibs)
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 8134936..64d1d2f 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -882,6 +882,7 @@
           { "grep",          required_argument, nullptr, 'e' },
           // hidden and undocumented reserved alias for --max-count
           { "head",          required_argument, nullptr, 'm' },
+          { "help",          no_argument,       nullptr, 'h' },
           { id_str,          required_argument, nullptr, 0 },
           { "last",          no_argument,       nullptr, 'L' },
           { "max-count",     required_argument, nullptr, 'm' },
@@ -900,9 +901,8 @@
         };
         // clang-format on
 
-        ret = getopt_long_r(argc, argv,
-                            ":cdDLt:T:gG:sQf:r:n:v:b:BSpP:m:e:", long_options,
-                            &option_index, &optctx);
+        ret = getopt_long_r(argc, argv, ":cdDhLt:T:gG:sQf:r:n:v:b:BSpP:m:e:",
+                            long_options, &option_index, &optctx);
         if (ret < 0) break;
 
         switch (ret) {
@@ -1304,6 +1304,11 @@
                              "Option -%c needs an argument\n", optctx.optopt);
                 goto exit;
 
+            case 'h':
+                show_help(context);
+                show_format_help(context);
+                goto exit;
+
             default:
                 logcat_panic(context, HELP_TRUE, "Unrecognized Option %c\n",
                              optctx.optopt);
diff --git a/logcat/logcatd.rc b/logcat/logcatd.rc
index b082a64..06cc90d 100644
--- a/logcat/logcatd.rc
+++ b/logcat/logcatd.rc
@@ -34,9 +34,6 @@
 on property:logd.logpersistd.enable=true && property:logd.logpersistd=logcatd
     # all exec/services are called with umask(077), so no gain beyond 0700
     mkdir /data/misc/logd 0700 logd log
-    # logd for write to /data/misc/logd, log group for read from pstore (-L)
-    # b/28788401 b/30041146 b/30612424
-    # exec - logd log -- /system/bin/logcat -L -b ${logd.logpersistd.buffer:-all} -v threadtime -v usec -v printable -D -f /data/misc/logd/logcat -r 1024 -n ${logd.logpersistd.size:-256} --id=${ro.build.id}
     start logcatd
 
 # stop logcatd service and clear data
@@ -57,7 +54,7 @@
     stop logcatd
 
 # logcatd service
-service logcatd /system/bin/logcat -b ${logd.logpersistd.buffer:-all} -v threadtime -v usec -v printable -D -f /data/misc/logd/logcat -r 1024 -n ${logd.logpersistd.size:-256} --id=${ro.build.id}
+service logcatd /system/bin/logcatd -L -b ${logd.logpersistd.buffer:-all} -v threadtime -v usec -v printable -D -f /data/misc/logd/logcat -r 1024 -n ${logd.logpersistd.size:-256} --id=${ro.build.id}
     class late_start
     disabled
     # logd for write to /data/misc/logd, log group for read from log daemon
diff --git a/logcat/logcatd_main.cpp b/logcat/logcatd_main.cpp
new file mode 100644
index 0000000..9109eb1
--- /dev/null
+++ b/logcat/logcatd_main.cpp
@@ -0,0 +1,71 @@
+/*
+ * 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 <signal.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <string>
+#include <vector>
+
+#include <log/logcat.h>
+
+int main(int argc, char** argv, char** envp) {
+    android_logcat_context ctx = create_android_logcat();
+    if (!ctx) return -1;
+
+    signal(SIGPIPE, exit);
+
+    // Save and detect presence of -L or --last flag
+    std::vector<std::string> args;
+    bool last = false;
+    for (int i = 0; i < argc; ++i) {
+        if (!argv[i]) continue;
+        args.push_back(std::string(argv[i]));
+        if (!strcmp(argv[i], "-L") || !strcmp(argv[i], "--last")) last = true;
+    }
+
+    // Generate argv from saved content
+    std::vector<const char*> argv_hold;
+    for (auto& str : args) argv_hold.push_back(str.c_str());
+    argv_hold.push_back(nullptr);
+
+    int ret = 0;
+    if (last) {
+        // Run logcat command with -L flag
+        ret = android_logcat_run_command(ctx, -1, -1, argv_hold.size() - 1,
+                                         (char* const*)&argv_hold[0], envp);
+        // Remove -L and --last flags from argument list
+        for (std::vector<const char*>::iterator it = argv_hold.begin();
+             it != argv_hold.end();) {
+            if (!*it || (strcmp(*it, "-L") && strcmp(*it, "--last"))) {
+                ++it;
+            } else {
+                it = argv_hold.erase(it);
+            }
+        }
+        // fall through to re-run the command regardless of the arguments
+        // passed in.  For instance, we expect -h to report help stutter.
+    }
+
+    // Run logcat command without -L flag
+    int retval = android_logcat_run_command(ctx, -1, -1, argv_hold.size() - 1,
+                                            (char* const*)&argv_hold[0], envp);
+    if (!ret) ret = retval;
+    retval = android_logcat_destroy(&ctx);
+    if (!ret) ret = retval;
+    return ret;
+}
diff --git a/logcat/tests/Android.mk b/logcat/tests/Android.mk
index 22aca17..defd3c4 100644
--- a/logcat/tests/Android.mk
+++ b/logcat/tests/Android.mk
@@ -50,6 +50,7 @@
 
 test_src_files := \
     logcat_test.cpp \
+    logcatd_test.cpp \
     liblogcat_test.cpp \
 
 # Build tests for the device (with .so). Run with:
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index a3a0176..21868f2 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -20,6 +20,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/cdefs.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <sys/wait.h>
@@ -39,6 +40,10 @@
 #define logcat_pclose(context, fp) pclose(fp)
 #define logcat_system(command) system(command)
 #endif
+#ifndef logcat_executable
+#define USING_LOGCAT_EXECUTABLE_DEFAULT
+#define logcat_executable "logcat"
+#endif
 
 #define BIG_BUFFER (5 * 1024)
 
@@ -64,14 +69,13 @@
 
 #undef LOG_TAG
 #define LOG_TAG "inject"
-    RLOGE("logcat.buckets");
+    RLOGE(logcat_executable ".buckets");
     sleep(1);
 
-    ASSERT_TRUE(
-        NULL !=
-        (fp = logcat_popen(
-             ctx,
-             "logcat -b radio -b events -b system -b main -d 2>/dev/null")));
+    ASSERT_TRUE(NULL !=
+                (fp = logcat_popen(
+                     ctx, logcat_executable
+                     " -b radio -b events -b system -b main -d 2>/dev/null")));
 
     char buffer[BIG_BUFFER];
 
@@ -101,8 +105,8 @@
     logcat_define(ctx);
 
     ASSERT_TRUE(NULL !=
-                (fp = logcat_popen(ctx,
-                                   "logcat -b events -d -s auditd "
+                (fp = logcat_popen(ctx, logcat_executable
+                                   " -b events -d -s auditd "
                                    "am_proc_start am_pss am_proc_bound "
                                    "dvm_lock_sample am_wtf 2>/dev/null")));
 
@@ -170,10 +174,9 @@
 #endif
         strftime(needle, sizeof(needle), "[ %Y-", ptm);
 
-        ASSERT_TRUE(
-            NULL !=
-            (fp = logcat_popen(
-                 ctx, "logcat -v long -v year -b all -t 3 2>/dev/null")));
+        ASSERT_TRUE(NULL != (fp = logcat_popen(
+                                 ctx, logcat_executable
+                                 " -v long -v year -b all -t 3 2>/dev/null")));
 
         char buffer[BIG_BUFFER];
 
@@ -237,8 +240,8 @@
         logcat_define(ctx);
 
         ASSERT_TRUE(NULL !=
-                    (fp = logcat_popen(ctx,
-                                       "logcat -v long -v America/Los_Angeles "
+                    (fp = logcat_popen(ctx, logcat_executable
+                                       " -v long -v America/Los_Angeles "
                                        "-b all -t 3 2>/dev/null")));
 
         char buffer[BIG_BUFFER];
@@ -264,10 +267,9 @@
     FILE* fp;
     logcat_define(ctx);
 
-    ASSERT_TRUE(NULL !=
-                (fp = logcat_popen(ctx,
-                                   "logcat -v long -v America/Los_Angeles -v "
-                                   "zone -b all -t 3 2>/dev/null")));
+    ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, logcat_executable
+                                           " -v long -v America/Los_Angeles -v "
+                                           "zone -b all -t 3 2>/dev/null")));
 
     char buffer[BIG_BUFFER];
 
@@ -435,11 +437,11 @@
 }
 
 TEST(logcat, tail_time) {
-    do_tail_time("logcat -v long -v nsec -b all");
+    do_tail_time(logcat_executable " -v long -v nsec -b all");
 }
 
 TEST(logcat, tail_time_epoch) {
-    do_tail_time("logcat -v long -v nsec -v epoch -b all");
+    do_tail_time(logcat_executable " -v long -v nsec -v epoch -b all");
 }
 
 TEST(logcat, End_to_End) {
@@ -452,8 +454,8 @@
     FILE* fp;
     logcat_define(ctx);
     ASSERT_TRUE(NULL !=
-                (fp = logcat_popen(
-                     ctx, "logcat -v brief -b events -t 100 2>/dev/null")));
+                (fp = logcat_popen(ctx, logcat_executable
+                                   " -v brief -b events -t 100 2>/dev/null")));
 
     char buffer[BIG_BUFFER];
 
@@ -492,8 +494,8 @@
     size_t num = 0;
     do {
         EXPECT_TRUE(NULL !=
-                    (fp[num] = logcat_popen(
-                         ctx[num], "logcat -v brief -b events -t 100")));
+                    (fp[num] = logcat_popen(ctx[num], logcat_executable
+                                            " -v brief -b events -t 100")));
         if (!fp[num]) {
             fprintf(stderr,
                     "WARNING: limiting to %zu simultaneous logcat operations\n",
@@ -604,22 +606,23 @@
 }
 
 TEST(logcat, get_size) {
-    ASSERT_EQ(4, get_groups("logcat -v brief -b radio -b events -b system -b "
+    ASSERT_EQ(4, get_groups(logcat_executable
+                            " -v brief -b radio -b events -b system -b "
                             "main -g 2>/dev/null"));
 }
 
 // duplicate test for get_size, but use comma-separated list of buffers
 TEST(logcat, multiple_buffer) {
     ASSERT_EQ(
-        4, get_groups(
-               "logcat -v brief -b radio,events,system,main -g 2>/dev/null"));
+        4, get_groups(logcat_executable
+                      " -v brief -b radio,events,system,main -g 2>/dev/null"));
 }
 
 TEST(logcat, bad_buffer) {
-    ASSERT_EQ(
-        0,
-        get_groups(
-            "logcat -v brief -b radio,events,bogo,system,main -g 2>/dev/null"));
+    ASSERT_EQ(0,
+              get_groups(
+                  logcat_executable
+                  " -v brief -b radio,events,bogo,system,main -g 2>/dev/null"));
 }
 
 #ifndef logcat
@@ -774,8 +777,8 @@
     char buf[sizeof(form)];
     ASSERT_TRUE(NULL != mkdtemp(strcpy(buf, form)));
 
-    static const char comm[] =
-        "logcat -b radio -b events -b system -b main"
+    static const char comm[] = logcat_executable
+        " -b radio -b events -b system -b main"
         " -d -f %s/log.txt -n 7 -r 1";
     char command[sizeof(buf) + sizeof(comm)];
     snprintf(command, sizeof(command), comm, buf);
@@ -820,8 +823,8 @@
     char tmp_out_dir[sizeof(tmp_out_dir_form)];
     ASSERT_TRUE(NULL != mkdtemp(strcpy(tmp_out_dir, tmp_out_dir_form)));
 
-    static const char logcat_cmd[] =
-        "logcat -b radio -b events -b system -b main"
+    static const char logcat_cmd[] = logcat_executable
+        " -b radio -b events -b system -b main"
         " -d -f %s/log.txt -n 10 -r 1";
     char command[sizeof(tmp_out_dir) + sizeof(logcat_cmd)];
     snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir);
@@ -880,7 +883,7 @@
 
     static const char log_filename[] = "log.txt";
     static const char logcat_cmd[] =
-        "logcat -b all -v nsec -d -f %s/%s -n 256 -r 1024";
+        logcat_executable " -b all -v nsec -d -f %s/%s -n 256 -r 1024";
     static const char cleanup_cmd[] = "rm -rf %s";
     char command[sizeof(tmp_out_dir) + sizeof(logcat_cmd) + sizeof(log_filename)];
     snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir, log_filename);
@@ -1005,7 +1008,8 @@
 
     static const char log_filename[] = "log.txt";
     static const unsigned num_val = 32;
-    static const char logcat_cmd[] = "logcat -b all -d -f %s/%s -n %d -r 1";
+    static const char logcat_cmd[] =
+        logcat_executable " -b all -d -f %s/%s -n %d -r 1";
     static const char clear_cmd[] = " -c";
     static const char cleanup_cmd[] = "rm -rf %s";
     char command[sizeof(tmp_out_dir) + sizeof(logcat_cmd) +
@@ -1109,9 +1113,9 @@
 
 TEST(logcat, logrotate_id) {
     static const char logcat_cmd[] =
-        "logcat -b all -d -f %s/%s -n 32 -r 1 --id=test";
+        logcat_executable " -b all -d -f %s/%s -n 32 -r 1 --id=test";
     static const char logcat_short_cmd[] =
-        "logcat -b all -t 10 -f %s/%s -n 32 -r 1 --id=test";
+        logcat_executable " -b all -t 10 -f %s/%s -n 32 -r 1 --id=test";
     static const char tmp_out_dir_form[] =
         "/data/local/tmp/logcat.logrotate.XXXXXX";
     static const char log_filename[] = "log.txt";
@@ -1155,8 +1159,8 @@
 
 TEST(logcat, logrotate_nodir) {
     // expect logcat to error out on writing content and not exit(0) for nodir
-    static const char command[] =
-        "logcat -b all -d"
+    static const char command[] = logcat_executable
+        " -b all -d"
         " -f /das/nein/gerfingerpoken/logcat/log.txt"
         " -n 256 -r 1024";
     EXPECT_FALSE(IsFalse(0 == logcat_system(command), command));
@@ -1292,7 +1296,7 @@
     FILE* fp;
     logcat_define(ctx);
 
-    fp = logcat_popen(ctx, "logcat -p 2>/dev/null");
+    fp = logcat_popen(ctx, logcat_executable " -p 2>/dev/null");
     if (fp == NULL) {
         fprintf(stderr, "ERROR: logcat -p 2>/dev/null\n");
         return false;
@@ -1330,7 +1334,8 @@
 
     char buffer[BIG_BUFFER];
 
-    snprintf(buffer, sizeof(buffer), "logcat -P '%s' 2>&1", list ? list : "");
+    snprintf(buffer, sizeof(buffer), logcat_executable " -P '%s' 2>&1",
+             list ? list : "");
     fp = logcat_popen(ctx, buffer);
     if (fp == NULL) {
         fprintf(stderr, "ERROR: %s\n", buffer);
@@ -1392,15 +1397,11 @@
     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
+#define logcat_regex_prefix ___STRING(logcat) "_test"
 
     snprintf(buffer, sizeof(buffer),
-             "logcat --pid %d -d -e " logcat_regex_prefix "_a+b", getpid());
+             logcat_executable " --pid %d -d -e " logcat_regex_prefix "_a+b",
+             getpid());
 
     LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, logcat_regex_prefix,
                                           logcat_regex_prefix "_ab"));
@@ -1437,8 +1438,8 @@
 
     char buffer[BIG_BUFFER];
 
-    snprintf(buffer, sizeof(buffer), "logcat --pid %d -d --max-count 3",
-             getpid());
+    snprintf(buffer, sizeof(buffer),
+             logcat_executable " --pid %d -d --max-count 3", getpid());
 
     LOG_FAILURE_RETRY(
         __android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test"));
@@ -1663,8 +1664,34 @@
 }
 
 TEST(logcat, security) {
-    EXPECT_FALSE(reportedSecurity("logcat -b all -g 2>&1"));
-    EXPECT_TRUE(reportedSecurity("logcat -b security -g 2>&1"));
-    EXPECT_TRUE(reportedSecurity("logcat -b security -c 2>&1"));
-    EXPECT_TRUE(reportedSecurity("logcat -b security -G 256K 2>&1"));
+    EXPECT_FALSE(reportedSecurity(logcat_executable " -b all -g 2>&1"));
+    EXPECT_TRUE(reportedSecurity(logcat_executable " -b security -g 2>&1"));
+    EXPECT_TRUE(reportedSecurity(logcat_executable " -b security -c 2>&1"));
+    EXPECT_TRUE(
+        reportedSecurity(logcat_executable " -b security -G 256K 2>&1"));
+}
+
+static size_t commandOutputSize(const char* command) {
+    logcat_define(ctx);
+    FILE* fp = logcat_popen(ctx, command);
+    if (!fp) return 0;
+
+    std::string ret;
+    if (!android::base::ReadFdToString(fileno(fp), &ret)) return 0;
+    if (logcat_pclose(ctx, fp) != 0) return 0;
+
+    return ret.size();
+}
+
+TEST(logcat, help) {
+    size_t logcatHelpTextSize = commandOutputSize(logcat_executable " -h 2>&1");
+    EXPECT_LT(4096UL, logcatHelpTextSize);
+    size_t logcatLastHelpTextSize =
+        commandOutputSize(logcat_executable " -L -h 2>&1");
+#ifdef USING_LOGCAT_EXECUTABLE_DEFAULT  // logcat and liblogcat
+    EXPECT_EQ(logcatHelpTextSize, logcatLastHelpTextSize);
+#else
+    // logcatd -L -h prints the help twice, as designed.
+    EXPECT_EQ(logcatHelpTextSize * 2, logcatLastHelpTextSize);
+#endif
 }
diff --git a/logcat/tests/logcatd_test.cpp b/logcat/tests/logcatd_test.cpp
new file mode 100644
index 0000000..bb7534e
--- /dev/null
+++ b/logcat/tests/logcatd_test.cpp
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+#define logcat logcatd
+#define logcat_executable "logcatd"
+
+#include "logcat_test.cpp"