Merge "Add LLNDK liblog stub library for the VNDK"
diff --git a/base/Android.bp b/base/Android.bp
index 121da42..3af7686 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -98,6 +98,7 @@
         "parseint_test.cpp",
         "parsenetaddress_test.cpp",
         "quick_exit_test.cpp",
+        "scopeguard_test.cpp",
         "stringprintf_test.cpp",
         "strings_test.cpp",
         "test_main.cpp",
diff --git a/base/file.cpp b/base/file.cpp
index d4e5894..7fbebc5 100644
--- a/base/file.cpp
+++ b/base/file.cpp
@@ -28,8 +28,9 @@
 #include <string>
 #include <vector>
 
-#include "android-base/macros.h"  // For TEMP_FAILURE_RETRY on Darwin.
 #include "android-base/logging.h"
+#include "android-base/macros.h"  // For TEMP_FAILURE_RETRY on Darwin.
+#include "android-base/unique_fd.h"
 #include "android-base/utf8.h"
 #include "utils/Compat.h"
 
@@ -69,13 +70,11 @@
   content->clear();
 
   int flags = O_RDONLY | O_CLOEXEC | O_BINARY | (follow_symlinks ? 0 : O_NOFOLLOW);
-  int fd = TEMP_FAILURE_RETRY(open(path.c_str(), flags));
+  android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), flags)));
   if (fd == -1) {
     return false;
   }
-  bool result = ReadFdToString(fd, content);
-  close(fd);
-  return result;
+  return ReadFdToString(fd, content);
 }
 
 bool WriteStringToFd(const std::string& content, int fd) {
@@ -106,7 +105,7 @@
                        bool follow_symlinks) {
   int flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_BINARY |
               (follow_symlinks ? 0 : O_NOFOLLOW);
-  int fd = TEMP_FAILURE_RETRY(open(path.c_str(), flags, mode));
+  android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), flags, mode)));
   if (fd == -1) {
     PLOG(ERROR) << "android::WriteStringToFile open failed";
     return false;
@@ -126,7 +125,6 @@
     PLOG(ERROR) << "android::WriteStringToFile write failed";
     return CleanUpAfterFailedWrite(path);
   }
-  close(fd);
   return true;
 }
 #endif
@@ -135,14 +133,11 @@
                        bool follow_symlinks) {
   int flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_BINARY |
               (follow_symlinks ? 0 : O_NOFOLLOW);
-  int fd = TEMP_FAILURE_RETRY(open(path.c_str(), flags, DEFFILEMODE));
+  android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), flags, DEFFILEMODE)));
   if (fd == -1) {
     return false;
   }
-
-  bool result = WriteStringToFd(content, fd);
-  close(fd);
-  return result || CleanUpAfterFailedWrite(path);
+  return WriteStringToFd(content, fd) || CleanUpAfterFailedWrite(path);
 }
 
 bool ReadFully(int fd, void* data, size_t byte_count) {
diff --git a/base/include/android-base/scopeguard.h b/base/include/android-base/scopeguard.h
new file mode 100644
index 0000000..abcf4bc
--- /dev/null
+++ b/base/include/android-base/scopeguard.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef ANDROID_BASE_SCOPEGUARD_H
+#define ANDROID_BASE_SCOPEGUARD_H
+
+#include <utility>  // for std::move
+
+namespace android {
+namespace base {
+
+template <typename F>
+class ScopeGuard {
+ public:
+  ScopeGuard(F f) : f_(f), active_(true) {}
+
+  ScopeGuard(ScopeGuard&& that) : f_(std::move(that.f_)), active_(that.active_) {
+    that.active_ = false;
+  }
+
+  ~ScopeGuard() {
+    if (active_) f_();
+  }
+
+  ScopeGuard() = delete;
+  ScopeGuard(const ScopeGuard&) = delete;
+  void operator=(const ScopeGuard&) = delete;
+  void operator=(ScopeGuard&& that) = delete;
+
+  void Disable() { active_ = false; }
+
+  bool active() const { return active_; }
+
+ private:
+  F f_;
+  bool active_;
+};
+
+template <typename T>
+ScopeGuard<T> make_scope_guard(T f) {
+  return ScopeGuard<T>(f);
+}
+
+}  // namespace base
+}  // namespace android
+
+#endif  // ANDROID_BASE_SCOPEGUARD_H
diff --git a/base/scopeguard_test.cpp b/base/scopeguard_test.cpp
new file mode 100644
index 0000000..e11154a
--- /dev/null
+++ b/base/scopeguard_test.cpp
@@ -0,0 +1,46 @@
+/*
+ * 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 "android-base/scopeguard.h"
+
+#include <utility>
+
+#include <gtest/gtest.h>
+
+TEST(scopeguard, normal) {
+  bool guarded_var = true;
+  {
+    auto scopeguard = android::base::make_scope_guard([&guarded_var] { guarded_var = false; });
+  }
+  ASSERT_FALSE(guarded_var);
+}
+
+TEST(scopeguard, disabled) {
+  bool guarded_var = true;
+  {
+    auto scopeguard = android::base::make_scope_guard([&guarded_var] { guarded_var = false; });
+    scopeguard.Disable();
+  }
+  ASSERT_TRUE(guarded_var);
+}
+
+TEST(scopeguard, moved) {
+  int guarded_var = true;
+  auto scopeguard = android::base::make_scope_guard([&guarded_var] { guarded_var = false; });
+  { decltype(scopeguard) new_guard(std::move(scopeguard)); }
+  EXPECT_FALSE(scopeguard.active());
+  ASSERT_FALSE(guarded_var);
+}
diff --git a/bootstat/Android.bp b/bootstat/Android.bp
index 95c9af5..bc90a6e 100644
--- a/bootstat/Android.bp
+++ b/bootstat/Android.bp
@@ -74,6 +74,7 @@
 // -----------------------------------------------------------------------------
 cc_test {
     name: "bootstat_tests",
+    test_suites: ["device-tests"],
     defaults: ["bootstat_defaults"],
     host_supported: true,
     static_libs: [
diff --git a/bootstat/AndroidTest.xml b/bootstat/AndroidTest.xml
new file mode 100644
index 0000000..f3783fa
--- /dev/null
+++ b/bootstat/AndroidTest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Config for bootstat_tests">
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="cleanup" value="true" />
+        <option name="push" value="bootstat_tests->/data/local/tmp/bootstat_tests" />
+    </target_preparer>
+    <option name="test-suite-tag" value="apct" />
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="bootstat_tests" />
+    </test>
+</configuration>
\ No newline at end of file
diff --git a/debuggerd/client/debuggerd_client.cpp b/debuggerd/client/debuggerd_client.cpp
index 3b84853..2be13c6 100644
--- a/debuggerd/client/debuggerd_client.cpp
+++ b/debuggerd/client/debuggerd_client.cpp
@@ -140,7 +140,9 @@
   }
 
   bool backtrace = dump_type == kDebuggerdBacktrace;
-  send_signal(pid, backtrace);
+  if (!send_signal(pid, backtrace)) {
+    return false;
+  }
 
   rc = TEMP_FAILURE_RETRY(recv(set_timeout(sockfd.get()), &response, sizeof(response), MSG_TRUNC));
   if (rc == 0) {
diff --git a/include/system b/include/system
new file mode 120000
index 0000000..91d45be
--- /dev/null
+++ b/include/system
@@ -0,0 +1 @@
+../libsystem/include/system/
\ No newline at end of file
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/Android.mk b/init/Android.mk
index 1ca88d7..730ffc4 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -62,6 +62,7 @@
     action.cpp \
     capabilities.cpp \
     descriptors.cpp \
+    devices.cpp \
     import_parser.cpp \
     init_parser.cpp \
     log.cpp \
@@ -81,7 +82,6 @@
 LOCAL_SRC_FILES:= \
     bootchart.cpp \
     builtins.cpp \
-    devices.cpp \
     init.cpp \
     keychords.cpp \
     property_service.cpp \
@@ -138,6 +138,7 @@
 include $(CLEAR_VARS)
 LOCAL_MODULE := init_tests
 LOCAL_SRC_FILES := \
+    devices_test.cpp \
     init_parser_test.cpp \
     property_service_test.cpp \
     util_test.cpp \
diff --git a/init/action.cpp b/init/action.cpp
index 2ccf0bc..347edb0 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -16,16 +16,11 @@
 
 #include "action.h"
 
-#include <errno.h>
-
+#include <android-base/logging.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 
-#include "builtins.h"
-#include "error.h"
-#include "init_parser.h"
-#include "log.h"
 #include "util.h"
 
 using android::base::Join;
diff --git a/init/bootchart.cpp b/init/bootchart.cpp
index beabea1..825603a 100644
--- a/init/bootchart.cpp
+++ b/init/bootchart.cpp
@@ -17,7 +17,6 @@
 #include "bootchart.h"
 
 #include <dirent.h>
-#include <errno.h>
 #include <fcntl.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -31,9 +30,7 @@
 #include <condition_variable>
 #include <memory>
 #include <mutex>
-#include <string>
 #include <thread>
-#include <vector>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 64c00e9..e0a98f5 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -19,33 +19,27 @@
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <linux/loop.h>
+#include <linux/module.h>
 #include <mntent.h>
 #include <net/if.h>
-#include <signal.h>
 #include <sched.h>
+#include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <sys/socket.h>
 #include <sys/mount.h>
 #include <sys/resource.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
 #include <sys/syscall.h>
 #include <sys/time.h>
 #include <sys/types.h>
-#include <sys/stat.h>
 #include <sys/wait.h>
 #include <unistd.h>
-#include <linux/loop.h>
-#include <linux/module.h>
-
-#include <string>
-#include <thread>
-
-#include <selinux/android.h>
-#include <selinux/selinux.h>
-#include <selinux/label.h>
 
 #include <android-base/file.h>
+#include <android-base/logging.h>
 #include <android-base/parseint.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
@@ -55,14 +49,14 @@
 #include <ext4_utils/ext4_crypt.h>
 #include <ext4_utils/ext4_crypt_init_extensions.h>
 #include <fs_mgr.h>
-#include <logwrap/logwrap.h>
+#include <selinux/android.h>
+#include <selinux/label.h>
+#include <selinux/selinux.h>
 
 #include "action.h"
 #include "bootchart.h"
-#include "devices.h"
 #include "init.h"
 #include "init_parser.h"
-#include "log.h"
 #include "property_service.h"
 #include "reboot.h"
 #include "service.h"
diff --git a/init/descriptors.cpp b/init/descriptors.cpp
index 6e457cd..bc6bc8d 100644
--- a/init/descriptors.cpp
+++ b/init/descriptors.cpp
@@ -19,16 +19,15 @@
 #include <ctype.h>
 #include <fcntl.h>
 #include <sys/stat.h>
-#include <sys/types.h>
 #include <unistd.h>
 
+#include <android-base/logging.h>
 #include <android-base/stringprintf.h>
 #include <android-base/unique_fd.h>
 #include <cutils/android_get_control_file.h>
 #include <cutils/sockets.h>
 
 #include "init.h"
-#include "log.h"
 #include "util.h"
 
 DescriptorInfo::DescriptorInfo(const std::string& name, const std::string& type, uid_t uid,
diff --git a/init/devices.cpp b/init/devices.cpp
index 405f92e..760e0f5 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -19,41 +19,37 @@
 #include <fcntl.h>
 #include <fnmatch.h>
 #include <libgen.h>
+#include <linux/netlink.h>
 #include <stddef.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/sendfile.h>
 #include <sys/socket.h>
-#include <sys/stat.h>
 #include <sys/time.h>
 #include <sys/types.h>
 #include <sys/un.h>
 #include <sys/wait.h>
 #include <unistd.h>
 
-#include <linux/netlink.h>
-
 #include <memory>
 #include <thread>
 
-#include <selinux/selinux.h>
-#include <selinux/label.h>
-#include <selinux/android.h>
-#include <selinux/avc.h>
-
-#include <private/android_filesystem_config.h>
-
 #include <android-base/file.h>
+#include <android-base/logging.h>
 #include <android-base/stringprintf.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>
+#include <selinux/avc.h>
+#include <selinux/label.h>
+#include <selinux/selinux.h>
 
 #include "devices.h"
 #include "ueventd_parser.h"
 #include "util.h"
-#include "log.h"
 
 #define SYSFS_PREFIX    "/sys"
 static const char *firmware_dirs[] = { "/etc/firmware",
@@ -286,8 +282,7 @@
     }
 }
 
-static void add_platform_device(const char *path)
-{
+void add_platform_device(const char* path) {
     int path_len = strlen(path);
     struct platform_node *bus;
     const char *name = path;
@@ -329,8 +324,7 @@
     return NULL;
 }
 
-static void remove_platform_device(const char *path)
-{
+void remove_platform_device(const char* path) {
     struct listnode *node;
     struct platform_node *bus;
 
@@ -473,8 +467,7 @@
     }
 }
 
-static char **get_character_device_symlinks(struct uevent *uevent)
-{
+char** get_character_device_symlinks(struct uevent* uevent) {
     const char *parent;
     const char *slash;
     char **links;
@@ -526,8 +519,24 @@
     return NULL;
 }
 
-static char **get_block_device_symlinks(struct uevent *uevent)
-{
+// replaces any unacceptable characters with '_', the
+// length of the resulting string is equal to the input string
+void sanitize_partition_name(char* s) {
+    const char* accept =
+        "abcdefghijklmnopqrstuvwxyz"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+        "0123456789"
+        "_-.";
+
+    if (!s) return;
+
+    while (*s) {
+        s += strspn(s, accept);
+        if (*s) *s++ = '_';
+    }
+}
+
+char** get_block_device_symlinks(struct uevent* uevent) {
     const char *device;
     struct platform_node *pdev;
     const char *slash;
@@ -562,7 +571,7 @@
 
     if (uevent->partition_name) {
         p = strdup(uevent->partition_name);
-        sanitize(p);
+        sanitize_partition_name(p);
         if (strcmp(uevent->partition_name, p)) {
             LOG(VERBOSE) << "Linking partition '" << uevent->partition_name << "' as '" << p << "'";
         }
diff --git a/init/devices.h b/init/devices.h
index 26a064b..abf1e7c 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -17,9 +17,10 @@
 #ifndef _INIT_DEVICES_H
 #define _INIT_DEVICES_H
 
-#include <functional>
 #include <sys/stat.h>
 
+#include <functional>
+
 enum coldboot_action_t {
     // coldboot continues without creating the device for the uevent
     COLDBOOT_CONTINUE = 0,
@@ -55,4 +56,11 @@
                          unsigned short wildcard);
 int get_device_fd();
 
-#endif	/* _INIT_DEVICES_H */
+// Exposed for testing
+void add_platform_device(const char* path);
+void remove_platform_device(const char* path);
+char** get_character_device_symlinks(uevent* uevent);
+char** get_block_device_symlinks(struct uevent* uevent);
+void sanitize_partition_name(char* s);
+
+#endif /* _INIT_DEVICES_H */
diff --git a/init/devices_test.cpp b/init/devices_test.cpp
new file mode 100644
index 0000000..f79c96d
--- /dev/null
+++ b/init/devices_test.cpp
@@ -0,0 +1,270 @@
+/*
+ * 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 "devices.h"
+
+#include <string>
+#include <vector>
+
+#include <android-base/scopeguard.h>
+#include <gtest/gtest.h>
+
+template <char** (*Function)(uevent*)>
+void test_get_symlinks(const std::string& platform_device_name, uevent* uevent,
+                       const std::vector<std::string> expected_links) {
+    add_platform_device(platform_device_name.c_str());
+    auto platform_device_remover = android::base::make_scope_guard(
+        [&platform_device_name]() { remove_platform_device(platform_device_name.c_str()); });
+
+    char** result = Function(uevent);
+    auto result_freer = android::base::make_scope_guard([result]() {
+        if (result) {
+            for (int i = 0; result[i]; i++) {
+                free(result[i]);
+            }
+            free(result);
+        }
+    });
+
+    auto expected_size = expected_links.size();
+    if (expected_size == 0) {
+        ASSERT_EQ(nullptr, result);
+    } else {
+        ASSERT_NE(nullptr, result);
+        // First assert size is equal, so we don't overrun expected_links
+        unsigned int size = 0;
+        while (result[size]) ++size;
+        ASSERT_EQ(expected_size, size);
+
+        for (unsigned int i = 0; i < size; ++i) {
+            EXPECT_EQ(expected_links[i], result[i]);
+        }
+    }
+}
+
+TEST(devices, get_character_device_symlinks_success) {
+    const char* platform_device = "/devices/platform/some_device_name";
+    uevent uevent = {
+        .path = "/devices/platform/some_device_name/usb/usb_device/name/tty2-1:1.0",
+        .subsystem = "tty",
+    };
+    std::vector<std::string> expected_result{"/dev/usb/ttyname"};
+
+    test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_character_device_symlinks_no_pdev_match) {
+    const char* platform_device = "/devices/platform/some_device_name";
+    uevent uevent = {
+        .path = "/device/name/tty2-1:1.0", .subsystem = "tty",
+    };
+    std::vector<std::string> expected_result;
+
+    test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_character_device_symlinks_nothing_after_platform_device) {
+    const char* platform_device = "/devices/platform/some_device_name";
+    uevent uevent = {
+        .path = "/devices/platform/some_device_name", .subsystem = "tty",
+    };
+    std::vector<std::string> expected_result;
+
+    test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_character_device_symlinks_no_usb_found) {
+    const char* platform_device = "/devices/platform/some_device_name";
+    uevent uevent = {
+        .path = "/devices/platform/some_device_name/bad/bad/", .subsystem = "tty",
+    };
+    std::vector<std::string> expected_result;
+
+    test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_character_device_symlinks_no_roothub) {
+    const char* platform_device = "/devices/platform/some_device_name";
+    uevent uevent = {
+        .path = "/devices/platform/some_device_name/usb/", .subsystem = "tty",
+    };
+    std::vector<std::string> expected_result;
+
+    test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_character_device_symlinks_no_usb_device) {
+    const char* platform_device = "/devices/platform/some_device_name";
+    uevent uevent = {
+        .path = "/devices/platform/some_device_name/usb/usb_device/", .subsystem = "tty",
+    };
+    std::vector<std::string> expected_result;
+
+    test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_character_device_symlinks_no_final_slash) {
+    const char* platform_device = "/devices/platform/some_device_name";
+    uevent uevent = {
+        .path = "/devices/platform/some_device_name/usb/usb_device/name", .subsystem = "tty",
+    };
+    std::vector<std::string> expected_result;
+
+    test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_character_device_symlinks_no_final_name) {
+    const char* platform_device = "/devices/platform/some_device_name";
+    uevent uevent = {
+        .path = "/devices/platform/some_device_name/usb/usb_device//", .subsystem = "tty",
+    };
+    std::vector<std::string> expected_result;
+
+    test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_block_device_symlinks_success_platform) {
+    // These are actual paths from bullhead
+    const char* platform_device = "/devices/soc.0/f9824900.sdhci";
+    uevent uevent = {
+        .path = "/devices/soc.0/f9824900.sdhci/mmc_host/mmc0/mmc0:0001/block/mmcblk0",
+        .partition_name = nullptr,
+        .partition_num = -1,
+    };
+    std::vector<std::string> expected_result{"/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0"};
+
+    test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_block_device_symlinks_success_platform_with_partition) {
+    // These are actual paths from bullhead
+    const char* platform_device = "/devices/soc.0/f9824900.sdhci";
+    uevent uevent = {
+        .path = "/devices/soc.0/f9824900.sdhci/mmc_host/mmc0/mmc0:0001/block/mmcblk0p1",
+        .partition_name = "modem",
+        .partition_num = 1,
+    };
+    std::vector<std::string> expected_result{
+        "/dev/block/platform/soc.0/f9824900.sdhci/by-name/modem",
+        "/dev/block/platform/soc.0/f9824900.sdhci/by-num/p1",
+        "/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1",
+    };
+
+    test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_block_device_symlinks_success_platform_with_partition_only_num) {
+    const char* platform_device = "/devices/soc.0/f9824900.sdhci";
+    uevent uevent = {
+        .path = "/devices/soc.0/f9824900.sdhci/mmc_host/mmc0/mmc0:0001/block/mmcblk0p1",
+        .partition_name = nullptr,
+        .partition_num = 1,
+    };
+    std::vector<std::string> expected_result{
+        "/dev/block/platform/soc.0/f9824900.sdhci/by-num/p1",
+        "/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1",
+    };
+
+    test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_block_device_symlinks_success_platform_with_partition_only_name) {
+    const char* platform_device = "/devices/soc.0/f9824900.sdhci";
+    uevent uevent = {
+        .path = "/devices/soc.0/f9824900.sdhci/mmc_host/mmc0/mmc0:0001/block/mmcblk0p1",
+        .partition_name = "modem",
+        .partition_num = -1,
+    };
+    std::vector<std::string> expected_result{
+        "/dev/block/platform/soc.0/f9824900.sdhci/by-name/modem",
+        "/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1",
+    };
+
+    test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_block_device_symlinks_success_pci) {
+    const char* platform_device = "/devices/do/not/match";
+    uevent uevent = {
+        .path = "/devices/pci0000:00/0000:00:1f.2/mmcblk0",
+        .partition_name = nullptr,
+        .partition_num = -1,
+    };
+    std::vector<std::string> expected_result{"/dev/block/pci/pci0000:00/0000:00:1f.2/mmcblk0"};
+
+    test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_block_device_symlinks_success_vbd) {
+    const char* platform_device = "/devices/do/not/match";
+    uevent uevent = {
+        .path = "/devices/vbd-1234/mmcblk0", .partition_name = nullptr, .partition_num = -1,
+    };
+    std::vector<std::string> expected_result{"/dev/block/vbd/1234/mmcblk0"};
+
+    test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_block_device_symlinks_no_matches) {
+    const char* platform_device = "/devices/soc.0/f9824900.sdhci";
+    uevent uevent = {
+        .path = "/devices/soc.0/not_the_device/mmc_host/mmc0/mmc0:0001/block/mmcblk0p1",
+        .partition_name = nullptr,
+        .partition_num = -1,
+    };
+    std::vector<std::string> expected_result;
+
+    test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, sanitize_null) {
+    sanitize_partition_name(nullptr);
+}
+
+TEST(devices, sanitize_empty) {
+    std::string empty;
+    sanitize_partition_name(&empty[0]);
+    EXPECT_EQ(0u, empty.size());
+}
+
+TEST(devices, sanitize_allgood) {
+    std::string good =
+        "abcdefghijklmnopqrstuvwxyz"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+        "0123456789"
+        "_-.";
+    std::string good_copy = good;
+    sanitize_partition_name(&good[0]);
+    EXPECT_EQ(good_copy, good);
+}
+
+TEST(devices, sanitize_somebad) {
+    std::string string = "abc!@#$%^&*()";
+    sanitize_partition_name(&string[0]);
+    EXPECT_EQ("abc__________", string);
+}
+
+TEST(devices, sanitize_allbad) {
+    std::string string = "!@#$%^&*()";
+    sanitize_partition_name(&string[0]);
+    EXPECT_EQ("__________", string);
+}
+
+TEST(devices, sanitize_onebad) {
+    std::string string = ")";
+    sanitize_partition_name(&string[0]);
+    EXPECT_EQ("_", string);
+}
diff --git a/init/import_parser.cpp b/init/import_parser.cpp
index d52247b..8a2bcc2 100644
--- a/init/import_parser.cpp
+++ b/init/import_parser.cpp
@@ -16,12 +16,8 @@
 
 #include "import_parser.h"
 
-#include "errno.h"
+#include <android-base/logging.h>
 
-#include <string>
-#include <vector>
-
-#include "log.h"
 #include "util.h"
 
 bool ImportParser::ParseSection(const std::vector<std::string>& args,
diff --git a/init/init.cpp b/init/init.cpp
index e14034f..023d727 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "init.h"
+
 #include <ctype.h>
 #include <dirent.h>
 #include <errno.h>
@@ -42,6 +44,7 @@
 
 #include <android-base/chrono_utils.h>
 #include <android-base/file.h>
+#include <android-base/logging.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
@@ -59,7 +62,6 @@
 #include "devices.h"
 #include "fs_mgr.h"
 #include "import_parser.h"
-#include "init.h"
 #include "init_parser.h"
 #include "keychords.h"
 #include "log.h"
@@ -716,14 +718,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)) {
@@ -795,7 +801,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/init/init_parser.cpp b/init/init_parser.cpp
index a192862..53e670b 100644
--- a/init/init_parser.cpp
+++ b/init/init_parser.cpp
@@ -14,18 +14,17 @@
  * limitations under the License.
  */
 
+#include "init_parser.h"
+
 #include <dirent.h>
-#include <errno.h>
 #include <fcntl.h>
 
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+
 #include "action.h"
-#include "init_parser.h"
-#include "log.h"
 #include "parser.h"
 #include "service.h"
-#include "util.h"
-
-#include <android-base/stringprintf.h>
 
 Parser::Parser() {
 }
diff --git a/init/init_parser.h b/init/init_parser.h
index f66ba52..6935fdf 100644
--- a/init/init_parser.h
+++ b/init/init_parser.h
@@ -18,6 +18,7 @@
 #define _INIT_INIT_PARSER_H_
 
 #include <map>
+#include <memory>
 #include <string>
 #include <vector>
 
diff --git a/init/init_parser_test.cpp b/init/init_parser_test.cpp
index 52aaa37..d8fd2ba 100644
--- a/init/init_parser_test.cpp
+++ b/init/init_parser_test.cpp
@@ -18,9 +18,7 @@
 
 #include "init.h"
 #include "service.h"
-#include "util.h"
 
-#include <errno.h>
 #include <gtest/gtest.h>
 
 #include <string>
diff --git a/init/keychords.cpp b/init/keychords.cpp
index 5801ea8..c572cee 100644
--- a/init/keychords.cpp
+++ b/init/keychords.cpp
@@ -14,19 +14,17 @@
  * limitations under the License.
  */
 
-#include <errno.h>
 #include <fcntl.h>
 #include <stdlib.h>
-#include <string.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <linux/keychord.h>
 #include <unistd.h>
 
+#include <android-base/logging.h>
 #include <android-base/properties.h>
 
 #include "init.h"
-#include "log.h"
 #include "service.h"
 
 static struct input_keychord *keychords = 0;
diff --git a/init/log.cpp b/init/log.cpp
index 6b32526..ee6489b 100644
--- a/init/log.cpp
+++ b/init/log.cpp
@@ -17,9 +17,10 @@
 #include "log.h"
 
 #include <fcntl.h>
+#include <linux/audit.h>
 #include <string.h>
 
-#include <linux/audit.h>
+#include <android-base/logging.h>
 #include <netlink/netlink.h>
 #include <selinux/selinux.h>
 
diff --git a/init/log.h b/init/log.h
index 8fa6d74..29a27af 100644
--- a/init/log.h
+++ b/init/log.h
@@ -17,7 +17,7 @@
 #ifndef _INIT_LOG_H_
 #define _INIT_LOG_H_
 
-#include <android-base/logging.h>
+#include <sys/cdefs.h>
 
 void InitKernelLogging(char* argv[]);
 
diff --git a/init/parser.cpp b/init/parser.cpp
index 45862b7..5953a88 100644
--- a/init/parser.cpp
+++ b/init/parser.cpp
@@ -4,7 +4,7 @@
 #include <stdio.h>
 #include <string.h>
 
-#include "log.h"
+#include <android-base/logging.h>
 
 void parse_error(struct parse_state *state, const char *fmt, ...)
 {
diff --git a/init/property_service.cpp b/init/property_service.cpp
index a4d8b5f..9857223 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -14,46 +14,44 @@
  * limitations under the License.
  */
 
+#include "property_service.h"
+
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
 #include <inttypes.h>
+#include <limits.h>
+#include <netinet/in.h>
+#include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <unistd.h>
 #include <string.h>
-#include <ctype.h>
-#include <fcntl.h>
-#include <stdarg.h>
-#include <dirent.h>
-#include <limits.h>
-#include <errno.h>
+#include <sys/mman.h>
 #include <sys/poll.h>
-
-#include <memory>
-#include <vector>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <unistd.h>
 
 #define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
 #include <sys/_system_properties.h>
 
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <sys/select.h>
-#include <sys/types.h>
-#include <netinet/in.h>
-#include <sys/mman.h>
+#include <memory>
+#include <vector>
 
-#include <selinux/android.h>
-#include <selinux/selinux.h>
-#include <selinux/label.h>
-
-#include <fs_mgr.h>
 #include <android-base/file.h>
+#include <android-base/logging.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
-#include "bootimg.h"
+#include <bootimg.h>
+#include <fs_mgr.h>
+#include <selinux/android.h>
+#include <selinux/label.h>
+#include <selinux/selinux.h>
 
-#include "property_service.h"
 #include "init.h"
 #include "util.h"
-#include "log.h"
 
 using android::base::StringPrintf;
 
diff --git a/init/property_service.h b/init/property_service.h
index 994da63..585cd0f 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -20,6 +20,7 @@
 #include <stddef.h>
 #include <sys/socket.h>
 #include <sys/system_properties.h>
+
 #include <string>
 
 struct property_audit_data {
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 62e5c85..53bdeb1 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -13,6 +13,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+#include "reboot.h"
+
 #include <dirent.h>
 #include <fcntl.h>
 #include <mntent.h>
@@ -27,11 +30,11 @@
 
 #include <memory>
 #include <set>
-#include <string>
 #include <thread>
 #include <vector>
 
 #include <android-base/file.h>
+#include <android-base/logging.h>
 #include <android-base/macros.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
@@ -41,11 +44,8 @@
 #include <fs_mgr.h>
 #include <logwrap/logwrap.h>
 
-#include "log.h"
 #include "property_service.h"
-#include "reboot.h"
 #include "service.h"
-#include "util.h"
 
 using android::base::StringPrintf;
 
diff --git a/init/reboot.h b/init/reboot.h
index 3956249..6432fa5 100644
--- a/init/reboot.h
+++ b/init/reboot.h
@@ -17,6 +17,8 @@
 #ifndef _INIT_REBOOT_H
 #define _INIT_REBOOT_H
 
+#include <string>
+
 /* Reboot / shutdown the system.
  * cmd ANDROID_RB_* as defined in android_reboot.h
  * reason Reason string like "reboot", "userrequested"
diff --git a/init/service.cpp b/init/service.cpp
index e89de9a..8cb8dc1 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -25,27 +25,21 @@
 #include <sys/resource.h>
 #include <sys/stat.h>
 #include <sys/time.h>
-#include <sys/types.h>
 #include <sys/wait.h>
 #include <termios.h>
 #include <unistd.h>
 
-#include <selinux/selinux.h>
-
-#include <android-base/chrono_utils.h>
 #include <android-base/file.h>
+#include <android-base/logging.h>
 #include <android-base/parseint.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
+#include <processgroup/processgroup.h>
+#include <selinux/selinux.h>
 #include <system/thread_defs.h>
 
-#include <processgroup/processgroup.h>
-
-#include "action.h"
 #include "init.h"
-#include "init_parser.h"
-#include "log.h"
 #include "property_service.h"
 #include "util.h"
 
diff --git a/init/service.h b/init/service.h
index d84ce02..5e89b9f 100644
--- a/init/service.h
+++ b/init/service.h
@@ -19,14 +19,13 @@
 
 #include <sys/types.h>
 
-#include <cutils/iosched_policy.h>
-
 #include <memory>
 #include <set>
 #include <string>
 #include <vector>
 
 #include <android-base/chrono_utils.h>
+#include <cutils/iosched_policy.h>
 
 #include "action.h"
 #include "capabilities.h"
diff --git a/init/signal_handler.cpp b/init/signal_handler.cpp
index 5e3acac..4d56d84 100644
--- a/init/signal_handler.cpp
+++ b/init/signal_handler.cpp
@@ -14,22 +14,17 @@
  * limitations under the License.
  */
 
-#include <errno.h>
-#include <fcntl.h>
 #include <signal.h>
-#include <stdio.h>
+#include <string.h>
 #include <sys/socket.h>
 #include <sys/types.h>
-#include <sys/wait.h>
 #include <unistd.h>
 
+#include <android-base/logging.h>
 #include <android-base/stringprintf.h>
 
-#include "action.h"
 #include "init.h"
-#include "log.h"
 #include "service.h"
-#include "util.h"
 
 static int signal_write_fd = -1;
 static int signal_read_fd = -1;
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index ba53e47..ea767a8 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "ueventd.h"
+
 #include <ctype.h>
 #include <fcntl.h>
 #include <grp.h>
@@ -24,17 +26,15 @@
 #include <stdlib.h>
 #include <string.h>
 
-#include <sys/types.h>
-
+#include <android-base/logging.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <selinux/selinux.h>
 
-#include "ueventd.h"
-#include "log.h"
-#include "util.h"
 #include "devices.h"
+#include "log.h"
 #include "ueventd_parser.h"
+#include "util.h"
 
 int ueventd_main(int argc, char **argv)
 {
diff --git a/init/ueventd.h b/init/ueventd.h
index d12d7fe..d44d1ca 100644
--- a/init/ueventd.h
+++ b/init/ueventd.h
@@ -17,9 +17,10 @@
 #ifndef _INIT_UEVENTD_H_
 #define _INIT_UEVENTD_H_
 
-#include <cutils/list.h>
 #include <sys/types.h>
 
+#include <cutils/list.h>
+
 enum devname_src_t {
     DEVNAME_UNKNOWN = 0,
     DEVNAME_UEVENT_DEVNAME,
diff --git a/init/ueventd_parser.cpp b/init/ueventd_parser.cpp
index 554c1e3..510d7bb 100644
--- a/init/ueventd_parser.cpp
+++ b/init/ueventd_parser.cpp
@@ -14,18 +14,18 @@
  * limitations under the License.
  */
 
+#include "ueventd_parser.h"
+
 #include <ctype.h>
-#include <errno.h>
-#include <stdio.h>
-#include <unistd.h>
 #include <stdarg.h>
+#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <unistd.h>
 
-#include "ueventd.h"
-#include "ueventd_parser.h"
+#include <android-base/logging.h>
+
 #include "parser.h"
-#include "log.h"
 #include "util.h"
 
 static list_declare(subsystem_list);
diff --git a/init/util.cpp b/init/util.cpp
index bf4109c..bbc59cb 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -14,26 +14,21 @@
  * limitations under the License.
  */
 
+#include "util.h"
+
 #include <ctype.h>
 #include <errno.h>
 #include <fcntl.h>
-#include <ftw.h>
 #include <pwd.h>
 #include <stdarg.h>
-#include <stdlib.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
+#include <sys/socket.h>
+#include <sys/un.h>
 #include <time.h>
 #include <unistd.h>
 
-#include <selinux/android.h>
-#include <selinux/label.h>
-
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/un.h>
-
 #include <thread>
 
 #include <android-base/file.h>
@@ -42,15 +37,13 @@
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
-
 #include <cutils/android_reboot.h>
-/* for ANDROID_SOCKET_* */
 #include <cutils/sockets.h>
+#include <selinux/android.h>
+#include <selinux/label.h>
 
 #include "init.h"
-#include "log.h"
 #include "reboot.h"
-#include "util.h"
 
 using android::base::boot_clock;
 
@@ -235,27 +228,6 @@
     return 0;
 }
 
-/*
- * replaces any unacceptable characters with '_', the
- * length of the resulting string is equal to the input string
- */
-void sanitize(char *s)
-{
-    const char* accept =
-            "abcdefghijklmnopqrstuvwxyz"
-            "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
-            "0123456789"
-            "_-.";
-
-    if (!s)
-        return;
-
-    while (*s) {
-        s += strspn(s, accept);
-        if (*s) *s++ = '_';
-    }
-}
-
 int wait_for_file(const char* filename, std::chrono::nanoseconds timeout) {
     boot_clock::time_point timeout_time = boot_clock::now() + timeout;
     while (boot_clock::now() < timeout_time) {
diff --git a/init/util.h b/init/util.h
index 1034c9b..38a7bdb 100644
--- a/init/util.h
+++ b/init/util.h
@@ -61,7 +61,6 @@
 unsigned int decode_uid(const char *s);
 
 int mkdir_recursive(const char *pathname, mode_t mode);
-void sanitize(char *p);
 int wait_for_file(const char *filename, std::chrono::nanoseconds timeout);
 void import_kernel_cmdline(bool in_qemu,
                            const std::function<void(const std::string&, const std::string&, bool)>&);
diff --git a/init/watchdogd.cpp b/init/watchdogd.cpp
index b196147..21c1e5b 100644
--- a/init/watchdogd.cpp
+++ b/init/watchdogd.cpp
@@ -16,11 +16,12 @@
 
 #include <errno.h>
 #include <fcntl.h>
+#include <linux/watchdog.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
-#include <linux/watchdog.h>
+#include <android-base/logging.h>
 
 #include "log.h"
 #include "util.h"
diff --git a/libcutils/fs_config.c b/libcutils/fs_config.c
index daa9ff5..5fc2386 100644
--- a/libcutils/fs_config.c
+++ b/libcutils/fs_config.c
@@ -227,21 +227,24 @@
     /* clang-format on */
 };
 
+static size_t strip(const char* path, size_t len, const char suffix[]) {
+    if (len < strlen(suffix)) return len;
+    if (strncmp(path + len - strlen(suffix), suffix, strlen(suffix))) return len;
+    return len - strlen(suffix);
+}
+
 static int fs_config_open(int dir, int which, const char* target_out_path) {
     int fd = -1;
 
     if (target_out_path && *target_out_path) {
         /* target_out_path is the path to the directory holding content of
-         * system partition but as we cannot guaranty it ends with '/system'
-         * we need this below skip_len logic */
+         * system partition but as we cannot guarantee it ends with '/system'
+         * or with or without a trailing slash, need to strip them carefully. */
         char* name = NULL;
-        int target_out_path_len = strlen(target_out_path);
-        int skip_len = strlen("/system");
-
-        if (target_out_path[target_out_path_len] == '/') {
-            skip_len++;
-        }
-        if (asprintf(&name, "%s%s", target_out_path, conf[which][dir] + skip_len) != -1) {
+        size_t len = strlen(target_out_path);
+        len = strip(target_out_path, len, "/");
+        len = strip(target_out_path, len, "/system");
+        if (asprintf(&name, "%.*s%s", (int)len, target_out_path, conf[which][dir]) != -1) {
             fd = TEMP_FAILURE_RETRY(open(name, O_RDONLY | O_BINARY));
             free(name);
         }
diff --git a/liblog/event_tag_map.cpp b/liblog/event_tag_map.cpp
index bdad2c2..0b977c2 100644
--- a/liblog/event_tag_map.cpp
+++ b/liblog/event_tag_map.cpp
@@ -445,7 +445,7 @@
           mmap(NULL, end[which], which ? PROT_READ : PROT_READ | PROT_WRITE,
                which ? MAP_SHARED : MAP_PRIVATE, fd[which], 0);
       save_errno = errno;
-      close(fd[which]);
+      close(fd[which]); /* fd DONE */
       fd[which] = -1;
       if ((newTagMap->mapAddr[which] != MAP_FAILED) &&
           (newTagMap->mapAddr[which] != NULL)) {
@@ -465,6 +465,7 @@
       delete newTagMap;
       return NULL;
     }
+    /* See 'fd DONE' comments above and below, no need to clean up here */
   }
 
   return newTagMap;
@@ -473,7 +474,7 @@
   save_errno = EINVAL;
   delete newTagMap;
 fail_close:
-  for (which = 0; which < NUM_MAPS; ++which) close(fd[which]);
+  for (which = 0; which < NUM_MAPS; ++which) close(fd[which]); /* fd DONE */
 fail_errno:
   errno = save_errno;
   return NULL;
diff --git a/liblog/properties.c b/liblog/properties.c
index 0b0ef52..adf1900 100644
--- a/liblog/properties.c
+++ b/liblog/properties.c
@@ -95,7 +95,7 @@
   /* calculate the size of our key temporary buffer */
   const size_t taglen = tag ? len : 0;
   /* sizeof(log_namespace) = strlen(log_namespace) + 1 */
-  char key[sizeof(log_namespace) + taglen]; /* may be > PROP_NAME_MAX */
+  char key[sizeof(log_namespace) + taglen];
   char* kp;
   size_t i;
   char c = 0;
@@ -108,7 +108,8 @@
    * Where the missing tag matches all tags and becomes the
    * system global default. We do not support ro.log.tag* .
    */
-  static char last_tag[PROP_NAME_MAX];
+  static char* last_tag;
+  static size_t last_tag_len;
   static uint32_t global_serial;
   /* some compilers erroneously see uninitialized use. !not_locked */
   uint32_t current_global_serial = 0;
@@ -147,25 +148,29 @@
   if (taglen) {
     int local_change_detected = change_detected;
     if (!not_locked) {
-      if (!last_tag[0] || (last_tag[0] != tag[0]) ||
-          strncmp(
-              last_tag + 1, tag + 1,
-              (len < sizeof(last_tag)) ? (len - 1) : (sizeof(last_tag) - 1)) ||
-          ((len < sizeof(last_tag)) && last_tag[len])) {
+      if (!last_tag || !last_tag[0] || (last_tag[0] != tag[0]) ||
+          strncmp(last_tag + 1, tag + 1, last_tag_len - 1)) {
         /* invalidate log.tag.<tag> cache */
         for (i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) {
           tag_cache[i].cache.pinfo = NULL;
           tag_cache[i].c = '\0';
         }
-        last_tag[0] = '\0';
+        if (last_tag) last_tag[0] = '\0';
         local_change_detected = 1;
       }
-      if (!last_tag[0]) {
-        if (len < sizeof(last_tag)) {
+      if (!last_tag || !last_tag[0]) {
+        if (!last_tag) {
+          last_tag = calloc(1, len + 1);
+          last_tag_len = 0;
+          if (last_tag) last_tag_len = len + 1;
+        } else if (len >= last_tag_len) {
+          last_tag = realloc(last_tag, len + 1);
+          last_tag_len = 0;
+          if (last_tag) last_tag_len = len + 1;
+        }
+        if (last_tag) {
           strncpy(last_tag, tag, len);
           last_tag[len] = '\0';
-        } else {
-          strncpy(last_tag, tag, sizeof(last_tag));
         }
       }
     }
@@ -435,7 +440,7 @@
                                                            int flag) {
   struct cache_property property = { { NULL, -1 }, { 0 } };
   if (flag & BOOL_DEFAULT_FLAG_PERSIST) {
-    char newkey[PROP_NAME_MAX];
+    char newkey[strlen("persist.") + strlen(key) + 1];
     snprintf(newkey, sizeof(newkey), "ro.%s", key);
     refresh_cache_property(&property, newkey);
     property.cache.pinfo = NULL;
@@ -600,8 +605,8 @@
     evaluate_property_get_size
     /* clang-format on */
   };
-  char key_persist[PROP_NAME_MAX];
-  char key_ro[PROP_NAME_MAX];
+  char key_persist[strlen(global_tunable) + strlen(".security") + 1];
+  char key_ro[strlen(global_default) + strlen(".security") + 1];
   struct cache2_property_size local = {
     /* clang-format off */
     PTHREAD_MUTEX_INITIALIZER, 0,
diff --git a/libsystem/Android.bp b/libsystem/Android.bp
new file mode 100644
index 0000000..4d076d5
--- /dev/null
+++ b/libsystem/Android.bp
@@ -0,0 +1,4 @@
+cc_library_headers {
+    name: "libsystem_headers",
+    export_include_dirs: ["include"],
+}
diff --git a/include/system/camera.h b/libsystem/include/system/camera.h
similarity index 100%
rename from include/system/camera.h
rename to libsystem/include/system/camera.h
diff --git a/include/system/graphics.h b/libsystem/include/system/graphics.h
similarity index 100%
rename from include/system/graphics.h
rename to libsystem/include/system/graphics.h
diff --git a/include/system/qemu_pipe.h b/libsystem/include/system/qemu_pipe.h
similarity index 100%
rename from include/system/qemu_pipe.h
rename to libsystem/include/system/qemu_pipe.h
diff --git a/include/system/radio.h b/libsystem/include/system/radio.h
similarity index 100%
rename from include/system/radio.h
rename to libsystem/include/system/radio.h
diff --git a/include/system/thread_defs.h b/libsystem/include/system/thread_defs.h
similarity index 100%
rename from include/system/thread_defs.h
rename to libsystem/include/system/thread_defs.h
diff --git a/include/system/window.h b/libsystem/include/system/window.h
similarity index 100%
rename from include/system/window.h
rename to libsystem/include/system/window.h
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index ee646de..e35593f 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -54,6 +54,7 @@
         "ElfInterfaceArm.cpp",
         "Log.cpp",
         "Regs.cpp",
+        "Maps.cpp",
         "Memory.cpp",
         "Symbols.cpp",
     ],
@@ -97,6 +98,7 @@
         "tests/ElfInterfaceTest.cpp",
         "tests/ElfTest.cpp",
         "tests/LogFake.cpp",
+        "tests/MapsTest.cpp",
         "tests/MemoryFake.cpp",
         "tests/MemoryFileTest.cpp",
         "tests/MemoryLocalTest.cpp",
diff --git a/libunwindstack/Maps.cpp b/libunwindstack/Maps.cpp
new file mode 100644
index 0000000..b369c43
--- /dev/null
+++ b/libunwindstack/Maps.cpp
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2016 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 <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/unique_fd.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "Maps.h"
+
+MapInfo* Maps::Find(uint64_t pc) {
+  if (maps_.empty()) {
+    return nullptr;
+  }
+  size_t first = 0;
+  size_t last = maps_.size();
+  while (first < last) {
+    size_t index = (first + last) / 2;
+    MapInfo* cur = &maps_[index];
+    if (pc >= cur->start && pc < cur->end) {
+      return cur;
+    } else if (pc < cur->start) {
+      last = index;
+    } else {
+      first = index + 1;
+    }
+  }
+  return nullptr;
+}
+
+bool Maps::ParseLine(const char* line, MapInfo* map_info) {
+  char permissions[5];
+  int name_pos;
+  // Linux /proc/<pid>/maps lines:
+  // 6f000000-6f01e000 rwxp 00000000 00:0c 16389419   /system/lib/libcomposer.so
+  if (sscanf(line, "%" SCNx64 "-%" SCNx64 " %4s %" SCNx64 " %*x:%*x %*d %n", &map_info->start,
+             &map_info->end, permissions, &map_info->offset, &name_pos) != 4) {
+    return false;
+  }
+  map_info->flags = PROT_NONE;
+  if (permissions[0] == 'r') {
+    map_info->flags |= PROT_READ;
+  }
+  if (permissions[1] == 'w') {
+    map_info->flags |= PROT_WRITE;
+  }
+  if (permissions[2] == 'x') {
+    map_info->flags |= PROT_EXEC;
+  }
+
+  map_info->name = &line[name_pos];
+  size_t length = map_info->name.length() - 1;
+  if (map_info->name[length] == '\n') {
+    map_info->name.erase(length);
+  }
+  // Mark a device map in /dev/and not in /dev/ashmem/ specially.
+  if (!map_info->name.empty() && map_info->name.substr(0, 5) == "/dev/" &&
+      map_info->name.substr(5, 7) != "ashmem/") {
+    map_info->flags |= MAPS_FLAGS_DEVICE_MAP;
+  }
+
+  return true;
+}
+
+bool Maps::Parse() {
+  std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(GetMapsFile().c_str(), "re"), fclose);
+  if (!fp) {
+    return false;
+  }
+
+  bool valid = true;
+  char* line = nullptr;
+  size_t line_len;
+  while (getline(&line, &line_len, fp.get()) > 0) {
+    MapInfo map_info;
+    if (!ParseLine(line, &map_info)) {
+      valid = false;
+      break;
+    }
+
+    maps_.push_back(map_info);
+  }
+  free(line);
+
+  return valid;
+}
+
+Maps::~Maps() {
+  for (auto& map : maps_) {
+    delete map.elf;
+    map.elf = nullptr;
+  }
+}
+
+bool BufferMaps::Parse() {
+  const char* start_of_line = buffer_;
+  do {
+    std::string line;
+    const char* end_of_line = strchr(start_of_line, '\n');
+    if (end_of_line == nullptr) {
+      line = start_of_line;
+    } else {
+      end_of_line++;
+      line = std::string(start_of_line, end_of_line - start_of_line);
+    }
+
+    MapInfo map_info;
+    if (!ParseLine(line.c_str(), &map_info)) {
+      return false;
+    }
+    maps_.push_back(map_info);
+
+    start_of_line = end_of_line;
+  } while (start_of_line != nullptr && *start_of_line != '\0');
+  return true;
+}
+
+const std::string RemoteMaps::GetMapsFile() const {
+  return "/proc/" + std::to_string(pid_) + "/maps";
+}
+
+bool OfflineMaps::Parse() {
+  // Format of maps information:
+  //   <uint64_t> StartOffset
+  //   <uint64_t> EndOffset
+  //   <uint64_t> offset
+  //   <uint16_t> flags
+  //   <uint16_t> MapNameLength
+  //   <VariableLengthValue> MapName
+  android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(file_.c_str(), O_RDONLY)));
+  if (fd == -1) {
+    return false;
+  }
+
+  std::vector<char> name;
+  while (true) {
+    MapInfo map_info;
+    ssize_t bytes = TEMP_FAILURE_RETRY(read(fd, &map_info.start, sizeof(map_info.start)));
+    if (bytes == 0) {
+      break;
+    }
+    if (bytes == -1 || bytes != sizeof(map_info.start)) {
+      return false;
+    }
+    bytes = TEMP_FAILURE_RETRY(read(fd, &map_info.end, sizeof(map_info.end)));
+    if (bytes == -1 || bytes != sizeof(map_info.end)) {
+      return false;
+    }
+    bytes = TEMP_FAILURE_RETRY(read(fd, &map_info.offset, sizeof(map_info.offset)));
+    if (bytes == -1 || bytes != sizeof(map_info.offset)) {
+      return false;
+    }
+    bytes = TEMP_FAILURE_RETRY(read(fd, &map_info.flags, sizeof(map_info.flags)));
+    if (bytes == -1 || bytes != sizeof(map_info.flags)) {
+      return false;
+    }
+    uint16_t len;
+    bytes = TEMP_FAILURE_RETRY(read(fd, &len, sizeof(len)));
+    if (bytes == -1 || bytes != sizeof(len)) {
+      return false;
+    }
+    if (len > 0) {
+      name.resize(len);
+      bytes = TEMP_FAILURE_RETRY(read(fd, name.data(), len));
+      if (bytes == -1 || bytes != len) {
+        return false;
+      }
+      map_info.name = std::string(name.data(), len);
+    }
+    maps_.push_back(map_info);
+  }
+  return true;
+}
diff --git a/libunwindstack/Maps.h b/libunwindstack/Maps.h
new file mode 100644
index 0000000..239b64a
--- /dev/null
+++ b/libunwindstack/Maps.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef _LIBUNWINDSTACK_MAPS_H
+#define _LIBUNWINDSTACK_MAPS_H
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+#include "Elf.h"
+#include "MapInfo.h"
+
+// Special flag to indicate a map is in /dev/. However, a map in
+// /dev/ashmem/... does not set this flag.
+static constexpr int MAPS_FLAGS_DEVICE_MAP = 0x8000;
+
+class Maps {
+ public:
+  Maps() = default;
+  virtual ~Maps();
+
+  MapInfo* Find(uint64_t pc);
+
+  bool ParseLine(const char* line, MapInfo* map_info);
+
+  virtual bool Parse();
+
+  virtual const std::string GetMapsFile() const { return ""; }
+
+  typedef std::vector<MapInfo>::iterator iterator;
+  iterator begin() { return maps_.begin(); }
+  iterator end() { return maps_.end(); }
+
+  typedef std::vector<MapInfo>::const_iterator const_iterator;
+  const_iterator begin() const { return maps_.begin(); }
+  const_iterator end() const { return maps_.end(); }
+
+  size_t Total() { return maps_.size(); }
+
+ protected:
+  std::vector<MapInfo> maps_;
+};
+
+class RemoteMaps : public Maps {
+ public:
+  RemoteMaps(pid_t pid) : pid_(pid) {}
+  virtual ~RemoteMaps() = default;
+
+  virtual const std::string GetMapsFile() const override;
+
+ private:
+  pid_t pid_;
+};
+
+class LocalMaps : public RemoteMaps {
+ public:
+  LocalMaps() : RemoteMaps(getpid()) {}
+  virtual ~LocalMaps() = default;
+};
+
+class BufferMaps : public Maps {
+ public:
+  BufferMaps(const char* buffer) : buffer_(buffer) {}
+  virtual ~BufferMaps() = default;
+
+  bool Parse() override;
+
+ private:
+  const char* buffer_;
+};
+
+class FileMaps : public Maps {
+ public:
+  FileMaps(const std::string& file) : file_(file) {}
+  virtual ~FileMaps() = default;
+
+  const std::string GetMapsFile() const override { return file_; }
+
+ protected:
+  const std::string file_;
+};
+
+class OfflineMaps : public FileMaps {
+ public:
+  OfflineMaps(const std::string& file) : FileMaps(file) {}
+  virtual ~OfflineMaps() = default;
+
+  bool Parse() override;
+};
+
+#endif  // _LIBUNWINDSTACK_MAPS_H
diff --git a/libunwindstack/tests/MapsTest.cpp b/libunwindstack/tests/MapsTest.cpp
new file mode 100644
index 0000000..7eb9bae
--- /dev/null
+++ b/libunwindstack/tests/MapsTest.cpp
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2016 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 <sys/mman.h>
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+
+#include "Maps.h"
+
+TEST(MapsTest, parse_permissions) {
+  BufferMaps maps(
+      "1000-2000 ---- 00000000 00:00 0\n"
+      "2000-3000 r--- 00000000 00:00 0\n"
+      "3000-4000 -w-- 00000000 00:00 0\n"
+      "4000-5000 --x- 00000000 00:00 0\n"
+      "5000-6000 rwx- 00000000 00:00 0\n");
+
+  ASSERT_TRUE(maps.Parse());
+  ASSERT_EQ(5U, maps.Total());
+  auto it = maps.begin();
+  ASSERT_EQ(PROT_NONE, it->flags);
+  ASSERT_EQ(0x1000U, it->start);
+  ASSERT_EQ(0x2000U, it->end);
+  ASSERT_EQ(0U, it->offset);
+  ASSERT_EQ("", it->name);
+  ++it;
+  ASSERT_EQ(PROT_READ, it->flags);
+  ASSERT_EQ(0x2000U, it->start);
+  ASSERT_EQ(0x3000U, it->end);
+  ASSERT_EQ(0U, it->offset);
+  ASSERT_EQ("", it->name);
+  ++it;
+  ASSERT_EQ(PROT_WRITE, it->flags);
+  ASSERT_EQ(0x3000U, it->start);
+  ASSERT_EQ(0x4000U, it->end);
+  ASSERT_EQ(0U, it->offset);
+  ASSERT_EQ("", it->name);
+  ++it;
+  ASSERT_EQ(PROT_EXEC, it->flags);
+  ASSERT_EQ(0x4000U, it->start);
+  ASSERT_EQ(0x5000U, it->end);
+  ASSERT_EQ(0U, it->offset);
+  ASSERT_EQ("", it->name);
+  ++it;
+  ASSERT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, it->flags);
+  ASSERT_EQ(0x5000U, it->start);
+  ASSERT_EQ(0x6000U, it->end);
+  ASSERT_EQ(0U, it->offset);
+  ASSERT_EQ("", it->name);
+  ++it;
+  ASSERT_EQ(it, maps.end());
+}
+
+TEST(MapsTest, parse_name) {
+  BufferMaps maps(
+      "720b29b000-720b29e000 rw-p 00000000 00:00 0\n"
+      "720b29e000-720b29f000 rw-p 00000000 00:00 0 /system/lib/fake.so\n"
+      "720b29f000-720b2a0000 rw-p 00000000 00:00 0");
+
+  ASSERT_TRUE(maps.Parse());
+  ASSERT_EQ(3U, maps.Total());
+  auto it = maps.begin();
+  ASSERT_EQ("", it->name);
+  ASSERT_EQ(0x720b29b000U, it->start);
+  ASSERT_EQ(0x720b29e000U, it->end);
+  ASSERT_EQ(0U, it->offset);
+  ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags);
+  ++it;
+  ASSERT_EQ("/system/lib/fake.so", it->name);
+  ASSERT_EQ(0x720b29e000U, it->start);
+  ASSERT_EQ(0x720b29f000U, it->end);
+  ASSERT_EQ(0U, it->offset);
+  ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags);
+  ++it;
+  ASSERT_EQ("", it->name);
+  ASSERT_EQ(0x720b29f000U, it->start);
+  ASSERT_EQ(0x720b2a0000U, it->end);
+  ASSERT_EQ(0U, it->offset);
+  ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags);
+  ++it;
+  ASSERT_EQ(it, maps.end());
+}
+
+TEST(MapsTest, parse_offset) {
+  BufferMaps maps(
+      "a000-e000 rw-p 00000000 00:00 0 /system/lib/fake.so\n"
+      "e000-f000 rw-p 00a12345 00:00 0 /system/lib/fake.so\n");
+
+  ASSERT_TRUE(maps.Parse());
+  ASSERT_EQ(2U, maps.Total());
+  auto it = maps.begin();
+  ASSERT_EQ(0U, it->offset);
+  ASSERT_EQ(0xa000U, it->start);
+  ASSERT_EQ(0xe000U, it->end);
+  ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags);
+  ASSERT_EQ("/system/lib/fake.so", it->name);
+  ++it;
+  ASSERT_EQ(0xa12345U, it->offset);
+  ASSERT_EQ(0xe000U, it->start);
+  ASSERT_EQ(0xf000U, it->end);
+  ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags);
+  ASSERT_EQ("/system/lib/fake.so", it->name);
+  ++it;
+  ASSERT_EQ(maps.end(), it);
+}
+
+TEST(MapsTest, device) {
+  BufferMaps maps(
+      "a000-e000 rw-p 00000000 00:00 0 /dev/\n"
+      "f000-f100 rw-p 00000000 00:00 0 /dev/does_not_exist\n"
+      "f100-f200 rw-p 00000000 00:00 0 /dev/ashmem/does_not_exist\n"
+      "f200-f300 rw-p 00000000 00:00 0 /devsomething/does_not_exist\n");
+
+  ASSERT_TRUE(maps.Parse());
+  ASSERT_EQ(4U, maps.Total());
+  auto it = maps.begin();
+  ASSERT_TRUE(it->flags & 0x8000);
+  ASSERT_EQ("/dev/", it->name);
+  ++it;
+  ASSERT_TRUE(it->flags & 0x8000);
+  ASSERT_EQ("/dev/does_not_exist", it->name);
+  ++it;
+  ASSERT_FALSE(it->flags & 0x8000);
+  ASSERT_EQ("/dev/ashmem/does_not_exist", it->name);
+  ++it;
+  ASSERT_FALSE(it->flags & 0x8000);
+  ASSERT_EQ("/devsomething/does_not_exist", it->name);
+}
+
+TEST(MapsTest, file_smoke) {
+  TemporaryFile tf;
+  ASSERT_TRUE(tf.fd != -1);
+
+  ASSERT_TRUE(
+      android::base::WriteStringToFile("720b29b000-720b29e000 r-xp a0000000 00:00 0   /fake.so\n"
+                                       "720b2b0000-720b2e0000 r-xp b0000000 00:00 0   /fake2.so\n"
+                                       "720b2e0000-720b2f0000 r-xp c0000000 00:00 0   /fake3.so\n",
+                                       tf.path, 0660, getuid(), getgid()));
+
+  FileMaps maps(tf.path);
+
+  ASSERT_TRUE(maps.Parse());
+  ASSERT_EQ(3U, maps.Total());
+  auto it = maps.begin();
+  ASSERT_EQ(0x720b29b000U, it->start);
+  ASSERT_EQ(0x720b29e000U, it->end);
+  ASSERT_EQ(0xa0000000U, it->offset);
+  ASSERT_EQ(PROT_READ | PROT_EXEC, it->flags);
+  ASSERT_EQ("/fake.so", it->name);
+  ++it;
+  ASSERT_EQ(0x720b2b0000U, it->start);
+  ASSERT_EQ(0x720b2e0000U, it->end);
+  ASSERT_EQ(0xb0000000U, it->offset);
+  ASSERT_EQ(PROT_READ | PROT_EXEC, it->flags);
+  ASSERT_EQ("/fake2.so", it->name);
+  ++it;
+  ASSERT_EQ(0x720b2e0000U, it->start);
+  ASSERT_EQ(0x720b2f0000U, it->end);
+  ASSERT_EQ(0xc0000000U, it->offset);
+  ASSERT_EQ(PROT_READ | PROT_EXEC, it->flags);
+  ASSERT_EQ("/fake3.so", it->name);
+  ++it;
+  ASSERT_EQ(it, maps.end());
+}
+
+TEST(MapsTest, find) {
+  BufferMaps maps(
+      "1000-2000 r--p 00000010 00:00 0 /system/lib/fake1.so\n"
+      "3000-4000 -w-p 00000020 00:00 0 /system/lib/fake2.so\n"
+      "6000-8000 --xp 00000030 00:00 0 /system/lib/fake3.so\n"
+      "a000-b000 rw-p 00000040 00:00 0 /system/lib/fake4.so\n"
+      "e000-f000 rwxp 00000050 00:00 0 /system/lib/fake5.so\n");
+  ASSERT_TRUE(maps.Parse());
+  ASSERT_EQ(5U, maps.Total());
+
+  ASSERT_TRUE(maps.Find(0x500) == nullptr);
+  ASSERT_TRUE(maps.Find(0x2000) == nullptr);
+  ASSERT_TRUE(maps.Find(0x5010) == nullptr);
+  ASSERT_TRUE(maps.Find(0x9a00) == nullptr);
+  ASSERT_TRUE(maps.Find(0xf000) == nullptr);
+  ASSERT_TRUE(maps.Find(0xf010) == nullptr);
+
+  MapInfo* info = maps.Find(0x1000);
+  ASSERT_TRUE(info != nullptr);
+  ASSERT_EQ(0x1000U, info->start);
+  ASSERT_EQ(0x2000U, info->end);
+  ASSERT_EQ(0x10U, info->offset);
+  ASSERT_EQ(PROT_READ, info->flags);
+  ASSERT_EQ("/system/lib/fake1.so", info->name);
+
+  info = maps.Find(0x3020);
+  ASSERT_TRUE(info != nullptr);
+  ASSERT_EQ(0x3000U, info->start);
+  ASSERT_EQ(0x4000U, info->end);
+  ASSERT_EQ(0x20U, info->offset);
+  ASSERT_EQ(PROT_WRITE, info->flags);
+  ASSERT_EQ("/system/lib/fake2.so", info->name);
+
+  info = maps.Find(0x6020);
+  ASSERT_TRUE(info != nullptr);
+  ASSERT_EQ(0x6000U, info->start);
+  ASSERT_EQ(0x8000U, info->end);
+  ASSERT_EQ(0x30U, info->offset);
+  ASSERT_EQ(PROT_EXEC, info->flags);
+  ASSERT_EQ("/system/lib/fake3.so", info->name);
+
+  info = maps.Find(0xafff);
+  ASSERT_TRUE(info != nullptr);
+  ASSERT_EQ(0xa000U, info->start);
+  ASSERT_EQ(0xb000U, info->end);
+  ASSERT_EQ(0x40U, info->offset);
+  ASSERT_EQ(PROT_READ | PROT_WRITE, info->flags);
+  ASSERT_EQ("/system/lib/fake4.so", info->name);
+
+  info = maps.Find(0xe500);
+  ASSERT_TRUE(info != nullptr);
+  ASSERT_EQ(0xe000U, info->start);
+  ASSERT_EQ(0xf000U, info->end);
+  ASSERT_EQ(0x50U, info->offset);
+  ASSERT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, info->flags);
+  ASSERT_EQ("/system/lib/fake5.so", info->name);
+}
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/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
index d0101ed..88cb67a 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/tests/logd_test.cpp
@@ -999,16 +999,18 @@
     }
 
     // We may have DAC, but let's not have MAC
-    if (setcon("u:object_r:shell:s0") < 0) {
+    if ((setcon("u:object_r:shell:s0") < 0) && (setcon("u:r:shell:s0") < 0)) {
         int save_errno = errno;
         security_context_t context;
         getcon(&context);
-        fprintf(stderr, "setcon(\"u:r:shell:s0\") failed @\"%s\" %s\n", context,
-                strerror(save_errno));
-        freecon(context);
-        _exit(-1);
-        // NOTREACHED
-        return 0;
+        if (strcmp(context, "u:r:shell:s0")) {
+            fprintf(stderr, "setcon(\"u:r:shell:s0\") failed @\"%s\" %s\n",
+                    context, strerror(save_errno));
+            freecon(context);
+            _exit(-1);
+            // NOTREACHED
+            return 0;
+        }
     }
 
     // The key here is we are root, but we are in u:r:shell:s0,