Merge "Allocate a GID range for external cache files."
diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp
index 7da94ce..7a87df4 100644
--- a/adb/daemon/main.cpp
+++ b/adb/daemon/main.cpp
@@ -32,10 +32,10 @@
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <libminijail.h>
+#include <log/log_properties.h>
 #include <scoped_minijail.h>
 
 #include <private/android_filesystem_config.h>
-#include <private/android_logger.h>
 #include "debuggerd/handler.h"
 #include "selinux/android.h"
 
diff --git a/adb/services.cpp b/adb/services.cpp
index f764c52..4327044 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -43,7 +43,7 @@
 #include <android-base/properties.h>
 #include <bootloader_message/bootloader_message.h>
 #include <cutils/android_reboot.h>
-#include <private/android_logger.h>
+#include <log/log_properties.h>
 #endif
 
 #include "adb.h"
diff --git a/adb/set_verity_enable_state_service.cpp b/adb/set_verity_enable_state_service.cpp
index 76b156d..d4dd256 100644
--- a/adb/set_verity_enable_state_service.cpp
+++ b/adb/set_verity_enable_state_service.cpp
@@ -26,7 +26,7 @@
 
 #include "android-base/properties.h"
 #include "android-base/stringprintf.h"
-#include <private/android_logger.h>
+#include <log/log_properties.h>
 
 #include "adb.h"
 #include "adb_io.h"
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index c05903f..59a48f5 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -32,7 +32,7 @@
 
 #if !ADB_HOST
 #include <android-base/properties.h>
-#include <private/android_logger.h>
+#include <log/log_properties.h>
 #endif
 
 #include "adb.h"
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index e635e53..4ddcc52 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -48,8 +48,8 @@
 #include <linux/fs.h>
 #include <linux/loop.h>
 #include <linux/magic.h>
+#include <log/log_properties.h>
 #include <logwrap/logwrap.h>
-#include <private/android_logger.h>  // for __android_log_is_debuggable()
 
 #include "fs_mgr.h"
 #include "fs_mgr_avb.h"
diff --git a/init/README.md b/init/README.md
index fc50730..8cb1e52 100644
--- a/init/README.md
+++ b/init/README.md
@@ -370,10 +370,6 @@
   _options_ include "barrier=1", "noauto\_da\_alloc", "discard", ... as
   a comma separated string, eg: barrier=1,noauto\_da\_alloc
 
-`powerctl`
-> Internal implementation detail used to respond to changes to the
-  "sys.powerctl" system property, used to implement rebooting.
-
 `restart <service>`
 > Stops and restarts a running service, does nothing if the service is currently
   restarting, otherwise, it just starts the service.
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 2327cdf..43eb420 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -598,58 +598,6 @@
     return 0;
 }
 
-static int do_powerctl(const std::vector<std::string>& args) {
-    const std::string& command = args[1];
-    unsigned int cmd = 0;
-    std::vector<std::string> cmd_params = android::base::Split(command, ",");
-    std::string reason_string = cmd_params[0];
-    std::string reboot_target = "";
-    bool runFsck = false;
-    bool commandInvalid = false;
-
-    if (cmd_params.size() > 3) {
-        commandInvalid = true;
-    } else if (cmd_params[0] == "shutdown") {
-        cmd = ANDROID_RB_POWEROFF;
-        if (cmd_params.size() == 2 && cmd_params[1] == "userrequested") {
-            // The shutdown reason is PowerManager.SHUTDOWN_USER_REQUESTED.
-            // Run fsck once the file system is remounted in read-only mode.
-            runFsck = true;
-            reason_string = cmd_params[1];
-        }
-    } else if (cmd_params[0] == "reboot") {
-        cmd = ANDROID_RB_RESTART2;
-        if (cmd_params.size() >= 2) {
-            reboot_target = cmd_params[1];
-            // When rebooting to the bootloader notify the bootloader writing
-            // also the BCB.
-            if (reboot_target == "bootloader") {
-                std::string err;
-                if (!write_reboot_bootloader(&err)) {
-                    LOG(ERROR) << "reboot-bootloader: Error writing "
-                                  "bootloader_message: "
-                               << err;
-                }
-            }
-            // If there is an additional bootloader parameter, pass it along
-            if (cmd_params.size() == 3) {
-                reboot_target += "," + cmd_params[2];
-            }
-        }
-    } else if (command == "thermal-shutdown") {  // no additional parameter allowed
-        cmd = ANDROID_RB_THERMOFF;
-    } else {
-        commandInvalid = true;
-    }
-    if (commandInvalid) {
-        LOG(ERROR) << "powerctl: unrecognized command '" << command << "'";
-        return -EINVAL;
-    }
-
-    DoReboot(cmd, reason_string, reboot_target, runFsck);
-    return 0;
-}
-
 static int do_trigger(const std::vector<std::string>& args) {
     ActionManager::GetInstance().QueueEventTrigger(args[1]);
     return 0;
@@ -919,7 +867,6 @@
         {"mount_all",               {1,     kMax, do_mount_all}},
         {"mount",                   {3,     kMax, do_mount}},
         {"umount",                  {1,     1,    do_umount}},
-        {"powerctl",                {1,     1,    do_powerctl}},
         {"restart",                 {1,     1,    do_restart}},
         {"restorecon",              {1,     kMax, do_restorecon}},
         {"restorecon_recursive",    {1,     kMax, do_restorecon_recursive}},
diff --git a/init/devices.cpp b/init/devices.cpp
index ad313a0..6e13863 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "devices.h"
+
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -27,23 +29,19 @@
 #include <sys/sendfile.h>
 #include <sys/socket.h>
 #include <sys/time.h>
-#include <sys/types.h>
 #include <sys/un.h>
 #include <sys/wait.h>
 #include <unistd.h>
 
 #include <algorithm>
 #include <memory>
-#include <string>
 #include <thread>
-#include <vector>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
-#include <cutils/list.h>
 #include <cutils/uevent.h>
 #include <private/android_filesystem_config.h>
 #include <selinux/android.h>
@@ -51,119 +49,67 @@
 #include <selinux/label.h>
 #include <selinux/selinux.h>
 
-#include "devices.h"
 #include "ueventd_parser.h"
 #include "util.h"
 
-#define SYSFS_PREFIX    "/sys"
-
 extern struct selabel_handle *sehandle;
 
 static android::base::unique_fd device_fd;
 
-struct perms_ {
-    char *name;
-    char *attr;
-    mode_t perm;
-    unsigned int uid;
-    unsigned int gid;
-    unsigned short prefix;
-    unsigned short wildcard;
-};
-
-struct perm_node {
-    struct perms_ dp;
-    struct listnode plist;
-};
-
-static list_declare(sys_perms);
-static list_declare(dev_perms);
-
-int add_dev_perms(const char *name, const char *attr,
-                  mode_t perm, unsigned int uid, unsigned int gid,
-                  unsigned short prefix,
-                  unsigned short wildcard) {
-    struct perm_node *node = (perm_node*) calloc(1, sizeof(*node));
-    if (!node)
-        return -ENOMEM;
-
-    node->dp.name = strdup(name);
-    if (!node->dp.name) {
-        free(node);
-        return -ENOMEM;
+Permissions::Permissions(const std::string& name, mode_t perm, uid_t uid, gid_t gid)
+    : name_(name), perm_(perm), uid_(uid), gid_(gid), prefix_(false), wildcard_(false) {
+    // If the first * is the last character, then we'll treat name_ as a prefix
+    // Otherwise, if a * is present, then we do a full fnmatch().
+    auto wildcard_position = name_.find('*');
+    if (wildcard_position == name_.length() - 1) {
+        prefix_ = true;
+        name_.pop_back();
+    } else if (wildcard_position != std::string::npos) {
+        wildcard_ = true;
     }
-
-    if (attr) {
-        node->dp.attr = strdup(attr);
-        if (!node->dp.attr) {
-            free(node->dp.name);
-            free(node);
-            return -ENOMEM;
-        }
-    }
-
-    node->dp.perm = perm;
-    node->dp.uid = uid;
-    node->dp.gid = gid;
-    node->dp.prefix = prefix;
-    node->dp.wildcard = wildcard;
-
-    if (attr)
-        list_add_tail(&sys_perms, &node->plist);
-    else
-        list_add_tail(&dev_perms, &node->plist);
-
-    return 0;
 }
 
-static bool perm_path_matches(const char *path, struct perms_ *dp)
-{
-    if (dp->prefix) {
-        if (strncmp(path, dp->name, strlen(dp->name)) == 0)
-            return true;
-    } else if (dp->wildcard) {
-        if (fnmatch(dp->name, path, FNM_PATHNAME) == 0)
-            return true;
+bool Permissions::Match(const std::string& path) const {
+    if (prefix_) {
+        return android::base::StartsWith(path, name_.c_str());
+    } else if (wildcard_) {
+        return fnmatch(name_.c_str(), path.c_str(), FNM_PATHNAME) == 0;
     } else {
-        if (strcmp(path, dp->name) == 0)
-            return true;
+        return path == name_;
     }
 
     return false;
 }
 
-static bool match_subsystem(perms_* dp, const char* pattern,
-                            const char* path, const char* subsystem) {
-    if (!pattern || !subsystem || strstr(dp->name, subsystem) == NULL) {
-        return false;
+bool SysfsPermissions::MatchWithSubsystem(const std::string& path,
+                                          const std::string& subsystem) const {
+    std::string path_basename = android::base::Basename(path);
+    if (name().find(subsystem) != std::string::npos) {
+        if (Match("/sys/class/" + subsystem + "/" + path_basename)) return true;
+        if (Match("/sys/bus/" + subsystem + "/devices/" + path_basename)) return true;
     }
-
-    std::string subsys_path = android::base::StringPrintf(pattern, subsystem, basename(path));
-    return perm_path_matches(subsys_path.c_str(), dp);
+    return Match(path);
 }
 
-static void fixup_sys_perms(const std::string& upath, const std::string& subsystem) {
+void SysfsPermissions::SetPermissions(const std::string& path) const {
+    std::string attribute_file = path + "/" + attribute_;
+    LOG(INFO) << "fixup " << attribute_file << " " << uid() << " " << gid() << " " << std::oct
+              << perm();
+    chown(attribute_file.c_str(), uid(), gid());
+    chmod(attribute_file.c_str(), perm());
+}
+
+// TODO: Move these to be member variables of a future devices class.
+std::vector<Permissions> dev_permissions;
+std::vector<SysfsPermissions> sysfs_permissions;
+
+static void fixup_sys_permissions(const std::string& upath, const std::string& subsystem) {
     // upaths omit the "/sys" that paths in this list
     // contain, so we prepend it...
-    std::string path = SYSFS_PREFIX + upath;
+    std::string path = "/sys" + upath;
 
-    listnode* node;
-    list_for_each(node, &sys_perms) {
-        perms_* dp = &(node_to_item(node, perm_node, plist))->dp;
-        if (match_subsystem(dp, SYSFS_PREFIX "/class/%s/%s", path.c_str(), subsystem.c_str())) {
-            ; // matched
-        } else if (match_subsystem(dp, SYSFS_PREFIX "/bus/%s/devices/%s", path.c_str(),
-                                   subsystem.c_str())) {
-            ; // matched
-        } else if (!perm_path_matches(path.c_str(), dp)) {
-            continue;
-        }
-
-        std::string attr_file = path + "/" + dp->attr;
-        LOG(INFO) << "fixup " << attr_file
-                  << " " << dp->uid << " " << dp->gid << " " << std::oct << dp->perm;
-        chown(attr_file.c_str(), dp->uid, dp->gid);
-        chmod(attr_file.c_str(), dp->perm);
+    for (const auto& s : sysfs_permissions) {
+        if (s.MatchWithSubsystem(path, subsystem)) s.SetPermissions(path);
     }
 
     if (access(path.c_str(), F_OK) == 0) {
@@ -172,42 +118,26 @@
     }
 }
 
-static mode_t get_device_perm(const char* path, const std::vector<std::string>& links,
-                              unsigned* uid, unsigned* gid) {
-    struct listnode *node;
-    struct perm_node *perm_node;
-    struct perms_ *dp;
-
-    /* search the perms list in reverse so that ueventd.$hardware can
-     * override ueventd.rc
-     */
-    list_for_each_reverse(node, &dev_perms) {
-        perm_node = node_to_item(node, struct perm_node, plist);
-        dp = &perm_node->dp;
-
-        if (perm_path_matches(path, dp) ||
-            std::any_of(links.begin(), links.end(),
-                        [dp](const auto& link) { return perm_path_matches(link.c_str(), dp); })) {
-            *uid = dp->uid;
-            *gid = dp->gid;
-            return dp->perm;
+static std::tuple<mode_t, uid_t, gid_t> get_device_permissions(
+    const std::string& path, const std::vector<std::string>& links) {
+    // Search the perms list in reverse so that ueventd.$hardware can override ueventd.rc.
+    for (auto it = dev_permissions.rbegin(); it != dev_permissions.rend(); ++it) {
+        if (it->Match(path) || std::any_of(links.begin(), links.end(),
+                                           [it](const auto& link) { return it->Match(link); })) {
+            return {it->perm(), it->uid(), it->gid()};
         }
     }
     /* Default if nothing found. */
-    *uid = 0;
-    *gid = 0;
-    return 0600;
+    return {0600, 0, 0};
 }
 
 static void make_device(const std::string& path, int block, int major, int minor,
                         const std::vector<std::string>& links) {
-    unsigned uid;
-    unsigned gid;
-    mode_t mode;
     dev_t dev;
     char *secontext = NULL;
 
-    mode = get_device_perm(path.c_str(), links, &uid, &gid) | (block ? S_IFBLK : S_IFCHR);
+    auto [mode, uid, gid] = get_device_permissions(path, links);
+    mode |= (block ? S_IFBLK : S_IFCHR);
 
     if (sehandle) {
         std::vector<const char*> c_links;
@@ -607,7 +537,7 @@
 static void handle_device_event(struct uevent *uevent)
 {
     if (uevent->action == "add" || uevent->action == "change" || uevent->action == "online") {
-        fixup_sys_perms(uevent->path, uevent->subsystem);
+        fixup_sys_permissions(uevent->path, uevent->subsystem);
     }
 
     if (uevent->subsystem == "block") {
diff --git a/init/devices.h b/init/devices.h
index b8b039f..2cbae66 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -18,6 +18,7 @@
 #define _INIT_DEVICES_H
 
 #include <sys/stat.h>
+#include <sys/types.h>
 
 #include <functional>
 #include <string>
@@ -47,15 +48,48 @@
     int minor;
 };
 
+class Permissions {
+  public:
+    Permissions(const std::string& name, mode_t perm, uid_t uid, gid_t gid);
+
+    bool Match(const std::string& path) const;
+
+    mode_t perm() const { return perm_; }
+    uid_t uid() const { return uid_; }
+    gid_t gid() const { return gid_; }
+
+  protected:
+    const std::string& name() const { return name_; }
+
+  private:
+    std::string name_;
+    mode_t perm_;
+    uid_t uid_;
+    gid_t gid_;
+    bool prefix_;
+    bool wildcard_;
+};
+
+class SysfsPermissions : public Permissions {
+  public:
+    SysfsPermissions(const std::string& name, const std::string& attribute, mode_t perm, uid_t uid,
+                     gid_t gid)
+        : Permissions(name, perm, uid, gid), attribute_(attribute) {}
+
+    bool MatchWithSubsystem(const std::string& path, const std::string& subsystem) const;
+    void SetPermissions(const std::string& path) const;
+
+  private:
+    const std::string attribute_;
+};
+
+extern std::vector<Permissions> dev_permissions;
+extern std::vector<SysfsPermissions> sysfs_permissions;
+
 typedef std::function<coldboot_action_t(struct uevent* uevent)> coldboot_callback;
 extern coldboot_action_t handle_device_fd(coldboot_callback fn = nullptr);
 extern void device_init(const char* path = nullptr, coldboot_callback fn = nullptr);
 extern void device_close();
-
-extern int add_dev_perms(const char *name, const char *attr,
-                         mode_t perm, unsigned int uid,
-                         unsigned int gid, unsigned short prefix,
-                         unsigned short wildcard);
 int get_device_fd();
 
 // Exposed for testing
diff --git a/init/devices_test.cpp b/init/devices_test.cpp
index 693fb54..66521db 100644
--- a/init/devices_test.cpp
+++ b/init/devices_test.cpp
@@ -316,3 +316,94 @@
     sanitize_partition_name(&string);
     EXPECT_EQ("_", string);
 }
+
+TEST(devices, DevPermissionsMatchNormal) {
+    // Basic from ueventd.rc
+    // /dev/null                 0666   root       root
+    Permissions permissions("/dev/null", 0666, 0, 0);
+    EXPECT_TRUE(permissions.Match("/dev/null"));
+    EXPECT_FALSE(permissions.Match("/dev/nullsuffix"));
+    EXPECT_FALSE(permissions.Match("/dev/nul"));
+    EXPECT_EQ(0666U, permissions.perm());
+    EXPECT_EQ(0U, permissions.uid());
+    EXPECT_EQ(0U, permissions.gid());
+}
+
+TEST(devices, DevPermissionsMatchPrefix) {
+    // Prefix from ueventd.rc
+    // /dev/dri/*                0666   root       graphics
+    Permissions permissions("/dev/dri/*", 0666, 0, 1000);
+    EXPECT_TRUE(permissions.Match("/dev/dri/some_dri_device"));
+    EXPECT_TRUE(permissions.Match("/dev/dri/some_other_dri_device"));
+    EXPECT_TRUE(permissions.Match("/dev/dri/"));
+    EXPECT_FALSE(permissions.Match("/dev/dr/non_match"));
+    EXPECT_EQ(0666U, permissions.perm());
+    EXPECT_EQ(0U, permissions.uid());
+    EXPECT_EQ(1000U, permissions.gid());
+}
+
+TEST(devices, DevPermissionsMatchWildcard) {
+    // Wildcard example
+    // /dev/device*name                0666   root       graphics
+    Permissions permissions("/dev/device*name", 0666, 0, 1000);
+    EXPECT_TRUE(permissions.Match("/dev/devicename"));
+    EXPECT_TRUE(permissions.Match("/dev/device123name"));
+    EXPECT_TRUE(permissions.Match("/dev/deviceabcname"));
+    EXPECT_FALSE(permissions.Match("/dev/device123name/subdevice"));
+    EXPECT_FALSE(permissions.Match("/dev/deviceame"));
+    EXPECT_EQ(0666U, permissions.perm());
+    EXPECT_EQ(0U, permissions.uid());
+    EXPECT_EQ(1000U, permissions.gid());
+}
+
+TEST(devices, DevPermissionsMatchWildcardPrefix) {
+    // Wildcard+Prefix example
+    // /dev/device*name*                0666   root       graphics
+    Permissions permissions("/dev/device*name*", 0666, 0, 1000);
+    EXPECT_TRUE(permissions.Match("/dev/devicename"));
+    EXPECT_TRUE(permissions.Match("/dev/device123name"));
+    EXPECT_TRUE(permissions.Match("/dev/deviceabcname"));
+    EXPECT_TRUE(permissions.Match("/dev/device123namesomething"));
+    // FNM_PATHNAME doesn't match '/' with *
+    EXPECT_FALSE(permissions.Match("/dev/device123name/something"));
+    EXPECT_FALSE(permissions.Match("/dev/deviceame"));
+    EXPECT_EQ(0666U, permissions.perm());
+    EXPECT_EQ(0U, permissions.uid());
+    EXPECT_EQ(1000U, permissions.gid());
+}
+
+TEST(devices, SysfsPermissionsMatchWithSubsystemNormal) {
+    // /sys/devices/virtual/input/input*   enable      0660  root   input
+    SysfsPermissions permissions("/sys/devices/virtual/input/input*", "enable", 0660, 0, 1001);
+    EXPECT_TRUE(permissions.MatchWithSubsystem("/sys/devices/virtual/input/input0", "input"));
+    EXPECT_FALSE(permissions.MatchWithSubsystem("/sys/devices/virtual/input/not_input0", "input"));
+    EXPECT_EQ(0660U, permissions.perm());
+    EXPECT_EQ(0U, permissions.uid());
+    EXPECT_EQ(1001U, permissions.gid());
+}
+
+TEST(devices, SysfsPermissionsMatchWithSubsystemClass) {
+    // /sys/class/input/event*   enable      0660  root   input
+    SysfsPermissions permissions("/sys/class/input/event*", "enable", 0660, 0, 1001);
+    EXPECT_TRUE(permissions.MatchWithSubsystem(
+        "/sys/devices/soc.0/f9924000.i2c/i2c-2/2-0020/input/input0/event0", "input"));
+    EXPECT_FALSE(permissions.MatchWithSubsystem(
+        "/sys/devices/soc.0/f9924000.i2c/i2c-2/2-0020/input/input0/not_event0", "input"));
+    EXPECT_FALSE(permissions.MatchWithSubsystem(
+        "/sys/devices/soc.0/f9924000.i2c/i2c-2/2-0020/input/input0/event0", "not_input"));
+    EXPECT_EQ(0660U, permissions.perm());
+    EXPECT_EQ(0U, permissions.uid());
+    EXPECT_EQ(1001U, permissions.gid());
+}
+
+TEST(devices, SysfsPermissionsMatchWithSubsystemBus) {
+    // /sys/bus/i2c/devices/i2c-*   enable      0660  root   input
+    SysfsPermissions permissions("/sys/bus/i2c/devices/i2c-*", "enable", 0660, 0, 1001);
+    EXPECT_TRUE(permissions.MatchWithSubsystem("/sys/devices/soc.0/f9967000.i2c/i2c-5", "i2c"));
+    EXPECT_FALSE(permissions.MatchWithSubsystem("/sys/devices/soc.0/f9967000.i2c/not-i2c", "i2c"));
+    EXPECT_FALSE(
+        permissions.MatchWithSubsystem("/sys/devices/soc.0/f9967000.i2c/i2c-5", "not_i2c"));
+    EXPECT_EQ(0660U, permissions.perm());
+    EXPECT_EQ(0U, permissions.uid());
+    EXPECT_EQ(1001U, permissions.gid());
+}
diff --git a/init/init.cpp b/init/init.cpp
index 181b18d..9a4aa24 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -67,6 +67,7 @@
 #include "keychords.h"
 #include "log.h"
 #include "property_service.h"
+#include "reboot.h"
 #include "service.h"
 #include "signal_handler.h"
 #include "ueventd.h"
@@ -153,8 +154,13 @@
     return true;
 }
 
-void property_changed(const char *name, const char *value)
-{
+void property_changed(const std::string& name, const std::string& value) {
+    // If the property is sys.powerctl, we bypass the event queue and immediately handle it.
+    // This is to ensure that init will always and immediately shutdown/reboot, regardless of
+    // if there are other pending events to process or if init is waiting on an exec service or
+    // waiting on a property.
+    if (name == "sys.powerctl") HandlePowerctlMessage(value);
+
     if (property_triggers_enabled)
         ActionManager::GetInstance().QueuePropertyTrigger(name, value);
     if (waiting_for_prop) {
diff --git a/init/init.h b/init/init.h
index fe850ef..1da3350 100644
--- a/init/init.h
+++ b/init/init.h
@@ -26,7 +26,7 @@
 
 void handle_control_message(const std::string& msg, const std::string& arg);
 
-void property_changed(const char *name, const char *value);
+void property_changed(const std::string& name, const std::string& value);
 
 void register_epoll_handler(int fd, void (*fn)());
 
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 2aa89ff..20a2aa1 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -205,7 +205,7 @@
     if (persistent_properties_loaded && android::base::StartsWith(name, "persist.")) {
         write_persistent_property(name.c_str(), value.c_str());
     }
-    property_changed(name.c_str(), value.c_str());
+    property_changed(name, value);
     return PROP_SUCCESS;
 }
 
diff --git a/init/reboot.cpp b/init/reboot.cpp
index d9ebd91..4d65437 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -425,3 +425,54 @@
     RebootSystem(cmd, rebootTarget);
     abort();
 }
+
+bool HandlePowerctlMessage(const std::string& command) {
+    unsigned int cmd = 0;
+    std::vector<std::string> cmd_params = android::base::Split(command, ",");
+    std::string reason_string = cmd_params[0];
+    std::string reboot_target = "";
+    bool run_fsck = false;
+    bool command_invalid = false;
+
+    if (cmd_params.size() > 3) {
+        command_invalid = true;
+    } else if (cmd_params[0] == "shutdown") {
+        cmd = ANDROID_RB_POWEROFF;
+        if (cmd_params.size() == 2 && cmd_params[1] == "userrequested") {
+            // The shutdown reason is PowerManager.SHUTDOWN_USER_REQUESTED.
+            // Run fsck once the file system is remounted in read-only mode.
+            run_fsck = true;
+            reason_string = cmd_params[1];
+        }
+    } else if (cmd_params[0] == "reboot") {
+        cmd = ANDROID_RB_RESTART2;
+        if (cmd_params.size() >= 2) {
+            reboot_target = cmd_params[1];
+            // When rebooting to the bootloader notify the bootloader writing
+            // also the BCB.
+            if (reboot_target == "bootloader") {
+                std::string err;
+                if (!write_reboot_bootloader(&err)) {
+                    LOG(ERROR) << "reboot-bootloader: Error writing "
+                                  "bootloader_message: "
+                               << err;
+                }
+            }
+            // If there is an additional bootloader parameter, pass it along
+            if (cmd_params.size() == 3) {
+                reboot_target += "," + cmd_params[2];
+            }
+        }
+    } else if (command == "thermal-shutdown") {  // no additional parameter allowed
+        cmd = ANDROID_RB_THERMOFF;
+    } else {
+        command_invalid = true;
+    }
+    if (command_invalid) {
+        LOG(ERROR) << "powerctl: unrecognized command '" << command << "'";
+        return false;
+    }
+
+    DoReboot(cmd, reason_string, reboot_target, run_fsck);
+    return true;
+}
diff --git a/init/reboot.h b/init/reboot.h
index 6432fa5..b304b3c 100644
--- a/init/reboot.h
+++ b/init/reboot.h
@@ -29,4 +29,7 @@
 void DoReboot(unsigned int cmd, const std::string& reason, const std::string& rebootTarget,
               bool runFsck) __attribute__((__noreturn__));
 
+// Parses and handles a setprop sys.powerctl message.
+bool HandlePowerctlMessage(const std::string& command);
+
 #endif
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index ea767a8..b6c6a01 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -101,8 +101,6 @@
     mode_t perm;
     uid_t uid;
     gid_t gid;
-    int prefix = 0;
-    int wildcard = 0;
     char *endptr;
 
     if (nargs == 0)
@@ -125,15 +123,6 @@
         return;
     }
 
-    int len = strlen(name);
-    char *wildcard_chr = strchr(name, '*');
-    if ((name[len - 1] == '*') && (wildcard_chr == (name + len - 1))) {
-        prefix = 1;
-        name[len - 1] = '\0';
-    } else if (wildcard_chr) {
-        wildcard = 1;
-    }
-
     perm = strtol(args[1], &endptr, 8);
     if (!endptr || *endptr != '\0') {
         LOG(ERROR) << "invalid mode (" << fn << ":" << line << ") '" << args[1] << "'";
@@ -154,13 +143,9 @@
     }
     gid = grp->gr_gid;
 
-    if (add_dev_perms(name, attr, perm, uid, gid, prefix, wildcard) != 0) {
-        PLOG(ERROR) << "add_dev_perms(name=" << name <<
-                       ", attr=" << attr <<
-                       ", perm=" << std::oct << perm << std::dec <<
-                       ", uid=" << uid << ", gid=" << gid <<
-                       ", prefix=" << prefix << ", wildcard=" << wildcard <<
-                       ")";
-        return;
+    if (attr) {
+        sysfs_permissions.emplace_back(name, attr, perm, uid, gid);
+    } else {
+        dev_permissions.emplace_back(name, perm, uid, gid);
     }
 }
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index bb82f4d..0a3bab7 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -143,7 +143,10 @@
     },
 
     shared_libs: ["liblog"],
-    header_libs: ["libcutils_headers"],
+    header_libs: [
+        "libcutils_headers",
+        "libutils_headers",
+    ],
     export_header_lib_headers: ["libcutils_headers"],
 
     cflags: [
diff --git a/libcutils/sched_policy.cpp b/libcutils/sched_policy.cpp
index a9c061e..4a0b035 100644
--- a/libcutils/sched_policy.cpp
+++ b/libcutils/sched_policy.cpp
@@ -28,6 +28,13 @@
 
 #define UNUSED __attribute__((__unused__))
 
+#ifndef SLOGE
+#define SLOGE ALOGE
+#endif
+#ifndef SLOGW
+#define SLOGW ALOGW
+#endif
+
 /* Re-map SP_DEFAULT to the system default policy, and leave other values unchanged.
  * Call this any place a SchedPolicy is used as an input parameter.
  * Returns the possibly re-mapped policy.
diff --git a/libcutils/trace-dev.c b/libcutils/trace-dev.c
index 113f423..d45e5a9 100644
--- a/libcutils/trace-dev.c
+++ b/libcutils/trace-dev.c
@@ -29,7 +29,8 @@
 #include <cutils/compiler.h>
 #include <cutils/properties.h>
 #include <cutils/trace.h>
-#include <private/android_logger.h>
+#include <log/log.h>
+#include <log/log_properties.h>
 
 /**
  * Maximum size of a message that can be logged to the trace buffer.
diff --git a/liblog/event_tag_map.cpp b/liblog/event_tag_map.cpp
index 73ed16f..5fc7e35 100644
--- a/liblog/event_tag_map.cpp
+++ b/liblog/event_tag_map.cpp
@@ -31,6 +31,7 @@
 #include <unordered_map>
 
 #include <log/event_tag_map.h>
+#include <log/log_properties.h>
 #include <private/android_logger.h>
 #include <utils/FastStrcmp.h>
 #include <utils/RWLock.h>
diff --git a/liblog/include/log/log_properties.h b/liblog/include/log/log_properties.h
new file mode 100644
index 0000000..7d398a6
--- /dev/null
+++ b/liblog/include/log/log_properties.h
@@ -0,0 +1,35 @@
+/*
+**
+** Copyright 2017, The Android Open Source Project
+**
+** This file is dual licensed.  It may be redistributed and/or modified
+** under the terms of the Apache 2.0 License OR version 2 of the GNU
+** General Public License.
+*/
+
+#ifndef _LIBS_LOG_PROPERTIES_H
+#define _LIBS_LOG_PROPERTIES_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef __ANDROID_USE_LIBLOG_IS_DEBUGGABLE_INTERFACE
+#ifndef __ANDROID_API__
+#define __ANDROID_USE_LIBLOG_IS_DEBUGGABLE_INTERFACE 1
+#elif __ANDROID_API__ > 24 /* > Nougat */
+#define __ANDROID_USE_LIBLOG_IS_DEBUGGABLE_INTERFACE 1
+#else
+#define __ANDROID_USE_LIBLOG_IS_DEBUGGABLE_INTERFACE 0
+#endif
+#endif
+
+#if __ANDROID_USE_LIBLOG_IS_DEBUGGABLE_INTERFACE
+int __android_log_is_debuggable();
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBS_LOG_PROPERTIES_H */
diff --git a/liblog/include/log/log_time.h b/liblog/include/log/log_time.h
index 5f70f7d..9ece0b3 100644
--- a/liblog/include/log/log_time.h
+++ b/liblog/include/log/log_time.h
@@ -41,13 +41,12 @@
   static const uint32_t tv_sec_max = 0xFFFFFFFFUL;
   static const uint32_t tv_nsec_max = 999999999UL;
 
-  log_time(const timespec& T) {
-    tv_sec = static_cast<uint32_t>(T.tv_sec);
-    tv_nsec = static_cast<uint32_t>(T.tv_nsec);
+  log_time(const timespec& T)
+      : tv_sec(static_cast<uint32_t>(T.tv_sec)),
+        tv_nsec(static_cast<uint32_t>(T.tv_nsec)) {
   }
-  log_time(uint32_t sec, uint32_t nsec) {
-    tv_sec = sec;
-    tv_nsec = nsec;
+  explicit log_time(uint32_t sec, uint32_t nsec = 0)
+      : tv_sec(sec), tv_nsec(nsec) {
   }
 #ifdef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_
 #define __struct_log_time_private_defined
@@ -56,14 +55,14 @@
   log_time() {
   }
 #ifdef __linux__
-  log_time(clockid_t id) {
+  explicit log_time(clockid_t id) {
     timespec T;
     clock_gettime(id, &T);
     tv_sec = static_cast<uint32_t>(T.tv_sec);
     tv_nsec = static_cast<uint32_t>(T.tv_nsec);
   }
 #endif
-  log_time(const char* T) {
+  explicit log_time(const char* T) {
     const uint8_t* c = reinterpret_cast<const uint8_t*>(T);
     tv_sec = c[0] | (static_cast<uint32_t>(c[1]) << 8) |
              (static_cast<uint32_t>(c[2]) << 16) |
diff --git a/liblog/include/private/android_logger.h b/liblog/include/private/android_logger.h
index e3ccfcf..965de37 100644
--- a/liblog/include/private/android_logger.h
+++ b/liblog/include/private/android_logger.h
@@ -135,8 +135,6 @@
 int __android_log_security_bswrite(int32_t tag, const char* payload);
 int __android_log_security(); /* Device Owner is present */
 
-int __android_log_is_debuggable();
-
 #define BOOL_DEFAULT_FLAG_TRUE_FALSE 0x1
 #define BOOL_DEFAULT_FALSE 0x0        /* false if property not present   */
 #define BOOL_DEFAULT_TRUE 0x1         /* true if property not present    */
diff --git a/liblog/include_vndk/log/log_properties.h b/liblog/include_vndk/log/log_properties.h
new file mode 120000
index 0000000..bbec426
--- /dev/null
+++ b/liblog/include_vndk/log/log_properties.h
@@ -0,0 +1 @@
+../../include/log/log_properties.h
\ No newline at end of file
diff --git a/liblog/liblog.map.txt b/liblog/liblog.map.txt
index c00f2a0..58fb148 100644
--- a/liblog/liblog.map.txt
+++ b/liblog/liblog.map.txt
@@ -39,4 +39,5 @@
 LIBLOG_O {
   global:
     __android_log_is_loggable_len;
+    __android_log_is_debuggable; # vndk
 };
diff --git a/liblog/pmsg_writer.c b/liblog/pmsg_writer.c
index e71c176..dc42856 100644
--- a/liblog/pmsg_writer.c
+++ b/liblog/pmsg_writer.c
@@ -26,6 +26,7 @@
 #include <sys/types.h>
 #include <time.h>
 
+#include <log/log_properties.h>
 #include <private/android_filesystem_config.h>
 #include <private/android_logger.h>
 
diff --git a/liblog/properties.c b/liblog/properties.c
index adf1900..c71cbcf 100644
--- a/liblog/properties.c
+++ b/liblog/properties.c
@@ -277,7 +277,7 @@
   return logLevel >= 0 && prio >= logLevel;
 }
 
-LIBLOG_ABI_PRIVATE int __android_log_is_debuggable() {
+LIBLOG_ABI_PUBLIC int __android_log_is_debuggable() {
   static uint32_t serial;
   static struct cache_char tag_cache;
   static const char key[] = "ro.debuggable";
diff --git a/libsparse/Android.bp b/libsparse/Android.bp
index dd8b5fd..6ec0991 100644
--- a/libsparse/Android.bp
+++ b/libsparse/Android.bp
@@ -10,17 +10,23 @@
         "sparse.c",
         "sparse_crc32.c",
         "sparse_err.c",
-        "sparse_read.c",
+        "sparse_read.cpp",
     ],
     cflags: ["-Werror"],
     local_include_dirs: ["include"],
     export_include_dirs: ["include"],
     target: {
         host: {
-            shared_libs: ["libz-host"],
+            shared_libs: [
+                "libz-host",
+                "libbase",
+            ],
         },
         android: {
-            shared_libs: ["libz"],
+            shared_libs: [
+                "libz",
+                "libbase",
+            ],
         },
         windows: {
             enabled: true,
@@ -38,6 +44,7 @@
     static_libs: [
         "libsparse",
         "libz",
+        "libbase",
     ],
 
     cflags: ["-Werror"],
@@ -50,6 +57,7 @@
     static_libs: [
         "libsparse",
         "libz",
+        "libbase",
     ],
 
     cflags: ["-Werror"],
@@ -61,6 +69,7 @@
     static_libs: [
         "libsparse",
         "libz",
+        "libbase",
     ],
 
     cflags: ["-Werror"],
diff --git a/libsparse/output_file.h b/libsparse/output_file.h
index 474c1fc..b67e94e 100644
--- a/libsparse/output_file.h
+++ b/libsparse/output_file.h
@@ -17,6 +17,10 @@
 #ifndef _OUTPUT_FILE_H_
 #define _OUTPUT_FILE_H_
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 #include <sparse/sparse.h>
 
 struct output_file;
@@ -38,4 +42,8 @@
 
 int read_all(int fd, void *buf, size_t len);
 
+#ifdef __cplusplus
+}
+#endif
+
 #endif
diff --git a/libsparse/sparse_file.h b/libsparse/sparse_file.h
index 91a12e6..763f43f 100644
--- a/libsparse/sparse_file.h
+++ b/libsparse/sparse_file.h
@@ -17,6 +17,10 @@
 #ifndef _LIBSPARSE_SPARSE_FILE_H_
 #define _LIBSPARSE_SPARSE_FILE_H_
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 #include <sparse/sparse.h>
 
 struct sparse_file {
@@ -28,5 +32,8 @@
 	struct output_file *out;
 };
 
+#ifdef __cplusplus
+}
+#endif
 
 #endif /* _LIBSPARSE_SPARSE_FILE_H_ */
diff --git a/libsparse/sparse_format.h b/libsparse/sparse_format.h
index c41f12a..779e038 100644
--- a/libsparse/sparse_format.h
+++ b/libsparse/sparse_format.h
@@ -18,6 +18,10 @@
 #define _LIBSPARSE_SPARSE_FORMAT_H_
 #include "sparse_defs.h"
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 typedef struct sparse_header {
   __le32	magic;		/* 0xed26ff3a */
   __le16	major_version;	/* (0x1) - reject images with higher major versions */
@@ -52,4 +56,8 @@
  *  For a CRC32 chunk, it's 4 bytes of CRC32
  */
 
+#ifdef __cplusplus
+}
+#endif
+
 #endif
diff --git a/libsparse/sparse_read.c b/libsparse/sparse_read.cpp
similarity index 88%
rename from libsparse/sparse_read.c
rename to libsparse/sparse_read.cpp
index a188202..bd66873 100644
--- a/libsparse/sparse_read.c
+++ b/libsparse/sparse_read.cpp
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-#define _GNU_SOURCE
 #define _FILE_OFFSET_BITS 64
 #define _LARGEFILE64_SOURCE 1
 
+#include <algorithm>
 #include <inttypes.h>
 #include <fcntl.h>
 #include <stdarg.h>
@@ -25,17 +25,19 @@
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <string.h>
+#include <string>
 #include <unistd.h>
 
 #include <sparse/sparse.h>
 
+#include "android-base/stringprintf.h"
 #include "defs.h"
 #include "output_file.h"
 #include "sparse_crc32.h"
 #include "sparse_file.h"
 #include "sparse_format.h"
 
+
 #if defined(__APPLE__) && defined(__MACH__)
 #define lseek64 lseek
 #define off64_t off_t
@@ -45,57 +47,30 @@
 #define SPARSE_HEADER_LEN       (sizeof(sparse_header_t))
 #define CHUNK_HEADER_LEN (sizeof(chunk_header_t))
 
-#define COPY_BUF_SIZE (1024U*1024U)
+static constexpr int64_t COPY_BUF_SIZE = 1024 * 1024;
 static char *copybuf;
 
-#define min(a, b) \
-	({ typeof(a) _a = (a); typeof(b) _b = (b); (_a < _b) ? _a : _b; })
+static std::string ErrorString(int err)
+{
+	if (err == -EOVERFLOW) return "EOF while reading file";
+	if (err == -EINVAL) return "Invalid sparse file format";
+	if (err == -ENOMEM) return "Failed allocation while reading file";
+	return android::base::StringPrintf("Unknown error %d", err);
+}
 
 static void verbose_error(bool verbose, int err, const char *fmt, ...)
 {
-	char *s = "";
-	char *at = "";
+	if (!verbose) return;
+
+	std::string msg = ErrorString(err);
 	if (fmt) {
+		msg += " at ";
 		va_list argp;
-		int size;
-
 		va_start(argp, fmt);
-		size = vsnprintf(NULL, 0, fmt, argp);
+		android::base::StringAppendV(&msg, fmt, argp);
 		va_end(argp);
-
-		if (size < 0) {
-			return;
-		}
-
-		at = malloc(size + 1);
-		if (at == NULL) {
-			return;
-		}
-
-		va_start(argp, fmt);
-		vsnprintf(at, size, fmt, argp);
-		va_end(argp);
-		at[size] = 0;
-		s = " at ";
 	}
-	if (verbose) {
-#ifndef _WIN32
-		if (err == -EOVERFLOW) {
-			sparse_print_verbose("EOF while reading file%s%s\n", s, at);
-		} else
-#endif
-		if (err == -EINVAL) {
-			sparse_print_verbose("Invalid sparse file format%s%s\n", s, at);
-		} else if (err == -ENOMEM) {
-			sparse_print_verbose("Failed allocation while reading file%s%s\n",
-					s, at);
-		} else {
-			sparse_print_verbose("Unknown error %d%s%s\n", err, s, at);
-		}
-	}
-	if (fmt) {
-		free(at);
-	}
+	sparse_print_verbose("%s\n", msg.c_str());
 }
 
 static int process_raw_chunk(struct sparse_file *s, unsigned int chunk_size,
@@ -104,7 +79,7 @@
 {
 	int ret;
 	int chunk;
-	unsigned int len = blocks * s->block_size;
+	int64_t len = blocks * s->block_size;
 
 	if (chunk_size % s->block_size != 0) {
 		return -EINVAL;
@@ -121,7 +96,7 @@
 
 	if (crc32) {
 		while (len) {
-			chunk = min(len, COPY_BUF_SIZE);
+			chunk = std::min(len, COPY_BUF_SIZE);
 			ret = read_all(fd, copybuf, chunk);
 			if (ret < 0) {
 				return ret;
@@ -168,7 +143,7 @@
 		}
 
 		while (len) {
-			chunk = min(len, COPY_BUF_SIZE);
+			chunk = std::min(len, COPY_BUF_SIZE);
 			*crc32 = sparse_crc32(*crc32, copybuf, chunk);
 			len -= chunk;
 		}
@@ -190,7 +165,7 @@
 		memset(copybuf, 0, COPY_BUF_SIZE);
 
 		while (len) {
-			int chunk = min(len, COPY_BUF_SIZE);
+			int chunk = std::min(len, COPY_BUF_SIZE);
 			*crc32 = sparse_crc32(*crc32, copybuf, chunk);
 			len -= chunk;
 		}
@@ -284,7 +259,7 @@
 	off64_t offset;
 
 	if (!copybuf) {
-		copybuf = malloc(COPY_BUF_SIZE);
+		copybuf = (char *)malloc(COPY_BUF_SIZE);
 	}
 
 	if (!copybuf) {
@@ -357,7 +332,7 @@
 static int sparse_file_read_normal(struct sparse_file *s, int fd)
 {
 	int ret;
-	uint32_t *buf = malloc(s->block_size);
+	uint32_t *buf = (uint32_t *)malloc(s->block_size);
 	unsigned int block = 0;
 	int64_t remain = s->len;
 	int64_t offset = 0;
@@ -370,7 +345,7 @@
 	}
 
 	while (remain > 0) {
-		to_read = min(remain, s->block_size);
+		to_read = std::min(remain, (int64_t)(s->block_size));
 		ret = read_all(fd, buf, to_read);
 		if (ret < 0) {
 			error("failed to read sparse file");
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index 0c7019a..a6bea0c 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -43,6 +43,8 @@
 // Default
 #define log_buffer_size(id) mMaxSize[id]
 
+const log_time LogBuffer::pruneMargin(3, 0);
+
 void LogBuffer::init() {
     log_id_for_each(i) {
         mLastSet[i] = false;
@@ -672,6 +674,8 @@
         }
         times++;
     }
+    log_time watermark(log_time::tv_sec_max, log_time::tv_nsec_max);
+    if (oldest) watermark = oldest->mStart - pruneMargin;
 
     LogBufferElementCollection::iterator it;
 
@@ -693,7 +697,7 @@
                 mLastSet[id] = true;
             }
 
-            if (oldest && (oldest->mStart <= element->getRealTime().nsec())) {
+            if (oldest && (watermark <= element->getRealTime())) {
                 busy = true;
                 if (oldest->mTimeout.tv_sec || oldest->mTimeout.tv_nsec) {
                     oldest->triggerReader_Locked();
@@ -785,7 +789,7 @@
         while (it != mLogElements.end()) {
             LogBufferElement* element = *it;
 
-            if (oldest && (oldest->mStart <= element->getRealTime().nsec())) {
+            if (oldest && (watermark <= element->getRealTime())) {
                 busy = true;
                 if (oldest->mTimeout.tv_sec || oldest->mTimeout.tv_nsec) {
                     oldest->triggerReader_Locked();
@@ -939,7 +943,7 @@
             mLastSet[id] = true;
         }
 
-        if (oldest && (oldest->mStart <= element->getRealTime().nsec())) {
+        if (oldest && (watermark <= element->getRealTime())) {
             busy = true;
             if (whitelist) {
                 break;
@@ -983,7 +987,7 @@
                 mLastSet[id] = true;
             }
 
-            if (oldest && (oldest->mStart <= element->getRealTime().nsec())) {
+            if (oldest && (watermark <= element->getRealTime())) {
                 busy = true;
                 if (stats.sizes(id) > (2 * log_buffer_size(id))) {
                     // kick a misbehaving log reader client off the island
@@ -1090,13 +1094,15 @@
         // client wants to start from the beginning
         it = mLogElements.begin();
     } else {
-        LogBufferElementCollection::iterator last;
         // 3 second limit to continue search for out-of-order entries.
-        log_time min = start - log_time(3, 0);
+        log_time min = start - pruneMargin;
+
         // Cap to 300 iterations we look back for out-of-order entries.
         size_t count = 300;
+
         // Client wants to start from some specified time. Chances are
         // we are better off starting from the end of the time sorted list.
+        LogBufferElementCollection::iterator last;
         for (last = it = mLogElements.end(); it != mLogElements.begin();
              /* do nothing */) {
             --it;
@@ -1112,9 +1118,22 @@
 
     log_time max = start;
 
+    LogBufferElement* lastElement = nullptr;  // iterator corruption paranoia
+    static const size_t maxSkip = 4194304;    // maximum entries to skip
+    size_t skip = maxSkip;
     for (; it != mLogElements.end(); ++it) {
         LogBufferElement* element = *it;
 
+        if (!--skip) {
+            android::prdebug("reader.per: too many elements skipped");
+            break;
+        }
+        if (element == lastElement) {
+            android::prdebug("reader.per: identical elements");
+            break;
+        }
+        lastElement = element;
+
         if (!privileged && (element->getUid() != uid)) {
             continue;
         }
@@ -1159,6 +1178,7 @@
             return max;
         }
 
+        skip = maxSkip;
         pthread_mutex_lock(&mLogElementsLock);
     }
     pthread_mutex_unlock(&mLogElementsLock);
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index 19d11cb..1272c20 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -174,6 +174,7 @@
    private:
     static constexpr size_t minPrune = 4;
     static constexpr size_t maxPrune = 256;
+    static const log_time pruneMargin;
 
     void maybePrune(log_id_t id);
     bool prune(log_id_t id, unsigned long pruneRows, uid_t uid = AID_ROOT);
diff --git a/logd/LogTags.cpp b/logd/LogTags.cpp
index 67649b1..fcd45bd 100644
--- a/logd/LogTags.cpp
+++ b/logd/LogTags.cpp
@@ -32,8 +32,8 @@
 #include <android-base/macros.h>
 #include <android-base/stringprintf.h>
 #include <log/log_event_list.h>
+#include <log/log_properties.h>
 #include <private/android_filesystem_config.h>
-#include <private/android_logger.h>
 
 #include "LogTags.h"
 #include "LogUtils.h"
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
index 88cb67a..a1d154a 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/tests/logd_test.cpp
@@ -670,8 +670,12 @@
     while (--i) {
         int fd = socket_local_client("logdr", ANDROID_SOCKET_NAMESPACE_RESERVED,
                                      SOCK_SEQPACKET);
-        EXPECT_LT(0, fd);
-        if (fd < 0) _exit(fd);
+        int save_errno = errno;
+        if (fd < 0) {
+            fprintf(stderr, "failed to open /dev/socket/logdr %s\n",
+                    strerror(save_errno));
+            _exit(fd);
+        }
 
         std::string ask = android::base::StringPrintf(
             "dumpAndClose lids=0,1,2,3,4,5 timeout=6 start=%" PRIu32
@@ -723,8 +727,12 @@
         // active _or_ inactive during the test.
         if (content_timeout) {
             log_time msg(msg_timeout.entry.sec, msg_timeout.entry.nsec);
-            EXPECT_FALSE(msg < now);
-            if (msg < now) _exit(-1);
+            if (msg < now) {
+                fprintf(stderr, "%u.%09u < %u.%09u\n", msg_timeout.entry.sec,
+                        msg_timeout.entry.nsec, (unsigned)now.tv_sec,
+                        (unsigned)now.tv_nsec);
+                _exit(-1);
+            }
             if (msg > now) {
                 now = msg;
                 now.tv_sec += 30;
diff --git a/rootdir/init.rc b/rootdir/init.rc
index a666f75..56e736e 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -638,9 +638,6 @@
     class_reset late_start
     class_reset main
 
-on property:sys.powerctl=*
-    powerctl ${sys.powerctl}
-
 on property:sys.boot_completed=1
     bootchart stop